• דף הבית
    • אינדקס קישורים
    • פוסטים אחרונים
    • משתמשים
    • חיפוש בהגדרות המתקדמות
    • חיפוש גוגל בפורום
    • ניהול המערכת
    • ניהול המערכת - שרת private
    • הרשמה
    • התחברות

    תגובות | יצירת כותרת מוגדרת מראש לקובץ תוך כדי השמעה

    מתוזמן נעוץ נעול הועבר עזרה הדדית למשתמשים מתקדמים
    41 פוסטים 3 כותבים 90 צפיות 1 עוקבים
    טוען פוסטים נוספים
    • מהישן לחדש
    • מהחדש לישן
    • הכי הרבה הצבעות
    תגובה
    • תגובה כנושא
    התחברו כדי לפרסם תגובה
    נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
    • ה מנותק
      הדיבל המעופף @מסובך מאוד
      נערך לאחרונה על ידי הדיבל המעופף

      @מסובך-מאוד
      בלי זה זה לא עובד

      b3294fee-fde8-4f01-a48a-2b9c3526c796-image.jpeg

      מ 2 תגובות תגובה אחרונה תגובה ציטוט 0
      • מ מנותק
        מסובך מאוד @הדיבל המעופף
        נערך לאחרונה על ידי

        @הדיבל-המעופף
        מאיפה המדריך הזה?

        ה תגובה 1 תגובה אחרונה תגובה ציטוט 0
        • ה מנותק
          הדיבל המעופף @מסובך מאוד
          נערך לאחרונה על ידי

          @מסובך-מאוד
          מפה

          תגובה 1 תגובה אחרונה תגובה ציטוט 0
          • מ מנותק
            מסובך מאוד @הדיבל המעופף
            נערך לאחרונה על ידי

            @הדיבל-המעופף
            זה ההגדרות בשלוחה

            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
            
            
            
            
            ה תגובה 1 תגובה אחרונה תגובה ציטוט 0
            • ה מנותק
              הדיבל המעופף @מסובך מאוד
              נערך לאחרונה על ידי

              @מסובך-מאוד כתב:

              say_title_voice=yes

              כי יש אותו כבר למעלה

              מ 2 תגובות תגובה אחרונה תגובה ציטוט 0
              • מ מנותק
                מסובך מאוד @הדיבל המעופף
                נערך לאחרונה על ידי

                @הדיבל-המעופף
                עכשיו הכל ברור (כמעט)
                בשלוחה הספציפית הזאת יש לי את ההגדרה הזאת
                למעלה, כבר הרבה זמן ואני לא יודע מאיפה היא היגיע
                לשלוחה ולא ידעתי מה היא עושה אז לא מחקתי אותה

                מ תגובה 1 תגובה אחרונה תגובה ציטוט 0
                • מ מנותק
                  מסובך מאוד @מסובך מאוד
                  נערך לאחרונה על ידי מסובך מאוד

                  זה החדש

                  /**
                   * 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' }
                    });
                  }
                  
                  ה מ 2 תגובות תגובה אחרונה תגובה ציטוט 0
                  • ה מנותק
                    הדיבל המעופף @מסובך מאוד
                    נערך לאחרונה על ידי

                    @מסובך-מאוד
                    זה מה ששלחתי לך?

                    מ תגובה 1 תגובה אחרונה תגובה ציטוט 0
                    • מ מנותק
                      מסובך מאוד @מסובך מאוד
                      נערך לאחרונה על ידי מסובך מאוד

                      הai אומר לי להוסיף את זה

                      api_url_post=yes
                      
                      תגובה 1 תגובה אחרונה תגובה ציטוט 0
                      • מ מנותק
                        מסובך מאוד @הדיבל המעופף
                        נערך לאחרונה על ידי

                        @הדיבל-המעופף
                        כן

                        תגובה 1 תגובה אחרונה תגובה ציטוט 0
                        • 1 לא נמצא
                          1668
                          נערך לאחרונה על ידי

                          משהו יכול להריץ לי זה חסום לי אני בנפרי😖 😖

                          תגובה 1 תגובה אחרונה תגובה ציטוט 0

                          שלום! נראה שהשיחה הזו מעניינת אותך, אבל עדיין אין לך חשבון.

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

                          בעזרת התרומה שלך, הפוסט הזה יכול להיות אפילו טוב יותר 💗

                          הרשמה התחברות
                          • פוסט ראשון
                            פוסט אחרון