תגובות | יצירת כותרת מוגדרת מראש לקובץ תוך כדי השמעה
-
say_title_voice=yes
כי יש אותו כבר למעלה
-
@הדיבל-המעופף
עכשיו הכל ברור (כמעט)
בשלוחה הספציפית הזאת יש לי את ההגדרה הזאת
למעלה, כבר הרבה זמן ואני לא יודע מאיפה היא היגיע
לשלוחה ולא ידעתי מה היא עושה אז לא מחקתי אותה -
זה החדש
/** * Cloudflare Worker: TitleVoiceUploader * ------------------------------------------------------------------ * מתחבר למודול ה-API של ימות המשיח (type=api, control_play=send_api) * במודול "השמעת קבצים" (playfile). זהו הפורט של TitleVoiceUploader.gs * (Google Apps Script) ל-Cloudflare Workers - אותה לוגיקה בדיוק. * * תרחיש: המאזין לוחץ מקש מוגדר תוך כדי השמעת קובץ -> השלוחה שולחת בקשה * אחת (GET או POST, ראו הערה למטה) המכילה: * what - הנתיב המלא של הקובץ המושמע (נשלח אוטומטית ע"י send_api) * token - טוקן ה-API למפתחים (ערך סטטי בשלוחה, api_add) * secret - טקסט הכותרת הממוחשבת עבור הקובץ הזה (ערך סטטי בשלוחה, api_add) * ApiPhone - מספר הטלפון של המתקשר הנוכחי (נשלח אוטומטית כברירת מחדל) * * הרשאה: לפני שמירת הכותרת, ה-Worker קורא את קובץ המטא-דאטה של ההודעה * (לדוגמה 001.txt לצד 001.wav), שמכיל שורה בפורמט: * Record-CustomerDID-0795697842-Phone-0533165488-Date-2026-09-16-00-26-07-Folder-5/3-File-001 * ומוודא שהמספר שמופיע אחרי "Phone-" זהה ל-ApiPhone של הלוחץ על המקש כרגע. * רק אם יש התאמה - הכותרת נשמרת. אחרת מוחזרת הודעת שגיאה. * * פריסה: * 1. wrangler.toml סטנדרטי (name, main = "titleVoiceUploader-worker.js", compatibility_date). * 2. wrangler deploy - כתובת ה-Worker שתתקבל (למשל * https://title-voice-uploader.<subdomain>.workers.dev) היא מה שמצמידים * ל-api_link בשלוחה. * 3. אין צורך במשתני סביבה - הכל (token, secret) מגיע מהשלוחה בכל בקשה, * בדיוק כמו בגרסת ה-Apps Script. * * הערה חשובה מהניסיון בפועל עם Apps Script: טקסט עברי ב-api_add לפעמים * מגיע פגום/ריק כש-ימות שולחת GET (בעיית URL-encoding בצד ימות), בעוד * שב-POST זה עובד תקין. מומלץ להוסיף בשלוחה: api_url_post=yes * (ה-Worker תומך גם ב-GET וגם ב-POST ללא צורך בשינוי קוד). */ const YM_API_BASE = 'https://www.call2all.co.il/ym/api/'; export default { async fetch(request) { const params = await getParams(request); return handleRequest(params); } }; /** מאחד פרמטרים מ-GET (query string) או מ-POST (form body) לאובייקט אחד */ async function getParams(request) { const url = new URL(request.url); const params = {}; for (const [key, value] of url.searchParams.entries()) { params[key] = value; } if (request.method === 'POST') { const contentType = request.headers.get('content-type') || ''; if (contentType.includes('application/json')) { // למשל כשמגיעים דרך שרת מתווך/router שמעביר הלאה כ-JSON const body = await request.json().catch(() => ({})); Object.assign(params, body); } else if (contentType.includes('application/x-www-form-urlencoded') || contentType.includes('multipart/form-data')) { const form = await request.formData(); for (const [key, value] of form.entries()) { params[key] = value; } } else { // fallback: גוף כ-query string רגיל const bodyText = await request.text(); const bodyParams = new URLSearchParams(bodyText); for (const [key, value] of bodyParams.entries()) { params[key] = value; } } } return params; } async function handleRequest(p) { const token = p.token; const titleText = p.secret; // הפרמטר שמכיל את טקסט הכותרת const what = p.what; const callerPhone = p.ApiPhone; if (!token || !what || !titleText) { return ymText('id_list_message=t-חסרים נתונים בבקשה, אנא בדקו את הגדרות השלוחה'); } try { const lastSlash = what.lastIndexOf('/'); const folder = what.substring(0, lastSlash); const fileNumber = what.substring(lastSlash + 1).replace(/\.[^.]+$/, ''); const allowed = await isAllowedToTag(folder, fileNumber, callerPhone, token); if (!allowed) { return ymText('id_list_message=t-רק מי שהשאיר את ההודעה רשאי להוסיף לה כותרת'); } await saveTitle(folder, fileNumber, titleText, token); return ymText('id_list_message=t-הכותרת נשמרה בהצלחה'); } catch (err) { return ymText('id_list_message=t-אירעה שגיאה בשמירת הכותרת'); } } /** * בודק שהמספר המחייג הנוכחי הוא זה שהשאיר את ההודעה, לפי קובץ המטא-דאטה * (לדוגמה 001.txt), שמכיל שורה כמו: * Record-CustomerDID-0795697842-Phone-0533165488-Date-2026-09-16-00-26-07-Folder-5/3-File-001 */ async function isAllowedToTag(folder, fileNumber, callerPhone, token) { if (!callerPhone) return false; const metaPath = folder + '/' + fileNumber + '.txt'; const meta = await getTextFile(metaPath, token); if (!meta) return false; // אין קובץ מטא-דאטה - אי אפשר לאמת, אוסרים const m = meta.match(/Phone-(\d+)-/); if (!m) return false; const recordedPhone = m[1]; return normalizePhone(recordedPhone) === normalizePhone(callerPhone); } function normalizePhone(phone) { // משווים לפי 9 הספרות האחרונות, כדי להתעלם מהבדלי אפס מוביל/קידומת const digits = String(phone).replace(/\D/g, ''); return digits.slice(-9); } /** שומר/מעדכן שורה בקובץ TitleVoice.ini שבאותה שלוחה: 001=<הכותרת> */ async function saveTitle(folder, fileNumber, titleText, token) { const iniPath = folder + '/TitleVoice.ini'; const current = await getTextFile(iniPath, token); // '' אם הקובץ עדיין לא קיים const updated = setIniValue(current, fileNumber, titleText); await uploadTextFile(iniPath, updated, token); } async function getTextFile(path, token) { const url = YM_API_BASE + 'GetTextFile?token=' + encodeURIComponent(token) + '&what=' + encodeURIComponent(path); const res = await fetch(url); const data = await res.json(); if (data.responseStatus === 'OK' && typeof data.contents === 'string') { return data.contents; } return ''; // הקובץ לא קיים } async function uploadTextFile(path, contents, token) { const body = new URLSearchParams({ token, what: path, contents }); const res = await fetch(YM_API_BASE + 'UploadTextFile', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString() }); const data = await res.json(); if (data.responseStatus !== 'OK') { throw new Error(data.message || 'UploadTextFile failed'); } } /** מעדכן/מוסיף שורה מסוג key=value בתוך טקסט ini, ושומר על שאר השורות כפי שהן */ function setIniValue(iniText, key, value) { const lines = iniText ? iniText.split(/\r?\n/) : []; let found = false; for (let i = 0; i < lines.length; i++) { const m = lines[i].match(/^\s*([^=]+?)\s*=\s*(.*)$/); if (m && m[1] === key) { lines[i] = key + '=' + value; found = true; break; } } if (!found) { while (lines.length && lines[lines.length - 1].trim() === '') lines.pop(); lines.push(key + '=' + value); } return lines.join('\n') + '\n'; } function ymText(str) { return new Response(str, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }); } -
@מסובך-מאוד
זה מה ששלחתי לך? -
הai אומר לי להוסיף את זה
api_url_post=yes -
-
משהו יכול להריץ לי זה חסום לי אני בנפרי

-
@הדיבל-המעופף
באיזה שפת קוד קלאודפלייר משתמש? -
@מסובך-מאוד
טעות השפה הקלאסית היא JavaScript -
-
@הדיבל-המעופף
וחוץ מהקלאסית?
שלום! נראה שהשיחה הזו מעניינת אותך, אבל עדיין אין לך חשבון.
נמאס לכם לגלול בין אותם הפוסטים בכל ביקור? כשנרשמים לחשבון, תמיד תחזרו בדיוק למקום שבו הייתם קודם, ותוכלו לבחור לקבל התראות על תגובות חדשות (בין אם במייל, ובין אם בהתראת פוש). תוכלו גם לשמור סימניות ולפרגן ב-upvote לפוסטים כדי להביע הערכה לחברי קהילה אחרים.
בעזרת התרומה שלך, הפוסט הזה יכול להיות אפילו טוב יותר 💗
הרשמה התחברות