• דף הבית
    • אינדקס קישורים
    • פוסטים אחרונים
    • משתמשים
    • חיפוש בהגדרות המתקדמות
    • חיפוש גוגל בפורום
    • ניהול המערכת
    • ניהול המערכת - שרת private
    • הרשמה
    • התחברות
    1. דף הבית
    2. BEN ZION
    3. הגבוה ביותר
    B
    מנותק
    • פרופיל
    • עוקב אחרי 2
    • עוקבים 4
    • נושאים 92
    • פוסטים 910
    • קבוצות 0

    פוסטים

    פוסטים אחרונים הגבוה ביותר שנוי במחלוקת
    • מזמור לתודה

      אני לא יודע אם זה מעשה שהיה או מעשה שיכול היה להיות

      ישנו סיפור על יהודי אחד שלא היו לו ילדים כמה שנים הוא נסע לבבא סאלי לקבל ברכה אחרי שנה נולד לו בן נסע לבבא סאלי אם קרטון בקבוקי ערק אמר לרב שהוא בא לומר תודה הרב אמר לו אתה יודע כמה יהודים באים אלי לבקש ברכה אף אחד לא מגיע לבשר בשורות טובות בזכות זה תזכה להרבה בשורות טובות את המעשה הזה סיפר הבן 14 של אותו יהודי

      ולענייננו:
      לפעמים יהודי טורח במיוחד בשבילך.
      מחפש מידע, בודק, מסדר, מנסח, וכותב לך הכול בצורה ברורה, מסודרת ומובנת – רק כדי לעזור באמת.

      וכל מה שהוא רוצה לדעת בסוף, זה דבר אחד פשוט:
      האם זה באמת עזר לך? האם היה שווה את ההשקעה?

      נכון, יש מערכת של 👍 ו-👎,
      אבל זה בדרך כלל מבטא את ההתרשמות הכללית מהרגע – מהרעיון, מהניסוח, או מהכיוון.
      זה עדיין לא תמיד אומר אם באמת נעזרת, אם הסתדרת, ואם זה פתר לך את הבעיה.

      אז אם נעזרת, אם הסתדרת, אם זה קידם אותך –
      תכתוב מילה.
      תעדכן.

      פורסם בעל הפורום
      B
      BEN ZION
    • RE: קריסה

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

      פורסם בבאגים במערכת
      B
      BEN ZION
    • RE: קריסה

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

      פורסם בבאגים במערכת
      B
      BEN ZION
    • RE: איטיות בכלל הקו/קווים? 📠 וניתוקים ☎ ואי אפשר להתקשר????! ❎😒☹

      @lavitoren-הטי-ל

      @lavitoren-הטי-ל כתב באיטיות בכלל הקו/קווים? 📠 וניתוקים ☎ ואי אפשר להתקשר????! ❎😒☹:

      כי מי אני שאתלונן על דבר שקבלנוהו חינם על מגש של זהב טהור 🧇

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

      פורסם בבאגים במערכת
      B
      BEN ZION
    • RE: האם זה "מוזר" לכתוב כל כך טוב?

      @מוזר אני אוהב את הסגנון שאתה כותב לי אין סבלנות לעשות את זה אבל באמת אם כל אחד היה חושב לפני שהוא כותב, הרבה שאלות לא היו כי היה יותר קל למצוא תשובות מפוסטים שכבר נכתבו
      תודה על ההשקעה ועל ההערכה מערכים מאד👍 👑

      פורסם בעל הפורום
      B
      BEN ZION
    • RE: פילטר הפניה עם API

      @אביי-ורבא אתה משתמש כשלוחה? או כהטמעה בכל מודול?
      לפי התיעוד GOTO זה רק כמודול בפני עצמו,
      כהטמעה ניתן להחזיר רק

      OK = ממשיך מיד לשלוחה בצורה רגילה

      NO = יוצא מהשלוחה עם הודעה M1102 אין לכם הרשאת גישה לתיקיה זו

      Out = יוצא מהשלוחה ללא הודעה

      פורסם בפורום מפתחים API
      B
      BEN ZION
    • קוד לספר טלפונים אם זיהוי דיבור

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

      <?php
      ob_start();
      
      require __DIR__ . '/vendor/autoload.php';
      
      use Google\Cloud\Speech\V1\SpeechClient;
      use Google\Cloud\Speech\V1\RecognitionConfig;
      use Google\Cloud\Speech\V1\RecognitionConfig\AudioEncoding;
      use Google\Cloud\Speech\V1\RecognitionAudio;
      
      /* ---------------- 1. פונקציות עזר ---------------- */
      
      function normalizeText($text) {
          $text = mb_strtolower($text, 'UTF-8');
          $text = preg_replace('/[^\p{Hebrew}\p{Latin}\s]/u', '', $text);
          return trim(preg_replace('/\s+/', ' ', $text));
      }
      
      function smartNameMatchScore($transcript, $nameTemplate) {
          $wordsTranscript = explode(' ', normalizeText($transcript));
          $wordsTemplate   = explode(' ', normalizeText($nameTemplate));
      
          $scoreFamily = 0;   // משקל 55
          $bestNameMatch = 0; // השם הכי חזק יקבל 35
          $secondBestName = 0; // השם הבא בתור יקבל 10
      
          $familyWord = $wordsTemplate[0] ?? '';
          $givenNamesTemplate = [];
          if (isset($wordsTemplate[1])) $givenNamesTemplate[] = $wordsTemplate[1];
          if (isset($wordsTemplate[2])) $givenNamesTemplate[] = $wordsTemplate[2];
      
          foreach ($wordsTranscript as $wordUser) {
              // בדיקת שם משפחה
              similar_text($wordUser, $familyWord, $pFamily);
              if ($pFamily > $scoreFamily) $scoreFamily = $pFamily;
      
              // בדיקה דינמית מול השמות הפרטיים
              foreach ($givenNamesTemplate as $templateName) {
                  similar_text($wordUser, $templateName, $pName);
                  if ($pName > $bestNameMatch) {
                      $secondBestName = $bestNameMatch;
                      $bestNameMatch = $pName;
                  } elseif ($pName > $secondBestName) {
                      $secondBestName = $pName;
                  }
              }
          }
      
          $finalScore = ($scoreFamily * 0.55) + ($bestNameMatch * 0.35) + ($secondBestName * 0.10);
      
          // חסימה אם לא נאמר שם פרטי ברור (מונע זיהוי שגוי של משפחה בלבד)
          if ($bestNameMatch < 70) {
              $finalScore = min($finalScore, 68); 
          }
      
          return $finalScore;
      }
      
      /* ---------------- 2. ניהול תיקיות ---------------- */
      
      $callId = $_GET['ApiCallId'] ?? 'unknown';
      $cacheDir = __DIR__ . '/people_cache';
      $logDir = __DIR__ . '/logs';
      
      if (!is_dir($cacheDir)) @mkdir($cacheDir, 0775, true);
      if (!is_dir($logDir)) @mkdir($logDir, 0775, true);
      
      $sessionFile = $cacheDir . '/session_' . $callId . '.json';
      $token = $_GET['token'] ?? '';
      $call = $_GET['call'] ?? '';
      $exit = $_GET['exit'] ?? '';
      
      /* ---------------- 3. טיפול בהקשות (MySelection) ---------------- */
      
      if (isset($_GET['MySelection'])) {
          $choice = trim($_GET['MySelection']);
          $sessionData = file_exists($sessionFile) ? json_decode(file_get_contents($sessionFile), true) : [];
          
          ob_end_clean();
          header('Content-Type: text/plain; charset=utf-8');
      
          if ($choice == '1') {
              $txt = $sessionData['full_text'] ?? "אין נתונים לשמיעה חוזרת";
              die("read=t-$txt=MySelection,no,1,1,7,no,no,no,,1.2.3.4,,,,,no");
          } elseif ($choice == '2') {
              $phone = $sessionData['phone'] ?? '';
              if ($phone && $token) {
                  $updateUrl = "https://www.call2all.co.il/ym/api/UpdateExtension?token=" . urlencode($token) . "&path=ivr2:/15/NIT&type=nitoviya&nitoviya_dial_to=" . urlencode($phone);
                  @file_get_contents($updateUrl);
              }
              @unlink($sessionFile);
              die("go_to_folder=NIT");
          } elseif ($choice == '3') {
              @unlink($sessionFile); die($_GET['call'] ?? '');
          } elseif ($choice == '4') {
              @unlink($sessionFile); die($_GET['exit'] ?? '');
          }
          exit;
      }
      
      /* ---------------- 4. תמלול וחיפוש ---------------- */
      
      $rapi = $_GET['rapi'] ?? '';
      $templateId = $_GET['templateId'] ?? '';
      if (!$token || !$rapi) exit;
      
      $downloadUrl = "https://www.call2all.co.il/ym/api/DownloadFile?token=" . urlencode($token) . "&path=ivr2:" . urlencode($rapi);
      $audioData = @file_get_contents($downloadUrl);
      
      $transcriptText = '';
      if ($audioData) {
          try {
              $client = new SpeechClient(['credentials' => __DIR__ . '/key.json']);
              $audio = (new RecognitionAudio())->setContent($audioData);
              $config = (new RecognitionConfig())->setEncoding(AudioEncoding::LINEAR16)->setLanguageCode('he-IL');
              $response = $client->recognize($config, $audio);
              foreach ($response->getResults() as $result) {
                  $alt = $result->getAlternatives();
                  if (!empty($alt[0])) $transcriptText .= $alt[0]->getTranscript() . " ";
              }
          } catch (Exception $e) {
              ob_end_clean(); die('go_to_folder=/95');
          } finally {
              if (isset($client)) $client->close();
          }
      }
      
      $normalized = normalizeText($transcriptText);
      if (!$normalized) { ob_end_clean(); die('go_to_folder=/95'); }
      
      /* בדיקה ב-Cache */
      $transcriptKey = md5($normalized);
      $fastCacheFile = $cacheDir . '/fast_text_' . $transcriptKey . '.json';
      
      $matchedEntry = null;
      $bestScore = 0;
      $source = "Cache";
      
      if (file_exists($fastCacheFile)) {
          $matchedEntry = json_decode(file_get_contents($fastCacheFile), true);
          $bestScore = 100; 
      } else {
          $source = "New Search";
          $templateUrl = "https://www.call2all.co.il/ym/api/GetTemplateEntries?token=" . urlencode($token) . "&templateId=" . urlencode($templateId);
          $templateData = json_decode(@file_get_contents($templateUrl), true);
          
          if (isset($templateData['entries'])) {
              foreach ($templateData['entries'] as $entry) {
                  $score = smartNameMatchScore($normalized, $entry['name'] ?? '');
                  if ($score > $bestScore) {
                      $bestScore = $score;
                      $matchedEntry = $entry;
                  }
              }
          }
          if ($matchedEntry && $bestScore >= 75) {
              file_put_contents($fastCacheFile, json_encode($matchedEntry + ['transcript' => $transcriptText], JSON_UNESCAPED_UNICODE));
          }
      }
      
      /* ---------------- 5. שמירת לוג לבקרה מורחבת ---------------- */
      
      $logContent = "--- [" . date('Y-m-d H:i:s') . "] ---\n";
      $logContent .= "Call ID: $callId\n";
      $logContent .= "Audio Path (rapi): $rapi\n";
      $logContent .= "Transcript: $transcriptText\n";
      $logContent .= "Source: $source\n";
      if ($matchedEntry) {
          $logContent .= "Matched Name: " . ($matchedEntry['name'] ?? 'N/A') . " (" . round($bestScore, 2) . "%)\n";
          $logContent .= "Result: SUCCESS\n";
      } else {
          $logContent .= "Best Score Found: " . round($bestScore, 2) . "%\n";
          $logContent .= "Result: FAILED\n";
      }
      $logContent .= "---------------------------\n\n";
      
      file_put_contents($logDir . '/audit_log_' . date('Y-m') . '.txt', $logContent, FILE_APPEND);
      
      /* ---------------- 6. פלט סופי ---------------- */
      
      ob_end_clean();
      header('Content-Type: text/plain; charset=utf-8');
      
      if ($matchedEntry && $bestScore >= 75) {
          $name = $matchedEntry['name'] ?? '';
          $phone = $matchedEntry['phone'] ?? '';
          $info = $matchedEntry['moreinfo'] ?? 'אין מידע נוסף';
          
          $cleanInfo = str_replace([',', '=', '.', '"', "'"], ' ', $info);
          $fullSayText = "נמצאו פרטים עבור $name טלפון $phone כתובת $cleanInfo לשמיעה חוזרת הקישו 1, לחיוג הקישו 2, לחיפוש חדש הקישו 3, לביטול וחזרה הקש 4";
          
          file_put_contents($sessionFile, json_encode(['full_text' => $fullSayText, 'phone' => $phone], JSON_UNESCAPED_UNICODE));
          
          echo "read=t-$fullSayText=MySelection,no,1,1,7,no,no,no,,1.2.3.4,,,,,no";
      } else {
          echo 'go_to_folder=/95';
      }
      exit;
      

      הקוד עבר עדכון החיפוש יותר חכם
      אצלי מוגדר בשגיאה מעבר לשלוחה 95 שזה ניתוק
      ההגדרות בשלוחה

      type=api
      title=אלפון קהילה
      api_link=קישור לשרת
      api_add_0=token=טוקן אני השתמשתי בחדש
      api_add_1=templateId=מזהה רשימת תפוצה
      api_add_2=call=go_to_folder=/נתיב השלוחה לחיפוש חוזר
      api_add_3=exit=go_to_folder=/נתיב השלוחה לביטול וחזרה אצלי זה בתוך מערכת וואצפון זה מחזיר למערכת
      
      api_000=rapi,,record,/15/api,,no
      
      
      
      api_hangup_send=no
      api_phone_send=no
      api_did_send=no
      api_enter_id_send=no
      api_enter_id_name_send=no
      api_time_send=no
      api_call_id_send=yes
      

      הקוד שומר כל זיהוי טקסט שנמצא אם הטלפון והמידע הנוסף ככה בפעמים הבאות הוא לא צריך לחפש ברשימה אלא שולף מהזיכרון יש 4 אופציות שמיעה חוזרת, חיוג, חיפוש חדש, ביטול וחזרה
      אשמח לקבל הערות והארות

      אם מישהו יוכל להגדיר תמלול בגימיני ושישלחו מפתח גימני אני אולי אפרסם קישור פתוח לתועלת הציבור

      פורסם בהסברים מסודרים ממשתמשים
      B
      BEN ZION
    • RE: הגדרת תנאי משתמש

      @לומד-עס תודה רבה

      פורסם בשאלות ועזרה הדדית
      B
      BEN ZION
    • RE: מייל פון

      @יוני2 אין צורך להגיב לנושא מלפני 4 שנים

      פורסם בעזרה הדדית למשתמשים מתקדמים
      B
      BEN ZION
    • RE: למה אין נתונים לקווים? 🤔

      @קו-המוסיקה @אa @565906
      מצורף קוד HTML בסיסי ניתן לשדרג עוד
      אין לי במערכת לוג האזנה אז אני לא יודע איך עובד נראה טוב
      יש לו באג שאין לי כוח כרגע לתקן שבציון דקות שהם מעל רף של דקות נתין לבחור כמה דקות הוא מציין בשיחות מתחת לרף כל מעבר בשלוחה שלא התעכבו בה את הרף
      עוד לא מצאתי איך לחשב לפי שיחה אולי בהמשך אני אשדרג

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>מערכת סיכום דקות מתקדמת - ימות המשיח</title>
        <style>
          body { font-family: Arial, sans-serif; background: #f0f2f5; margin: 0; padding: 20px; color: #333; }
          .container { max-width: 1200px; margin: auto; background: #fff; padding: 25px; border-radius: 15px; box-shadow: 0 8px 30px rgba(0,0,0,0.1); }
          h1 { margin-top: 0; color: #1a4388; text-align: center; border-bottom: 2px solid #eee; padding-bottom: 15px; }
          
          .setup-panel { background: #f8fafc; padding: 20px; border-radius: 12px; border: 1px solid #e2e8f0; margin-bottom: 20px; display: flex; flex-wrap: wrap; gap: 15px; align-items: flex-end; }
          .input-group { display: flex; flex-direction: column; gap: 5px; }
          label { font-weight: bold; font-size: 14px; }
          input, select { padding: 10px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 14px; }
          
          button { border: none; background: #1a4388; color: #fff; padding: 10px 20px; border-radius: 8px; cursor: pointer; font-weight: bold; transition: background 0.2s; }
          button:hover { background: #133266; }
          button.secondary { background: #10b981; }
          button.secondary:hover { background: #059669; }
      
          .summary-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 15px; margin-bottom: 20px; }
          .card { background: #fff; padding: 15px; border-radius: 10px; border: 1px solid #e2e8f0; box-shadow: 0 2px 4px rgba(0,0,0,0.02); text-align: center; }
          .card .val { font-size: 22px; font-weight: bold; color: #1a4388; display: block; margin-bottom: 5px; }
          .card .lbl { font-size: 13px; color: #64748b; font-weight: bold; }
      
          .tabs { display: flex; gap: 10px; margin-bottom: 15px; border-bottom: 2px solid #e2e8f0; padding-bottom: 10px; display: none; }
          .tab-btn { background: #e2e8f0; color: #333; padding: 10px 15px; border-radius: 8px; cursor: pointer; font-weight: bold; border: none; }
          .tab-btn.active { background: #1a4388; color: #fff; }
          
          .tab-content { display: none; }
          .tab-content.active { display: block; }
      
          .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
          @media (max-width: 768px) { .grid-2 { grid-template-columns: 1fr; } }
      
          .table-container { overflow-x: auto; max-height: 400px; border: 1px solid #e2e8f0; border-radius: 10px; margin-bottom: 20px; background: #fff;}
          table { width: 100%; border-collapse: collapse; text-align: right; }
          th { position: sticky; top: 0; background: #1a4388; color: white; padding: 10px; font-size: 14px; }
          td { padding: 10px; border-bottom: 1px solid #f1f5f9; font-size: 14px; }
          tr:hover { background: #f8fafc; }
      
          .error { color: #991b1b; background: #fef2f2; padding: 12px; border-radius: 8px; border: 1px solid #fee2e2; margin: 10px 0; }
          .success { color: #065f46; background: #ecfdf5; padding: 12px; border-radius: 8px; border: 1px solid #d1fae5; margin: 10px 0; }
          .warning { color: #854d0e; background: #fefce8; padding: 12px; border-radius: 8px; border: 1px solid #fef08a; margin: 10px 0; }
          
          h3 { color: #1a4388; margin-bottom: 10px; border-bottom: 1px solid #e2e8f0; padding-bottom: 5px; }
        </style>
      </head>
      <body>
        
      <div class="container">
        <h1>ניהול נתוני שיחות מתקדם - ימות המשיח</h1>
        
        <div class="setup-panel">
          <div class="input-group">
            <label>טוקן API:</label>
            <input type="password" id="token" placeholder="הכנס טוקן..." />
          </div>
          <div class="input-group">
            <label>שנה:</label>
            <input type="number" id="year" value="2026" style="width: 80px;" />
          </div>
          <div class="input-group">
            <label>חודש:</label>
            <select id="month">
              <option value="01">01</option><option value="02">02</option>
              <option value="03">03</option><option value="04" selected>04</option>
              <option value="05">05</option><option value="06">06</option>
              <option value="07">07</option><option value="08">08</option>
              <option value="09">09</option><option value="10">10</option>
              <option value="11">11</option><option value="12">12</option>
            </select>
          </div>
          <div class="input-group">
            <label>רף דקות להגדרת שיחה (מעל/מתחת):</label>
            <input type="number" id="minLimit" value="15" style="width: 100px;" />
          </div>
          <button onclick="fetchData()">משוך נתונים</button>
        </div>
        
        <div id="statusMessage"></div>
        
        <div id="summaryCards" class="summary-cards" style="display:none;">
          <div class="card"><span class="val" id="totalCalls">0</span><span class="lbl">סה"כ כניסות למערכת</span></div>
          <div class="card"><span class="val" id="totalListeners">0</span><span class="lbl">מאזינים יוניקיים</span></div>
          <div class="card"><span class="val" id="avgListenerTime">0:00</span><span class="lbl">ממוצע למאזין</span></div>
          <div class="card"><span class="val" id="callsOverLimit">0</span><span class="lbl">שיחות מעל הרף</span></div>
          <div class="card"><span class="val" id="callsUnderLimit">0</span><span class="lbl">שיחות מתחת לרף</span></div>
        </div>
      
        <div class="tabs" id="tabsMenu">
          <button class="tab-btn active" onclick="switchTab('tab-extensions')">נתוני שלוחות</button>
          <button class="tab-btn" onclick="switchTab('tab-files')">נתוני קבצים והאזנות</button>
          <button class="tab-btn" onclick="switchTab('tab-daily')">סטטיסטיקה יומית ושעות</button>
        </div>
      
        <div id="tab-extensions" class="tab-content active">
          <div class="grid-2">
            <div>
              <h3>השלוחות המובילות בזמן האזנה (טופ 10)</h3>
              <div class="table-container">
                <table>
                  <thead><tr><th>מקום</th><th>שלוחה</th><th>שם</th><th>זמן כולל</th></tr></thead>
                  <tbody id="topTimeExtensions"></tbody>
                </table>
              </div>
            </div>
            <div>
              <h3>השלוחות עם הכי הרבה כניסות</h3>
              <div class="table-container">
                <table>
                  <thead><tr><th>מקום</th><th>שלוחה</th><th>שם</th><th>כמות כניסות</th></tr></thead>
                  <tbody id="topCallsExtensions"></tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
      
        <div id="tab-files" class="tab-content">
          <div class="grid-2">
            <div>
              <h3>הקבצים המושמעים ביותר</h3>
              <div class="table-container">
                <table>
                  <thead><tr><th>שלוחה</th><th>קובץ</th><th>כמות השמעות</th><th>זמן השמעה מצטבר</th></tr></thead>
                  <tbody id="topFiles"></tbody>
                </table>
              </div>
            </div>
            <div>
              <h3>קבצים עם הכי הרבה עזיבות (האזנה חלקית)</h3>
              <div class="table-container">
                <table>
                  <thead><tr><th>שלוחה</th><th>קובץ</th><th>מספר עזיבות באמצע</th></tr></thead>
                  <tbody id="dropoffFiles"></tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
      
        <div id="tab-daily" class="tab-content">
          <div class="grid-2">
            <div>
              <h3>מאזינים לפי ימים</h3>
              <div class="table-container">
                <table>
                  <thead><tr><th>תאריך</th><th>סה"כ כניסות</th><th>מאזינים יוניקיים</th></tr></thead>
                  <tbody id="dailyStats"></tbody>
                </table>
              </div>
            </div>
            <div>
              <h3>השעות הפעילות ביותר (לפי כניסות)</h3>
              <div class="table-container">
                <table>
                  <thead><tr><th>שעה</th><th>כמות כניסות</th></tr></thead>
                  <tbody id="hourlyStats"></tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
      
      </div>
        
      <script>
        function showMsg(text, type) {
          const el = document.getElementById("statusMessage");
          el.innerHTML = `<div class="${type}">${text}</div>`;
        }
      
        function switchTab(tabId) {
          document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
          document.querySelectorAll('.tab-btn').forEach(el => el.classList.remove('active'));
          document.getElementById(tabId).classList.add('active');
          event.currentTarget.classList.add('active');
        }
      
        function formatHMS(s) {
          if (isNaN(s) || s < 0) return "0:00";
          const h = Math.floor(s / 3600);
          const m = Math.floor((s % 3600) / 60);
          const sec = Math.floor(s % 60);
          return h > 0 ? `${h}:${String(m).padStart(2,'0')}:${String(sec).padStart(2,'0')}` : `${m}:${String(sec).padStart(2,'0')}`;
        }
      
        async function fetchData() {
          const token = document.getElementById("token").value;
          const year = document.getElementById("year").value;
          const month = document.getElementById("month").value;
          
          if (!token) { showMsg("אנא הזן טוקן תקני", "error"); return; }
          
          const pathEnterExit = `ivr2:Log/LogFolderEnterExit-${year}-${month}.ymgr`;
          const pathPlayback = `ivr2:Log/LogPlaybackPlayStop/LogPlaybackPlayStop.${year}-${month}.ymgr`;
      
          const urlEnterExit = `https://private.call2all.co.il/ym/api/RenderYMGRFile?token=${token}&wath=${pathEnterExit}&convertType=json&notLoadLang=1`;
          const urlPlayback = `https://private.call2all.co.il/ym/api/RenderYMGRFile?token=${token}&wath=${pathPlayback}&convertType=json&notLoadLang=1`;
      
          showMsg("מתחבר לשרת ימות המשיח, מושך נתונים...", "success");
      
          try {
            const [resEnterExit, resPlayback] = await Promise.all([
              fetch(urlEnterExit).then(r => r.json()).catch(() => ({ responseStatus: "ERROR" })),
              fetch(urlPlayback).then(r => r.json()).catch(() => ({ responseStatus: "ERROR" }))
            ]);
      
            if (resEnterExit.responseStatus !== "OK" && resPlayback.responseStatus !== "OK") {
              showMsg("שגיאה מהשרת: לא נמצאו קבצי לוג לחודש זה.", "error");
              return;
            }
      
            let warning = "";
            if (resEnterExit.responseStatus !== "OK") warning += " לא נמצא לוג כניסות (EnterExit).";
            if (resPlayback.responseStatus !== "OK") warning += " לא נמצא לוג השמעות קבצים (PlaybackPlayStop).";
            if (warning) showMsg("הערה: " + warning, "warning");
            else showMsg("הנתונים נמשכו ועובדו בהצלחה!", "success");
      
            processData(resEnterExit.data || [], resPlayback.data || []);
            
          } catch (err) {
            showMsg("שגיאת תקשורת עם השרת.", "error");
          }
        }
      
        function processData(folderData, playbackData) {
          const limitMinutes = parseFloat(document.getElementById("minLimit").value) || 15;
          const limitSeconds = limitMinutes * 60;
      
          // ----- עיבוד נתוני כניסה/יציאה (FolderEnterExit) -----
          const extensions = {};
          const daily = {};
          const hourly = {};
          const phones = {};
          
          let gCalls = 0;
          let gSeconds = 0;
          let callsOverLimit = 0;
          let callsUnderLimit = 0;
      
          folderData.forEach(row => {
            const ext = row["Folder"];
            const seconds = parseFloat(row["TimeTotal"]) || 0;
            const title = row["PathTitle"] || row["ValName"] || "";
            const phone = row["Phone"];
            const date = row["EnterDate"];
            const time = row["EnterTime"];
      
            if (!ext) return;
      
            // שלוחות
            if (!extensions[ext]) extensions[ext] = { ext, title, calls: 0, seconds: 0 };
            extensions[ext].calls++;
            extensions[ext].seconds += seconds;
      
            // מאזינים (Phone)
            if (phone) {
              if (!phones[phone]) phones[phone] = { count: 0, totalSeconds: 0 };
              phones[phone].count++;
              phones[phone].totalSeconds += seconds;
            }
      
            // חתך זמן (רף)
            if (seconds >= limitSeconds) callsOverLimit++;
            else callsUnderLimit++;
      
            // נתונים יומיים
            if (date) {
              if (!daily[date]) daily[date] = { calls: 0, uniquePhones: new Set() };
              daily[date].calls++;
              if (phone) daily[date].uniquePhones.add(phone);
            }
      
            // שעות פעילות
            if (time) {
              const hour = time.split(":")[0];
              if (!hourly[hour]) hourly[hour] = 0;
              hourly[hour]++;
            }
      
            gCalls++;
            gSeconds += seconds;
          });
      
          // ----- עיבוד נתוני קבצים (PlaybackPlayStop) -----
          const files = {};
          
          playbackData.forEach(row => {
            const ext = row["Folder"] || "";
            const fileName = row["FileName"] || row["ValName"] || "";
            const playSeconds = parseFloat(row["TimeTotal"] || row["PlayTotalTime"]) || 0;
            const fileKey = ext + "/" + fileName;
      
            if (!fileName) return;
      
            if (!files[fileKey]) files[fileKey] = { ext, fileName, plays: 0, totalSeconds: 0, dropoffs: 0 };
            
            files[fileKey].plays++;
            files[fileKey].totalSeconds += playSeconds;
      
            // חישוב עזיבות אמצע: נניח שעזיבה היא אם הושמע פחות מ-80% מהקובץ או אם סטאטוס/סיבה היא לא רגילה
            // מכיוון שהמפתחות משתנים, הדרך הפשוטה לזהות "נטישה" מהירה היא שיחה שנותקה פחות מ-15 שניות לסיום (אם יש אורך) או חיתוך.
            // נשתמש בהגיון בסיסי: אם המאזין שמע פחות מ-10 שניות או פחות מהאורך (במידה וקיים).
            const fileLength = parseFloat(row["FileLength"]);
            if (fileLength && playSeconds < fileLength - 5) {
               files[fileKey].dropoffs++;
            } else if (!fileLength && playSeconds < 30) {
               // גיבוי למקרה שאין אורך קובץ מדויק
               files[fileKey].dropoffs++;
            }
          });
      
          renderUI(extensions, daily, hourly, phones, files, gCalls, callsOverLimit, callsUnderLimit);
        }
      
        function renderUI(extensions, daily, hourly, phones, files, gCalls, callsOverLimit, callsUnderLimit) {
          document.getElementById("summaryCards").style.display = "grid";
          document.getElementById("tabsMenu").style.display = "flex";
      
          const uniqueListenersCount = Object.keys(phones).length;
          let avgTime = 0;
          if (uniqueListenersCount > 0) {
            const totalSystemTime = Object.values(phones).reduce((sum, p) => sum + p.totalSeconds, 0);
            avgTime = totalSystemTime / uniqueListenersCount;
          }
      
          // כרטיסיות למעלה
          document.getElementById("totalCalls").innerText = gCalls;
          document.getElementById("totalListeners").innerText = uniqueListenersCount;
          document.getElementById("avgListenerTime").innerText = formatHMS(avgTime);
          document.getElementById("callsOverLimit").innerText = callsOverLimit;
          document.getElementById("callsUnderLimit").innerText = callsUnderLimit;
      
          const extArr = Object.values(extensions);
          const filesArr = Object.values(files);
      
          // 1. שלוחות טופ זמן
          const topTimeExt = [...extArr].sort((a, b) => b.seconds - a.seconds).slice(0, 10);
          document.getElementById("topTimeExtensions").innerHTML = topTimeExt.map((e, i) => 
            `<tr><td>${i+1}</td><td><strong>${e.ext}</strong></td><td>${e.title}</td><td>${formatHMS(e.seconds)}</td></tr>`
          ).join("");
      
          // 2. שלוחות טופ כניסות
          const topCallsExt = [...extArr].sort((a, b) => b.calls - a.calls);
          document.getElementById("topCallsExtensions").innerHTML = topCallsExt.map((e, i) => 
            `<tr><td>${i+1}</td><td><strong>${e.ext}</strong></td><td>${e.title}</td><td>${e.calls}</td></tr>`
          ).join("");
      
          // 3. קבצים מושמעים ביותר
          const topFiles = [...filesArr].sort((a, b) => b.plays - a.plays).slice(0, 30); // מציג עד 30
          document.getElementById("topFiles").innerHTML = topFiles.map(f => 
            `<tr><td>${f.ext}</td><td>${f.fileName}</td><td>${f.plays}</td><td>${formatHMS(f.totalSeconds)}</td></tr>`
          ).join("");
      
          // 4. עזיבות באמצע
          const dropoffFiles = [...filesArr].sort((a, b) => b.dropoffs - a.dropoffs).filter(f => f.dropoffs > 0).slice(0, 20);
          document.getElementById("dropoffFiles").innerHTML = dropoffFiles.map(f => 
            `<tr><td>${f.ext}</td><td>${f.fileName}</td><td style="color:red; font-weight:bold;">${f.dropoffs}</td></tr>`
          ).join("");
      
          // 5. יומי
          const dailyHtml = Object.keys(daily).sort().map(date => 
            `<tr><td>${date}</td><td>${daily[date].calls}</td><td>${daily[date].uniquePhones.size}</td></tr>`
          ).join("");
          document.getElementById("dailyStats").innerHTML = dailyHtml;
      
          // 6. שעות
          const hourlyHtml = Object.keys(hourly).sort((a, b) => hourly[b] - hourly[a]).map(hour => 
            `<tr><td>${hour}:00</td><td>${hourly[hour]}</td></tr>`
          ).join("");
          document.getElementById("hourlyStats").innerHTML = hourlyHtml;
        }
      </script>
        
      </body>
      </html>
      כמובן שכל אחד יכול לשדרג אם בינה
      
      פורסם בבקשות לפיתוח
      B
      BEN ZION
    • RE: בקשת יחידות

      @מוטי-מוטי-מוטי אני יעביר לך 20 ותחזיר לי 19

      פורסם בשאלות ועזרה הדדית
      B
      BEN ZION
    • RE: יש אפשרות לחסום כל משתמש מלהתקשר לקו תוכן חוץ ממספר אחד או שתים?

      @מי-יודע-0 יש לך פילטר לפי רשימה לבנה או לפי רשימת תפוצה

      פורסם בעזרה הדדית למשתמשים מתקדמים
      B
      BEN ZION
    • RE: מהפך במנוע הצינתוקים

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

      פורסם בעזרה הדדית למשתמשים מתקדמים
      B
      BEN ZION
    • RE: מהפך במנוע הצינתוקים

      @אבו

      @אבו כתב במהפך במנוע הצינתוקים:

      אף אחד לא חייב לנו כלום

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

      פורסם בעזרה הדדית למשתמשים מתקדמים
      B
      BEN ZION
    • RE: קומבינה לשיחה יוצאת עם זיהוי מערכת על בסיס שלוחת API ושלוחת תור החדשה

      @עידו העלת קובץ 000.wav אם שאלה ?

      פורסם בעזרה הדדית למשתמשים מתקדמים
      B
      BEN ZION
    • RE: מעבר לשלוחה שלא קיימת ⛔🎫⛪

      @פלוס

      @פלוס כתב במעבר לשלוחה שלא קיימת ⛔🎫⛪:

      אין כזה הגדרה, והוא לא הביא כזאת.

      לא הסברתי טוב
      יש את הקובץ

      ExtensionNumbersAndAssociations.ini
      

      שאתה יכול הגדיר בו בשלוחה מה קורה שמקישים כל ספרה משמש בעיקר למעבר לשלוחות שהשם שלהם הוא אותיות ולא ספרות אתה יכול ליצור את הקובץ הזה
      אם כל השלוחות מ 1 עד 999 ולבחור מה יקרה לכל מקש
      דוגמא לקובץ HTML שיכין לך את כל השלוחות אם ברירת מחדל שלוחה מסוימת ולשלוחות ספציפיות הגדרה שונה

      <!DOCTYPE html>
      <html lang="he" dir="rtl">
      <head>
          <meta charset="UTF-8">
          <title>מחולל קובץ שלוחות מתקדם</title>
          <style>
              body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f0f2f5; display: flex; justify-content: center; padding: 20px; }
              .card { background: white; padding: 25px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); width: 100%; max-width: 500px; }
              h2 { color: #1a73e8; text-align: center; margin-top: 0; }
              label { font-weight: bold; display: block; margin-top: 15px; }
              input { width: 100%; padding: 10px; margin-top: 5px; border: 1px solid #ccc; border-radius: 6px; box-sizing: border-box; }
              .exception-row { display: flex; align-items: center; gap: 10px; margin-top: 5px; background: #f9f9f9; padding: 5px; border-radius: 4px; }
              .exception-row span { min-width: 80px; font-size: 0.9em; }
              button { width: 100%; padding: 12px; margin-top: 20px; cursor: pointer; border: none; border-radius: 6px; font-weight: bold; transition: 0.3s; }
              .btn-generate { background-color: #1a73e8; color: white; }
              .btn-download { background-color: #34a853; color: white; margin-top: 10px; display: none; }
              button:hover { opacity: 0.9; }
              #result { margin-top: 20px; background: #272822; color: #f8f8f2; padding: 15px; border-radius: 6px; white-space: pre-wrap; font-family: monospace; font-size: 0.9em; max-height: 200px; overflow-y: auto; }
          </style>
      </head>
      <body>
      
      <div class="card">
          <h2>הגדרות שלוחות</h2>
          
          <label>טווח שלוחות (מ- עד-):</label>
          <div style="display: flex; gap: 10px;">
              <input type="number" id="from" placeholder="מכלוחה" value="1">
              <input type="number" id="to" placeholder="עד שלוחה" value="10">
          </div>
      
          <label>נתיב ברירת מחדל לכל הטווח:</label>
          <input type="text" id="defaultPath" placeholder="לדוגמה: /02" value="/02">
      
          <label>מספרי שלוחות חריגות (הפרד בפסיק):</label>
          <input type="text" id="exceptionInput" placeholder="לדוגמה: 3, 7" oninput="updateExceptionFields()">
      
          <div id="dynamicExceptions"></div>
      
          <button class="btn-generate" onclick="processData()">תצוגה מקדימה</button>
          <div id="result">הקוד יופיע כאן...</div>
          <button id="downloadBtn" class="btn-download" onclick="downloadIni()">הורד קובץ ExtensionNumbersAndAssociations.ini</button>
      </div>
      
      <script>
          let finalContent = "";
      
          function updateExceptionFields() {
              const container = document.getElementById('dynamicExceptions');
              const val = document.getElementById('exceptionInput').value;
              const nums = val.split(',').map(n => n.trim()).filter(n => n !== "" && !isNaN(n));
              
              // שומרים ערכים קיימים כדי לא למחוק למשתמש בזמן הקלדה
              const currentValues = {};
              document.querySelectorAll('.ex-path').forEach(input => {
                  currentValues[input.dataset.num] = input.value;
              });
      
              container.innerHTML = nums.length > 0 ? "<label>הזן נתיב לכל חריג:</label>" : "";
      
              nums.forEach(num => {
                  const row = document.createElement('div');
                  row.className = 'exception-row';
                  const savedVal = currentValues[num] || "";
                  row.innerHTML = `<span>שלוחה ${num}:</span> <input type="text" class="ex-path" data-num="${num}" value="${savedVal}" placeholder="נתיב לשלוחה ${num}">`;
                  container.appendChild(row);
              });
          }
      
          function processData() {
              const from = parseInt(document.getElementById('from').value);
              const to = parseInt(document.getElementById('to').value);
              const defaultPath = document.getElementById('defaultPath').value;
              
              const exceptionPaths = {};
              document.querySelectorAll('.ex-path').forEach(input => {
                  exceptionPaths[input.dataset.num] = input.value;
              });
      
              let output = "";
              for (let i = from; i <= to; i++) {
                  const path = exceptionPaths[i] !== undefined ? exceptionPaths[i] : defaultPath;
                  output += i + "=" + path + "\r\n";
              }
      
              finalContent = output;
              document.getElementById('result').textContent = output;
              document.getElementById('downloadBtn').style.display = "block";
          }
      
          function downloadIni() {
              if (!finalContent) return;
              const blob = new Blob([finalContent], { type: 'text/plain' });
              const elem = window.document.createElement('a');
              elem.href = window.URL.createObjectURL(blob);
              elem.download = 'ExtensionNumbersAndAssociations.ini';
              document.body.appendChild(elem);
              elem.click();
              document.body.removeChild(elem);
          }
      </script>
      
      </body>
      </html>
      

      זה יגדיר לכל שלוחה ברירת מחדל לאן לעבור ושלוחות חריגות לכל שלוחה תוכל לבחור נתיב
      תעלה לשלוחה גם את ההגדרה הזו

      text_extensions=yes
      
      פורסם בעזרה הדדית למשתמשים מתקדמים
      B
      BEN ZION
    • RE: חדש! מודול ניתוב שיחות לפי תור ללא עלות יחידות - שרשור מתעדכן

      @מוזר הגיוני שהוא הגדיר מודל תור רגיל לכולם זה עובד בלי יחי' אין סיבה שרק לו זה יחייב יחי'

      פורסם בחדש במערכת
      B
      BEN ZION
    • RE: תפריט לבחירת קובץ

      @יוסף9 צריך M גדולה

      פורסם בבאגים במערכת
      B
      BEN ZION
    • RE: יצירת תיקייה באמצעות api

      @Ppgishonim אם אתה רוצה להעלות קובץ פשוט תרשום את הנתיב וזה ייצר את התיקיה

      פורסם בפורום מפתחים API
      B
      BEN ZION
    • RE: ניתוב למשאיר ההודעה בחינם

      @איל-משולש עכשיו שמודל תור ללא פרסומות וללא עלות אתה יכול לעשות את אותו דבר על מודל תור

      פורסם בטיפים עצות והדגמות מהמשתמשים
      B
      BEN ZION