תגובות | יצירת כותרת מוגדרת מראש לקובץ תוך כדי השמעה
-
-
@הדיבל-המעופף
מאיפה המדריך הזה? -
-
@הדיבל-המעופף
זה ההגדרות בשלוחהtype=playfile title=שמיעת תגובות say_title_voice=yes control_play2=delete_file delete_file_open=yes control_play7=say_details_message say_details_message=phone,date,time say_details_message_skip_menu=yes control_play*=noop ;; ***DUP*** say_title_voice=yes control_play9=send_api api_link=כתובת הסקריפט api_add_0=token=טוקן api_add_1=secret=%D7%9E%D7%A9%D7%A8%D7%94%20%D7%96%D7%95%20%D7%9B%D7%91%D7%A8%20%D7%90%D7%99%D7%A0%D7%A0%D7%94%20%D7%A8%D7%9C%D7%95%D7%95%D7%A0%D7%98%D7%99%D7%AA control_play0=more_optionsA control_play_moreA0=noop control_play_moreA3=noop control_play_moreA4=noop control_play_moreA5=noop control_play_moreA6=noop control_play_moreA7=noop control_play_moreA8=noop control_play_moreA9=noop control_play_moreA1=say_details_message ;; ***DUP*** say_details_message=phone,date,time ;; ***DUP*** say_details_message_skip_menu=yes -
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 -
שלום! נראה שהשיחה הזו מעניינת אותך, אבל עדיין אין לך חשבון.
נמאס לכם לגלול בין אותם הפוסטים בכל ביקור? כשנרשמים לחשבון, תמיד תחזרו בדיוק למקום שבו הייתם קודם, ותוכלו לבחור לקבל התראות על תגובות חדשות (בין אם במייל, ובין אם בהתראת פוש). תוכלו גם לשמור סימניות ולפרגן ב-upvote לפוסטים כדי להביע הערכה לחברי קהילה אחרים.
בעזרת התרומה שלך, הפוסט הזה יכול להיות אפילו טוב יותר 💗
הרשמה התחברות