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

    BEN ZION

    @BEN ZION

    145
    מוניטין
    122
    צפיות בפרופיל
    908
    פוסטים
    4
    עוקבים
    2
    עוקב אחרי
    תאריך הצטרפות
    נראה לאחרונה
    מיקום נוף הגליל

    BEN ZION הפסקת מעקב מעקב

    הפוסטים הטובים ביותר שנוצרו על ידי BEN ZION

    • מזמור לתודה

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

      ישנו סיפור על יהודי אחד שלא היו לו ילדים כמה שנים הוא נסע לבבא סאלי לקבל ברכה אחרי שנה נולד לו בן נסע לבבא סאלי אם קרטון בקבוקי ערק אמר לרב שהוא בא לומר תודה הרב אמר לו אתה יודע כמה יהודים באים אלי לבקש ברכה אף אחד לא מגיע לבשר בשורות טובות בזכות זה תזכה להרבה בשורות טובות את המעשה הזה סיפר הבן 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

    פוסטים אחרונים שנוצרו על ידי BEN ZION

    • RE: פילטר לפי רשימת תפוצה

      @יענקי-פולק תעשה שישמיע את הודעת הקמפיין למי שלא רשום ברשית"פ עיין פה להגדרות

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

      @אליהו-קטורזה אתה צריך להגדיר את השלוחה כ API למחוק את ההגדרות של שלוחת הקלטה

      פורסם בטיפים עצות והדגמות מהמשתמשים
      B
      BEN ZION
    • RE: בשורה משמחת! - 😀 מאגר השירים המלא מוכן לשיתוף! - 👏👏🎸🎶🪗🎤

      @קו-המוסיקה

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

      פורסם בשאלות ועזרה הדדית
      B
      BEN ZION
    • RE: תגובות | פיתוח פרטי | ניתוב למספר בהשמע"ק לפי הקשה בהקלטה

      @121244 תקרא את הנושא כאן

      עושה רושם שאתה מזלזל בתבונתו של cubase אם הוא עשה את זה ככה כנראה יש צורך

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

      @יצחק-י. המקליט עצמו הוא לא בעל השמחה ?

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

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

      פורסם בבאגים במערכת
      B
      BEN ZION
    • RE: תגובה ל"הכירו את מודול ההתראות: חדש וייחודי מבית ימות המשיח 🤩"

      @CUBASE במייל כתוב מפורש שזה לא ידרוש ממנו שום הגדרה

      פורסם בעזרה הדדית למשתמשים מתקדמים
      B
      BEN ZION
    • RE: תגובות: מדריך | כך תגדירו וירטואל+ במערכת רגילה!

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

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

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

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

      @CUBASE גימני פרו

      פורסם בעזרה הדדית למשתמשים מתקדמים
      B
      BEN ZION