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

️ (כמובן שלכל מקש יהיה כתובת ופרמטרים משלו)לדוגמא שיהיה ככה
control_play1=send_api api1_link=https://example.com/YemotApi api1_add_0=foo=111 api1_add_1=bar=222ולמשל למקש 5
control_play5=send_api api5_link=https://example.com/YemotApi api5_add_0=foo=111 api5_add_1=bar=222מצטרף לבקשה...
-
@יצחק-י.
זה הקוד לשרת/** * Cloudflare Worker - ניתוב לפי מקש שנלחץ (send_api ממודול Playfile של ימות המשיח) * ------------------------------------------------------------------------------------ * הפתרון כאן: כל כתובות היעד מוגדרות ישירות ב-ext.ini של ימות באמצעות api_add, * ונשלחות אוטומטית עם כל בקשה (יחד עם PressKey - המקש שנלחץ). ה-Worker רק * קורא את הפרמטר target_<PressKey> מתוך הבקשה עצמה ומעביר אליו את הקריאה. * אין צורך בקובץ ini נפרד, DownloadFile, או עדכון קוד כשמוסיפים/משנים מקש. * * הגדרות בקובץ ext.ini של השלוחה (playfile): * type=playfile * control_play1=send_api * control_play2=send_api * control_play_moreA2=send_api ; מקש *2 (תפריט אפשרויות נוספות) * api_link=https://your-worker-subdomain.workers.dev * api_url_post=yes * api_add_0=target_1=https://server-a.example.com/webhook * api_add_1=target_2=https://server-b.example.com/webhook * api_add_2=target_star2=https://server-c.example.com/webhook * api_add_3=note_1=פנייה למכירות * api_add_4=note_2=פנייה לתמיכה * * כדי להוסיף מקש 4: מוסיפים control_play4=send_api ועוד שורת api_add * בפורמט api_add_N=target_4=https://... - וזהו, בלי לגעת בקוד ה-Worker. * * מקשים דרך "תפריט אפשרויות נוספות" (אחרי הקשה על *, למשל *2): ימות שולח * במקרה כזה PressKey בפורמט "מקש-ראשון-מקש-שני" (למשל "*-2"). כדי שזה יעבוד * בנוחות עם שמות פרמטרים תקינים, ה-Worker ממיר אוטומטית "*" למילה "star", * "#" למילה "hash", ומוריד מקפים - כך ש-PressKey="*-2" הופך ל"star2", ולכן * בהגדרות כותבים target_star2 / note_star2 וכו'. * * כל פרמטר עם סיומת _<מקש מנורמל> (למשל note_1, dept_2, note_star2) ייחשב * "שייך" לאותו מקש בלבד. ה-Worker מסנן ומעביר ליעד רק את הפרמטרים ששייכים * למקש שבאמת נלחץ (עם הסיומת מוסרת), ומשמיט פרמטרים ששייכים למקשים אחרים. * פרמטרים בלי סיומת כזו (כמו PressKey, ApiExtension, ApiPhone) מועברים כרגיל. */ // תשובת ברירת מחדל לימות אם המקש לא נמצא בפרמטרים (בפורמט הפקודות של ימות) const FALLBACK_RESPONSE = "go_to_folder="; // הופך PressKey כפי שנשלח מימות (כולל אפשרויות כמו "*-2") לסיומת שם פרמטר // בטוחה: "*" -> "star", "#" -> "hash", מקפים מוסרים. לדוגמה: "*-2" -> "star2" function sanitizeKeySuffix(rawKey) { return String(rawKey) .trim() .replace(/\*/g, "star") .replace(/#/g, "hash") .replace(/-/g, ""); } // בונה את אובייקט הפרמטרים שיישלח ליעד: פרמטרים עם סיומת _<מקש מנורמל> נשארים // רק אם הסיומת תואמת למקש שנלחץ (והסיומת מוסרת), פרמטרים ששייכים למקש אחר // מושמטים, ופרמטרים בלי סיומת כזו מועברים כמו שהם. function buildForwardParams(params, keySuffix) { const forwarded = {}; for (const [key, value] of Object.entries(params)) { const match = key.match(/^(.*)_(\d+|star\d*|hash\d*)$/); if (match) { const [, baseName, keyNum] = match; if (keyNum === keySuffix) { forwarded[baseName] = value; } // אחרת - שייך למקש אחר, מושמט בכוונה } else { forwarded[key] = value; } } return forwarded; } export default { async fetch(request) { const url = new URL(request.url); let params; if (request.method === "POST") { const contentType = request.headers.get("content-type") || ""; if (contentType.includes("application/json")) { params = await request.json().catch(() => ({})); } else { const text = await request.text(); params = Object.fromEntries(new URLSearchParams(text)); } } else { params = Object.fromEntries(url.searchParams); } // ימות שולח את המקש שנלחץ בפרמטר PressKey (יכול להיות "2" רגיל, או // "*-2" אם זה מקש דרך תפריט אפשרויות נוספות) - מנרמלים לסיומת בטוחה const rawPressKey = params.PressKey ?? params.pressKey ?? ""; const pressKey = sanitizeKeySuffix(rawPressKey); const targetUrl = params[`target_${pressKey}`]; if (!targetUrl) { return new Response(FALLBACK_RESPONSE, { status: 200, headers: { "content-type": "text/plain; charset=utf-8" }, }); } try { const forwardUrl = new URL(targetUrl); const forwardParams = buildForwardParams(params, pressKey); let forwardResponse; if (request.method === "POST") { forwardResponse = await fetch(forwardUrl.toString(), { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(forwardParams), }); } else { for (const [key, value] of Object.entries(forwardParams)) { forwardUrl.searchParams.set(key, String(value)); } forwardResponse = await fetch(forwardUrl.toString(), { method: "GET" }); } const bodyText = await forwardResponse.text(); return new Response(bodyText, { status: 200, headers: { "content-type": "text/plain; charset=utf-8" }, }); } catch (err) { return new Response(FALLBACK_RESPONSE, { status: 200, headers: { "content-type": "text/plain; charset=utf-8" }, }); } }, };וזה אשף להגדרת השלוחה
<!DOCTYPE html> <html lang="he" dir="rtl"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> <title>אשף הגדרות ניתוב מקשים - ימות המשיח</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Heebo:wght@400;500;700;800&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet"> <style> :root { --paper: #FAF7F1; --panel: #FFFFFF; --ink: #201F1D; --ink-soft: #5B5750; --border: #E4DFD4; --accent: #1F7A6C; --accent-ink: #0E3F37; --accent-soft: #E4F1EE; --warn: #B3672B; --warn-soft: #FBEEE0; --danger: #B3413B; --danger-soft: #FBEAE8; --terminal-bg: #17211D; --terminal-ink: #DDEEE7; --terminal-accent: #7FD6BE; --radius: 10px; --shadow: 0 1px 2px rgba(32,31,29,0.06), 0 6px 16px rgba(32,31,29,0.06); } @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --paper: #14120F; --panel: #1D1A16; --ink: #F1EDE5; --ink-soft: #B7AF9F; --border: #35302A; --accent: #4FBFA8; --accent-ink: #C9F1E5; --accent-soft: #1E2E2A; --warn: #E0A868; --warn-soft: #2E2418; --danger: #E08A83; --danger-soft: #2E1B19; --terminal-bg: #0E1613; --terminal-ink: #DDEEE7; --terminal-accent: #7FD6BE; --shadow: 0 1px 2px rgba(0,0,0,0.3), 0 6px 20px rgba(0,0,0,0.35); } } :root[data-theme="dark"] { --paper: #14120F; --panel: #1D1A16; --ink: #F1EDE5; --ink-soft: #B7AF9F; --border: #35302A; --accent: #4FBFA8; --accent-ink: #C9F1E5; --accent-soft: #1E2E2A; --warn: #E0A868; --warn-soft: #2E2418; --danger: #E08A83; --danger-soft: #2E1B19; --terminal-bg: #0E1613; --terminal-ink: #DDEEE7; --terminal-accent: #7FD6BE; --shadow: 0 1px 2px rgba(0,0,0,0.3), 0 6px 20px rgba(0,0,0,0.35); } * { box-sizing: border-box; } html { scroll-padding-top: env(safe-area-inset-top, 0px); } body { margin: 0; background: var(--paper); color: var(--ink); font-family: 'Heebo', system-ui, sans-serif; padding-top: env(safe-area-inset-top, 0px); padding-bottom: env(safe-area-inset-bottom, 0px); line-height: 1.5; } .wrap { max-width: 1080px; margin: 0 auto; padding: 28px 20px 80px; } header.page-head { margin-bottom: 28px; } header.page-head .eyebrow { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--accent-ink); background: var(--accent-soft); border: 1px solid var(--accent); border-radius: 999px; padding: 4px 12px; margin-bottom: 14px; } header.page-head h1 { font-size: clamp(24px, 4vw, 34px); font-weight: 800; margin: 0 0 8px; letter-spacing: -0.01em; } header.page-head p { color: var(--ink-soft); max-width: 62ch; margin: 0; font-size: 15px; } .grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 20px; align-items: start; } @media (max-width: 880px) { .grid { grid-template-columns: 1fr; } } .card { background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); padding: 20px; } .card + .card { margin-top: 18px; } .card h2 { font-size: 16px; font-weight: 700; margin: 0 0 4px; } .card .sub { color: var(--ink-soft); font-size: 13px; margin: 0 0 16px; } label { display: block; font-size: 13px; font-weight: 500; color: var(--ink-soft); margin-bottom: 6px; } input[type="text"], input[type="url"] { width: 100%; padding: 9px 11px; border: 1px solid var(--border); border-radius: 8px; background: var(--paper); color: var(--ink); font-family: inherit; font-size: 14px; } input[type="text"]:focus, input[type="url"]:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); } .field { margin-bottom: 14px; } .field-row { display: flex; gap: 10px; } .field-row .field { flex: 1; margin-bottom: 0; } .toggle-row { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--ink); margin-top: 4px; } .toggle-row input { width: 16px; height: 16px; } /* key rows - styled like small keypad tiles */ .key-row { border: 1px solid var(--border); border-radius: 10px; padding: 14px; margin-bottom: 14px; background: var(--paper); position: relative; } .key-row-top { display: grid; grid-template-columns: 84px 1fr auto; gap: 10px; align-items: end; } @media (max-width: 520px) { .key-row-top { grid-template-columns: 1fr; } } .key-cell input { text-align: center; font-family: 'JetBrains Mono', monospace; font-weight: 700; font-size: 16px; color: var(--accent-ink); background: var(--accent-soft); border-color: var(--accent); } .key-hint { font-size: 11px; color: var(--ink-soft); margin-top: 4px; } .row-remove { align-self: end; } .extra-params { margin-top: 12px; } .extra-params-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } .extra-params-head span { font-size: 12px; font-weight: 600; color: var(--ink-soft); } .extra-param-row { display: grid; grid-template-columns: 1fr 1.4fr auto; gap: 8px; margin-bottom: 8px; } .extra-param-row input { font-size: 13px; padding: 7px 9px; } button { font-family: inherit; cursor: pointer; border-radius: 8px; border: 1px solid transparent; font-size: 13px; font-weight: 600; padding: 8px 14px; transition: transform .05s ease, background .15s ease, border-color .15s ease; } button:active { transform: translateY(1px); } .btn-primary { background: var(--accent); color: #ffffff; border-color: var(--accent); } .btn-primary:hover { filter: brightness(1.05); } .btn-ghost { background: transparent; color: var(--ink); border-color: var(--border); } .btn-ghost:hover { border-color: var(--accent); color: var(--accent-ink); } .btn-danger-ghost { background: transparent; color: var(--danger); border-color: var(--danger); padding: 6px 10px; font-size: 12px; } .btn-danger-ghost:hover { background: var(--danger-soft); } .btn-small { padding: 5px 10px; font-size: 12px; } .add-key-btn { width: 100%; padding: 12px; border: 1.5px dashed var(--border); background: transparent; color: var(--ink-soft); font-weight: 600; } .add-key-btn:hover { border-color: var(--accent); color: var(--accent-ink); background: var(--accent-soft); } .warn-box, .danger-box { border-radius: 8px; padding: 10px 12px; font-size: 13px; margin-bottom: 14px; } .warn-box { background: var(--warn-soft); color: var(--warn); border: 1px solid var(--warn); } .danger-box { background: var(--danger-soft); color: var(--danger); border: 1px solid var(--danger); } /* output / terminal panel */ .output-card { position: sticky; top: 16px; } @media (max-width: 880px) { .output-card { position: static; } } .terminal { background: var(--terminal-bg); color: var(--terminal-ink); border-radius: 8px; padding: 16px; font-family: 'JetBrains Mono', monospace; font-size: 12.5px; line-height: 1.65; white-space: pre-wrap; word-break: break-all; direction: ltr; text-align: left; max-height: 60vh; overflow: auto; } .terminal .comment { color: #7C9187; } .terminal .key { color: var(--terminal-accent); } .terminal .val { color: var(--terminal-ink); } .output-actions { display: flex; gap: 8px; margin-top: 12px; } .toast { position: fixed; bottom: calc(20px + env(safe-area-inset-bottom, 0px)); left: 50%; transform: translateX(-50%) translateY(20px); background: var(--ink); color: var(--paper); padding: 10px 18px; border-radius: 999px; font-size: 13px; font-weight: 600; opacity: 0; pointer-events: none; transition: opacity .2s ease, transform .2s ease; z-index: 50; } .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); } .footnote { font-size: 12px; color: var(--ink-soft); margin-top: 10px; } </style> </head> <body> <div class="wrap"> <header class="page-head"> <span class="eyebrow">☎️ מחולל הגדרות למודול Playfile</span> <h1>אשף ניתוב מקשים ל-API בימות המשיח</h1> <p> הוסיפו מקשים, לכל מקש כתובת Worker יעודית ופרמטרים נוספים - והאשף יבנה עבורכם את שורות ה-<code>ext.ini</code> המוכנות להדבקה, כולל <code>control_play</code> ו-<code>api_add</code>. </p> </header> <div class="grid"> <div> <div class="card"> <h2>הגדרות כלליות</h2> <p class="sub">כתובת ה-Worker אחת בלבד, זהה לכל המקשים - הניתוב עצמו קורה בתוך ה-Worker.</p> <div class="field"> <label for="workerUrl">כתובת ה-Worker (api_link)</label> <input type="url" id="workerUrl" placeholder="https://your-worker-subdomain.workers.dev" autocomplete="off"> </div> <div class="toggle-row"> <input type="checkbox" id="postToggle" checked> <label for="postToggle" style="margin:0;font-weight:400;color:var(--ink);">שליחה כ-POST (api_url_post=yes)</label> </div> </div> <div class="card"> <h2>מקשים וכתובות יעד</h2> <p class="sub"> מקש רגיל: תו אחד - <code>1</code>-<code>9</code>, <code>0</code>, <code>*</code> או <code>#</code>. מקש דרך "אפשרויות נוספות": שני תווים, למשל <code>*2</code> (כוכבית ואז ספרה). </p> <div id="warnings"></div> <div id="keyRows"></div> <button class="add-key-btn" id="addKeyBtn" type="button">+ הוספת מקש</button> </div> </div> <div class="output-card"> <div class="card"> <h2>תוצאה - להדביק ב-ext.ini</h2> <p class="sub">מתעדכן אוטומטית עם כל שינוי.</p> <div class="terminal" id="output" aria-live="polite"></div> <div class="output-actions"> <button class="btn-primary" id="copyBtn" type="button">העתקת ההגדרות</button> <button class="btn-ghost" id="resetBtn" type="button">איפוס הכל</button> </div> <p class="footnote">השמירה נשמרת אוטומטית בדפדפן הזה בלבד, כדי שלא תאבדו נתונים ברענון.</p> </div> </div> </div> </div> <div class="toast" id="toast">ההגדרות הועתקו</div> <script> (function () { "use strict"; var STORAGE_KEY = "yemotKeyRouterWizardState"; var state = { workerUrl: "", postMode: true, keys: [ { key: "1", target: "", params: [] }, { key: "2", target: "", params: [] } ] }; function loadState() { try { var raw = localStorage.getItem(STORAGE_KEY); if (raw) { var parsed = JSON.parse(raw); if (parsed && Array.isArray(parsed.keys) && parsed.keys.length) { state = parsed; } } } catch (e) { /* קאש לא זמין - ממשיכים עם ברירת מחדל */ } } function saveState() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch (e) { /* לא קריטי */ } } function sanitizeKeySuffix(rawKey) { return String(rawKey || "") .trim() .replace(/\*/g, "star") .replace(/#/g, "hash") .replace(/-/g, ""); } function keyInfo(rawKey) { var key = String(rawKey || "").trim(); if (!key) return { valid: false, reason: "מקש ריק" }; if (key.length === 1) { var okSingle = /^[0-9*#]$/.test(key); if (!okSingle) return { valid: false, reason: "מקש לא מוכר: " + key }; var suffix = key === "*" ? "star" : key === "#" ? "hash" : key; return { valid: true, controlLine: "control_play" + key + "=send_api", suffix: suffix, label: key }; } if (key.length === 2) { var first = key[0], second = key[1]; var firstOk = first === "*" || first === "#"; var secondOk = /^[0-9]$/.test(second); if (!firstOk || !secondOk) { return { valid: false, reason: "מקש דו-תווי חייב להיות * או # ואחריו ספרה, למשל *2" }; } var firstWord = first === "*" ? "star" : "hash"; return { valid: true, controlLine: "control_play_moreA" + second + "=send_api", suffix: firstWord + second, label: key, nonDefault: first === "#" }; } return { valid: false, reason: "מקש ארוך מדי (עד 2 תווים)" }; } function el(tag, attrs, children) { var node = document.createElement(tag); attrs = attrs || {}; Object.keys(attrs).forEach(function (k) { if (k === "class") node.className = attrs[k]; else if (k === "text") node.textContent = attrs[k]; else node.setAttribute(k, attrs[k]); }); (children || []).forEach(function (c) { node.appendChild(c); }); return node; } function renderKeyRows() { var container = document.getElementById("keyRows"); container.innerHTML = ""; state.keys.forEach(function (row, idx) { var wrap = el("div", { class: "key-row" }); var top = el("div", { class: "key-row-top" }); var keyCell = el("div", { class: "field key-cell" }); keyCell.appendChild(el("label", { text: "מקש" })); var keyInput = el("input", { type: "text", maxlength: "2", value: row.key }); keyInput.addEventListener("input", function (e) { row.key = e.target.value; syncOutputs(); }); keyCell.appendChild(keyInput); keyCell.appendChild(el("div", { class: "key-hint", text: "לדוגמה: 1 או *2" })); top.appendChild(keyCell); var targetCell = el("div", { class: "field" }); targetCell.appendChild(el("label", { text: "כתובת יעד עבור המקש הזה" })); var targetInput = el("input", { type: "url", placeholder: "https://server.example.com/webhook", value: row.target }); targetInput.addEventListener("input", function (e) { row.target = e.target.value; syncOutputs(); }); targetCell.appendChild(targetInput); top.appendChild(targetCell); var removeCell = el("div", { class: "field row-remove" }); removeCell.appendChild(el("label", { text: "\u00A0" })); var removeBtn = el("button", { class: "btn-danger-ghost", type: "button", text: "הסרת מקש" }); removeBtn.addEventListener("click", function () { state.keys.splice(idx, 1); update(); }); removeCell.appendChild(removeBtn); top.appendChild(removeCell); wrap.appendChild(top); // extra params var extra = el("div", { class: "extra-params" }); var extraHead = el("div", { class: "extra-params-head" }, [ el("span", { text: "פרמטרים נוספים למקש זה (לא חובה)" }) ]); var addParamBtn = el("button", { class: "btn-ghost btn-small", type: "button", text: "+ הוספת פרמטר" }); addParamBtn.addEventListener("click", function () { row.params.push({ name: "", value: "" }); update(); }); extraHead.appendChild(addParamBtn); extra.appendChild(extraHead); row.params.forEach(function (p, pIdx) { var pRow = el("div", { class: "extra-param-row" }); var nameInput = el("input", { type: "text", placeholder: "שם הפרמטר, למשל note", value: p.name }); nameInput.addEventListener("input", function (e) { p.name = e.target.value; syncOutputs(); }); var valueInput = el("input", { type: "text", placeholder: "ערך, למשל פנייה למכירות", value: p.value }); valueInput.addEventListener("input", function (e) { p.value = e.target.value; syncOutputs(); }); var delBtn = el("button", { class: "btn-danger-ghost btn-small", type: "button", text: "הסרה" }); delBtn.addEventListener("click", function () { row.params.splice(pIdx, 1); update(); }); pRow.appendChild(nameInput); pRow.appendChild(valueInput); pRow.appendChild(delBtn); extra.appendChild(pRow); }); wrap.appendChild(extra); container.appendChild(wrap); }); } function renderWarnings() { var box = document.getElementById("warnings"); box.innerHTML = ""; var problems = []; var suffixSeen = {}; state.keys.forEach(function (row) { var info = keyInfo(row.key); if (!info.valid && row.key.trim()) { problems.push({ type: "danger", text: "מקש \"" + row.key + "\": " + info.reason }); return; } if (info.valid) { if (suffixSeen[info.suffix]) { problems.push({ type: "danger", text: "המקשים \"" + suffixSeen[info.suffix] + "\" ו-\"" + row.key + "\" מתנגשים (אותה הגדרה בפועל)" }); } suffixSeen[info.suffix] = row.key; if (info.nonDefault) { problems.push({ type: "warn", text: "מקש \"" + row.key + "\": כניסה דרך # לתפריט אפשרויות נוספות אינה ברירת המחדל בימות - יש לוודא שהוגדרה כך בשלוחה" }); } } if (info.valid && !row.target.trim()) { problems.push({ type: "warn", text: "למקש \"" + row.key + "\" אין כתובת יעד עדיין" }); } }); if (!state.workerUrl.trim()) { problems.push({ type: "warn", text: "לא הוזנה כתובת ה-Worker (api_link)" }); } problems.forEach(function (p) { box.appendChild(el("div", { class: p.type === "danger" ? "danger-box" : "warn-box", text: p.text })); }); } function escapeForIni(value) { return String(value).replace(/[\r\n]/g, " ").trim(); } function buildOutput() { var lines = []; lines.push({ c: "comment", t: "; --- הגדרות שלוחה מסוג playfile ---" }); lines.push({ c: "comment", t: "type=playfile" }); if (state.workerUrl.trim()) { lines.push({ c: "plain", t: "api_link=" + escapeForIni(state.workerUrl) }); } else { lines.push({ c: "comment", t: "; api_link=<יש להזין כתובת Worker למעלה>" }); } lines.push({ c: "plain", t: "api_url_post=" + (state.postMode ? "yes" : "no") }); lines.push({ c: "comment", t: "" }); lines.push({ c: "comment", t: "; --- מקשים ---" }); var controlLines = []; var addLines = []; var counter = 0; state.keys.forEach(function (row) { var info = keyInfo(row.key); if (!info.valid) return; controlLines.push(info.controlLine); if (row.target.trim()) { addLines.push("api_add_" + counter + "=target_" + info.suffix + "=" + escapeForIni(row.target)); counter++; } row.params.forEach(function (p) { if (p.name.trim() && p.value.trim()) { var safeName = p.name.trim().replace(/[^A-Za-z0-9_\u05D0-\u05EA]/g, ""); if (safeName) { addLines.push("api_add_" + counter + "=" + safeName + "_" + info.suffix + "=" + escapeForIni(p.value)); counter++; } } }); }); controlLines.forEach(function (l) { lines.push({ c: "plain", t: l }); }); lines.push({ c: "comment", t: "" }); lines.push({ c: "comment", t: "; --- כתובות יעד ופרמטרים נוספים לכל מקש ---" }); addLines.forEach(function (l) { lines.push({ c: "plain", t: l }); }); return lines; } function renderOutput() { var out = document.getElementById("output"); var lines = buildOutput(); out.innerHTML = ""; lines.forEach(function (line) { var div = document.createElement("div"); if (line.c === "comment") { div.className = "comment"; div.textContent = line.t || "\u00A0"; } else { var eqIdx = line.t.indexOf("="); if (eqIdx > -1) { var keySpan = document.createElement("span"); keySpan.className = "key"; keySpan.textContent = line.t.slice(0, eqIdx + 1); var valSpan = document.createElement("span"); valSpan.className = "val"; valSpan.textContent = line.t.slice(eqIdx + 1); div.appendChild(keySpan); div.appendChild(valSpan); } else { div.textContent = line.t; } } out.appendChild(div); }); } function getOutputText() { return buildOutput().map(function (l) { return l.t; }).join("\n"); } // עדכון מלא: כשמבנה השורות משתנה (הוספה/הסרה של מקש או פרמטר) function update() { renderKeyRows(); renderWarnings(); renderOutput(); saveState(); } // עדכון קל: כשרק תוכן שדה משתנה (הקלדה) - לא נוגעים ב-DOM של השורות, // כדי לא לאבד פוקוס מהשדה שממנו מקלידים function syncOutputs() { renderWarnings(); renderOutput(); saveState(); } function showToast(msg) { var toast = document.getElementById("toast"); toast.textContent = msg; toast.classList.add("show"); clearTimeout(showToast._t); showToast._t = setTimeout(function () { toast.classList.remove("show"); }, 1800); } document.getElementById("workerUrl").addEventListener("input", function (e) { state.workerUrl = e.target.value; syncOutputs(); }); document.getElementById("postToggle").addEventListener("change", function (e) { state.postMode = e.target.checked; update(); }); document.getElementById("addKeyBtn").addEventListener("click", function () { state.keys.push({ key: "", target: "", params: [] }); update(); }); document.getElementById("resetBtn").addEventListener("click", function () { if (!confirm("לאפס את כל ההגדרות?")) return; state = { workerUrl: "", postMode: true, keys: [{ key: "1", target: "", params: [] }] }; update(); }); document.getElementById("copyBtn").addEventListener("click", function () { var text = getOutputText(); if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(text).then(function () { showToast("ההגדרות הועתקו"); }).catch(function () { fallbackCopy(text); }); } else { fallbackCopy(text); } }); function fallbackCopy(text) { var ta = document.createElement("textarea"); ta.value = text; ta.style.position = "fixed"; ta.style.opacity = "0"; document.body.appendChild(ta); ta.select(); try { document.execCommand("copy"); showToast("ההגדרות הועתקו"); } catch (e) { showToast("לא ניתן להעתיק אוטומטית - יש לסמן ולהעתיק ידנית"); } document.body.removeChild(ta); } loadState(); document.getElementById("workerUrl").value = state.workerUrl; document.getElementById("postToggle").checked = state.postMode; update(); })(); </script> </body> </html> -
@הדיבל-המעופף תודה רבה,
איזה שרת חינמי מומלץ?
@הדיבל-המעופף אני רואה שניתן להגדיר במודול מקש תוך כדי השמעה, ומקש בתפריט כוכבית (target_star1)
אני רוצה להגדיר במקש control_after_play_moreAX
בתפריט לאחר ההשמעה (after_play_tfr=tfr_more_options)
תודה -
@יצחק-י.
אני משתמש ב Cloudflare
הוא די נחמדולגבי זה
@יצחק-י. כתב:
אני רוצה להגדיר במקש control_after_play_moreAX
בתפריט לאחר ההשמעה (after_play_tfr=tfr_more_options)אני צריך לשאול את קלוד והוא גמור כרגע (אבל נראלי שמה שנשלח לשרת זה כמו הקשה רגילה)
אם אתה מסתבך אם השרת אני יכול לשלוח לך כתובת במייל
ולמה אני לא מצליח לתייג אותך?
-
ולמה אני לא מצליח לתייג אותך?
אתה כותב @ וזה לא מופיע?
-
@יצחק-י. זה מופיע ברשימה הנפתחת אבל לא נהיה כחול???
-
@הדיבל-המעופף צילום מסך בבקשה
-
@יצחק-י.
אתה כן רואה בכחול?
-
(אבל נראלי שמה שנשלח לשרת זה כמו הקשה רגילה)
אני יכול לעשות ככה?
control_after_play_moreA1=send_api api_add_0=target_1=ATTPS -
@הדיבל-המעופף חשבתי שכל אחד רואה את שלו בשחור...
-
@הדיבל-המעופף
כנראה בגלל הנקודה אחרי הי'
יש הרבה שיש להם נקודה, לא נראה קשור...
אני באמת אף פעם לא מקבל התראה על תיוג....
@שמואל ??? -
@יצחק-י. כתב:
אני יכול לעשות ככה?
control_after_play_moreA1=send_api
api_add_0=target_1=APPTSכנראה שכן
@יצחק-י. כתב:
@הדיבל-המעופף כנראה בגלל הנקודה אחרי הי'
יש הרבה שיש להם נקודה, לא נראה קשור...
אני באמת אף פעם לא מקבל התראה על תיוג....
@שמואל ???כנראה התפספסת לשרת שמתייג

שלום! נראה שהשיחה הזו מעניינת אותך, אבל עדיין אין לך חשבון.
נמאס לכם לגלול בין אותם הפוסטים בכל ביקור? כשנרשמים לחשבון, תמיד תחזרו בדיוק למקום שבו הייתם קודם, ותוכלו לבחור לקבל התראות על תגובות חדשות (בין אם במייל, ובין אם בהתראת פוש). תוכלו גם לשמור סימניות ולפרגן ב-upvote לפוסטים כדי להביע הערכה לחברי קהילה אחרים.
בעזרת התרומה שלך, הפוסט הזה יכול להיות אפילו טוב יותר 💗
הרשמה התחברות