• 0 הצבעות
    1 פוסטים
    7 צפיות
    אין תגובות
  • הודעת מערכת M1103 בקול הרובוט

    לא נפתר שאלות ועזרה הדדית
    4
    0 הצבעות
    4 פוסטים
    13 צפיות
    S
    סליחה כקובץ M0000.tts
  • הפניה לקו אחר

    שאלות ועזרה הדדית
    5
    0 הצבעות
    5 פוסטים
    28 צפיות
    מ
    @BEN-ZION כתב בהפניה לקו אחר: @מושקה בפשטות אין דרך רק אם API אתה בטוח שאין או רק בפשטות אם יש מישהו שיודע על דרך לעשות את זה אשמח אם יוכל לעדכן אותי איך עושים את זה
  • לשמוע הוראות התפעול

    שאלות ועזרה הדדית
    2
    0 הצבעות
    2 פוסטים
    20 צפיות
    פ
    בדר"כ כוכבית 8 במהלך האזנה.
  • 0 הצבעות
    5 פוסטים
    11 צפיות
    ל
    @פיתה מה אומר ?! קיבלת תשובה?
  • סינון הודעות AI

    לא נפתר שאלות ועזרה הדדית
    10
    0 הצבעות
    10 פוסטים
    84 צפיות
    נ
    הקפצה....
  • ניתוב לקו אחר דרך API

    לא נפתר פורום מפתחים API
    11
    0 הצבעות
    11 פוסטים
    181 צפיות
    א
    @CUBASE צודק לא קשור...
  • 13 הצבעות
    150 פוסטים
    5k צפיות
    ג
    @אהרון-דויד כתב בחדש! מודול ניתוב שיחות לפי תור ללא עלות יחידות - שרשור מתעדכן: @אלחנן1 יש בעיה במודל השיחות לא מגיעות לנציגים ומצד המתקשר זה נשמע רגיל לא עזר למחוק את השלוחה וכו גם אצלי ואני מקבל הרבה תלונות מלקוחות שהשיחות לא מגיעות אליהם
  • ניתוב פשוט בשלוחת התפריט לא עובד

    שאלות ועזרה הדדית
    1
    0 הצבעות
    1 פוסטים
    17 צפיות
    אין תגובות
  • 10 הצבעות
    78 פוסטים
    2k צפיות
    B
    @m0548520998 הפוסט הזה נועד לנושא ספציפי ניתן לפתוח נושא חדש עיין פה למידע על צינתוקים
  • 7 הצבעות
    24 פוסטים
    362 צפיות
    B
    @אa מי שרוצה כתבתי קוד PHP שמקבל וובהוק מנדרים ומוסיף למערכת בהערות צריך לכתוב את שם המתרים <?php // הגדרות API $token = "{token}"; $api_url = "https://www.call2all.co.il/ym/api/"; // נתיבים $path_names = 'ivr2:Points/EnterIDValName.ini'; $path_total = 'ivr2:Points/points_total.ymgr'; $path_log = 'ivr2:Points/manual_log.ini'; // קבצי דיבוג מקומיים בשרת ה-PHP $debug_file = 'debug_webhook.log'; $unassigned_csv = 'unassigned_donations.csv'; // 1. קבלת הנתונים ושמירה לדיבוג ראשוני $json_data = file_get_contents('php://input'); $data = json_decode($json_data, true); // רישום כל כניסה לקובץ דיבוג כדי שלא יאבד כלום $timestamp_log = date("Y-m-d H:i:s"); file_put_contents($debug_file, "[$timestamp_log] Raw Data: " . $json_data . PHP_EOL, FILE_APPEND); if (!$data) die("No data received"); $comment_name = trim($data['Comments'] ?? ''); $amount = floatval($data['Amount'] ?? 0); function call_yemot($method, $path, $token, $contents = null) { global $api_url; $url = $api_url . $method . "?token=" . $token . "&what=" . $path; if ($contents !== null) $url .= "&contents=" . urlencode($contents); $res = file_get_contents($url); return json_decode($res, true); } // 2. חיפוש גמיש (Fuzzy Matching) $fundraiser_id = null; $best_score = -1; $res_names = call_yemot('GetTextFile', $path_names, $token); if (isset($res_names['contents']) && !empty($comment_name)) { $lines = explode("\n", $res_names['contents']); foreach ($lines as $line) { $parts = explode('=', $line); if (count($parts) === 2) { $current_id = trim($parts[0]); $current_name = trim($parts[1]); // חישוב דמיון similar_text($comment_name, $current_name, $percent); if ($percent > $best_score) { $best_score = $percent; $fundraiser_id = $current_id; } } } } // 3. ביצוע עדכון או שמירה ל-CSV במקרה של כישלון // הורדתי את הסף ל-50% בגלל תארים כמו "הרב" ו"שליט"א" if ($fundraiser_id && $best_score > 50) { // א. עדכון YMGR $res_total = call_yemot('GetTextFile', $path_total, $token); $t_lines = explode("\n", trim($res_total['contents'] ?? "")); $found = false; foreach ($t_lines as &$line) { if (strpos($line, "EnterId#$fundraiser_id") !== false) { $found = true; preg_match('/PointsTotalAll#([\d.]+)/', $line, $matches); $new_val = (isset($matches[1]) ? floatval($matches[1]) : 0) + $amount; $line = preg_replace('/PointsTotalAll#[\d.]+/', "PointsTotalAll#$new_val", $line); $line = preg_replace('/PointsTotal#[\d.]+/', "PointsTotal#$new_val", $line); } } if (!$found) $t_lines[] = "EnterId#{$fundraiser_id}%PointsTotalAll#{$amount}%PointsTotal#{$amount}"; call_yemot('UploadTextFile', $path_total, $token, implode("\n", $t_lines)); // ב. רישום ללוג ה-HTML $res_log = call_yemot('GetTextFile', $path_log, $token); $time_ms = round(microtime(true) * 1000); $time_hi = date("H:i"); $new_entry = "{$time_ms}|{$fundraiser_id}|{$amount}|{$time_hi}"; call_yemot('UploadTextFile', $path_log, $token, $new_entry . "\n" . ($res_log['contents'] ?? "")); file_put_contents($debug_file, "[$timestamp_log] SUCCESS: Matched $comment_name to ID $fundraiser_id ($best_score%)" . PHP_EOL, FILE_APPEND); echo "Success"; } else { // שמירה ל-CSV עבור תרומות שלא זוהו $is_new = !file_exists($unassigned_csv); $fp = fopen($unassigned_csv, 'a'); if ($is_new) { fputs($fp, chr(0xEF) . chr(0xBB) . chr(0xBF)); // BOM לעברית fputcsv($fp, ['תאריך', 'שם מהערה', 'סכום', 'טלפון', 'אחוז התאמה הכי קרוב']); } fputcsv($fp, [$timestamp_log, $comment_name, $amount, $data['Phone'] ?? '', $best_score . "%"]); fclose($fp); file_put_contents($debug_file, "[$timestamp_log] FAILED: No match for $comment_name (Best: $best_score%)" . PHP_EOL, FILE_APPEND); echo "Saved to CSV"; } Spoiler מי שרוצה שידרגתי את שתי הקודים כך שיש גם יעד אישי לכל מתרים מסך ניהול <!DOCTYPE html> <html lang="he" dir="rtl"> <head> <meta charset="UTF-8"> <title>מנהל קמפיין - Points Sync</title> <style> :root { --primary: #0ea5e9; --success: #22c55e; --bg: #0f172a; --card: #1e293b; --border: #334155; --gold: #f59e0b; } body { background: var(--bg); color: #f1f5f9; font-family: system-ui, sans-serif; margin: 0; padding: 20px; text-align: right; } .manual-entry-box { max-width: 600px; margin: 50px auto; background: var(--card); padding: 30px; border-radius: 20px; box-shadow: 0 10px 25px rgba(0,0,0,0.3); border: 1px solid var(--border); } h1 { text-align: center; color: var(--primary); } label { display: block; margin-top: 15px; font-weight: bold; } input { width: 100%; padding: 15px; margin: 10px 0; border-radius: 10px; border: 1px solid var(--border); background: #0f172a; color: white; font-size: 1.2rem; box-sizing: border-box; } .main-btn { background: var(--success); color: white; border: none; padding: 15px; border-radius: 10px; cursor: pointer; font-weight: bold; width: 100%; font-size: 1.1rem; margin-top: 20px; transition: 0.3s; } .main-btn:hover { filter: brightness(1.1); transform: translateY(-2px); } .settings-trigger { position: fixed; top: 20px; left: 20px; background: #334155; color: white; border: none; padding: 12px 20px; border-radius: 50px; cursor: pointer; font-weight: bold; display: flex; align-items: center; gap: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.2); } .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.85); z-index: 1000; backdrop-filter: blur(5px); } .modal-content { background: var(--card); width: 90%; max-width: 800px; margin: 40px auto; border-radius: 20px; padding: 25px; border: 1px solid var(--border); position: relative; max-height: 85vh; overflow-y: auto; } .close-btn { position: absolute; top: 15px; right: 20px; font-size: 1.5rem; cursor: pointer; } .tabs { display: flex; gap: 5px; border-bottom: 2px solid var(--border); margin-bottom: 20px; } .tab-btn { background: none; border: none; color: #8b949e; padding: 10px 15px; cursor: pointer; font-size: 0.95rem; } .tab-btn.active { color: var(--primary); border-bottom: 2px solid var(--primary); font-weight: bold; } .tab-pane { display: none; } .tab-pane.active { display: block; } .log-list { background: #0f172a; padding: 10px; border-radius: 8px; max-height: 350px; overflow-y: auto; } .log-item { display: flex; justify-content: space-between; padding: 12px; border-bottom: 1px solid var(--border); align-items: center; } .log-item label { display: flex; align-items: center; cursor: pointer; width: 100%; gap: 15px; } .badge { font-size: 0.75rem; padding: 3px 8px; border-radius: 5px; color: white; font-weight: bold; } .badge-manual { background: var(--primary); } .badge-phone { background: var(--success); } .status { margin-top: 15px; font-size: 0.9rem; text-align: center; font-weight: bold; } </style> </head> <body> <button class="settings-trigger" onclick="openModal()"><span>⚙️</span> הגדרות קמפיין</button> <div class="manual-entry-box"> <h1>💰 הזנת תרומה ידנית</h1> <label>מספר אישי של המתרים:</label> <input type="number" id="manualId" placeholder="למשל 215" autofocus> <label>סכום התרומה (₪):</label> <input type="number" id="manualAmount" placeholder="0.00"> <button class="main-btn" onclick="executeManualDonation()">✅ אשר והוסף למערכת</button> <div id="mainStatus" class="status"></div> </div> <div id="settingsModal" class="modal"> <div class="modal-content"> <span class="close-btn" onclick="closeModal()">✖️</span> <h2 style="border-bottom: 1px solid var(--border); padding-bottom: 10px;">⚙️ ניהול והגדרות</h2> <div class="tabs"> <button class="tab-btn active" onclick="switchTab(event, 'tabNames')">👥 שמות ויעדים</button> <button class="tab-btn" onclick="switchTab(event, 'tabGoal')">📈 יעד ותאריך</button> <button class="tab-btn" onclick="switchTab(event, 'tabTotals')">📊 עריכת סכומים</button> <button class="tab-btn" onclick="switchTab(event, 'tabLogs')">🗑️ מחיקת לוג</button> </div> <div id="tabNames" class="tab-pane active"> <div style="display:flex; gap:10px;"> <input type="text" id="editId" placeholder="מספר אישי"> <button onclick="findUser()" style="width:100px; background:var(--primary); border:none; border-radius:8px; color:white; cursor:pointer;">חפש</button> </div> <input type="text" id="editName" placeholder="שם המתרים"> <input type="number" id="editGoal" placeholder="יעד אישי (₪) - אופציונלי"> <button class="main-btn" style="background:var(--primary)" onclick="saveUser()">שמור פרטים ויעד</button> </div> <div id="tabGoal" class="tab-pane"> <label>יעד הקמפיין הכולל (₪):</label><input type="number" id="goalVal"> <label>תאריך ושעת סיום:</label><input type="datetime-local" id="dateVal"> <button class="main-btn" onclick="saveConfig()">🔄 עדכן יעד ותאריך</button> <hr style="border:0; border-top:1px solid var(--border); margin:25px 0;"> <label style="color:var(--gold)">מפתח אבטחה (Token) API:</label> <input type="password" id="tokenInput" placeholder="הכנס טוקן חדש"> <button class="main-btn" style="background:#6366f1" onclick="updateToken()">🔄 עדכן מפתח Token</button> </div> <div id="tabTotals" class="tab-pane"><div id="totalsList" class="log-list">טוען נתונים...</div></div> <div id="tabLogs" class="tab-pane"> <div id="logsList" class="log-list">טוען לוג פעולות מהשרת...</div> <button class="main-btn" style="background:#ef4444" onclick="deleteLogs()">❌ מחק פעולות וסנכרן שרת</button> </div> <div id="modalStatus" class="status"></div> </div> </div> <script> let CURRENT_TOKEN = localStorage.getItem('campaign_token') || 'YOUR_TOKEN_HERE'; let nameMap = {}; let goalMap = {}; // נתיבים בשרת - הכל בתיקיית Points const PATH_TOTAL = 'ivr2:Points/points_total.ymgr'; const PATH_PHONE_LOG = 'ivr2:Points/points_log.2026-02.ymgr'; const PATH_MANUAL_LOG = 'ivr2:Points/manual_log.ini'; const PATH_CONFIG = 'ivr2:Points/config.ini'; const PATH_NAMES = 'ivr2:Points/EnterIDValName.ini'; const PATH_GOALS = 'ivr2:Points/EnterIDGoal.ini'; document.getElementById('tokenInput').value = CURRENT_TOKEN; function updateToken() { const newToken = document.getElementById('tokenInput').value.trim(); if(!newToken) return alert("נא להזין טוקן"); CURRENT_TOKEN = newToken; localStorage.setItem('campaign_token', newToken); alert("הטוקן עודכן במחשב זה!"); location.reload(); } function openModal() { document.getElementById('settingsModal').style.display = 'block'; } function closeModal() { document.getElementById('settingsModal').style.display = 'none'; } async function callApi(cmd, path, content = "") { let url = `https://www.call2all.co.il/ym/api/${cmd}?token=${CURRENT_TOKEN}&what=${path}`; if(content) url += `&contents=${encodeURIComponent(content)}`; return fetch(url).then(r => r.json()); } async function switchTab(e, id) { document.querySelectorAll('.tab-pane, .tab-btn').forEach(el => el.classList.remove('active')); document.getElementById(id).classList.add('active'); e.currentTarget.classList.add('active'); await fetchNames(); if(id === 'tabGoal') loadCurrentConfig(); if(id === 'tabTotals') fetchTotals(); if(id === 'tabLogs') fetchLogs(); } async function fetchNames() { const [resNames, resGoals] = await Promise.all([ callApi('GetTextFile', PATH_NAMES), callApi('GetTextFile', PATH_GOALS) ]); nameMap = {}; goalMap = {}; if (resNames.contents) { resNames.contents.split(/[\r\n]+/).forEach(line => { const parts = line.split('='); if (parts.length === 2) nameMap[parts[0].trim()] = parts[1].trim(); }); } if (resGoals.contents) { resGoals.contents.split(/[\r\n]+/).forEach(line => { const parts = line.split('='); if (parts.length === 2) goalMap[parts[0].trim()] = parseFloat(parts[1].trim()); }); } } async function findUser() { const id = document.getElementById('editId').value; if (!id) return alert("נא להזין מספר אישי"); await fetchNames(); document.getElementById('editName').value = nameMap[id] || ""; document.getElementById('editGoal').value = goalMap[id] || ""; } async function saveUser() { const id = document.getElementById('editId').value; const name = document.getElementById('editName').value; const goal = document.getElementById('editGoal').value; if (!id) return alert("חובה להזין מספר אישי"); let [resNames, resGoals] = await Promise.all([ callApi('GetTextFile', PATH_NAMES), callApi('GetTextFile', PATH_GOALS) ]); let linesNames = (resNames.contents || "").split('\n').filter(l => l.trim() && !l.startsWith(`${id}=`)); if (name) linesNames.push(`${id}=${name}`); let linesGoals = (resGoals.contents || "").split('\n').filter(l => l.trim() && !l.startsWith(`${id}=`)); if (goal) linesGoals.push(`${id}=${goal}`); await Promise.all([ callApi('UploadTextFile', PATH_NAMES, linesNames.join('\n')), callApi('UploadTextFile', PATH_GOALS, linesGoals.join('\n')) ]); alert("פרטי המתרים והיעד האישי עודכנו בהצלחה!"); await fetchNames(); } async function executeManualDonation() { const id = document.getElementById('manualId').value; const amt = parseFloat(document.getElementById('manualAmount').value); if(!id || !amt) return alert("מלא מספר וסכום"); document.getElementById('mainStatus').innerText = "⏳ מעדכן שרת..."; let resT = await callApi('GetTextFile', PATH_TOTAL); let tLines = (resT.contents || "").trim().split('\n'); let found = false; let updatedT = tLines.map(l => { if(l.includes(`EnterId#${id}`)) { found = true; let old = parseFloat((l.match(/PointsTotalAll#([\d.]+)/) || [0,0])[1]); let sum = old + amt; return l.replace(/PointsTotalAll#[\d.]+/, `PointsTotalAll#${sum}`).replace(/PointsTotal#[\d.]+/, `PointsTotal#${sum}`); } return l; }); if(!found) updatedT.push(`EnterId#${id}%PointsTotalAll#${amt}%PointsTotal#${amt}`); let resL = await callApi('GetTextFile', PATH_MANUAL_LOG); let currentManualLog = resL.contents || ""; const timeStr = new Date().toLocaleTimeString('he-IL', {hour:'2-digit', minute:'2-digit'}); const newEntry = `${Date.now()}|${id}|${amt}|${timeStr}`; const updatedManualLog = newEntry + "\n" + currentManualLog; await Promise.all([ callApi('UploadTextFile', PATH_TOTAL, updatedT.join('\n')), callApi('UploadTextFile', PATH_MANUAL_LOG, updatedManualLog) ]); document.getElementById('mainStatus').innerText = "✅ נרשם וסונכרן לשרת!"; document.getElementById('manualAmount').value = ""; } async function fetchLogs() { let [resPhone, resManual] = await Promise.all([ callApi('GetTextFile', PATH_PHONE_LOG), callApi('GetTextFile', PATH_MANUAL_LOG) ]); let allEntries = []; const todayStr = new Date().toISOString().split('T')[0]; if (resPhone.contents) { resPhone.contents.trim().split('\n').forEach(l => { let id = (l.match(/id#(\d+)/) || [])[1]; let pts = (l.match(/Points#(\d+)/) || [])[1]; let timeM = (l.match(/(\d{2}:\d{2})/) || [""])[0]; if (id) allEntries.push({id, pts, time: timeM, type: 'phone', fullLine: l, ts: timeM ? new Date(`${todayStr}T${timeM}:00`).getTime() : 0}); }); } if (resManual.contents) { resManual.contents.trim().split('\n').forEach(l => { let [ts, id, amt, time] = l.split('|'); if (id) allEntries.push({id, pts: amt, time, type: 'manual', fullLine: l, ts: parseInt(ts)}); }); } allEntries.sort((a, b) => b.ts - a.ts); let html = allEntries.map(entry => { let name = nameMap[entry.id] || entry.id; return `<div class="log-item"><label><input type="checkbox" class="log-cb" data-id="${entry.id}" data-amt="${entry.pts}" data-type="${entry.type}" data-line="${entry.fullLine}"><span><span class="badge ${entry.type === 'manual' ? 'badge-manual' : 'badge-phone'}">${entry.type === 'manual' ? 'ידני' : 'טלפון'}</span> [${entry.time}] <b>${name}</b> - ₪${entry.pts}</span></label></div>`; }).join(''); document.getElementById('logsList').innerHTML = html || "אין היסטוריה."; } async function deleteLogs() { const checkboxes = document.querySelectorAll('.log-cb:checked'); if (checkboxes.length === 0) return alert("בחר פעולות"); if (!confirm("זה ימחק את השורות מכל המחשבים ויקזז סכומים. המשך?")) return; document.getElementById('modalStatus').innerText = "⏳ מסנכרן שרת..."; let [resP, resM, resT] = await Promise.all([ callApi('GetTextFile', PATH_PHONE_LOG), callApi('GetTextFile', PATH_MANUAL_LOG), callApi('GetTextFile', PATH_TOTAL) ]); let phoneLines = resP.contents ? resP.contents.trim().split('\n') : []; let manualLines = resM.contents ? resM.contents.trim().split('\n') : []; let totalLines = resT.contents ? resT.contents.trim().split('\n') : []; checkboxes.forEach(cb => { const id = cb.getAttribute('data-id'), amt = parseFloat(cb.getAttribute('data-amt')), type = cb.getAttribute('data-type'), line = cb.getAttribute('data-line'); if (type === 'phone') phoneLines = phoneLines.filter(l => l !== line); else manualLines = manualLines.filter(l => l !== line); totalLines = totalLines.map(tL => { if(tL.includes(`EnterId#${id}`)) { let old = parseFloat((tL.match(/PointsTotalAll#([\d.]+)/) || [0,0])[1]); let sum = Math.max(0, old - amt); return tL.replace(/PointsTotalAll#[\d.]+/, `PointsTotalAll#${sum}`).replace(/PointsTotal#[\d.]+/, `PointsTotal#${sum}`); } return tL; }); }); await Promise.all([ callApi('UploadTextFile', PATH_PHONE_LOG, phoneLines.join('\n')), callApi('UploadTextFile', PATH_MANUAL_LOG, manualLines.join('\n')), callApi('UploadTextFile', PATH_TOTAL, totalLines.join('\n')) ]); document.getElementById('modalStatus').innerText = "✅ הסנכרון הושלם בכל המחשבים!"; fetchLogs(); } async function loadCurrentConfig() { const res = await callApi('GetTextFile', PATH_CONFIG); if(res.contents) { const goal = (res.contents.match(/goal=(.*)/) || [])[1]; const date = (res.contents.match(/date=(.*)/) || [])[1]; if(goal) document.getElementById('goalVal').value = goal; if(date) document.getElementById('dateVal').value = date; } } async function saveConfig() { const content = `goal=${document.getElementById('goalVal').value}\ndate=${document.getElementById('dateVal').value}`; await callApi('UploadTextFile', PATH_CONFIG, content); alert("עודכן בשרת!"); } async function fetchTotals() { let res = await callApi('GetTextFile', PATH_TOTAL); if(!res.contents) return; let html = res.contents.trim().split('\n').map(l => { let id = (l.match(/EnterId#(\d+)/) || [])[1]; let val = (l.match(/PointsTotalAll#([\d.]+)/) || [])[1]; if(!id) return ''; let name = nameMap[id] || id; return `<div class="log-item"><span><b>${name}</b> (${id})</span><div style="display:flex; gap:5px;"><input type="number" id="inp-${id}" value="${val}" style="width:110px;"><button onclick="updateTotal('${id}')" style="background:var(--primary); color:white; border:none; padding:8px 12px; border-radius:8px; cursor:pointer;">עדכן</button></div></div>`; }).join(''); document.getElementById('totalsList').innerHTML = html; } async function updateTotal(id) { const newVal = document.getElementById(`inp-${id}`).value; let res = await callApi('GetTextFile', PATH_TOTAL); let updated = res.contents.trim().split('\n').map(l => l.includes(`EnterId#${id}`) ? l.replace(/PointsTotalAll#[\d.]+/, `PointsTotalAll#${newVal}`).replace(/PointsTotal#[\d.]+/, `PointsTotal#${newVal}`) : l); await callApi('UploadTextFile', PATH_TOTAL, updated.join('\n')); alert("סכום עודכן בשרת!"); } </script> </body> </html> מסך התרמה <!DOCTYPE html> <html lang="he" dir="rtl"> <head> <meta charset="UTF-8"> <title>מערכת התרמה LIVE - Points Sync</title> <style> :root { --accent: #0ea5e9; --gold: #fbbf24; --bg: #020617; } body { background: var(--bg); color: #f8fafc; font-family: system-ui, sans-serif; margin: 0; padding: 10px; overflow: hidden; } .container { display: grid; grid-template-columns: 320px 1fr 320px; gap: 15px; height: 95vh; } .card { background: rgba(30, 41, 59, 0.6); border: 1px solid rgba(255,255,255,0.1); border-radius: 20px; padding: 15px; backdrop-filter: blur(10px); display: flex; flex-direction: column; overflow: hidden; } .center-card { display: flex; flex-direction: column; justify-content: space-between; align-items: center; text-align: center; padding: 40px 20px; } h2 { font-size: 1.3rem; border-bottom: 2px solid var(--accent); padding-bottom: 8px; margin: 0 0 10px 0; color: white; text-align: center; } .countdown-timer { display: flex; gap: 15px; margin-bottom: 20px; } .time-unit { background: rgba(14, 165, 233, 0.2); padding: 10px 15px; border-radius: 12px; border: 1px solid var(--accent); min-width: 60px; } .time-num { display: block; font-size: 2rem; font-weight: bold; color: white; } .time-label { font-size: 0.8rem; opacity: 0.7; } .total-amount { font-size: 8rem; font-weight: 900; color: var(--accent); text-shadow: 0 0 30px rgba(14, 165, 233, 0.4); margin: 10px 0; line-height: 1; } .progress-wrapper { width: 90%; } .progress-bg { background: #1e293b; height: 40px; border-radius: 50px; border: 2px solid #334155; overflow: hidden; position: relative; } .progress-fill { height: 100%; background: linear-gradient(90deg, #0ea5e9, #22d3ee); width: 0%; transition: width 2s; } .list-box { flex-grow: 1; overflow: hidden; display: flex; flex-direction: column; padding-right: 5px; } .item { display: flex; flex-direction: column; align-items: stretch; padding: 10px; background: rgba(255,255,255,0.05); margin-bottom: 8px; border-radius: 10px; border-right: 4px solid var(--accent); flex-shrink: 0; transition: all 0.5s; } .item-header { display: flex; justify-content: space-between; align-items: center; } .item-info { display: flex; flex-direction: column; } .item-name { font-weight: bold; font-size: 1.1rem; display: flex; align-items: center; gap: 6px; } .item-time { font-size: 0.8rem; color: #94a3b8; font-weight: bold; } .item-val { font-weight: 900; color: var(--gold); font-size: 1.3rem; } .badge { font-size: 0.6rem; padding: 2px 5px; border-radius: 4px; font-weight: bold; } .badge-manual { background: #0ea5e9; color: white; } .badge-phone { background: #22c55e; color: white; } .live-dot { height: 10px; width: 10px; background: #ef4444; border-radius: 50%; display: inline-block; margin-left: 8px; animation: pulse 1s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } /* עיצוב לאנימציה של הגעה ליעד */ @keyframes goldGlow { 0% { box-shadow: 0 0 5px rgba(251, 191, 36, 0.2); border-color: rgba(251, 191, 36, 0.5); } 50% { box-shadow: 0 0 20px rgba(251, 191, 36, 0.8); border-color: rgba(251, 191, 36, 1); } 100% { box-shadow: 0 0 5px rgba(251, 191, 36, 0.2); border-color: rgba(251, 191, 36, 0.5); } } .goal-reached { animation: goldGlow 2s infinite; background: rgba(251, 191, 36, 0.1) !important; border-right-color: var(--gold) !important; } .goal-badge { background: var(--gold); color: black; font-size: 0.75rem; padding: 2px 8px; border-radius: 12px; font-weight: bold; margin-right: 10px; display: inline-block; } </style> </head> <body> <div class="container"> <div class="card"> <h2>🏆 מובילים</h2> <div id="leaderboard" class="list-box" style="overflow-y: auto;"></div> </div> <div class="card center-card"> <div> <div style="margin-bottom: 10px; font-size: 1.2rem; font-weight: bold;"><span class="live-dot"></span>הקמפיין מסתיים בעוד:</div> <div id="countdown" class="countdown-timer"> <div class="time-unit"><span id="days" class="time-num">00</span><span class="time-label">ימים</span></div> <div class="time-unit"><span id="hours" class="time-num">00</span><span class="time-label">שעות</span></div> <div class="time-unit"><span id="minutes" class="time-num">00</span><span class="time-label">דקות</span></div> <div class="time-unit"><span id="seconds" class="time-num">00</span><span class="time-label">שניות</span></div> </div> </div> <div> <h1 style="margin:0; font-size: 1.8rem; opacity: 0.9;">סך הכל נאסף:</h1> <div id="main-total" class="total-amount">₪0</div> </div> <div class="progress-wrapper"> <div class="progress-bg"><div id="progress-bar" class="progress-fill"></div></div> <div id="percent-label" style="font-size: 3rem; color: var(--gold); font-weight: bold; margin-top: 10px;">0%</div> <div id="goal-text" style="font-size: 1.5rem; opacity: 0.6;">מחשב נתונים...</div> </div> </div> <div class="card"> <h2>🔔 תרומות אחרונות</h2> <div id="recent-log" class="list-box" style="overflow-y: auto;"></div> </div> </div> <script> // הגדרות שלוחה - הכל תחת תיקיית Points const TOKEN = 'WU1BUElL.apik_MmtjfW7MhNRZGw4h--R54Q.zPUwopxRh5JxRpUutkWFKUhyDWtLfvYwWKQ30PTuXZw'; const PATH_TOTAL = 'ivr2:Points/points_total.ymgr'; const PATH_PHONE_LOG = 'ivr2:Points/points_log.2026-02.ymgr'; const PATH_MANUAL_LOG = 'ivr2:Points/manual_log.ini'; const PATH_CONFIG = 'ivr2:Points/config.ini'; const PATH_NAMES = 'ivr2:Points/EnterIDValName.ini'; const PATH_GOALS = 'ivr2:Points/EnterIDGoal.ini'; let GOAL = 500000; let DEADLINE = new Date(); let nameMap = {}; let goalMap = {}; function updateCountdown() { const now = new Date(); const diff = DEADLINE - now; if (diff <= 0) { document.querySelectorAll('.time-num').forEach(el => el.innerText = "00"); return; } document.getElementById('days').innerText = String(Math.floor(diff / (1000 * 60 * 60 * 24))).padStart(2, '0'); document.getElementById('hours').innerText = String(Math.floor((diff / (1000 * 60 * 60)) % 24)).padStart(2, '0'); document.getElementById('minutes').innerText = String(Math.floor((diff / 1000 / 60) % 60)).padStart(2, '0'); document.getElementById('seconds').innerText = String(Math.floor((diff / 1000) % 60)).padStart(2, '0'); } async function update() { try { const [resTotal, resLogPhone, resNames, resConfig, resLogManual, resGoals] = await Promise.all([ fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_TOTAL}`).then(r => r.json()), fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_PHONE_LOG}`).then(r => r.json()), fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_NAMES}`).then(r => r.json()), fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_CONFIG}`).then(r => r.json()), fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_MANUAL_LOG}`).then(r => r.json()), fetch(`https://www.call2all.co.il/ym/api/GetTextFile?token=${TOKEN}&what=${PATH_GOALS}`).then(r => r.json()) ]); if (resConfig.contents) { const g = resConfig.contents.match(/goal=(.*)/); const d = resConfig.contents.match(/date=(.*)/); if (g) { GOAL = parseFloat(g[1]); document.getElementById('goal-text').innerText = `מתוך יעד של ${GOAL.toLocaleString()} ₪`; } if (d) DEADLINE = new Date(d[1]); } if (resNames.contents) { resNames.contents.split(/[\r\n]+/).forEach(line => { const parts = line.split('='); if (parts.length === 2) nameMap[parts[0].trim()] = parts[1].trim(); }); } if (resGoals.contents) { goalMap = {}; resGoals.contents.split(/[\r\n]+/).forEach(line => { const parts = line.split('='); if (parts.length === 2) goalMap[parts[0].trim()] = parseFloat(parts[1].trim()); }); } if (resTotal.contents) { const lines = resTotal.contents.trim().split(/[\r\n]+/); let totalSum = 0; let users = []; lines.forEach(l => { const idMatch = l.match(/EnterId#(\d+)/); const scoreMatch = l.match(/PointsTotalAll#([\d.]+)/); if (idMatch && scoreMatch) { const val = parseFloat(scoreMatch[1]); totalSum += val; users.push({id: idMatch[1], val: val}); } }); document.getElementById('main-total').innerText = '₪' + totalSum.toLocaleString(); document.getElementById('progress-bar').style.width = Math.min((totalSum/GOAL)*100, 100) + '%'; document.getElementById('percent-label').innerText = ((totalSum/GOAL)*100).toFixed(1) + '%'; users.sort((a,b) => b.val - a.val); document.getElementById('leaderboard').innerHTML = users.map((u,i) => { const personalGoal = goalMap[u.id]; let goalHtml = ''; let reachedClass = ''; let goalBadgeHtml = ''; if (personalGoal && personalGoal > 0) { const rawPct = (u.val / personalGoal) * 100; const pct = Math.min(rawPct, 100).toFixed(0); if (rawPct >= 100) { reachedClass = 'goal-reached'; goalBadgeHtml = `<span class="goal-badge">הגיע ליעד! 👑</span>`; } goalHtml = ` <div style="font-size: 0.8rem; color: #94a3b8; display: flex; align-items: center; gap: 8px; margin-top: 8px;"> <div style="background: rgba(255,255,255,0.1); height: 6px; flex-grow: 1; border-radius: 5px; overflow: hidden;"> <div style="background: var(--gold); height: 100%; width: ${pct}%; transition: width 1s;"></div> </div> <span style="min-width: 40px; text-align: left; font-weight: bold; color: ${rawPct >= 100 ? 'var(--gold)' : 'inherit'}">${pct}%</span> </div> `; } return ` <div class="item ${reachedClass}"> <div class="item-header"> <span class="item-name"><b>${i+1}.</b> ${nameMap[u.id] || u.id} ${goalBadgeHtml}</span> <span style="color:var(--accent); font-weight:bold;">₪${u.val.toLocaleString()}</span> </div> ${goalHtml} </div> `; }).join(''); } let allItems = []; const todayStr = new Date().toISOString().split('T')[0]; if (resLogPhone.contents) { resLogPhone.contents.trim().split(/[\r\n]+/).forEach((line, idx) => { const idM = line.match(/id#(\d+)/); const ptM = line.match(/Points#(\d+)/); const timeM = line.match(/(\d{2}:\d{2})/); if (idM && ptM) { let ts = timeM ? new Date(`${todayStr}T${timeM[1]}:00`).getTime() : (Date.now() - 500000); allItems.push({ id: idM[1], amount: ptM[1], type: 'phone', timestamp: ts, timeStr: timeM ? timeM[1] : "--:--" }); } }); } if (resLogManual.contents) { resLogManual.contents.trim().split('\n').forEach(l => { let [ts, id, amt, time] = l.split('|'); if (id) allItems.push({ id, amount: amt, type: 'manual', timestamp: parseInt(ts), timeStr: time }); }); } allItems.sort((a, b) => b.timestamp - a.timestamp); document.getElementById('recent-log').innerHTML = allItems.map(item => ` <div class="item" style="flex-direction: row; justify-content: space-between; align-items: center;"> <div class="item-info"> <span class="item-name"> <span class="badge ${item.type === 'manual' ? 'badge-manual' : 'badge-phone'}">${item.type === 'manual' ? 'ידני' : 'טלפון'}</span> ${nameMap[item.id] || 'מתרים ' + item.id} </span> <span class="item-time">${item.timeStr}</span> </div> <span class="item-val">₪${parseFloat(item.amount).toLocaleString()}</span> </div> `).join(''); } catch (e) { console.error("Update Error:", e); } } setInterval(updateCountdown, 1000); setInterval(update, 5000); updateCountdown(); update(); </script> </body> </html> קרדיט ל @אa על הפיתוח המועיל
  • הקשת 2 ספרות

    שאלות ועזרה הדדית
    2
    0 הצבעות
    2 פוסטים
    16 צפיות
    B
    @יעקב-1 אצלי בהגדרות השלוחה מוגדר ככה זה עובר אוטומטי api_000=date,no,,2,1,Number,yes,yes,,,3,Ok,null,,no
  • חיבור sip trunk אפשרי?

    לא נפתר טלפונים ומערכות SIP
    2
    1
    0 הצבעות
    2 פוסטים
    21 צפיות
    B
    @מחפש כתב בחיבור sip trunk אפשרי?: לreetelai מה זה השירות הזה? אם זה סוג של חייגן SIP תפתח חשבון ויש לך שם כתובת שרת סימסה ושם משתמש [image: 1771923084157-%D7%A6%D7%99%D7%9C%D7%95%D7%9D-%D7%9E%D7%A1%D7%9A-2026-02-24-104851.png]
  • מחזיר שגיאה בקריאת לשרת

    עזרה הדדית למשתמשים מתקדמים
    3
    0 הצבעות
    3 פוסטים
    38 צפיות
    T
    @לימדי אצלי עזר נקודה בסוף המשפט
  • קריאת API אחרי הקלטת זיהוי

    שאלות ועזרה הדדית
    1
    0 הצבעות
    1 פוסטים
    22 צפיות
    אין תגובות
  • שמות למספרי משנה

    טיפים עצות והדגמות מהמשתמשים
    8
    1 הצבעות
    8 פוסטים
    26 צפיות
    C
    @BEN-ZION אז אולי תעשה את זה משולב, כי עם קריאה לשירות שציינתי לעיל אתה מקבל את רשימת המספרים המאושרים לשיחות והמאושרים לסמס, כך שאם אתה מקליד לדוג׳ send_sms_from= אז מופיעים לך רק המספרים המאושרים לסמס
  • אני רוצה לעשות כזה דבר בטלפון

    שאלות ועזרה הדדית
    1
    0 הצבעות
    1 פוסטים
    21 צפיות
    אין תגובות
  • מערכת מכירות

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

    שאלות ועזרה הדדית
    1
    0 הצבעות
    1 פוסטים
    17 צפיות
    אין תגובות
  • קניית יחידות

    שאלות ועזרה הדדית
    3
    0 הצבעות
    3 פוסטים
    25 צפיות
    א
    @מנסה רגיל! שיחה