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

    הקלטת השיחה בלבד במודול תור

    מתוזמן נעוץ נעול הועבר עזרה הדדית למשתמשים מתקדמים
    18 פוסטים 3 כותבים 43 צפיות 2 עוקבים
    טוען פוסטים נוספים
    • מהישן לחדש
    • מהחדש לישן
    • הכי הרבה הצבעות
    תגובה
    • תגובה כנושא
    התחברו כדי לפרסם תגובה
    נושא זה נמחק. רק משתמשים עם הרשאות מתאימות יוכלו לצפות בו.
    • C מחובר
      CUBASE @BEN ZION
      נערך לאחרונה על ידי

      @BEN-ZION זה לא הבעיה, השאלה אם אפשר בגוגל סקריפט להוריד קובץ לזיכרון

      B תגובה 1 תגובה אחרונה תגובה ציטוט 0
      • B מחובר
        BEN ZION @CUBASE
        נערך לאחרונה על ידי

        @CUBASE תירגמתי אותו לסקירפט גוגל עובד מעולה
        משתמש באחסון של גוגל דרייב
        יש בעיה שגוגל יכול לרוץ עד 6 דקות אני הגדרתי לו שכל פעם הוא סורק את כל הלוג לוקח את נתיב ההקלטה וחותך את זמן ההמתנה למענה ושיחות שהוא טיפל בהם הוא שומר בקובץ בדרייב רק שמות וככה הוא יודע לא לערוך אותם שוב אבל עדיין זה יכול לקחת זמן
        אני לא יודע מה מגיע במייל אם ההקלטה אבל צריך להגדיר לו שיקח משם את נתיב ההקלטה אם הוא מופיע ואז יחפש בלוג רק את זמן ההמתנה למענה וככה כל מייל נכנס הוא יעבוד על שיחה אחת
        זה הקוד

        // --- הגדרות מערכת ---
        const TOKEN = "כאן להזין טוקן";
        const LOG_PATH = "ivr2:Log/LogQueueOK.2026-03-30.ymgr";
        const API_URL = "https://www.call2all.co.il/ym/api/";
        
        // --- הגדרות דרייב ---
        const DRIVE_FOLDER_NAME = "Yemot_Audio_Processing"; // שם התיקייה שתיווצר בדרייב
        const JSON_FILENAME = "processed_calls.json";
        
        function processYmCalls() {
          Logger.log("--- תחילת ריצת מערכת ---");
          
          // השגת או יצירת תיקיית העבודה בדרייב
          let workFolder = getOrCreateDriveFolder(DRIVE_FOLDER_NAME);
          
          // טעינת רשימת השיחות שכבר טופלו
          let processedPaths = getProcessedCalls(workFolder);
          
          Logger.log("שולף את קובץ הלוג: " + LOG_PATH);
          
          // --- שלב 1: קריאת הלוג מימות המשיח ---
          let logRes = getYmApi("RenderYMGRFile", {
            token: TOKEN,
            wath: LOG_PATH,
            convertType: 'json',
            notLoadLang: 1
          });
          
          let data = JSON.parse(logRes.getContentText());
          
          if (!data || !data.data) {
            Logger.log("שגיאה: לא נמצאו נתונים בלוג או שהטוקן/נתיב שגויים.");
            return;
          }
          
          Logger.log("קובץ הלוג נקרא בהצלחה, מתחיל סריקת רשומות...");
          
          // --- שלב 2: עיבוד הרשומות ---
          for (let i = 0; i < data.data.length; i++) {
            let row = data.data[i];
            
            // בודק אם זו שיחה שנענתה ויש נתיב להקלטה
            if (row.QueueStatus === 'ANSWER' && row.QueueRecordPath) {
              let remotePath = row.QueueRecordPath;
              let waitSeconds = parseInt(row.QueueWaitingSeconds || 0);
              
              // בדיקה האם הקובץ כבר טופל
              if (processedPaths.indexOf(remotePath) !== -1) {
                continue; // מדלג
              }
              
              if (waitSeconds > 0) {
                Logger.log("זוהתה שיחה חדשה. נתיב: " + remotePath + " | זמן חיתוך: " + waitSeconds + " שניות.");
                
                // הורדת הקובץ
                Logger.log("מוריד את הקובץ...");
                let fileResponse = getYmApi("DownloadFile", { token: TOKEN, path: remotePath });
                let originalBlob = fileResponse.getBlob();
                
                if (originalBlob.getBytes().length > 44) {
                  originalBlob.setName("orig_" + waitSeconds + ".wav");
                  
                  // שמירת הקובץ המקורי בדרייב לצורך ניתוח
                  let origDriveFile = workFolder.createFile(originalBlob);
                  Logger.log("הקובץ המקורי נשמר בדרייב לניתוח.");
                  
                  // ביצוע החיתוך
                  Logger.log("מתחיל חיתוך...");
                  let trimmedBlob = smartTrimWavGas(origDriveFile.getBlob(), waitSeconds);
                  
                  if (trimmedBlob) {
                    // שמירת הקובץ החתוך בדרייב
                    let trimDriveFile = workFolder.createFile(trimmedBlob);
                    
                    // יצירת הנתיב החדש
                    let pathParts = remotePath.split('/');
                    let fileName = pathParts.pop();
                    let newRemotePath = pathParts.join('/') + '/חתוכים/' + fileName;
                    
                    Logger.log("מעלה קובץ חדש: " + newRemotePath);
                    
                    // העלאה חזרה לימות המשיח
                    let uploadRes = UrlFetchApp.fetch(API_URL + "UploadFile", {
                      method: 'post',
                      payload: {
                        token: TOKEN,
                        path: newRemotePath,
                        file: trimDriveFile.getBlob()
                      },
                      muteHttpExceptions: true
                    });
                    
                    let uploadData = JSON.parse(uploadRes.getContentText());
                    if (uploadData.responseStatus === 'OK') {
                      Logger.log("הקובץ הועלה בהצלחה.");
                      
                      // עדכון ה-JSON
                      processedPaths.push(remotePath);
                      saveProcessedCalls(workFolder, processedPaths);
                    } else {
                      Logger.log("שגיאה בהעלאה: " + uploadRes.getContentText());
                    }
                    
                    // ניקוי הקובץ החתוך מהדרייב (העברה לאשפה)
                    trimDriveFile.setTrashed(true);
                  } else {
                    Logger.log("שגיאה בחיתוך הקובץ.");
                  }
                  
                  // ניקוי הקובץ המקורי מהדרייב
                  origDriveFile.setTrashed(true);
                  
                } else {
                  Logger.log("שגיאה: הקובץ ריק או קצר מדי.");
                }
              } else {
                // זמן המתנה 0 - נסמן כטופל ונדלג
                Logger.log("שיחה בנתיב " + remotePath + " ללא זמן המתנה. מסמן כטופל ומדלג.");
                processedPaths.push(remotePath);
                saveProcessedCalls(workFolder, processedPaths);
              }
              Logger.log("--------------------------------------------------");
            }
          }
          Logger.log("--- הריצה הסתיימה בהצלחה ---");
        }
        
        // ================= פונקציות עזר =================
        
        /**
         * פונקציה לתקשורת API בסיסית
         */
        function getYmApi(endpoint, params) {
          let url = API_URL + endpoint;
          let options = {
            method: 'post',
            payload: params,
            muteHttpExceptions: true
          };
          return UrlFetchApp.fetch(url, options);
        }
        
        /**
         * פונקציה להשגת תיקיית העבודה בדרייב או יצירתה במידה ואינה קיימת
         */
        function getOrCreateDriveFolder(folderName) {
          let folders = DriveApp.getFoldersByName(folderName);
          if (folders.hasNext()) {
            return folders.next();
          } else {
            return DriveApp.createFolder(folderName);
          }
        }
        
        /**
         * קריאת רשימת השיחות שטופלו מתוך ה-JSON בדרייב
         */
        function getProcessedCalls(folder) {
          let files = folder.getFilesByName(JSON_FILENAME);
          if (files.hasNext()) {
            let file = files.next();
            let content = file.getBlob().getDataAsString();
            try {
              return JSON.parse(content || "[]");
            } catch (e) {
              return [];
            }
          }
          return [];
        }
        
        /**
         * שמירת רשימת השיחות המעודכנת לקובץ ה-JSON בדרייב
         */
        function saveProcessedCalls(folder, data) {
          let files = folder.getFilesByName(JSON_FILENAME);
          let content = JSON.stringify(data);
          if (files.hasNext()) {
            files.next().setContent(content);
          } else {
            folder.createFile(JSON_FILENAME, content, MimeType.PLAIN_TEXT);
          }
        }
        
        /**
         * פונקציה לחיתוך קובץ WAV בגוגל סקריפט ברמת ה-Bytes (ללא תוספים)
         */
        function smartTrimWavGas(sourceBlob, secondsToSkip) {
          let gasBytes = sourceBlob.getBytes();
          
          // המרת הבתים ממערך חתום (של GAS) למערך לא-חתום (של JS) לצורך קריאת משתנים מדויקת
          let buffer = new ArrayBuffer(gasBytes.length);
          let uint8View = new Uint8Array(buffer);
          for (let i = 0; i < gasBytes.length; i++) {
            uint8View[i] = gasBytes[i] < 0 ? gasBytes[i] + 256 : gasBytes[i];
          }
          
          let dataView = new DataView(buffer);
          
          // שליפת ByteRate ו-BlockAlign מה-Header
          let byteRate = dataView.getUint32(28, true); // little-endian
          let blockAlign = dataView.getUint16(32, true); // little-endian
          
          let bytesToSkip = Math.floor((secondsToSkip * byteRate) / blockAlign) * blockAlign;
          let dataStart = 44 + bytesToSkip;
          
          if (dataStart >= gasBytes.length) {
            return null; // זמן החיתוך ארוך מהקובץ
          }
          
          let newDataLength = gasBytes.length - dataStart;
          
          // בניית קובץ חדש בזיכרון
          let newBuffer = new ArrayBuffer(44 + newDataLength);
          let newUint8View = new Uint8Array(newBuffer);
          let newDataView = new DataView(newBuffer);
          
          // העתקת ה-Header המקורי
          for (let i = 0; i < 44; i++) {
            newUint8View[i] = uint8View[i];
          }
          
          // העתקת נתוני הקול שנשארו (החל מהנקודה שחתכנו)
          for (let i = 0; i < newDataLength; i++) {
            newUint8View[44 + i] = uint8View[dataStart + i];
          }
          
          // עדכון גדלים ב-Header
          let newSubChunk2Size = newDataLength;
          let newChunkSize = newSubChunk2Size + 36;
          
          newDataView.setUint32(4, newChunkSize, true);
          newDataView.setUint32(40, newSubChunk2Size, true);
          
          // המרה חזרה למערך בתים חתום ש-GAS יודע לעבוד איתו
          let finalGasBytes = [];
          for (let i = 0; i < newUint8View.length; i++) {
            let b = newUint8View[i];
            finalGasBytes.push(b > 127 ? b - 256 : b);
          }
          
          return Utilities.newBlob(finalGasBytes, 'audio/wav', 'trimmed_' + Date.now() + '.wav');
        }
        
        C תגובה 1 תגובה אחרונה תגובה ציטוט 1
        • C מחובר
          CUBASE @BEN ZION
          נערך לאחרונה על ידי CUBASE

          @BEN-ZION תודה, בכל זאת אעדיף ליצור זאת בעצמי תוך בדיקה שהכל עובד כשורה, חשבתי מראש על אחסון בדרייב אבל אני פחות מעדיף, אברר את זה עם ג׳מיני..

          אגב, את הקודים האלו כתבת ב-AI, נכון?
          א"כ באיזה AI?

          B תגובה 1 תגובה אחרונה תגובה ציטוט 0
          • B מחובר
            BEN ZION @CUBASE
            נערך לאחרונה על ידי

            @CUBASE גימני פרו

            א C 2 תגובות תגובה אחרונה תגובה ציטוט 1
            • א מנותק
              אA @BEN ZION
              נערך לאחרונה על ידי

              @BEN-ZION
              מה זה פרו?
              3.1 או בסטודיו?

              B תגובה 1 תגובה אחרונה תגובה ציטוט 0
              • B מחובר
                BEN ZION @אA
                נערך לאחרונה על ידי

                @אA בגימני הרגיל במצב פרו

                תגובה 1 תגובה אחרונה תגובה ציטוט 1
                • C מחובר
                  CUBASE @BEN ZION
                  נערך לאחרונה על ידי

                  @BEN-ZION אני כנ"ל משתמש ב-AI Studio כיון ששם ה-3.1 Pro הוא חינמי ללא הגבלה

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

                    @CUBASE
                    דווקא יש לו מגבלה (או שרק אני הצלחתי להגיע אליה...לא נראה לי😊 )
                    אבל אפשר לעבוד איתו הרבה זמן

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

                      @אA כנראה מעומס הקשר, זו מגבלה טכנית.

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

                        @CUBASE
                        כלומר?
                        שהשיחה ארוכה מידי?

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

                          @אA כן, קרה לי כמה פעמים, מוחקים הודעות לא חשובות, מבקשים ממנו סיכום ומתחילים שיחה חדשה.

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

                            @CUBASE
                            כי הוא כותב הגעת להגבלה

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

                              @אA מה כתוב בדיוק?

                              תגובה 1 תגובה אחרונה תגובה ציטוט 0
                              • פוסט ראשון
                                פוסט אחרון