• דף הבית
    • אינדקס קישורים
    • פוסטים אחרונים
    • משתמשים
    • חיפוש בהגדרות המתקדמות
    • חיפוש גוגל בפורום
    • ניהול המערכת
    • ניהול המערכת - שרת private
    • הרשמה
    • התחברות
    1. דף הבית
    2. שלוחה-404
    ש מנותק
    • פרופיל
    • עוקב אחרי 0
    • עוקבים 5
    • נושאים 34
    • פוסטים 590
    • קבוצות 0

    שלוחה-404

    @שלוחה-404

    154
    מוניטין
    132
    צפיות בפרופיל
    590
    פוסטים
    5
    עוקבים
    0
    עוקב אחרי
    תאריך הצטרפות
    נראה לאחרונה
    מיקום YM-H65FK4

    שלוחה-404 הפסקת מעקב מעקב

    הפוסטים הטובים ביותר שנוצרו על ידי שלוחה-404

    • ממשק HTML לאקסס פילטר

      אתמול לראשונה הצטרכתי למודל אקסס פילטר והסתבכתי אם ההגדרות שלו החלטתי לתת לגימיני לבנות ממשק HTML מונגש שמחזיר לך הגדרות לשלוחה
      מצורף קוד 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>מחולל הגדרות מתקדם - פילטר גישה (Access Filter)</title>
          <style>
              :root {
                  --primary-color: #0284c7;
                  --primary-hover: #0369a1;
                  --secondary-color: #f1f5f9;
                  --border-color: #cbd5e1;
                  --text-main: #0f172a;
                  --text-muted: #475569;
                  --bg-main: #ffffff;
                  --card-bg: #f8fafc;
                  --danger: #ef4444;
                  --danger-hover: #dc2626;
                  --success-bg: #1e293b;
                  --success-text: #10b981;
              }
      
              * {
                  box-sizing: border-box;
              }
      
              body {
                  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                  background-color: var(--secondary-color);
                  color: var(--text-main);
                  margin: 0;
                  padding: 20px;
                  line-height: 1.6;
              }
      
              h1, h2, h3, h4 {
                  color: var(--primary-color);
                  margin-top: 0;
              }
      
              .container {
                  max-width: 1200px;
                  margin: 0 auto;
                  background: var(--bg-main);
                  padding: 40px;
                  border-radius: 12px;
                  box-shadow: 0 4px 20px rgba(0,0,0,0.05);
              }
      
              .section {
                  background: var(--card-bg);
                  border: 1px solid var(--border-color);
                  padding: 25px;
                  border-radius: 10px;
                  margin-bottom: 30px;
              }
      
              .form-group {
                  margin-bottom: 20px;
              }
      
              .grid-2-col {
                  display: grid;
                  grid-template-columns: 1fr 1fr;
                  gap: 25px;
              }
      
              label {
                  display: block;
                  font-weight: 600;
                  margin-bottom: 8px;
                  color: var(--text-main);
              }
      
              .help-text {
                  display: block;
                  font-size: 0.85em;
                  color: var(--text-muted);
                  margin-top: 4px;
              }
      
              input[type="text"], 
              input[type="number"], 
              select, 
              input[type="time"], 
              input[type="date"], 
              input[type="datetime-local"] {
                  width: 100%;
                  padding: 12px;
                  border: 1px solid var(--border-color);
                  border-radius: 6px;
                  font-family: inherit;
                  font-size: 15px;
                  transition: border-color 0.2s;
              }
      
              input[type="text"]:focus, 
              input[type="number"]:focus, 
              select:focus {
                  border-color: var(--primary-color);
                  outline: none;
              }
      
              .checkbox-group {
                  display: flex;
                  flex-wrap: wrap;
                  gap: 12px;
                  background: var(--bg-main);
                  padding: 15px;
                  border: 1px solid var(--border-color);
                  border-radius: 8px;
              }
      
              .checkbox-group label {
                  display: flex;
                  align-items: center;
                  font-weight: normal;
                  background: #e2e8f0;
                  padding: 8px 16px;
                  border-radius: 20px;
                  cursor: pointer;
                  transition: all 0.2s;
                  margin: 0;
                  font-size: 14px;
              }
      
              .checkbox-group label:hover {
                  background: #cbd5e1;
              }
      
              .checkbox-group input[type="checkbox"], 
              .checkbox-group input[type="radio"] {
                  margin-left: 10px;
                  cursor: pointer;
              }
      
              .inline-group {
                  display: flex;
                  align-items: center;
                  gap: 15px;
                  background: var(--bg-main);
                  padding: 12px;
                  border: 1px solid var(--border-color);
                  border-radius: 8px;
              }
      
              .filter-card {
                  background: var(--bg-main);
                  border: 2px solid var(--primary-color);
                  padding: 30px;
                  border-radius: 10px;
                  margin-bottom: 30px;
                  position: relative;
              }
      
              .filter-header {
                  display: flex;
                  justify-content: space-between;
                  align-items: center;
                  border-bottom: 2px solid var(--secondary-color);
                  padding-bottom: 15px;
                  margin-bottom: 25px;
              }
      
              button {
                  cursor: pointer;
                  font-family: inherit;
                  transition: background-color 0.2s;
              }
      
              .primary-btn {
                  background-color: var(--primary-color);
                  color: white;
                  border: none;
                  padding: 14px 28px;
                  border-radius: 8px;
                  font-size: 16px;
                  font-weight: bold;
                  display: inline-flex;
                  align-items: center;
                  gap: 10px;
              }
      
              .primary-btn:hover {
                  background-color: var(--primary-hover);
              }
      
              .danger-btn {
                  background-color: var(--danger);
                  color: white;
                  border: none;
                  padding: 8px 16px;
                  border-radius: 6px;
                  font-size: 14px;
                  font-weight: bold;
              }
      
              .danger-btn:hover {
                  background-color: var(--danger-hover);
              }
      
              .dynamic-fields-container {
                  margin-top: 12px;
                  padding: 15px;
                  background: #e0f2fe;
                  border-radius: 8px;
                  border: 1px dashed #7dd3fc;
                  display: none;
              }
      
              .output-section {
                  background: var(--success-bg);
                  color: var(--success-text);
                  padding: 25px;
                  border-radius: 10px;
                  margin-top: 40px;
              }
      
              textarea {
                  width: 100%;
                  height: 350px;
                  background: #0f172a;
                  color: var(--success-text);
                  border: 1px solid #334155;
                  padding: 20px;
                  font-family: 'Courier New', Courier, monospace;
                  font-size: 16px;
                  resize: vertical;
                  direction: ltr;
                  text-align: left;
                  border-radius: 8px;
              }
          </style>
      </head>
      <body>
      
      <div class="container">
          <h1>מחולל פילטר גישה מורחב (Access Filter)</h1>
          <p>ממשק זה מאפשר יצירה ויזואלית של קובץ ה-<code>ext.ini</code>. סמן את האפשרויות, והקוד יתעדכן בזמן אמת בתחתית העמוד.</p>
      
          <div class="section">
              <h2>הגדרות בסיסיות וניתובים</h2>
              
              <div class="form-group">
                  <label>1. סוג ההטמעה (חובה):</label>
                  <div class="checkbox-group" style="background: transparent; border: none; padding: 0;">
                      <label><input type="radio" name="module_type" value="standalone" checked onchange="updateOutput()"> שלוחה בפני עצמה (type=access_filter)</label>
                      <label><input type="radio" name="module_type" value="embedded" onchange="updateOutput()"> הטמעה בתוך שלוחה אחרת (check_access_filter=yes)</label>
                  </div>
              </div>
      
              <div class="grid-2-col">
                  <div class="form-group">
                      <label>2. הגדרות זיהוי ולוג:</label>
                      <div class="checkbox-group">
                          <label><input type="checkbox" id="enter_id" onchange="updateOutput()"> רישום הגבלות לפי ת.ז (enter_id=yes)</label>
                          <label><input type="checkbox" id="log_save" onchange="updateOutput()"> שמירת קובץ לוג בשלוחה (access_filter_log_save=yes)</label>
                      </div>
                  </div>
      
                  <div class="form-group">
                      <label>3. נתיב שמירת קובץ הלוג (אופציונלי):</label>
                      <input type="text" id="log_folder" placeholder="לדוגמה: /1/1" oninput="updateOutput()" dir="ltr">
                  </div>
      
                  <div class="form-group">
                      <label>4. לאן לעבור אם מאושר (ברירת מחדל):</label>
                      <input type="text" id="yes_goto" placeholder="לדוגמה: /2" oninput="updateOutput()" dir="ltr">
                  </div>
      
                  <div class="form-group">
                      <label>5. לאן לעבור אם נחסם / אין הרשאה:</label>
                      <input type="text" id="no_goto" placeholder="לדוגמה: enter או /3" oninput="updateOutput()" dir="ltr">
                  </div>
              </div>
          </div>
      
          <div id="filters-container"></div>
          
          <button type="button" class="primary-btn" onclick="addFilter()">
              <span>+</span> הוסף תנאי פילטר נוסף (Access Filter Rule)
          </button>
      
          <div class="output-section">
              <h2 style="color: white; margin-top:0;">הקוד שלך (מוכן להעתקה):</h2>
              <textarea id="outputCode" readonly></textarea>
          </div>
      </div>
      
      <script>
          let filterCount = 0;
      
          const daysArray = [
              { val: 'sun', label: 'ראשון' }, { val: 'mon', label: 'שני' }, 
              { val: 'tue', label: 'שלישי' }, { val: 'wed', label: 'רביעי' },
              { val: 'thu', label: 'חמישי' }, { val: 'fri', label: 'שישי' }, 
              { val: 'sat', label: 'שבת' }
          ];
      
          const monthsGregorian = [
              { val: 'jan', label: 'ינואר' }, { val: 'feb', label: 'פברואר' }, 
              { val: 'mar', label: 'מרץ' }, { val: 'apr', label: 'אפריל' },
              { val: 'may', label: 'מאי' }, { val: 'jun', label: 'יוני' },
              { val: 'jul', label: 'יולי' }, { val: 'aug', label: 'אוגוסט' }, 
              { val: 'sep', label: 'ספטמבר' }, { val: 'oct', label: 'אוקטובר' },
              { val: 'nov', label: 'נובמבר' }, { val: 'dec', label: 'דצמבר' }
          ];
      
          const monthsHebrew = [
              { val: '01', label: 'תשרי' }, { val: '02', label: 'חשוון' }, 
              { val: '03', label: 'כסלו' }, { val: '04', label: 'טבת' },
              { val: '05', label: 'שבט' }, { val: '06', label: 'אדר (רגיל)' },
              { val: '07', label: 'ניסן' }, { val: '08', label: 'אייר' }, 
              { val: '09', label: 'סיוון' }, { val: '10', label: 'תמוז' }, 
              { val: '11', label: 'אב' }, { val: '12', label: 'אלול' },
              { val: '13', label: 'אדר א\'' }, { val: '14', label: 'אדר ב\'' }
          ];
      
          function addFilter() {
              filterCount++;
              const id = filterCount;
              
              let daysHtml = daysArray.map(d => `<label><input type="checkbox" class="chk-day-${id}" value="${d.val}" onchange="updateOutput()"> ${d.label}</label>`).join('');
              
              const filterHTML = `
                  <div class="filter-card" id="filter_${id}">
                      <div class="filter-header">
                          <h3>הגדרת זמנים - חוק מס' ${id} (access_filter_${id})</h3>
                          <button class="danger-btn" onclick="removeFilter(${id})">הסר חוק זה</button>
                      </div>
                      
                      <div class="grid-2-col">
                          <div class="form-group">
                              <label>1. סוג תאריך (חובה):</label>
                              <select id="v1_${id}" onchange="toggleCalType(${id}); updateOutput()">
                                  <option value="g">לועזי (g)</option>
                                  <option value="h">עברי (h)</option>
                              </select>
                          </div>
      
                          <div class="form-group">
                              <label>2. טווח שעות ביממה (חובה):</label>
                              <div class="inline-group">
                                  <input type="time" id="t_start_${id}" value="09:00" onchange="updateOutput()"> 
                                  <span>עד</span>
                                  <input type="time" id="t_end_${id}" value="17:00" onchange="updateOutput()">
                                  <label style="margin-right: auto;"><input type="checkbox" id="t_all_${id}" checked onchange="toggleInputs(['t_start_${id}', 't_end_${id}'], this.checked); updateOutput()"> כל היממה (*)</label>
                              </div>
                          </div>
                      </div>
      
                      <div class="form-group">
                          <label>3. ימי שבוע מורשים (חובה):</label>
                          <div class="checkbox-group">${daysHtml}</div>
                          <label style="margin-top:10px; display:inline-block;"><input type="checkbox" checked onchange="toggleCheckboxes('chk-day-${id}', this.checked); updateOutput()"> סמן הכל (*)</label>
                      </div>
      
                      <div class="grid-2-col">
                          <div class="form-group">
                              <label>4. ימים בחודש (חובה):</label>
                              <div class="inline-group">
                                  מיום: <input type="number" id="dm_start_${id}" min="1" max="31" placeholder="1" oninput="updateOutput()">
                                  עד יום: <input type="number" id="dm_end_${id}" min="1" max="31" placeholder="31" oninput="updateOutput()">
                                  <label><input type="checkbox" id="dm_all_${id}" checked onchange="toggleInputs(['dm_start_${id}', 'dm_end_${id}'], this.checked); updateOutput()"> הכל (*)</label>
                              </div>
                          </div>
      
                          <div class="form-group">
                              <label>6. שנות כניסה מורשות (חובה):</label>
                              <div class="inline-group">
                                  משנה: <input type="number" id="y_start_${id}" placeholder="לדוגמה: 2024" oninput="updateOutput()">
                                  עד שנה: <input type="number" id="y_end_${id}" placeholder="לדוגמה: 2025" oninput="updateOutput()">
                                  <label><input type="checkbox" id="y_all_${id}" checked onchange="toggleInputs(['y_start_${id}', 'y_end_${id}'], this.checked); updateOutput()"> הכל (*)</label>
                              </div>
                          </div>
                      </div>
      
                      <div class="form-group">
                          <label>5. חודשים מורשים בשנה (חובה):</label>
                          <div class="checkbox-group" id="months_container_${id}"></div>
                          <label style="margin-top:10px; display:inline-block;"><input type="checkbox" checked onchange="toggleCheckboxes('chk-month-${id}', this.checked); updateOutput()"> בחר כל השנה (*)</label>
                      </div>
      
                      <hr style="border:0; border-top:2px dashed var(--border-color); margin: 30px 0;">
                      <h3 style="margin-top:0;">הגבלות זמן מתקדמות לפי היסטוריית המשתמש (ערכים 7 עד 10)</h3>
                      
                      <div class="grid-2-col">
                          <div class="form-group">
                              <label>7. זמן המתנה בין כניסות (מתי ייתן לעבור שוב):</label>
                              <select id="v7_${id}" onchange="renderDynamicUi(this.value, 7, ${id}); updateOutput()">
                                  <option value="">פעם אחת בכל שיחה - ברירת מחדל (thiscall)</option>
                                  <option value="duration">משך זמן קצוב (שעות / ימים / חודשים)</option>
                                  <option value="datetime">עד תאריך ושעה מדויקים</option>
                                  <option value="date">עד תאריך ספציפי בחצות</option>
                                  <option value="time">היום עד שעה ספציפית</option>
                                  <option value="thisweek">עד סוף השבוע הנוכחי</option>
                                  <option value="thismonth">עד סוף חודש לועזי נוכחי</option>
                                  <option value="thismonth-x">עד יום ספציפי בחודש לועזי</option>
                                  <option value="thishebmonth">עד סוף חודש עברי נוכחי</option>
                                  <option value="thishebmonth-x">עד יום ספציפי בחודש עברי</option>
                                  <option value="23:59:59">עד סוף היום הנוכחי - חצות</option>
                                  <option value="none">חסימה קבועה לאחר כניסה (none)</option>
                              </select>
                              <div id="dyn_7_${id}" class="dynamic-fields-container"></div>
                          </div>
      
                          <div class="form-group">
                              <label>8. כמות כניסות מקסימלית למשתמש:</label>
                              <select id="v8_${id}" onchange="renderDynamicUi(this.value, 8, ${id}); updateOutput()">
                                  <option value="">פעם אחת בלבד (ברירת מחדל)</option>
                                  <option value="*">ללא הגבלת כמות פעמים (*)</option>
                                  <option value="custom">מספר כניסות מוגדר מראש</option>
                              </select>
                              <div id="dyn_8_${id}" class="dynamic-fields-container"></div>
                          </div>
      
                          <div class="form-group">
                              <label>9. איפוס הספירה (מתי יתחיל לספור מחדש?):</label>
                              <select id="v9_${id}" onchange="renderDynamicUi(this.value, 9, ${id}); updateOutput()">
                                  <option value="">אחרי חודש מהכניסה הראשונה (ברירת מחדל)</option>
                                  <option value="*">אחרי 12 חודשים / שנה (*)</option>
                                  <option value="duration">איפוס לאחר משך זמן קצוב (שעות / ימים / חודשים)</option>
                                  <option value="datetime">איפוס בתאריך ושעה מדויקים</option>
                                  <option value="date">איפוס בתאריך ספציפי בחצות</option>
                                  <option value="time">איפוס כל יום בשעה ספציפית</option>
                                  <option value="thisweek">איפוס בסוף השבוע הנוכחי</option>
                                  <option value="thismonth">איפוס בסוף חודש לועזי נוכחי</option>
                                  <option value="thismonth-x">איפוס ביום ספציפי בחודש לועזי</option>
                                  <option value="thishebmonth">איפוס בסוף חודש עברי נוכחי</option>
                                  <option value="thishebmonth-x">איפוס ביום ספציפי בחודש עברי</option>
                                  <option value="23:59:59">איפוס בסוף היום הנוכחי - חצות</option>
                                  <option value="none">ללא איפוס בכלל (none)</option>
                              </select>
                              <div id="dyn_9_${id}" class="dynamic-fields-container"></div>
                          </div>
      
                          <div class="form-group">
                              <label>10. זמן חסימה מוחלטת לשלוחה:</label>
                              <select id="v10_${id}" onchange="renderDynamicUi(this.value, 10, ${id}); updateOutput()">
                                  <option value="">ללא חסימה מוחלטת (ברירת מחדל)</option>
                                  <option value="duration">חסימה לאחר משך זמן קצוב מהכניסה הראשונה</option>
                                  <option value="datetime">חסימה מתאריך ושעה מדויקים</option>
                                  <option value="date">חסימה מתאריך ספציפי בחצות</option>
                                  <option value="time">חסימה היום משעה ספציפית</option>
                                  <option value="thisweek">חסימה בסוף השבוע בו בוצעה הכניסה</option>
                                  <option value="thismonth">חסימה בסוף החודש הלועזי הנוכחי</option>
                                  <option value="thismonth-x">חסימה ביום ספציפי בחודש לועזי</option>
                                  <option value="thishebmonth">חסימה בסוף החודש העברי הנוכחי</option>
                                  <option value="thishebmonth-x">חסימה ביום ספציפי בחודש עברי</option>
                                  <option value="23:59:59">חסימה מוחלטת בסוף היום (חצות)</option>
                              </select>
                              <div id="dyn_10_${id}" class="dynamic-fields-container"></div>
                          </div>
                      </div>
                      
                      <div class="form-group" style="margin-top:20px; background: var(--secondary-color); padding: 15px; border-radius: 8px;">
                          <label>ניתוב ספציפי (Divert) לעומדים בתנאי של חוק זה:</label>
                          <input type="text" id="spec_goto_${id}" placeholder="לדוגמה: /5 (ידרוס את הניתוב הכללי למאשרים)" oninput="updateOutput()" dir="ltr">
                      </div>
                  </div>
              `;
              
              document.getElementById('filters-container').insertAdjacentHTML('beforeend', filterHTML);
              
              toggleCalType(id);
              toggleInputs([`t_start_${id}`, `t_end_${id}`], true);
              toggleInputs([`dm_start_${id}`, `dm_end_${id}`], true);
              toggleInputs([`y_start_${id}`, `y_end_${id}`], true);
              
              updateOutput();
          }
      
          function removeFilter(id) {
              document.getElementById(`filter_${id}`).remove();
              updateOutput();
          }
      
          function toggleCalType(id) {
              const type = document.getElementById(`v1_${id}`).value;
              const container = document.getElementById(`months_container_${id}`);
              const activeArray = type === 'g' ? monthsGregorian : monthsHebrew;
              
              container.innerHTML = activeArray.map(m => 
                  `<label><input type="checkbox" class="chk-month-${id}" value="${m.val}" onchange="updateOutput()"> ${m.label}</label>`
              ).join('');
              
              toggleCheckboxes(`chk-month-${id}`, true);
          }
      
          function toggleInputs(ids, disable) {
              ids.forEach(id => { 
                  const el = document.getElementById(id); 
                  if(el) {
                      el.disabled = disable; 
                      if(disable) el.value = ''; 
                  }
              });
          }
      
          function toggleCheckboxes(className, isChecked) {
              document.querySelectorAll(`.${className}`).forEach(cb => { 
                  cb.checked = isChecked; 
                  cb.disabled = isChecked; 
              });
          }
      
          function renderDynamicUi(type, fieldNum, id) {
              const container = document.getElementById(`dyn_${fieldNum}_${id}`);
              
              if (!type || ['*', 'thiscall', 'thisweek', 'thismonth', 'thishebmonth', '23:59:59', 'none'].includes(type)) {
                  container.style.display = 'none';
                  container.innerHTML = '';
                  return;
              }
              
              container.style.display = 'block';
              
              if (type === 'duration') {
                  container.innerHTML = `
                      <div class="inline-group" style="border:none; padding:0; background:transparent;">
                          <input type="number" id="dyn_val_${fieldNum}_${id}" min="1" placeholder="כמות" style="width:120px;" oninput="updateOutput()">
                          <select id="dyn_unit_${fieldNum}_${id}" onchange="updateOutput()">
                              <option value="s">שניות (s)</option>
                              <option value="m">דקות (m)</option>
                              <option value="h">שעות (h)</option>
                              <option value="d">ימים (d)</option>
                              <option value="M">חודשים (M)</option>
                          </select>
                      </div>`;
              } else if (type === 'datetime') {
                  container.innerHTML = `<input type="datetime-local" id="dyn_val_${fieldNum}_${id}" step="1" oninput="updateOutput()">`;
              } else if (type === 'date') {
                  container.innerHTML = `<input type="date" id="dyn_val_${fieldNum}_${id}" oninput="updateOutput()">`;
              } else if (type === 'time') {
                  container.innerHTML = `<input type="time" id="dyn_val_${fieldNum}_${id}" step="1" oninput="updateOutput()">`;
              } else if (type === 'thismonth-x' || type === 'thishebmonth-x') {
                  let maxDays = type === 'thismonth-x' ? 31 : 30;
                  container.innerHTML = `
                      <div class="inline-group" style="border:none; padding:0; background:transparent;">
                          יום ספציפי בחודש: <input type="number" id="dyn_val_${fieldNum}_${id}" min="1" max="${maxDays}" placeholder="1" style="width:100px;" oninput="updateOutput()">
                      </div>`;
              } else if (type === 'custom') {
                  container.innerHTML = `<input type="number" id="dyn_val_${fieldNum}_${id}" min="1" placeholder="הכנס מספר מדוייק..." oninput="updateOutput()">`;
              }
          }
      
          function calculateRangeFromCheckboxes(className, masterArray) {
              const checkboxes = document.querySelectorAll(`.${className}:not(:disabled)`);
              if(checkboxes.length === 0) return '*'; 
              
              let checkedIndices = [];
              checkboxes.forEach((cb, index) => { 
                  if(cb.checked) checkedIndices.push(index); 
              });
              
              if(checkedIndices.length === 0) return '*';
              
              const minIndex = Math.min(...checkedIndices);
              const maxIndex = Math.max(...checkedIndices);
              
              if (minIndex === maxIndex) {
                  return `${masterArray[minIndex].val}-${masterArray[minIndex].val}`;
              }
              return `${masterArray[minIndex].val}-${masterArray[maxIndex].val}`;
          }
      
          function formatNumberRange(startId, endId, allCheckboxId) {
              if (document.getElementById(allCheckboxId).checked) return '*';
              
              const startVal = document.getElementById(startId).value;
              const endVal = document.getElementById(endId).value;
              
              if (!startVal && !endVal) return '*';
              
              const pad = (num) => (num && num.length === 1) ? '0' + num : num;
              
              if (startVal && !endVal) return `${pad(startVal)}-${pad(startVal)}`;
              if (!startVal && endVal) return `${pad(endVal)}-${pad(endVal)}`;
              
              return `${pad(startVal)}-${pad(endVal)}`;
          }
      
          function getDynamicFieldValue(fieldNum, id) {
              const selectType = document.getElementById(`v${fieldNum}_${id}`).value;
              
              if (!selectType) return "*"; 
              if (['*', 'thiscall', 'thisweek', 'thismonth', 'thishebmonth', '23:59:59', 'none'].includes(selectType)) {
                  return selectType;
              }
              
              const inputEl = document.getElementById(`dyn_val_${fieldNum}_${id}`);
              if (!inputEl || !inputEl.value) return "*";
      
              if (selectType === 'duration') {
                  const unit = document.getElementById(`dyn_unit_${fieldNum}_${id}`).value;
                  return inputEl.value + unit;
              } else if (selectType === 'datetime') {
                  let dt = inputEl.value.replace('T', ' ');
                  if (dt.length === 16) dt += ":00"; 
                  return dt;
              } else if (selectType === 'time') {
                  let t = inputEl.value;
                  if (t.length === 5) t += ":00"; 
                  return t;
              } else if (selectType === 'thismonth-x') {
                  return `thismonth-${inputEl.value}`;
              } else if (selectType === 'thishebmonth-x') {
                  return `thishebmonth-${inputEl.value}`;
              } else if (selectType === 'date' || selectType === 'custom') {
                  return inputEl.value;
              }
              
              return "*";
          }
      
          function updateOutput() {
              let code = "";
              
              code += "; --- הגדרות בסיסיות לפילטר ---\n";
              const isStandalone = document.querySelector('input[name="module_type"]:checked').value === 'standalone';
              code += isStandalone ? "type=access_filter\n" : "check_access_filter=yes\n";
              
              if (document.getElementById('enter_id').checked) {
                  code += "enter_id=yes\n";
              }
              
              if (document.getElementById('log_save').checked) {
                  code += "access_filter_log_save=yes\n";
              }
      
              const logFolder = document.getElementById('log_folder').value.trim();
              if (logFolder) {
                  code += `access_filter_log_time_folder=${logFolder}\n`;
              }
              
              const yesGoto = document.getElementById('yes_goto').value.trim();
              if (yesGoto) code += `access_filter_yes_goto=${yesGoto}\n`;
              
              const noGoto = document.getElementById('no_goto').value.trim();
              if (noGoto) code += `access_filter_no_goto=${noGoto}\n`;
              
              code += "\n; --- חוקי הפילטר (זמנים והגבלות) ---\n";
      
              const filterCards = document.querySelectorAll('.filter-card');
              filterCards.forEach((card, index) => {
                  const ruleNumber = index + 1; 
                  const id = card.id.split('_')[1]; 
      
                  const specificGoto = document.getElementById(`spec_goto_${id}`).value.trim();
                  if (specificGoto) {
                      code += `access_filter_${ruleNumber}_yes_goto=${specificGoto}\n`;
                  }
      
                  const v1 = document.getElementById(`v1_${id}`).value; 
                  
                  const v2 = document.getElementById(`t_all_${id}`).checked ? '*' : 
                             `${document.getElementById(`t_start_${id}`).value}-${document.getElementById(`t_end_${id}`).value}`;
                  
                  const v3 = calculateRangeFromCheckboxes(`chk-day-${id}`, daysArray);
                  const v4 = formatNumberRange(`dm_start_${id}`, `dm_end_${id}`, `dm_all_${id}`);
                  
                  const currentMonthsArray = v1 === 'g' ? monthsGregorian : monthsHebrew;
                  const v5 = calculateRangeFromCheckboxes(`chk-month-${id}`, currentMonthsArray);
                  
                  const v6 = formatNumberRange(`y_start_${id}`, `y_end_${id}`, `y_all_${id}`);
                  
                  const v7 = getDynamicFieldValue(7, id);
                  const v8 = getDynamicFieldValue(8, id);
                  const v9 = getDynamicFieldValue(9, id);
                  const v10 = getDynamicFieldValue(10, id);
      
                  let valuesArray = [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10];
                  
                  for (let i = 0; i < valuesArray.length; i++) {
                      if (!valuesArray[i] || valuesArray[i] === "" || valuesArray[i] === "-") {
                          valuesArray[i] = "*";
                      }
                  }
      
                  // יצירת השורה עצמה: שים לב לנקודה בסוף המחרוזת כפי שביקשת
                  code += `access_filter_${ruleNumber}=${valuesArray.join('.')}.\n`;
              });
      
              document.getElementById('outputCode').value = code;
          }
      
          window.onload = () => {
              addFilter();
          };
      </script>
      
      </body>
      </html>
      

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

      מי שיותר נוח לו מצורף קישור לעבודה ישירה

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

      ‏עריכה:י"ז תשרי תשפ"ז

      חדש

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

      @echo off
      chcp 65001 >nul
      echo מוסיף אישור גלובלי למערכת ימות המשיח בדפדפן אדג'...
       
      :: הגדרת חריגים למדיניות השמע של אדג'
      reg add "HKLM\SOFTWARE\Policies\Microsoft\Edge\AutoplayAllowlist" /v "1" /t REG_SZ /d "https://call2all.co.il" /f >nul
      reg add "HKLM\SOFTWARE\Policies\Microsoft\Edge\AutoplayAllowlist" /v "2" /t REG_SZ /d "https://private.call2all.co.il" /f >nul
      reg add "HKLM\SOFTWARE\Policies\Microsoft\Edge\AutoplayAllowlist" /v "3" /t REG_SZ /d "https://sip.yemot.co.il" /f >nul
       
      echo התהליך הסתיים! סגור את כל חלונות הדפדפן ופתח מחדש.
      pause
      

      באג קטן

      בניתוק השיחה מחשבון SIP משום מה זה לא מנתק את השיחה הראשונה שנכנסה למערכת מהצינתוק מומלץ לנתק ידנית או להגדיר טיימר של 10 דקות

      התוסף נמצא בחנות הרשמית של גוגל כאן

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

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

      💡 איך זה עובד בדיוק?

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

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

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

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

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

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

      פורסם בטיפים עצות והדגמות מהמשתמשים
      ש
      שלוחה-404
    • RE: מה מרוויחים ממוניטין?

      @שואל-שאלה כתב:

      מוניטין מעיד עליך לשאר משתמשי הפורום - את כמות הידע שלך בתחום הקווים

      פעם מוניטין העיד על ידע בתחום הקווים היום מוניטין מעיד על "השקפה טהורה"

      פורסם בעל הפורום
      ש
      שלוחה-404
    • RE: הסוף לשוטט אחר הודעת מערכת!🔍 נא ונא לא להגיב!!!

      @פיתה למה אתה לא עושה חיפוש פנימי קונטרול F ברשימת ההודעות ?

      פורסם בטיפים עצות והדגמות מהמשתמשים
      ש
      שלוחה-404
    • חדש בקו: נפרדים מקבצי השקט! 🤫🔇

      כל מי שבונה קווים ומשתמש במודלים בדרכים יצירתיות מכיר את הטרחה: המערכת מתעקשת להשמיע הודעה שלא מתאימה לקונספט, ועד היום הפתרון היחיד היה להקליט או לייצר קובץ שמע שקט (blank), להיכנס ללשונית "הודעות" ולהחליף את קובץ המערכת ידנית.
      כנסו לפה

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ש
      שלוחה-404
    • RE: עריכת הפרופיל, איך?

      @שיחה-ממתינה אתה אוהב לעשות תחיית המתים
      תניח למתים לנוח על משכבם בשלום

      פורסם בעל הפורום
      ש
      שלוחה-404
    • רכישת יחידות

      האם יש מישהו שמוכר יחידות יותר זול מימות המשיח?
      אני צריך 10,000 יחידות

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ש
      שלוחה-404
    • RE: מידע מעניין על הפורום שלנו שווה להיכנס (ולעזור אולי??)

      @פיתה @lavitoren-הטי-ל מידע מעניין API=שפן

      פורסם בעל הפורום
      ש
      שלוחה-404
    • RE: באגים במערכת

      @שואל-שאלה נראה שחזרנו לרוסיה מה שלא מתאים מעלימים

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ש
      שלוחה-404
    • RE: כמות רשומים לרשימת תפוצה עזרה!!! 🙏🙏🙏

      @לימוד-בתורת-מרן עיין פה

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

      פורסם בשאלות ועזרה הדדית
      ש
      שלוחה-404

    פוסטים אחרונים שנוצרו על ידי שלוחה-404

    • RE: בוט שעוקב אחרי הודעות

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

      api_add_0=secret1=
      api_add_1=RECORD=/02/2
      api_add_2=NATIV=/02/1
      api_add_3=email1=@gmail.com
      

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

      <?php
      
      declare(strict_types=1);
      
      namespace App\Webhook;
      
      use RuntimeException;
      use Throwable;
      use CURLFile;
      use DateTimeImmutable;
      use CurlHandle;
      
      // ==========================================
      // ⚙️ טעינת הגדרות ראשיות
      // ==========================================
      if (file_exists($configPath)) {
          require_once $configPath;
      }
      
      date_default_timezone_set('Asia/Jerusalem');
      
      // ==========================================
      // 🛡️ Enums & DTOs
      // ==========================================
      
      enum Decision: string
      {
          case APPROVE = 'APPROVE';
          case REJECT  = 'REJECT';
          case UNKNOWN = 'UNKNOWN';
      }
      
      enum Engine: string
      {
          case GEMINI_AUDIO = 'Gemini (Audio)';
          case GEMINI_TEXT  = 'Gemini (Text Fallback)';
          case PYTHON_DICT  = 'Python + Dictionary';
          case ERROR        = 'System Error';
      }
      
      readonly class AnalysisResult
      {
          public function __construct(
              public Decision $decision,
              public string $transcription,
              public Engine $engine,
              public string $bannedWord = ''
          ) {}
      }
      
      // ==========================================
      // 🛠️ מחלקות תשתית ולוגיקה
      // ==========================================
      
      readonly class Logger
      {
          public function __construct(private string $logDir) {}
      
          public function log(string $type, string $message): void
          {
              $timeNow = date("Y-m-d H:i:s");
              $logEntry = sprintf("[%s] [%s] %s%s", $timeNow, $type, $message, PHP_EOL);
              
              $periodId = (int)floor(time() / (15 * 86400));
              $currentLogFile = rtrim($this->logDir, '/') . "/system_log_period_{$periodId}.txt";
              
              file_put_contents($currentLogFile, $logEntry, FILE_APPEND | LOCK_EX);
              
              $this->cleanupOldLogs($periodId);
          }
          
          private function cleanupOldLogs(int $currentPeriodId): void
          {
              $files = glob(rtrim($this->logDir, '/') . "/system_log_period_*.txt");
              if ($files === false) return;
      
              foreach ($files as $file) {
                  if (preg_match('/system_log_period_(\d+)\.txt$/', $file, $matches)) {
                      $filePeriod = (int)$matches[1];
                      if ($filePeriod < $currentPeriodId - 1) {
                          @unlink($file);
                      }
                  }
              }
          }
      }
      
      class SystemReleaser
      {
          public static function release(string $output = "OK"): void
          {
              if (function_exists('fastcgi_finish_request')) {
                  echo $output;
                  session_write_close();
                  fastcgi_finish_request();
              } else {
                  ignore_user_abort(true);
                  set_time_limit(300);
                  ob_start();
                  echo $output;
                  header("Content-Length: " . (string)ob_get_length());
                  header("Connection: close");
                  ob_end_flush();
                  flush();
              }
          }
      }
      
      readonly class YemotClient
      {
          public function __construct(
              private string $apiUrl,
              private string $token
          ) {}
      
          public function call(string $method, array $params = []): array
          {
              $params['token'] = $this->token;
              $ch = curl_init($this->apiUrl . $method);
              
              if (!$ch) {
                  throw new RuntimeException("Failed to init cURL for Yemot API.");
              }
      
              curl_setopt_array($ch, [
                  CURLOPT_POST           => true,
                  CURLOPT_POSTFIELDS     => http_build_query($params),
                  CURLOPT_RETURNTRANSFER => true,
                  CURLOPT_TIMEOUT        => 15
              ]);
      
              $response = curl_exec($ch);
              curl_close($ch);
      
              $decoded = json_decode((string)$response, true);
              return is_array($decoded) ? $decoded : [];
          }
      
          public function downloadAudio(string $path): string
          {
              $url = $this->apiUrl . "DownloadFile?token={$this->token}&path=ivr2:{$path}";
              $ch = curl_init($url);
              
              if (!$ch) {
                  throw new RuntimeException("Failed to init cURL for Audio Download.");
              }
      
              curl_setopt_array($ch, [
                  CURLOPT_RETURNTRANSFER => true,
                  CURLOPT_SSL_VERIFYPEER => false,
                  CURLOPT_TIMEOUT        => 30
              ]);
              
              $audio = curl_exec($ch);
              $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
              curl_close($ch);
      
              if ($audio === false || $httpCode !== 200) {
                  throw new RuntimeException("Failed to download audio from: {$path}");
              }
              return (string)$audio;
          }
      
          public function normalizePhone(string $phone): string
          {
              $clean = preg_replace('/[^0-9]/', '', $phone);
              if (str_starts_with($clean, '972')) {
                  $clean = substr($clean, 3);
              }
              return ltrim($clean, '0');
          }
      }
      
      class DictionaryManager
      {
          public function __construct(private readonly string $filePath)
          {
              if (!file_exists($this->filePath)) {
                  $initialWords = ];
                  file_put_contents($this->filePath, json_encode($initialWords, JSON_UNESCAPED_UNICODE));
              }
          }
      
          public function getWords(): array
          {
              $data = json_decode(file_get_contents($this->filePath) ?: '[]', true);
              return is_array($data) ? $data : [];
          }
      
          public function addWord(string $word): void
          {
              $word = trim($word);
              if ($word === '') return;
      
              $fp = fopen($this->filePath, 'c+');
              if ($fp && flock($fp, LOCK_EX)) {
                  $size = filesize($this->filePath);
                  $content = $size > 0 ? fread($fp, $size) : '[]';
                  $words = json_decode($content, true);
                  if (!is_array($words)) $words = [];
      
                  if (!in_array($word, $words, true)) {
                      $words[] = $word;
                      ftruncate($fp, 0);
                      rewind($fp);
                      fwrite($fp, json_encode($words, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
                  }
                  flock($fp, LOCK_UN);
                  fclose($fp);
              }
          }
      }
      
      class TzintukManager
      {
          public function __construct(
              private readonly string $numbersFile,
              private readonly Logger $logger
          ) {
              if (!file_exists($this->numbersFile)) {
                  file_put_contents($this->numbersFile, json_encode([]));
              }
          }
      
          public function updateHours(string $phone, string $m, string $h, string $adminPhone): void
          {
              $numbersData = json_decode(file_get_contents($this->numbersFile) ?: '[]', true) ?: [];
              $cleanPhone = str_starts_with(trim($phone), '0') ? trim($phone) : '0' . trim($phone);
      
              $numbersData[$cleanPhone] = [
                  'start' => str_replace("-", ":", $m),
                  'end'   => str_replace("-", ":", $h)
              ];
              
              file_put_contents($this->numbersFile, json_encode($numbersData, JSON_PRETTY_PRINT | LOCK_EX));
          }
      
          public function sendTzintuk(YemotClient $apiClient): void
          {
              $numbersData = json_decode(file_get_contents($this->numbersFile) ?: '[]', true) ?: [];
              $phonesToCall = [];
              $curTime = time();
      
              foreach ($numbersData as $num => $hours) {
                  $s = strtotime($hours['start']);
                  $e = strtotime($hours['end']);
                  if ($s && $e) {
                      $inRange = $s <= $e ? ($curTime >= $s && $curTime <= $e) : ($curTime >= $s || $curTime <= $e);
                      if ($inRange) $phonesToCall[] = $num;
                  }
              }
      
              $phonesParam = empty($phonesToCall) ? "" : "," . implode(",", $phonesToCall);
              try {
                  $apiClient->call("RunTzintuk", [
                      'callerId' => '',
                      'TzintukTimeOut' => 6,
                      'phones' => "tzl:women{$phonesParam}"
                  ]);
                  $this->logger->log("צינתוק", "נשלח צינתוק למנויים במערכת.");
              } catch (Throwable $e) {
                  // התעלמות
              }
          }
      }
      
      // ==========================================
      // 🧠 מנוע ניהול סינון מקבילי (Orchestrator)
      // ==========================================
      
      readonly class ModerationOrchestrator
      {
          private const GEMINI_API = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent';
      
          private const MODERATION_PROMPT = "You are a ZERO TOLERANCE content moderation AI for a community hotline. "
              . "Listen and transcribe perfectly in Hebrew. Pay close attention to the context of the words.\n"
              . "REJECT if the audio mentions ANY of the following:\n"
              . "1. Violence, threats, or physical conflicts.\n"
              . "2. Gossip, slander, speaking negatively about specific people, or talking 'behind someone's back' (e.g., 'מאחורי הגב').\n"
              . "3. Marital status changes, separation, divorce (e.g., 'נפרדתי מבעלי'), or airing personal family drama.\n"
              . "APPROVE:\n"
              . "1. Genuine requests for help, lost and found, technical info, benign greetings.\n"
              . "2. Updates or complaints about PUBLIC SERVICES or businesses (e.g., 'הספרייה לא נפתחת, אי אפשר לסמוך על זה' - This is ALLOWED because it's a consumer update, not personal gossip).\n"
              . "IMPORTANT: DO NOT REJECT merely because a sentence is cut off. DO NOT REJECT criticism of public institutions.\n"
              . "Return EXACT JSON: {\"decision\":\"APPROVE|REJECT\",\"transcription\":\"exact text\",\"reason_word\":\"worst phrase if rejected\"}";
      
          public function __construct(
              private string $geminiKey,
              private string $pythonUrl,
              private string $pythonKey,
              private DictionaryManager $dictionary,
              private TzintukManager $tzManager,
              private YemotClient $yemotClient
          ) {}
      
          // 🌟 הוספנו כאן את הפרמטר $skipTzintuk
          public function processParallelAndDecide(string $audioData, bool $isStrict, bool $skipTzintuk): AnalysisResult
          {
              $tmpFile = tempnam(sys_get_temp_dir(), 'py_') . '.wav';
              file_put_contents($tmpFile, $audioData);
              $base64Audio = base64_encode($audioData);
      
              $mh = curl_multi_init();
      
              $chGemini = $this->createGeminiAudioRequest($base64Audio);
              curl_multi_add_handle($mh, $chGemini);
      
              $chPython = $this->createPythonRequest($tmpFile);
              curl_multi_add_handle($mh, $chPython);
      
              $startTime = microtime(true);
              $tzintukSent = false;
              $active = null;
      
              do {
                  curl_multi_exec($mh, $active);
                  curl_multi_select($mh, 0.5);
      
                  $elapsed = microtime(true) - $startTime;
                  
                  // 🌟 הצינתוק יישלח רק אם המשתמש לא חסם אותו ($skipTzintuk == false)
                  if (!$skipTzintuk && !$isStrict && $elapsed > 35.0 && !$tzintukSent) {
                      $this->tzManager->sendTzintuk($this->yemotClient);
                      $tzintukSent = true;
                  }
      
              } while ($active > 0);
      
              $geminiResponse = curl_multi_getcontent($chGemini);
              $pythonResponse = curl_multi_getcontent($chPython);
      
              curl_multi_remove_handle($mh, $chGemini);
              curl_multi_remove_handle($mh, $chPython);
              curl_multi_close($mh);
              @unlink($tmpFile);
      
              $geminiParsed = $this->parseGeminiResponse((string)$geminiResponse);
              $finalResult = null;
      
              if ($geminiParsed['decision'] !== Decision::UNKNOWN) {
                  $finalResult = new AnalysisResult(
                      $geminiParsed['decision'], 
                      $geminiParsed['transcription'], 
                      Engine::GEMINI_AUDIO,
                      $geminiParsed['reason_word']
                  );
              } else {
                  $finalResult = $this->handlePythonFallback((string)$pythonResponse);
              }
      
              // 🌟 גם פה נוודא ששולחים צינתוק בסיום רק אם הוא לא בוטל
              if (!$skipTzintuk && !$tzintukSent && $finalResult->decision === Decision::APPROVE) {
                  $this->tzManager->sendTzintuk($this->yemotClient);
              } 
      
              if ($finalResult->bannedWord !== '') {
                  $this->dictionary->addWord($finalResult->bannedWord);
              }
      
              return $finalResult;
          }
      
          private function handlePythonFallback(string $pythonResponse): AnalysisResult
          {
              $data = json_decode($pythonResponse, true);
              $transcription = trim($data['recognized_text'] ?? '');
      
              if ($transcription === '') {
                  return new AnalysisResult(Decision::UNKNOWN, 'כשל בתמלול', Engine::ERROR);
              }
      
              $bannedWords = $this->dictionary->getWords();
              foreach ($bannedWords as $word) {
                  if (str_contains($transcription, $word)) {
                      return new AnalysisResult(Decision::REJECT, $transcription, Engine::PYTHON_DICT, $word);
                  }
              }
      
              return $this->analyzeTextWithGemini($transcription);
          }
      
          private function analyzeTextWithGemini(string $text): AnalysisResult
          {
              $ch = curl_init(self::GEMINI_API . '?key=' . $this->geminiKey);
              if (!$ch) throw new RuntimeException("Failed to init cURL for Gemini Text.");
      
              $payload = ['contents' => [['parts' => [['text' => self::MODERATION_PROMPT . "\nText: " . $text]]]], 'generationConfig' => ['temperature' => 0.0, 'responseMimeType' => 'application/json']];
              
              curl_setopt_array($ch, [
                  CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload),
                  CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
                  CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10
              ]);
              
              $res = curl_exec($ch);
              curl_close($ch);
              
              $parsed = $this->parseGeminiResponse((string)$res);
              return new AnalysisResult($parsed['decision'] !== Decision::UNKNOWN ? $parsed['decision'] : Decision::APPROVE, $text, Engine::GEMINI_TEXT, $parsed['reason_word']);
          }
      
          private function createGeminiAudioRequest(string $base64Audio): CurlHandle
          {
              $payload = [
                  'contents' => [['parts' => [['inlineData' => ['mimeType' => 'audio/wav', 'data' => $base64Audio]], ['text' => self::MODERATION_PROMPT]]]],
                  'generationConfig' => ['temperature' => 0.0, 'responseMimeType' => 'application/json']
              ];
      
              $ch = curl_init(self::GEMINI_API . '?key=' . $this->geminiKey);
              if (!$ch) throw new RuntimeException("Failed to init cURL for Gemini Audio.");
      
              curl_setopt_array($ch, [
                  CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($payload),
                  CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
                  CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 45
              ]);
              return $ch;
          }
      
          private function createPythonRequest(string $filePath): CurlHandle
          {
              $ch = curl_init($this->pythonUrl . '/process_by_file');
              if (!$ch) throw new RuntimeException("Failed to init cURL for Python Audio.");
      
              $headers = $this->pythonKey !== '' ? ['Authorization: Bearer ' . $this->pythonKey] : [];
              
              curl_setopt_array($ch, [
                  CURLOPT_POST => true, CURLOPT_POSTFIELDS => ['file' => new CURLFile($filePath, 'audio/wav', 'rec.wav')],
                  CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 60,
                  CURLOPT_SSL_VERIFYPEER => false
              ]);
              return $ch;
          }
      
          private function parseGeminiResponse(string $json): array
          {
              $data = json_decode($json, true);
              $text = $data['candidates'][0]['content']['parts'][0]['text'] ?? '{}';
              $result = json_decode($text, true) ?: [];
      
              $decStr = strtoupper(trim($result['decision'] ?? 'UNKNOWN'));
              $decision = match ($decStr) {
                  'APPROVE' => Decision::APPROVE,
                  'REJECT'  => Decision::REJECT,
                  default   => Decision::UNKNOWN,
              };
      
              return [
                  'decision'      => $decision,
                  'transcription' => trim($result['transcription'] ?? ''),
                  'reason_word'   => trim($result['reason_word'] ?? '')
              ];
          }
      }
      
      // ==========================================
      // 🚀 Main Execution
      // ==========================================
      
      $getString = fn(string $key): string => isset($_REQUEST[$key]) && is_string($_REQUEST[$key]) ? trim($_REQUEST[$key]) : '';
      
      $secret1 = $getString('secret1');
      $phone   = $getString('ApiPhone');
      $record  = $getString('RECORD');
      $nativ   = $getString('NATIV');
      $email   = $getString('email1');
      $m       = $getString('M');
      $h       = $getString('H');
      $adminPh = $getString('ApiRealPhone') ?: $phone;
      
      // 🌟 בדיקה אם התקבל פרמטר לביטול הצינתוק
      $noTzintukParam = $getString('NoTzintuk');
      $skipTzintuk = ($noTzintukParam === '1' || strtolower($noTzintukParam) === 'yes');
      
      $logger = new Logger(__DIR__);
      $expectedSecret = defined('SECRET1') ? SECRET1 : '';
      
      if ($secret1 !== $expectedSecret) {
          $logger->log("שגיאת גישה", "הסיסמה שהתקבלה מול המערכת אינה תואמת.");
          exit("Access Denied");
      }
      
      $yemotToken = defined('YEMOT_TOKEN') ? YEMOT_TOKEN : '';
      $yemotApi   = defined('YEMOT_API') ? YEMOT_API : 'https://www.call2all.co.il/ym/api/';
      $yemotClient = new YemotClient($yemotApi, $yemotToken);
      $tzManager  = new TzintukManager(__DIR__ . '/numbers1.json', $logger);
      
      if ($phone !== '' && $m !== '' && $h !== '') {
          $tzManager->updateHours($phone, $m, $h, $adminPh);
          SystemReleaser::release("OK");
          exit;
      }
      
      SystemReleaser::release("OK");
      
      try {
          if ($phone !== '' && $record !== '' && $nativ !== '') {
              $tzintukStatusStr = $skipTzintuk ? "מבוטל" : "פעיל";
              $logger->log("התחלת תהליך", "התקבלה הודעה לסינון ממספר: {$phone} | צינתוק להודעה זו: {$tzintukStatusStr}");
              
              $recordFolder = rtrim($record, '/');
              $nativFolder  = rtrim($nativ, '/');
              
              $logData = $yemotClient->call("RenderYMGRFile", ['wath' => "ivr2:{$recordFolder}/record_log.ymgr", 'convertType' => 'json', 'notLoadLang' => 1]);
              
              $lastFile = null;
              $searchPhone = $yemotClient->normalizePhone($phone);
              
              if (isset($logData['data']) && is_array($logData['data'])) {
                  foreach (array_reverse($logData['data']) as $row) {
                      $rowPhone = $yemotClient->normalizePhone($row['Phone'] ?? '');
                      
                      if ($rowPhone === $searchPhone) {
                          $rowDate = $row['Date'] ?? date('d/m/Y'); 
                          $rowTime = $row['Time'] ?? date('H:i:s');
                          
                          $fileDateTime = DateTimeImmutable::createFromFormat('d/m/Y H:i:s', "$rowDate $rowTime");
                          $now = new DateTimeImmutable();
                          
                          if ($fileDateTime && ($now->getTimestamp() - $fileDateTime->getTimestamp()) < 600) {
                              $lastFile = $row['FileNumber'] ?? null;
                          } 
                          break; 
                      }
                  }
              }
      
              if ($lastFile) {
                  $logger->log("איתור קובץ", "קובץ מספר {$lastFile} נמשך בהצלחה ומועבר ל-AI.");
                  
                  $fileYmPath = "{$nativFolder}/{$lastFile}.wav";
                  $audioData = $yemotClient->downloadAudio($fileYmPath);
      
                  $geminiKey = defined('GEMINI_KEY') ? GEMINI_KEY : '';
                  $pythonUrl = defined('PYTHON_URL') ? PYTHON_URL : '';
                  $pythonKey = defined('PYTHON_API_KEY') ? PYTHON_API_KEY : '';
                  
                  $strictNumbersFile = __DIR__ . '/strict_numbers.json';
                  if (!file_exists($strictNumbersFile)) {
                      file_put_contents($strictNumbersFile, json_encode(['0501234567'], JSON_PRETTY_PRINT));
                  }
                  $strictNumbers = json_decode(file_get_contents($strictNumbersFile) ?: '[]', true) ?: [];
                  $isStrictPhone = in_array($searchPhone, $strictNumbers, true);
                  
                  $dictManager = new DictionaryManager(__DIR__ . '/banned_words.json');
                  $orchestrator = new ModerationOrchestrator($geminiKey, $pythonUrl, $pythonKey, $dictManager, $tzManager, $yemotClient);
                  
                  // 🌟 שולחים את פרמטר ביטול הצינתוק לפונקציה
                  $result = $orchestrator->processParallelAndDecide($audioData, $isStrictPhone, $skipTzintuk);
      
                  if ($result->decision === Decision::REJECT) {
                      $yemotClient->call("FileAction", ['action' => 'delete', 'what' => "ivr2:{$fileYmPath}"]);
                      $yemotClient->call("FileAction", ['action' => 'delete', 'what' => "ivr2:{$nativFolder}/{$lastFile}.txt"]);
                      
                      $addedWordStr = $result->bannedWord !== '' ? $result->bannedWord : 'נפסל ע"י AI ללא מילה ספציפית';
                      $logger->log("הקלטה נמחקה", "מספר מקליט: {$phone} | מילה פסולה: '{$addedWordStr}' | תמלול מלא: {$result->transcription}");
      
                      if ($email !== '' && defined('MAIL_ENGINE') && file_exists(MAIL_ENGINE)) {
                          require_once MAIL_ENGINE;
                          $subject = "⚠️ מערכת סינון: הקלטה נמחקה ({$result->engine->value})";
                          $body = "<div dir='rtl'><h3>הקלטה נפסלה</h3><p>טלפון: " . htmlspecialchars($phone, ENT_QUOTES, 'UTF-8') . "</p><p>מילה שגרמה לפסילה: <b>" . htmlspecialchars($result->bannedWord, ENT_QUOTES, 'UTF-8') . "</b></p><p>תמלול מלא:<br>" . htmlspecialchars($result->transcription, ENT_QUOTES, 'UTF-8') . "</p></div>";
                          
                          $tmpMailFile = tempnam(sys_get_temp_dir(), 'mail_') . '.wav';
                          file_put_contents($tmpMailFile, $audioData);
                          if (function_exists('sendCustomMail')) {
                              sendCustomMail([$email], $subject, $body, 'רובוט סינון', $tmpMailFile, "Deleted_{$phone}.wav");
                          }
                          @unlink($tmpMailFile);
                      }
                  } elseif ($result->decision === Decision::APPROVE) {
                      $logger->log("הקלטה אושרה", "קובץ מספר {$lastFile} תומלל ואושר | מספר מקליט: {$phone}");
                  }
              } else {
                  $logger->log("שגיאת איתור", "לא נמצא קובץ תואם למספר בקובץ הלוג.");
              }
          } else {
              $logger->log("נתונים חסרים", "לא התקבלו כל הנתונים הנדרשים מהשרת.");
          }
      } catch (Throwable $e) {
          $logger->log("שגיאה קריטית", $e->getMessage());
      }
      
      

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ש
      שלוחה-404
    • RE: בוט שעוקב אחרי הודעות

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

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ש
      שלוחה-404
    • RE: שגיאה במודול API בשעות האחרונות

      @אז-נדברו יש לי כמה שלוחות לכמה שרתים שונים שעבדו עד היום חלק השרתים תקינים

      פורסם בפורום מפתחים API
      ש
      שלוחה-404
    • RE: שגיאה במודול API בשעות האחרונות

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

      פורסם בפורום מפתחים API
      ש
      שלוחה-404
    • RE: שגיאה במודול API בשעות האחרונות

      @אז-נדברו נכון לעכשיו כל שלוחות API במערכת שלי מחזירות אין מענה משרת API

      פורסם בפורום מפתחים API
      ש
      שלוחה-404
    • RE: באג כעת - האם זה כללי? לא שומעים קבצים בשלוחות השמעת קבצים

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

      פורסם בשאלות ועזרה הדדית
      ש
      שלוחה-404
    • RE: במקרה שנשלח צינתוק לרשימה ריקה השיחה מתנתקת

      @יענקי-פולק במשך שנה וחצי + היה לי כמה שלוחות שהפעילו צינתוק לרשימה ריקה וזה עבד (זה לא שלח צינתוק) אבל המערכת המשיכה לתפקד היום בלחיצה על צינתוק שהרשימה ריקה השיחה מנתקת ללא שמירת הקובץ

      פורסם בשאלות ועזרה הדדית
      ש
      שלוחה-404
    • RE: שליחת הודעה לכשרים בשיחה והשמעת ההודעה

      @קו-הרהיטים כמדומה ני שלהחזיר נקודה במחרוזת טקסט שובר את השרת

      פורסם בעזרה הדדית למשתמשים מתקדמים
      ש
      שלוחה-404
    • RE: חזרה לנקודת העצירה במקש API

      @מסובך-מאוד בעיקרון יש משהו מובנה לי זה לא עבד
      מקש API
      תדפדף קצת למעלה

      פורסם בשאלות ועזרה הדדית
      ש
      שלוחה-404
    • RE: מוזיקה בהמתנה לתגובת השרת במודל תור עם טעינת מספרים לחיוג בAPI

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

      @שיבקשו-שלשתם כתב:

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

      מניין לך שזה לא עובד אולי השרת פשוט מגיב מהר ואין צורך במוזיקה ?

      פורסם בפורום מפתחים API
      ש
      שלוחה-404