• 1 הצבעות
    11 פוסטים
    156 צפיות
    א
    הבאג טופל. מי שזה עדיין קורה לו בבקשה שישלח פרטי שיחה לשירות לקוחות
  • שמירת נתונים בין קריאות בשלוחות API

    בקשות לפיתוח
    2
    2 הצבעות
    2 פוסטים
    68 צפיות
    א
    יש פה אשכול שלם של בקשות לפיתוח במודל API https://f2.freeivr.co.il/post/145624
  • קמפיין שיגור שיחה וכניסה למערכת

    שאלות ועזרה הדדית
    6
    1 הצבעות
    6 פוסטים
    81 צפיות
    א
    @מוטי-לוין כתב: @שלוחה-404 @אז-נדברו מה ההבדל בין CallExtensionBridging לקמפיין פתוח בשרות הלקוחות בCallExtensionBridging זה עולה יחידה לכל דקה, בקמפיין פתוח זה מוגבל ל2 יחידות למספר.
  • משמיע צליל במעבר בין שלוחות

    שאלות ועזרה הדדית
    5
    0 הצבעות
    5 פוסטים
    29 צפיות
    מ
    @אז-נדברו תודה
  • הקלטות במודול תור

    עזרה הדדית למשתמשים מתקדמים
    25
    0 הצבעות
    25 פוסטים
    236 צפיות
    א
    @שנאור תכנס לגוגל דרייב תעשה "חדש" [image: 1792435825601-f1887310-3865-4912-9f4e-5d527ca8ecdd-image.jpeg] תבחר "עוד" - "גוגל סקריפט" [image: 1792435944196-%D7%A6%D7%99%D7%9C%D7%95%D7%9D-%D7%9E%D7%A1%D7%9A-2026-10-19-215037.png] תבחר יצירת סקריפט, ואז תדביק שם את הקוד הבא **שים לב שיש מקום להכניס את מספר המערכת, את הסיסמה, ואת הנתיב של השלוחה שבה ההקלטות היום, ונתיב שאליו אתה רוצה שזה יעבור, אתה יכול לשים שם מזהה של תקייה בדרייב אם אתה רוצה שזה ישמר גם שם /** * העתקת קבצים מתיקייה בימות המשיח – לתיקייה בימות ו/או לתיקייה בגוגל דרייב * כל קובץ מורד מהמקור (DownloadFile) ומועלה מחדש ליעד (UploadFile) – * ללא שימוש בהעתקה הפנימית של ימות. * ------------------------------------------------------------------------ * הוראות: * 1. ב-script.google.com צור פרויקט חדש והדבק את כל הקוד. * 2. מלא את ההגדרות ב-CONFIG למטה. * 3. הרץ פעם אחת ידנית את הפונקציה copyFiles (כדי לאשר הרשאות ולבדוק). * 4. לתזמון: הרץ פעם אחת את createTrigger (או דרך "טריגרים" בתפריט השמאלי). */ const CONFIG = { // פרטי התחברות למערכת USERNAME: '07XXXXXXXX', PASSWORD: 'XXXXXX', // תיקיית המקור בימות (לדוגמה: '/1/2') SOURCE_PATH: '/1', // מצב: 'yemot' = העתקה לתיקייה בימות | 'drive' = שמירה בדרייב | 'both' = לשניהם MODE: 'both', // יעד בימות (רלוונטי ל-yemot / both). הקבצים יורדו מהמקור ויועלו לכאן מחדש. YEMOT_TARGET_PATH: '/9', // מזהה תיקייה בדרייב (רלוונטי ל-drive / both) // מעתיקים מהכתובת: https://drive.google.com/drive/folders/<המזהה_כאן> DRIVE_FOLDER_ID: 'PUT_DRIVE_FOLDER_ID_HERE', // אילו סיומות להעתיק. מערך ריק [] = כל הקבצים EXTENSIONS: ['wav', 'mp3', 'ogg', 'txt', 'tts'], // כל כמה שעות להריץ (משמש את createTrigger) TRIGGER_EVERY_HOURS: 1 }; const API = 'https://www.call2all.co.il/ym/api/'; const MAX_RUNTIME_MS = 5 * 60 * 1000; // עצירה בטוחה לפני מגבלת 6 הדקות של גוגל const MAX_FILE_BYTES = 50 * 1024 * 1024; // מגבלת UrlFetchApp בגוגל /* ===================== פונקציה ראשית ===================== */ function copyFiles() { const start = Date.now(); const mode = String(CONFIG.MODE).toLowerCase(); const toYemot = mode === 'yemot' || mode === 'both'; const toDrive = mode === 'drive' || mode === 'both'; if (!toYemot && !toDrive) throw new Error('MODE חייב להיות yemot / drive / both'); const token = login_(); try { const sourceDir = normalizePath_(CONFIG.SOURCE_PATH); const files = listFiles_(token, sourceDir); Logger.log('נמצאו ' + files.length + ' קבצים במקור ' + sourceDir); // רשימת קבצים קיימים ביעד – כדי לדלג עליהם let yemotTarget, yemotExisting, driveFolder, driveExisting; if (toYemot) { yemotTarget = normalizePath_(CONFIG.YEMOT_TARGET_PATH); if (yemotTarget === sourceDir) throw new Error('תיקיית היעד בימות זהה לתיקיית המקור'); yemotExisting = new Set(listFiles_(token, yemotTarget, true).map(f => f.name)); } if (toDrive) { driveFolder = DriveApp.getFolderById(CONFIG.DRIVE_FOLDER_ID); driveExisting = new Set(); const it = driveFolder.getFiles(); while (it.hasNext()) driveExisting.add(it.next().getName()); } let copiedY = 0, copiedD = 0, skipped = 0, errors = 0; for (const f of files) { if (Date.now() - start > MAX_RUNTIME_MS) { Logger.log('הגעתי למגבלת זמן – ההמשך יתבצע בהרצה הבאה.'); break; } const srcFile = joinPath_(sourceDir, f.name); const needYemot = toYemot && !yemotExisting.has(f.name); const needDrive = toDrive && !driveExisting.has(f.name); if (!needYemot && !needDrive) { skipped++; continue; } if (f.size && f.size > MAX_FILE_BYTES) { errors++; Logger.log('דילוג על ' + srcFile + ' – גדול מ-50MB (מגבלת גוגל)'); continue; } // 1. הורדה פעם אחת מהמקור let blob; try { blob = downloadFile_(token, srcFile).setName(f.name); } catch (e) { errors++; Logger.log('שגיאה בהורדת ' + srcFile + ': ' + e.message); continue; } // 2. העלאה מחדש לתיקיית היעד בימות if (needYemot) { try { uploadFile_(token, joinPath_(yemotTarget, f.name), blob); yemotExisting.add(f.name); copiedY++; } catch (e) { errors++; Logger.log('שגיאה בהעלאה לימות ' + f.name + ': ' + e.message); } } // 3. שמירה בדרייב if (needDrive) { try { driveFolder.createFile(blob); driveExisting.add(f.name); copiedD++; } catch (e) { errors++; Logger.log('שגיאה בשמירה לדרייב ' + f.name + ': ' + e.message); } } } Logger.log('סיום: הועתקו לימות ' + copiedY + ', נשמרו בדרייב ' + copiedD + ', דולגו (קיימים) ' + skipped + ', שגיאות ' + errors); } finally { logout_(token); } } /* ===================== תזמון ===================== */ function createTrigger() { // מוחק טריגרים קודמים של הפונקציה כדי לא ליצור כפילויות ScriptApp.getProjectTriggers() .filter(t => t.getHandlerFunction() === 'copyFiles') .forEach(t => ScriptApp.deleteTrigger(t)); ScriptApp.newTrigger('copyFiles') .timeBased() .everyHours(CONFIG.TRIGGER_EVERY_HOURS) .create(); Logger.log('נוצר טריגר: כל ' + CONFIG.TRIGGER_EVERY_HOURS + ' שעות'); } function removeTriggers() { ScriptApp.getProjectTriggers() .filter(t => t.getHandlerFunction() === 'copyFiles') .forEach(t => ScriptApp.deleteTrigger(t)); Logger.log('הטריגרים נמחקו'); } /* ===================== פונקציות API ===================== */ function login_() { const data = apiCall_('Login', { username: CONFIG.USERNAME, password: CONFIG.PASSWORD }); if (!data.token) throw new Error('התחברות נכשלה – לא התקבל טוקן'); return data.token; } function logout_(token) { try { apiCall_('Logout', { token: token }); } catch (e) { /* לא קריטי */ } } /** רשימת קבצים בתיקייה (כמו בקובץ ה-PHP: GetIVR2Dir + סינון לפי סיומת) */ function listFiles_(token, dirPath, allowMissing) { const apiPath = dirPath === '/' ? '/' : dirPath + '/'; let data; try { data = apiCall_('GetIVR2Dir', { token: token, path: apiPath }); } catch (e) { if (allowMissing) return []; throw e; } const raw = Array.isArray(data.files) ? data.files : []; const exts = (CONFIG.EXTENSIONS || []).map(x => String(x).toLowerCase()); return raw.filter(f => { if (!f || !f.name) return false; if (exts.length === 0) return true; const ext = f.name.split('.').pop().toLowerCase(); return exts.indexOf(ext) !== -1 || f.fileType === 'AUDIO'; }); } /** העלאת קובץ לימות (UploadFile – POST multipart/form-data) */ function uploadFile_(token, targetPath, blob) { const resp = UrlFetchApp.fetch(API + 'UploadFile', { method: 'post', payload: { // אובייקט עם Blob => נשלח כ-multipart/form-data token: token, path: 'ivr2:' + targetPath.replace(/^\/+/, ''), convertAudio: '0', // שומר את הקובץ כמו שהוא, בלי המרה file: blob }, muteHttpExceptions: true }); let data; try { data = JSON.parse(resp.getContentText()); } catch (e) { throw new Error('UploadFile: תשובה לא תקינה (HTTP ' + resp.getResponseCode() + ')'); } if (data.responseStatus !== 'OK') { throw new Error('UploadFile: ' + (data.message || data.responseStatus) + (data.messageCode ? ' (קוד ' + data.messageCode + ')' : '')); } return data; } /** הורדת קובץ (DownloadFile) כ-Blob */ function downloadFile_(token, filePath) { const url = API + 'DownloadFile?' + toQuery_({ token: token, path: 'ivr2:' + filePath.replace(/^\/+/, '') }); const resp = UrlFetchApp.fetch(url, { muteHttpExceptions: true }); if (resp.getResponseCode() === 404) { throw new Error('הקובץ לא נמצא (404)'); } if (resp.getResponseCode() !== 200) { throw new Error('HTTP ' + resp.getResponseCode()); } // אם חזר JSON של שגיאה במקום הקובץ const ct = String(resp.getHeaders()['Content-Type'] || ''); if (ct.indexOf('application/json') !== -1) { throw new Error('שגיאת API: ' + resp.getContentText()); } return resp.getBlob(); } function apiCall_(method, params) { const resp = UrlFetchApp.fetch(API + method + '?' + toQuery_(params), { muteHttpExceptions: true }); let data; try { data = JSON.parse(resp.getContentText()); } catch (e) { throw new Error(method + ': תשובה לא תקינה (HTTP ' + resp.getResponseCode() + ')'); } if (data.responseStatus !== 'OK') { throw new Error(method + ': ' + (data.message || data.responseStatus || 'שגיאה לא ידועה')); } return data; } /* ===================== עזר ===================== */ function toQuery_(obj) { return Object.keys(obj) .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(obj[k])) .join('&'); } function normalizePath_(p) { const t = String(p || '').replace(/^ivr2:/, '').replace(/^\/+|\/+$/g, ''); return '/' + t; } function joinPath_(dir, name) { return (dir === '/' ? '' : dir) + '/' + name; } אח"כ תשמור, ואז תיצור טריגר [image: 1792436151054-357ef5c0-3427-4637-af4e-29113c74dded-image.jpeg] יש שם אפשרות לבחור כל כמה זמן זה יופעל, זה יבקש ממך אישור לבצע דברים תאשר, ואז כל פעם שזה יופעל זה יעתיק את השלוחות
  • קו שמשויך ולא עובד

    שאלות ועזרה הדדית
    9
    0 הצבעות
    9 פוסטים
    65 צפיות
    א
    @ע.ג. כי אז היה כשל זמני במערכת האוטומטית, זה תוקן.
  • 0 הצבעות
    2 פוסטים
    36 צפיות
    א
    @יפי-נוימן איפה נתקעת?
  • 1 הצבעות
    4 פוסטים
    59 צפיות
    א
    @זעליג אל תדאג, על כזה שינוי דרמטי ימות יעדכנו מספיק זמן מראש.
  • 16 הצבעות
    394 פוסטים
    23k צפיות
    פ
    @זעליג כתב: רעיון: לעשות כמו שיש - מי האזין להודעה, כך יהיה - מי מרשימת התפוצה לא האזין. הוספתי את זה להצעות לפיתוח נראה כמה יצביעו על זה אם זה שימושי אז אפתח כזה דבר [image: 1792417859232-f6b51c44-5144-40d3-9955-7c51202d9ba2-image.jpeg] להצבעה לחצו כאן https://ivr.pitronai.com/#/whats-new/ideas/30
  • פילטר לפי מספר טלפון

    בקשות לפיתוח
    4
    0 הצבעות
    4 פוסטים
    52 צפיות
    מ
    @יעקב-יצחק זה של @איל-משולש?
  • לייק / דסלייק - ושכלולים נוספים!! 👍🎀🏅✨

    לא נפתר בקשות לפיתוח
    227
    25 הצבעות
    227 פוסטים
    15k צפיות
    מ
    @פתרון-AI הגבתי לך
  • 4 הצבעות
    5 פוסטים
    94 צפיות
    L
    @אז-נדברו כנראה היה כי לא לקחתי את ההגדרות ממקום אחר בטוח!!! וגם מי זה המודל AI הטיפש שנתן את ההגדרה כך?! למה טיפש כל עוד זה עבד זה מצויין מעכשיו זה בעיה לא כך?
  • 0 הצבעות
    2 פוסטים
    33 צפיות
    L
    @יעקב-יצחק אין לי את ההקלטה אבל אם אתה צריך סתם הקלטה כזו אז אני אעלה לך לכאן בל"נ להלן בשלוחה זו עדיין אין השמעות בשלוחה זו עדיין אין השמעות.mp3
  • הודעה בשיחה יוצאת נקטעת מתחילת

    לא נפתר שאלות ועזרה הדדית
    6
    0 הצבעות
    6 פוסטים
    76 צפיות
    י
    @אז-נדברו תודה רבה
  • 2 הצבעות
    1 פוסטים
    59 צפיות
    אין תגובות
  • 0 הצבעות
    3 פוסטים
    71 צפיות
    ה
    @מזל-טוב-0 @שלוחה-404 זכור לי שיש ל@121244 (עזר פון) כזה דבר.
  • 0 הצבעות
    22 פוסטים
    658 צפיות
    ה
    @1668 אשמח שתוסיף גם את שלי הקישור לנושא
  • API בתוך השמעת קבצים

    שאלות ועזרה הדדית
    2
    0 הצבעות
    2 פוסטים
    39 צפיות
    ה
    @המומחה תעשה מעבר לשלוחה אחרת במקש שיעבור. control_play1=go_to_folder playfile_control_play_1_goto=השלוחה שאליו יעבור בדוגמא אם יקיש 1 יעבור לשלוחה שתגדיר.
  • 3 הצבעות
    1 פוסטים
    40 צפיות
    אין תגובות
  • הקלטת הודעות!

    שאלות ועזרה הדדית
    3
    0 הצבעות
    3 פוסטים
    45 צפיות
    ש
    @יענקי-פולק תודה! עזר לי!