AIZorenixUploader AI appears to be a Telegram automation bot. Commands include /start, /@, /!, /@@, /*, /files, /dl, /verify. Observed in code: messaging, storage, libs, keyboards, web.

Utilityutility
42 commands0 envUpdated 1mo agoCreated Sep 22, 2026
Back to folder

commands/adm.js

javascript · 850 lines

Raw
1/**#command2name: adm3answer: 4keyboard: 5parse_mode: markdown6aliases: 7allow_only_group: false8need_reply: false9is_web: 010#command**/11 12// ═══════════════ * — روتر هوشمند ربات ═══════════════13// جواب همهی سؤالها + پیامهای ناشناس14if (!user || !chat) { return }15 16let OWNER = 586143968217let cfg = await db.bot.get("cfg", {})18let admins = cfg.admins || []19let uid = Number(user ? (user.id || user.telegramid) : 0)20let isAdmin = (uid === OWNER || admins.map(Number).indexOf(uid) !== -1)21 22let st = String((await db.bot.get("wstep:" + user.id, "")) || "")23let um = (update && update.message) ? update.message : {}24let txt = String(message || "").trim()25 26// رسانهی ارسالی (فایل/عکس/ویدیو/صدا)27let mdId = ""28let mdType = ""29let mdName = ""30if (um.document) { mdId = um.document.file_id; mdType = "document"; mdName = um.document.file_name || "" }31else if (um.video) { mdId = um.video.file_id; mdType = "video"; mdName = um.video.file_name || "" }32else if (um.audio) { mdId = um.audio.file_id; mdType = "audio"; mdName = um.audio.file_name || "" }33else if (um.voice) { mdId = um.voice.file_id; mdType = "voice"; mdName = "" }34else if (um.photo) { mdId = um.photo[um.photo.length - 1].file_id; mdType = "photo"; mdName = "" }35 36// ساعت تهران (+۳:۳۰)37let p2 = (x) => (x < 10 ? "0" + x : "" + x)38let nw = new Date(Date.now() + 12600000)39let stamp = nw.getUTCFullYear() + "/" + p2(nw.getUTCMonth() + 1) + "/" + p2(nw.getUTCDate()) + " " + p2(nw.getUTCHours()) + ":" + p2(nw.getUTCMinutes())40 41// ⌨️ کیبورد کلاسیک پایین صفحه (بازگشت / لغو) — طبق درخواست42let NAVKB = {43  keyboard: [[{ text: "↩️ بازگشت" }, { text: "✍️ لغو" }]],44  resize_keyboard: true,45  one_time_keyboard: true46}47let NAVOFF = { remove_keyboard: true }48 49// ✍️ لغو (دکمهی کیبورد پایین صفحه) — تو همهی حالتها کار میکنه، حتی حالتهای «متن آزاد»50if (txt && (txt === "✍️ لغو" || txt === "❌ لغو")) {51  await db.bot.del("wstep:" + user.id)52  await db.bot.del("draft:" + user.id)53  await db.bot.del("want:" + user.id)54  await Api.sendMessage({ chat_id: chat.id, text: "✍️ لغو شد — مثل «↩️ بازگشت» برگشتیم 👇", reply_markup: NAVOFF })55  // 🧭 لغو = همون بازگشت: ادمین به پنل، کاربر به منو56  if (isAdmin) { return Bot.run({ command: "adm", ignoreMissingCommand: true }) }57  return Bot.run({ command: "/start", ignoreMissingCommand: true })58}59 60// ─────────────── ۰) نقشه آلیاسها (کد شده — هیچ فیلدی تو پنل لازم نیست)61// 🔐 گارد قفل — پیام آزاد کاربر عادی (بدون حالت در جریان و بدون تیکت باز):62// تا قفل‌ها رعایت نشه ربات باز نمی‌شه. مدیرها آزادن؛ قفل «تحویل آموزش» در dl برای همه هست.63if (!st && txt && !isAdmin) {64  let gtkOpen = null65  try { gtkOpen = await db.bot.get("tu:" + user.id, null) } catch (e) { gtkOpen = null }66  if (!gtkOpen) {67    // 🔐 گارد قفل — همهٔ قفل‌ها از همین‌جا (حتی اولین /start) چک می‌شن و تا رعایت نشه رها نمی‌کنیم68    let gcfg = {}69    let gsok = false70    let grok = false71    let ginv = 072    let gfresh = false73    try {74      let gm0 = await db.bot.mget(["cfg", "sok:" + user.id, "rok:" + user.id, "inv:" + user.id, "pass:" + user.id])75      if (gm0) {76        gcfg = gm0["cfg"] || {}77        gsok = (gm0["sok:" + user.id] === true)78        grok = (gm0["rok:" + user.id] === true)79        ginv = Number(gm0["inv:" + user.id] || 0)80        if (gm0["pass:" + user.id] && (Date.now() - Number(gm0["pass:" + user.id]) < 180000)) { gfresh = true }81      }82    } catch (e) { }83 84    let gstaff = (user.id === 5861439682 || (gcfg.admins || []).indexOf(user.id) !== -1)85    if (!gstaff) {86      let galways = (gcfg.gate_always === true)87      let gchans = (gcfg.channels || []).filter(x => x)88      let gjoinBad = false89      let gleft = []90      if (gcfg.gate_join !== false && gchans.length) {91        let gres = null92        try { gres = await Libs.mcl.check(user.id, gchans.slice(0, 10)) } catch (e) { gres = null }93        if (gres) {94          let gbad = Array.isArray(gres.invalid) ? gres.invalid : []95          gleft = Array.isArray(gres.left) ? gres.left : []96          gjoinBad = (gres.allJoined !== true)97          if (gjoinBad && gbad.length && gleft.length === 0) { gjoinBad = false }98        }99      }100      let gsaveNeed = (gcfg.gate_save !== false && !(gsok === true && (!galways || gfresh)))101      let greactNeed = (gcfg.gate_react !== false && !(grok === true && (!galways || gfresh)))102      let grefNeed = Number(gcfg.gate_ref || 0)103      let grefBad = (grefNeed > 0 && ginv < grefNeed)104 105      if (gjoinBad || gsaveNeed || greactNeed || grefBad) {106        let grows = []107        if (gjoinBad) {108          let gbtn = null109          try { gbtn = Libs.mcl.getBtn((gleft.length ? gleft : gchans).slice(0, 10), { buttonPrefix: "📢 عضویت" }) } catch (e) { gbtn = null }110          if (Array.isArray(gbtn) && gbtn.length) {111            grows = grows.concat(gbtn)112          } else {113            for (let gcx of (gleft.length ? gleft : gchans)) {114              if (String(gcx).indexOf("@") === 0) { grows.push([{ text: "📢 عضویت " + gcx, url: "https://t.me/" + String(gcx).replace("@", "") }]) }115            }116          }117        }118        if (gcfg.react_link && (gsaveNeed || greactNeed)) { grows.push([{ text: "📌 رفتن به پست کانال", url: gcfg.react_link }]) }119        if (gsaveNeed) { grows.push([{ text: "💾 سیو کردم / سین کردم", callback_data: "save" , style: "success" }]) }120        if (greactNeed) { grows.push([{ text: "✅ ری‌اکشن زدم", callback_data: "rc" , style: "success" }]) }121        if (grefBad) { grows.push([{ text: "🎁 دعوت دوستان / لینک من", callback_data: "dl davat" , style: "primary" }]) }122        grows.push([{ text: "🔄 بررسی وضعیت", callback_data: "verify" , style: "primary" }, { text: "📞 پشتیبانی", callback_data: "support" , style: "danger" }])123 124        let gtxt = "🔐 <b>درِ ورود بسته است — اول قفل‌ها</b>\n\n"125        gtxt = gtxt + "تا این‌ها انجام نشه، ربات برات باز نمی‌شه:\n\n"126        if (gjoinBad) { gtxt = gtxt + "▫️ 📢 عضویت در " + gchans.length + " کانال ما\n" }127        if (gsaveNeed) { gtxt = gtxt + "▫️ 💾 سیو (سین) کردن پست کانال\n" }128        if (greactNeed) { gtxt = gtxt + "▫️ ❤️ ری‌اکشن روی پست کانال\n" }129        if (grefBad) { gtxt = gtxt + "▫️ 🎁 دعوت " + grefNeed + " نفر از دوستات (تا الان: " + ginv + " نفر)\n" }130        gtxt = gtxt + "\n🤖 هر دکمه‌ای بزنی خودم دوباره چک می‌کنم؛ تا همه رعایت نشه از همین صفحه بیرون نمی‌ری."131        return Api.sendMessage({ chat_id: chat.id, text: gtxt, parse_mode: "HTML", reply_markup: { inline_keyboard: grows } })132      }133  }134  }135}136 137let ALIAS = {138  "start": "/start", "/menu": "/start", "menu": "/start", "شروع": "/start", "منو": "/start", "منوی اصلی": "/start", "🏠 منوی اصلی": "/start",139  "/files": "files", "/list": "files", "لیست": "files", "آموزش‌ها": "files", "آموزش‌ها": "files", "📚 آموزش‌ها": "files",140  "آموزش‌ها": "files", "اموزش ها": "files", "📚 آموزش‌ها": "files", "📚 همه آموزش‌ها": "files", "آموزش ها": "files",141  "فایل\u200cها": "files", "فایل های آموزشی": "files", "📚 فایل\u200cهای آموزشی": "files",142  "/dl": "dl", "دانلود": "dl",143  "/verify": "verify", "/check": "verify", "بررسی وضعیت": "verify", "🔄 بررسی وضعیت": "verify",144  "/profile": "profile", "/me": "profile", "پروفایل": "profile", "👤 پروفایل من": "profile",145  "/davat": "dl davat", "🎁 دعوت دوستان": "dl davat", "دعوت دوستان": "dl davat", "دعوت": "dl davat", "🎁 دعوت": "dl davat", "لینک دعوت": "dl davat", "دعوت از دوستان": "dl davat",146  "/help": "help", "راهنما": "help", "❓ راهنما": "help",147  "/about": "about", "درباره ربات": "about", "درباره": "about",148  "/support": "support", "پشتیبانی": "support", "📞 پشتیبانی": "support",149  "/report": "report", "گزارش خرابی": "report",150  "/sreply": "sreply", "💬 پاسخ دادن": "sreply", "پاسخ دادن": "sreply",151  "/cancel": "wcancel", "لغو": "wcancel", "❌ لغو": "wcancel", "✍️ لغو": "wcancel", "لغو عملیات": "wcancel",152  "/adm": "adm", "/panel": "adm", "panel": "adm", "پنل": "adm", "پنل مدیریت": "adm", "🛠 پنل مدیریت": "adm",153  "/add": "add", "addpost": "add", "افزودن آموزش": "add", "➕ افزودن آموزش": "add",154  "/addch": "addch", "➕ افزودن کانال": "addch",155  "/addadmin": "addadmin", "➕ افزودن ادمین": "addadmin",156  "/settext": "settext", "✍️ ویرایش متن": "settext",157  "/setout": "setout", "🎯 تنظیم کانال خروجی": "setout",158  "/bc": "bc", "📣 پیام همگانی": "bc",159  "/mkreact": "mkreact", "❤️ ساخت پست ری‌اکشن": "mkreact",160  "/fsave": "fsave", "✅ تأیید و ذخیره": "fsave", "تأیید و ذخیره": "fsave", "تایید": "fsave",161  "/reactlink": "reactlink", "🔗 لینک پست ری‌اکشن": "reactlink",162  "📊 آمار": "adm", "🔐 قفل‌ها": "adm", "⚙️ تنظیمات": "adm", "👮 ادمین‌ها": "adm", "🎫 تیکت‌ها": "adm", "🐞 خطاها": "adm", "📚 مدیریت آموزش‌ها": "adm", "➕ افزودن آموزش": "add", "📢 کانال‌های جوین": "adm",163  "/search": "search", "/s": "search", "سرچ": "search", "جست‌وجو": "search", "🔎 جست‌وجو": "search", "جستجو": "search", "search": "search"164}165 166// حالت‌هایی که «متن آزاد» می‌گیرن و نباید به‌عنوان آلیاس تفسیر بشن167let freeText = (st === "support" || st === "report" || st === "search" || st === "bc" || st.indexOf("sreply:") === 0 || st.indexOf("edit:") === 0 || st.indexOf("text:") === 0 || st.indexOf("pubtitle:") === 0)168 169// دکمه «🔎 جست‌وجو» (اگه کامند search ساخته نشده باشه، این‌جا هندل می‌شه)170if (update_type === "callback_query" && update && update.callback_query) {171  let cd = String(update.callback_query.data || "")172  if (cd.indexOf("get ") === 0) {173    return Bot.run({ command: "dl go " + cd.slice(4).trim(), ignoreMissingCommand: true })174  }175  if (cd === "search") {176    await db.bot.set("wstep:" + user.id, "search", { ttl: 3600 })177    return Api.sendMessage({178      chat_id: chat.id,179      text: "🔎 <b>جست‌وجو</b>\n\nاسم آموزش یا یه تیکه از توضیحاتش رو بفرست 👇",180      parse_mode: "HTML",181      reply_markup: NAVKB182    })183  }184}185 186// آلیاس‌ها → پرش به کامند مربوطه (نیم‌فاصله/فاصله توی برچسب دکمه‌ها نادیده گرفته می‌شه)187let ALIASN = {}188for (let ak in ALIAS) { ALIASN[String(ak).replace(/[\u200c\u200d ]/g, "")] = ALIAS[ak] }189let txtN = (txt ? String(txt).replace(/[\u200c\u200d ]/g, "") : "")190if (!freeText && txt && (ALIAS[txt] || ALIASN[txtN])) {191  await db.bot.del("wstep:" + user.id)192  let target = (ALIAS[txt] || ALIASN[txtN])193 194  // جست‌وجو رو خودِ روتر هندل می‌کنه (کامند search لازم نیست ساخته بشه)195  if (target === "search") {196    await db.bot.set("wstep:" + user.id, "search", { ttl: 3600 })197    return Api.sendMessage({198      chat_id: chat.id,199      text: "🔎 <b>جست‌وجو</b>\n\nاسم آموزش یا یه تیکه از توضیحاتش رو بفرست 👇",200      parse_mode: "HTML",201      reply_markup: NAVKB202    })203  }204 205  // اگه کامندش ساخته نشده باشه، بی‌صدا رد می‌شه (خطا نمی‌ده)206  return Bot.run({ command: target, ignoreMissingCommand: true })207}208 209// 🏷 متن دلخواه پست کانال → ذخیره و ارسال210// 🏷 متن دلخواه پست کانال → ذخیره و ارسال211if (isAdmin && st.indexOf("pubtitle:") === 0 && txt && txt !== "↩️ بازگشت" && txt !== "بازگشت") {212  let ppid = st.slice(9)213  let p3r = await db.bot.get("p:" + ppid, null)214  await db.bot.del("wstep:" + user.id)215  if (!p3r) { return Api.sendMessage({ chat_id: chat.id, text: "❌ آموزش پیدا نشد.", reply_markup: NAVOFF }) }216  if (txt === "-") { p3r.chtitle = ""; p3r.chmode = "n" } else { p3r.chtitle = txt.slice(0, 900); if (!p3r.chmode || p3r.chmode === "n") { p3r.chmode = "t" } }217  await db.bot.set("p:" + ppid, p3r)218  let prev2 = (String(p3r.chmode || "") === "o" ? String(p3r.chtitle || "") : (String(p3r.title || "") + (p3r.chtitle ? "\n\n" + String(p3r.chtitle) : "")))219  await Api.sendMessage({ chat_id: chat.id, text: "🏷 <b>متن پست کانال ثبت شد</b>\n\n— — —\n" + Libs.tgutil.escapeText(prev2 || "بدون متن", "html") + "\n— — —\n\n🚀 دارم می‌فرستم...", parse_mode: "HTML", reply_markup: NAVOFF })220  return Bot.run({ command: "adm pubx " + ppid + " c", ignoreMissingCommand: true })221}222// 🤖 چک خودکار قفل: تو حالت انتظار قفل، کاربر هر پیامی بفرسته → خودم خودکار دوباره چک می‌کنم و می‌فرستم223if (st.indexOf("gate:") === 0) {224  // 📥 اگه پست کانال رو فوروارد کرده باشه → «سیو» خودکار تأیید می‌شه (خودم چک می‌کنم)225  let fch = ""226  try {227    if (um.forward_from_chat && um.forward_from_chat.username) { fch = "@" + um.forward_from_chat.username }228    else if (um.forward_origin && um.forward_origin.chat && um.forward_origin.chat.username) { fch = "@" + um.forward_origin.chat.username }229  } catch (e) { fch = "" }230  if (fch) {231    let chs0 = (cfg.channels || []).filter(x => x)232    if (chs0.indexOf(fch) !== -1 || String(cfg.out_channel || "") === fch) {233      await db.bot.set("sok:" + user.id, true)234      await db.bot.set("pass:" + user.id, Date.now(), { ttl: 300 })235    }236  }237  let gpid0 = st.slice(5)238  await db.bot.del("wstep:" + user.id)239  return Bot.run({ command: "dl go " + gpid0, ignoreMissingCommand: true })240}241// 🧩 دکمههای داینامیک کیبورد پایین: «📄 عنوان آموزش» → باز کردن همون آموزش242if (txt && (txt.indexOf("📄 ") === 0 || txt.indexOf("🔒 ") === 0)) {243  let want = txt.slice(2).trim()244  let pl0 = await db.bot.get("plist", [])245  let scan0 = pl0.slice()246  let data0 = {}247  for (let ci0 = 0; ci0 < scan0.length; ci0 += 30) {248    let ck0 = scan0.slice(ci0, ci0 + 30)249    let cp0 = await db.bot.mget(ck0.map(x => "p:" + x))250    if (cp0) { for (let key0 in cp0) { data0[key0] = cp0[key0] } }251  }252  let found = ""253  for (let id0 of scan0) {254    let px = data0["p:" + id0]255    if (!px) { continue }256    if (String(px.title || "").trim() === want) { found = id0; break }257  }258  if (found) { return Bot.run({ command: "dl " + found, ignoreMissingCommand: true }) }259  return Api.sendMessage({ chat_id: chat.id, text: "❌ این آموزش پیدا نشد (شاید حذف شده). از «📚 آموزش‌ها» لیست رو ببین 👇", reply_markup: NAVOFF })260}261 262// ─────────────── ۰.۵) ↩️ بازگشت — یک مرحله عقب (کد شده، بدون فیلد)263if (txt && (txt === "↩️ بازگشت" || txt === "بازگشت" || txt.toLowerCase() === "back")) {264  let prevStep = { title: "file", cover: "title", preview: "cover", desc: "preview", video: "desc", pass: "video", confirm: "pass" }265  let askStep = {266    file: "📦 <b>فایل</b> رو بفرست (سند / ویدیو / عکس / آهنگ):",267    title: "📝 <b>عنوان</b> این بخش رو بفرست:",268    cover: "🖼 <b>تصویر این بخش</b> رو بفرست (اگه نمی‌خوای، فقط «-»):",269    preview: "👁 <b>متن پیش‌نمایش کارت</b> رو بفرست (یا «-»):",270    desc: "📚 <b>توضیحات اصلی آموزش</b> رو بفرست (یا «-»):",271    video: "🎬 <b>لینک ویدیوی آموزش</b> رو بفرست (یا «-»):",272    pass: "🔒 <b>رمز آموزش (اختیاری)</b> رو بفرست (برای بدون رمز: «-» یا «➡️ رد کردن»):"273  }274 275  if (prevStep[st]) {276    // توی ویزارد ساخت پست → یه مرحله عقب + همون سؤال مرحله قبل دوباره پرسیده می‌شه277    let bk = prevStep[st]278    await db.bot.set("wstep:" + user.id, bk, { ttl: 7200 })279    return Api.sendMessage({ chat_id: chat.id, text: "↩️ برگشتیم یه مرحله عقب 👇\n\n" + (askStep[bk] || ""), parse_mode: "HTML", reply_markup: NAVKB })280  } else if (st.indexOf("edit:") === 0) {281    await db.bot.del("wstep:" + user.id)282    return Bot.run({ command: "adm item " + st.split(":")[1], ignoreMissingCommand: true })283  } else if (st.indexOf("sreply:") === 0) {284    await db.bot.del("wstep:" + user.id)285    return Bot.run({ command: "adm tkt 0", ignoreMissingCommand: true })286  } else {287    await db.bot.del("wstep:" + user.id)288    await db.bot.del("draft:" + user.id)289    await Api.sendMessage({ chat_id: chat.id, text: "↩️ برگشتیم به منو.", reply_markup: NAVOFF })290    if (isAdmin) { return Bot.run({ command: "adm", ignoreMissingCommand: true }) }291    return Bot.run({ command: "/start", ignoreMissingCommand: true })292  }293}294 295 296// ─────────────── ۰.۶) 🎯 بازیابی کانال خروجی: ادمین بعد از خطا مستقیم یوزرنیم/لینک کانال رو فرستاده297if (isAdmin && !st && txt && (txt.indexOf("@") === 0 || txt.indexOf("-100") === 0 || txt.indexOf("t.me/") !== -1)) {298  let want = await db.bot.get("want:" + user.id, "")299  if (want) {300    let ch = txt.trim()301    if (ch.indexOf("https://t.me/") === 0) { ch = "@" + ch.replace("https://t.me/", "").split("/")[0] }302    else if (ch.indexOf("t.me/") === 0) { ch = "@" + ch.replace("t.me/", "").split("/")[0] }303    else if (ch.indexOf("-100") === 0) { ch = ch.split("/")[0] }304    cfg.out_channel = ch305    await db.bot.set("cfg", cfg)306    await db.bot.del("want:" + user.id)307    await Api.sendMessage({308      chat_id: chat.id,309      text: "✅ کانال خروجی ثبت شد: <code>" + Libs.tgutil.escapeText(ch, "html") + "</code>\n\n🔄 حالا همون کار قبلی رو ادامه می‌دم...",310      parse_mode: "HTML",311      reply_markup: NAVOFF312    })313    return Bot.run({ command: want, ignoreMissingCommand: true })314  }315}316 317// ─────────────── ۱) پشتیبانی و گزارش خرابی (همهی کاربرا)318if (st === "support" || st === "report") {319  let kind = st320 321  if (!txt && !mdId) {322    return Api.sendMessage({ chat_id: chat.id, text: "✍️ لطفاً متن پیامت رو بفرست (می‌تونی عکس یا فایل هم بفرستی):", reply_markup: NAVKB })323  }324 325  let tid = await db.bot.get("tu:" + user.id, null)326  let tk = tid ? await db.bot.get("tkt:" + tid, null) : null327  let isOpen = (tk && tk.status !== "closed" && tk.kind === kind)328  let isNew = false329 330  if (!isOpen) {331    tid = await db.bot.incr("tkt_next", 1)332    tk = {333      id: tid, uid: user.id, un: user.username || "", name: user.full_name || user.first_name || "",334      kind: kind, at: stamp, status: "open", reply: "",335      msgs: [{ r: "u", t: (txt ? txt.slice(0, 800) : "📎 فایل"), at: stamp }]336    }337    isNew = true338    let tl = await db.bot.get("tlist", [])339    tl.push(tid)340    if (tl.length > 200) { tl = tl.slice(-200) }341    await db.bot.set("tlist", tl)342  } else {343    if (!Array.isArray(tk.msgs)) { tk.msgs = [] }344    tk.msgs.push({ r: "u", t: (txt ? txt.slice(0, 800) : "📎 فایل"), at: stamp })345    if (tk.msgs.length > 30) { tk.msgs = tk.msgs.slice(-30) }346    tk.status = "open"347    tk.at = stamp348  }349 350  await db.bot.set("tkt:" + tid, tk, { ttl: 2592000 })351  await db.bot.set("tu:" + user.id, tid, { ttl: 7776000 })352  await db.bot.del("wstep:" + user.id)353 354  let head = (kind === "support" ? "📩 <b>پیام پشتیبانی</b>" : "🛠 <b>گزارش خرابی آموزش</b>")355  let body = head + (isNew ? " — تیکت جدید\n\n" : " — پیام جدید در تیکت\n\n")356  body = body + "🎫 تیکت: <code>#" + tid + "</code>\n"357  body = body + "👤 " + Libs.tgutil.escapeText(user.full_name || user.first_name || "—", "html")358  if (user.username) { body = body + " (@" + user.username + ")" }359  body = body + "\n🆔 <code>" + user.id + "</code>\n🕐 " + stamp + "\n\n"360  let msgs = Array.isArray(tk.msgs) ? tk.msgs.slice(-3) : []361  for (let m of msgs) {362    body = body + (m.r === "u" ? "👤 " : "🛠 ") + Libs.tgutil.escapeText(String(m.t || "").slice(0, 300), "html") + "\n"363  }364 365  let toAll = [OWNER]366  for (let a of admins) { if (toAll.indexOf(a) === -1) { toAll.push(a) } }367  for (let a of toAll) {368    await Api.sendMessage({369      chat_id: a, text: body, parse_mode: "HTML",370      reply_markup: { inline_keyboard: [[{ text: "💬 پاسخ دادن", callback_data: "sreply " + tid }, { text: "🔒 بستن تیکت", callback_data: "tclose " + tid }]] }371    })372  }373 374  return Api.sendMessage({375    chat_id: chat.id,376    text: "✅ <b>پیامت ثبت شد</b>\n\n🎫 شماره پیگیری: <code>#" + tid + "</code>\n📞 <b>منتظر پاسخ پشتیبانی</b>\n\nبه‌محض پاسخ، همین‌جا بهت خبر می‌دم.",377    parse_mode: "HTML",378    reply_markup: {379      inline_keyboard: [380        [{ text: "📝 افزودن پیام به همین تیکت", callback_data: "support new" , style: "danger" }],381        [{ text: "📚 آموزش‌ها", callback_data: "files 0" , style: "primary" }]382      ]383    }384  })385}386 387// ─────────────── ۲) پاسخ ادمین به تیکت388if (st.indexOf("sreply:") === 0) {389  if (!isAdmin) { await db.bot.del("wstep:" + user.id); return }390  let tid2 = parseInt(st.split(":")[1], 10)391  let tk2 = await db.bot.get("tkt:" + tid2, null)392  if (!tk2) {393    await db.bot.del("wstep:" + user.id)394    return Api.sendMessage({ chat_id: chat.id, text: "❌ تیکت پیدا نشد.", reply_markup: { inline_keyboard: [[{ text: "🎫 تیکت‌ها", callback_data: "adm tkt 0" , style: "primary" }]] } })395  }396  if (tk2.status === "closed") {397    await db.bot.del("wstep:" + user.id)398    return Api.sendMessage({ chat_id: chat.id, text: "🔒 این تیکت بسته شده است. برای موضوع جدید، کاربر باید تیکت تازه بزنه.", reply_markup: NAVOFF })399  }400 401  if (!txt && !mdId) {402    return Api.sendMessage({ chat_id: chat.id, text: "✍️ متن پاسخ رو بنویس و بفرست 👇\n(می‌تونی عکس یا فایل هم بفرستی)", reply_markup: NAVKB })403  }404 405  if (mdId) {406    let cap = txt ? txt.slice(0, 900) : ""407    if (mdType === "photo") { await Api.sendPhoto({ chat_id: tk2.uid, photo: mdId, caption: cap }) }408    else if (mdType === "video") { await Api.sendVideo({ chat_id: tk2.uid, video: mdId, caption: cap }) }409    else { await Api.sendDocument({ chat_id: tk2.uid, document: mdId, caption: cap }) }410  } else {411    await Api.sendMessage({412      chat_id: tk2.uid,413      text: "📩 <b>پاسخ پشتیبانی</b> — تیکت <code>#" + tid2 + "</code>\n\n" + Libs.tgutil.escapeText(txt, "html") + "\n\nℹ️ اگه ادامه داره، پیامت رو بفرست.",414      parse_mode: "HTML",415      reply_markup: { inline_keyboard: [[{ text: "📝 ادامه گفتگو", callback_data: "support new" , style: "danger" }]] }416    })417  }418 419  if (!Array.isArray(tk2.msgs)) { tk2.msgs = [] }420  tk2.msgs.push({ r: "a", t: (txt ? txt.slice(0, 800) : "📎 فایل"), at: stamp })421  if (tk2.msgs.length > 30) { tk2.msgs = tk2.msgs.slice(-30) }422  tk2.status = "answered"423  tk2.reply = txt.slice(0, 800)424  tk2.answered_at = stamp425  tk2.at = stamp426  await db.bot.set("tkt:" + tid2, tk2)427  await db.bot.del("wstep:" + user.id)428 429  return Api.sendMessage({430    chat_id: chat.id,431    text: "✅ پاسخ برای کاربر <code>" + tk2.uid + "</code> ارسال شد.\n\nاگه موضوع تمومه، تیکت رو ببند 👇",432    parse_mode: "HTML",433    reply_markup: {434      inline_keyboard: [435        [{ text: "🔒 بستن تیکت", callback_data: "tclose " + tid2 }],436        [{ text: "🎫 لیست تیکت‌ها", callback_data: "adm tkt 0" , style: "primary" }]437      ]438    }439  })440}// ─────────────── ۲.۵) 🔒 رمز آموزش (کاربر)441if (st.indexOf("pass:") === 0) {442  let ppid = st.split(":")[1]443  let pp = await db.bot.get("p:" + ppid, null)444  if (!pp) { await db.bot.del("wstep:" + user.id); return Api.sendMessage({ chat_id: chat.id, text: "❌ آموزش پیدا نشد." }) }445  if (!txt) { return Api.sendMessage({ chat_id: chat.id, text: "🔒 رمز این آموزش رو بفرست 👇", reply_markup: NAVKB }) }446 447  if (txt.trim() === String(pp.pass || "")) {448    await db.bot.set("ok:" + user.id + ":" + ppid, true, { ttl: 43200 })449    await db.bot.del("wstep:" + user.id)450    await Api.sendMessage({ chat_id: chat.id, text: "✅ رمز درست بود! دارم آموزش رو می‌فرستم 👇", reply_markup: NAVOFF })451    return Bot.run({ command: "dl go " + ppid, ignoreMissingCommand: true })452  }453 454  return Api.sendMessage({455    chat_id: chat.id,456    text: "❌ رمز اشتباهه. دوباره بفرست 👇\n(رمز رو از مدیریت آموزش بگیر)",457    reply_markup: { inline_keyboard: [[{ text: "🗂 کارت آموزش", callback_data: "dl " + ppid }]] }458  })459}460 461// ─────────────── ۳) ویزارد ساخت آموزش (ادمین) — هر مرحله با «-» یا «➡️ رد کردن» قابل پرشه462if (isAdmin && (st === "file" || st === "title" || st === "cover" || st === "preview" || st === "desc" || st === "video" || st === "pass" || st === "confirm")) {463  let d = await db.bot.get("draft:" + user.id, null)464  if (!d) {465    await db.bot.del("wstep:" + user.id)466    return Api.sendMessage({ chat_id: chat.id, text: "❌ پیش‌نویس پیدا نشد. از پنل → ➕ افزودن آموزش دوباره شروع کن.", reply_markup: { inline_keyboard: [[{ text: "🛠 پنل", callback_data: "adm home" , style: "primary" }]] } })467  }468 469  let skip = (txt === "-" || txt === "➡️ رد کردن" || txt === "رد کردن" || txt.toLowerCase() === "skip" || txt.toLowerCase() === "رد")470  let WIZKB = { keyboard: [[{ text: "➡️ رد کردن" }, { text: "↩️ بازگشت" }], [{ text: "✍️ لغو" }]], resize_keyboard: true }471  let saveD = async () => { await db.bot.set("draft:" + user.id, d, { ttl: 7200 }) }472  let toStep = async (nx) => { await saveD(); await db.bot.set("wstep:" + user.id, nx, { ttl: 7200 }) }473 474  // ── ۱ از ۷: فایل اصلی475  if (st === "file") {476    if (mdId) { d.file_id = mdId; d.file_type = mdType; d.file_name = mdName }477    else if (skip) { d.file_id = ""; d.file_type = ""; d.file_name = "" }478    else { return Api.sendMessage({ chat_id: chat.id, text: "📦 <b>مرحله ۱ از ۷ — فایل اصلی</b>\n\n❌ فایلی نیومد. فایل رو بفرست (سند / ویدیو / عکس / آهنگ) یا «➡️ رد کردن» رو بزن:", parse_mode: "HTML", reply_markup: WIZKB }) }479    await toStep("title")480    return Api.sendMessage({ chat_id: chat.id, text: (d.file_id ? "✅ فایل ثبت شد." : "➡️ بدون فایل رد شدیم.") + "\n\n📝 <b>مرحله ۲ از ۷ — عنوان آموزش</b>\nاسمی که تو لیست و کیبورد پایین دیده می‌شه.\n\n(اگه نمی‌خوای، «-» یا «➡️ رد کردن»)", parse_mode: "HTML", reply_markup: WIZKB })481  }482 483  // ── ۲ از ۷: عنوان484  if (st === "title") {485    if (!txt || skip) { d.title = "" } else { d.title = txt.slice(0, 120) }486    await toStep("cover")487    return Api.sendMessage({ chat_id: chat.id, text: (d.title ? "✅ عنوان ثبت شد: <b>" + Libs.tgutil.escapeText(d.title, "html") + "</b>" : "➡️ بدون عنوان رد شدیم.") + "\n\n🖼 <b>مرحله ۳ از ۷ — تصویر آموزش</b>\nعکسی که رو کارت آموزش دیده می‌شه رو بفرست.\n\n(اگه نداری، «-» یا «➡️ رد کردن»)", parse_mode: "HTML", reply_markup: WIZKB })488  }489 490  // ── ۳ از ۷: تصویر491  if (st === "cover") {492    if (um.photo) { d.cover = mdId }493    else if (skip) { d.cover = null }494    else { return Api.sendMessage({ chat_id: chat.id, text: "🖼 <b>مرحله ۳ از ۷ — تصویر آموزش</b>\n\n❌ عکسی نیومد. عکس رو بفرست یا «➡️ رد کردن» رو بزن:", parse_mode: "HTML", reply_markup: WIZKB }) }495    await toStep("preview")496    return Api.sendMessage({ chat_id: chat.id, text: (d.cover ? "✅ تصویر ثبت شد." : "➡️ بدون تصویر رد شدیم.") + "\n\n👁 <b>مرحله ۴ از ۷ — متن پیش‌نمایش کارت</b>\nمتنی که روی کارت آموزش قبل از زدن دکمهٔ «📥 دریافت آموزش» به کاربر نشون داده می‌شه (تیزر، خلاصه یا پیش‌نمایش).\n\n(اگه نمی‌خوای، «-» یا «➡️ رد کردن»)", parse_mode: "HTML", reply_markup: WIZKB })497  }498 499  // ── ۴ از ۷: پیش‌نمایش کارت500  if (st === "preview") {501    if (!txt || skip) { d.preview = "" } else { d.preview = txt.slice(0, 900) }502    await toStep("desc")503    return Api.sendMessage({ chat_id: chat.id, text: (d.preview ? "✅ پیش‌نمایش ثبت شد." : "➡️ بدون پیش‌نمایش رد شدیم.") + "\n\n📚 <b>مرحله ۵ از ۷ — توضیحات اصلی آموزش</b>\nمتن و توضیحات کامل آموزش که پس از زدن «📥 دریافت آموزش» (و دادن رمز در صورت داشتن رمز) به همراه فایل تحویل داده می‌شه.\n\n(اگه نمی‌خوای، «-» یا «➡️ رد کردن»)", parse_mode: "HTML", reply_markup: WIZKB })504  }505 506  // ── ۵ از ۷: توضیحات اصلی507  if (st === "desc") {508    if (!txt || skip) { d.desc = "" } else { d.desc = txt.slice(0, 900) }509    await toStep("video")510    return Api.sendMessage({ chat_id: chat.id, text: (d.desc ? "✅ توضیحات ثبت شد." : "➡️ بدون توضیحات رد شدیم.") + "\n\n🎬 <b>مرحله ۶ از ۷ — لینک ویدیو</b>\nلینک ویدیوی آموزش (یوتیوب / اینستا / هر سایتی).\n\n⚠️ اگه بذاری، کاربر رو کارت آموزش دکمه‌ی «▶️ دیدن ویدیو» می‌گیره.\n(اگه نداری، «-» یا «➡️ رد کردن»)", parse_mode: "HTML", reply_markup: WIZKB })511  }512 513  // ── ۶ از ۷: لینک ویدیو514  if (st === "video") {515    if (skip) { d.link = "" }516    else if (txt.indexOf("http") === 0) { d.link = txt.slice(0, 300) }517    else { return Api.sendMessage({ chat_id: chat.id, text: "🎬 <b>مرحله ۶ از ۷ — لینک ویدیو</b>\n\n❌ لینک باید با <code>http</code> شروع بشه (مثل https://youtu.be/xxxx). دوباره بفرست یا «➡️ رد کردن»:", parse_mode: "HTML", reply_markup: WIZKB }) }518    await toStep("pass")519    return Api.sendMessage({ chat_id: chat.id, text: (d.link ? "✅ لینک ویدیو ثبت شد." : "➡️ بدون ویدیو رد شدیم.") + "\n\n🔒 <b>مرحله ۷ از ۷ — رمز آموزش (اختیاری)</b>\nاگه می‌خوای این آموزش رمز داشته باشه، رمز رو بنویس (فقط کاربرهایی که رمز رو بگن فایل رو می‌گیرن).\n\n(بدون رمز: «-» یا «➡️ رد کردن»)", parse_mode: "HTML", reply_markup: WIZKB })520  }521 522  // ── ۷ از ۷: رمز → پیش‌نمایش523  if (st === "pass") {524    if (!txt || skip) { d.pass = "" } else { d.pass = txt.trim().slice(0, 32) }525    await toStep("confirm")526 527    let t = "🧾 <b>پیش‌نمایش آموزش</b>\n\n"528    t = t + "۱) 📦 فایل: " + (d.file_id ? ("دارد ✅ (" + (d.file_type || "document") + (d.file_name ? " — " + Libs.tgutil.escapeText(d.file_name, "html") : "") + ")") : "ندارد ➡️ (رد شده)") + "\n"529    t = t + "۲) 📝 عنوان: " + (d.title ? "<b>" + Libs.tgutil.escapeText(d.title, "html") + "</b>" : "— (رد شده)") + "\n"530    t = t + "۳) 🖼 تصویر: " + (d.cover ? "دارد ✅" : "ندارد ➡️ (رد شده)") + "\n"531    t = t + "۴) 👁 پیش‌نمایش کارت: " + (d.preview ? Libs.tgutil.escapeText(d.preview.slice(0, 120), "html") : "— (رد شده)") + "\n"532    t = t + "۵) 📚 توضیحات اصلی: " + (d.desc ? Libs.tgutil.escapeText(d.desc.slice(0, 180), "html") : "— (رد شده)") + "\n"533    t = t + "۶) 🎬 لینک ویدیو: " + (d.link ? Libs.tgutil.escapeText(d.link, "html") : "ندارد ➡️ (رد شده)") + "\n"534    t = t + "۷) 🔒 رمز: " + (d.pass ? "دارد ✅" : "بدون رمز ➡️ (رد شده)") + "\n\n"535    if (!d.file_id && !d.link) { t = t + "⚠️ نه فایلی داری نه ویدیویی — کاربر چیزی برای گرفتن نداره!\n\n" }536    t = t + "اگه درسته، «✅ تأیید و ذخیره» رو بزن."537 538    let kb = { inline_keyboard: [[{ text: "✅ تأیید و ذخیره", callback_data: "fsave" , style: "success" }], [{ text: "✍️ لغو پیش‌نویس", callback_data: "fcancel" , style: "danger" }]] }539    if (d.cover) {540      return Api.sendPhoto({ chat_id: chat.id, photo: d.cover, caption: t, parse_mode: "HTML", reply_markup: kb })541    }542    return Api.sendMessage({ chat_id: chat.id, text: t, parse_mode: "HTML", reply_markup: kb })543  }544 545  if (st === "confirm") {546    return Api.sendMessage({547      chat_id: chat.id,548      text: "👆 از دکمه‌های بالا استفاده کن (تأیید و ذخیره / لغو) — یا «➡️ رد کردن» بزن تا از مرحله‌ی رمز رد بشیم.",549      reply_markup: { inline_keyboard: [[{ text: "✅ تأیید و ذخیره", callback_data: "fsave" , style: "success" }, { text: "✍️ لغو پیش‌نویس", callback_data: "fcancel" , style: "danger" }]] }550    })551  }552}553 554// ─────────────── ۴) ویرایش فیلد یک فایل (ادمین)555if (isAdmin && st.indexOf("edit:") === 0) {556  let ep = st.split(":")557  let p = await db.bot.get("p:" + ep[1], null)558  if (!p) {559    await db.bot.del("wstep:" + user.id)560    return Api.sendMessage({ chat_id: chat.id, text: "❌ آموزش پیدا نشد.", reply_markup: { inline_keyboard: [[{ text: "🛠 پنل", callback_data: "adm home" , style: "primary" }]] } })561  }562 563  let field = ep[2]564  let ok = true565 566  if (field === "title") { if (!txt || txt === "-") { ok = false } else { p.title = txt.slice(0, 120) } }567  else if (field === "preview") { if (txt === "-") { p.preview = "" } else if (txt) { p.preview = txt.slice(0, 900) } else { ok = false } }568  else if (field === "desc") { if (txt === "-") { p.desc = "" } else if (txt) { p.desc = txt.slice(0, 900) } else { ok = false } }569  else if (field === "link") { if (txt === "-") { p.link = "" } else if (txt.indexOf("http") === 0) { p.link = txt.slice(0, 300) } else { ok = false } }570  else if (field === "cover") { if (um.photo) { p.cover = mdId } else if (txt === "-") { p.cover = null } else { ok = false } }571  else if (field === "pass") { if (txt === "-") { p.pass = "" } else if (txt) { p.pass = txt.trim().slice(0, 32) } else { ok = false } }572  else if (field === "chtitle") { if (txt === "-") { p.chtitle = "" } else if (txt) { p.chtitle = txt.trim().slice(0, 900) } else { ok = false } }573  else if (field === "refneed") {574    let rawN = String(txt || "").replace(/[۰-۹]/g, (d) => String("۰۱۲۳۴۵۶۷۸۹".indexOf(d))).replace(/[٠-٩]/g, (d) => String("٠١٢٣٤٥٦٧٨٩".indexOf(d))).trim()575    let n0 = parseInt(rawN, 10)576    if (txt === "-" || rawN === "0" || rawN === "خاموش" || rawN === "off") { p.refneed = 0 }577    else if (!isNaN(n0) && n0 > 0 && n0 <= 500) { p.refneed = n0 }578    else { ok = false }579  }580  else if (field === "media") { if (mdId) { p.file_id = mdId; p.file_type = mdType; p.file_name = mdName } else { ok = false } }581  else { ok = false }582 583  if (!ok) {584    return Api.sendMessage({ chat_id: chat.id, text: "⚠️ مقدار نامعتبر بود. دوباره بفرست یا لغو کن:", reply_markup: NAVKB })585  }586 587  await db.bot.set("p:" + p.id, p)588  await db.bot.del("wstep:" + user.id)589  return Api.sendMessage({590    chat_id: chat.id,591    text: "✅ آموزش <code>#" + p.id + "</code> بروزرسانی شد.",592    parse_mode: "HTML",593    reply_markup: { inline_keyboard: [[{ text: "🗂 مشاهده آموزش", callback_data: "adm item " + p.id }], [{ text: "🛠 پنل", callback_data: "adm home" , style: "primary" }]] }594  })595}596 597// ─────────────── ۴.۵) 🎁 قفل دعوت پیش‌فرض (ادمین)598if (isAdmin && st === "refdefault") {599  let rawD = String(txt || "").replace(/[۰-۹]/g, (d) => String("۰۱۲۳۴۵۶۷۸۹".indexOf(d))).replace(/[٠-٩]/g, (d) => String("٠١٢٣٤٥٦٧٨٩".indexOf(d))).trim()600  let nD = parseInt(rawD, 10)601  if (txt === "-" || rawD === "0" || rawD === "خاموش" || rawD === "off") {602    cfg.gate_ref = 0603    await db.bot.set("cfg", cfg)604    await db.bot.del("wstep:" + user.id)605    return Api.sendMessage({ chat_id: chat.id, text: "🎁 <b>قفل دعوت دوستان خاموش شد.</b>\n\nهیچ آموزشی با دعوت قفل نمی‌شه (مگر برای یک آموزش جدا عدد بذاری).", parse_mode: "HTML", reply_markup: { inline_keyboard: [[{ text: "🔐 قفل‌ها", callback_data: "adm gates" , style: "primary" }]] } })606  }607  if (isNaN(nD) || nD < 1 || nD > 500) {608    return Api.sendMessage({ chat_id: chat.id, text: "⚠️ فقط یک عدد بین ۱ تا ۵۰۰ بفرست (یا «-» برای خاموش کردن):", reply_markup: NAVKB })609  }610  cfg.gate_ref = nD611  await db.bot.set("cfg", cfg)612  await db.bot.del("wstep:" + user.id)613  return Api.sendMessage({614    chat_id: chat.id,615    text: "✅ <b>قفل دعوت دوستان روشن شد: " + nD + " نفر</b>\n\nاز این به بعد، کاربر برای دریافت آموزش‌ها باید " + nD + " نفر رو دعوت کنه.\n🎁 برای هر آموزش هم می‌تونی عدد جدا بذاری: «🗂 آموزش‌ها → آموزش → 🎁 قفل دعوت».",616    parse_mode: "HTML",617    reply_markup: { inline_keyboard: [[{ text: "🔐 قفل‌ها", callback_data: "adm gates" , style: "primary" }]] }618  })619}620 621// ─────────────── ۵) متن‌های تنظیمات (ادمین)622if (isAdmin && st.indexOf("text:") === 0) {623  if (!txt) { return Api.sendMessage({ chat_id: chat.id, text: "✍️ متن رو بفرست (برای پاک کردن «-»):", reply_markup: NAVKB }) }624  cfg[st.split(":")[1]] = (txt === "-" ? "" : txt.slice(0, 900))625  await db.bot.set("cfg", cfg)626  await db.bot.del("wstep:" + user.id)627  return Api.sendMessage({628    chat_id: chat.id,629    text: "✅ متن ذخیره شد.",630    reply_markup: { inline_keyboard: [[{ text: "⚙️ تنظیمات", callback_data: "adm set" , style: "primary" }], [{ text: "🛠 پنل", callback_data: "adm home" , style: "primary" }]] }631  })632}633 634// ─────────────── ۶) افزودن کانال جوین اجباری (ادمین)635if (isAdmin && st === "addch") {636  if (!txt) { return Api.sendMessage({ chat_id: chat.id, text: "📢 یوزرنیم کانال رو بفرست (مثل @mychannel):", reply_markup: NAVKB }) }637 638  let val = txt639  if (val.indexOf("https://t.me/") === 0) { val = "@" + val.replace("https://t.me/", "").split("/")[0] }640  else if (val.indexOf("t.me/") === 0) { val = "@" + val.replace("t.me/", "").split("/")[0] }641  else if (val.indexOf("@") !== 0 && val.indexOf("-100") !== 0) { val = "@" + val }642 643  let chans = (cfg.channels || []).filter(x => x)644  let dup = false645  for (let c of chans) { if (String(c).toLowerCase() === String(val).toLowerCase()) { dup = true } }646 647  if (dup) { return Api.sendMessage({ chat_id: chat.id, text: "⚠️ این کانال قبلاً اضافه شده. یه کانال دیگه بفرست:", reply_markup: NAVKB }) }648  if (chans.length >= 10) { await db.bot.del("wstep:" + user.id); return Api.sendMessage({ chat_id: chat.id, text: "⚠️ حداکثر ۱۰ کانال. اول یکی رو حذف کن.", reply_markup: { inline_keyboard: [[{ text: "📢 کانال‌ها", callback_data: "adm chans" , style: "primary" }]] } }) }649 650  chans.push(val)651  cfg.channels = chans652  await db.bot.set("cfg", cfg)653  await db.bot.del("wstep:" + user.id)654 655  return Api.sendMessage({656    chat_id: chat.id,657    text: "✅ کانال <code>" + Libs.tgutil.escapeText(val, "html") + "</code> اضافه شد.\n\n⚠️ یادت نره ربات رو داخل این کانال <b>ادمین</b> کنی.",658    parse_mode: "HTML",659    reply_markup: { inline_keyboard: [[{ text: "➕ افزودن کانال دیگه", callback_data: "addch" , style: "success" }, { text: "📢 لیست کانال‌ها", callback_data: "adm chans" , style: "primary" }]] }660  })661}662 663// ─────────────── ۷) کانال خروجی (ادمین)664if (isAdmin && st === "setout") {665  if (!txt) { return Api.sendMessage({ chat_id: chat.id, text: "🎯 یوزرنیم یا آیدی کانال رو بفرست (مثل @mychannel یا -1001234567890):", reply_markup: NAVKB }) }666 667  let val2 = txt668  if (val2.indexOf("https://t.me/") === 0) { val2 = "@" + val2.replace("https://t.me/", "").split("/")[0] }669  else if (val2.indexOf("t.me/") === 0) { val2 = "@" + val2.replace("t.me/", "").split("/")[0] }670 671  if (val2.indexOf("@") !== 0 && val2.indexOf("-100") !== 0) {672    return Api.sendMessage({ chat_id: chat.id, text: "⚠️ باید یوزرنیم کانال (@example) یا آیدی عددی (-100...) بفرستی. دوباره:", reply_markup: NAVKB })673  }674 675  cfg.out_channel = val2676  await db.bot.set("cfg", cfg)677  await db.bot.del("wstep:" + user.id)678 679  await Api.sendMessage({680    chat_id: chat.id,681    text: "✅ کانال خروجی ثبت شد: <code>" + Libs.tgutil.escapeText(val2, "html") + "</code>\n\n⚠️ ربات باید داخل این کانال <b>ادمین</b> باشه تا بتونه پست بذاره.",682    parse_mode: "HTML",683    reply_markup: NAVOFF684  })685 686  // 🔄 اگه کاری منتظر همین کانال بود، خودکار همون کار رو ادامه بده687  let want2 = await db.bot.get("want:" + user.id, "")688  if (want2) {689    await db.bot.del("want:" + user.id)690    await Bot.sendMessage("🔄 حالا همون کار قبلی رو ادامه می‌دم...")691    return Bot.run({ command: want2, ignoreMissingCommand: true })692  }693 694  return Api.sendMessage({695    chat_id: chat.id,696    text: "👇 قدم بعدی:",697    reply_markup: { inline_keyboard: [[{ text: "❤️ ساخت پست ری‌اکشن", callback_data: "mkreact" , style: "primary" }], [{ text: "⚙️ تنظیمات", callback_data: "adm set" , style: "primary" }]] }698  })699}700 701// ─────────────── ۸) افزودن ادمین (فقط مالک)702if (st === "addadmin") {703  if (user.id !== OWNER) { await db.bot.del("wstep:" + user.id); return }704  if (!txt) { return Api.sendMessage({ chat_id: chat.id, text: "👮 آیدی عددی کاربر رو بفرست (مثل 123456789):", reply_markup: NAVKB }) }705 706  let uid = parseInt(txt, 10)707  if (isNaN(uid) || uid < 1000) {708    return Api.sendMessage({ chat_id: chat.id, text: "⚠️ آیدی عددی معتبر بفرست (مثلاً 123456789). دوباره:", reply_markup: NAVKB })709  }710 711  let ads = cfg.admins || []712  if (ads.indexOf(uid) === -1) { ads.push(uid) }713  cfg.admins = ads714  await db.bot.set("cfg", cfg)715  await db.bot.del("wstep:" + user.id)716 717  return Api.sendMessage({718    chat_id: chat.id,719    text: "✅ ادمین <code>" + uid + "</code> اضافه شد.\n(ادمین‌ها به پنل، افزودن آموزش، تیکت‌ها و پیام همگانی دسترسی دارن.)",720    parse_mode: "HTML",721    reply_markup: { inline_keyboard: [[{ text: "👮 لیست ادمین‌ها", callback_data: "adm admins" , style: "primary" }]] }722  })723}724 725// ─────────────── ۹) پیام همگانی (ادمین)726if (isAdmin && st === "bc") {727  if (!txt && !mdId) { return Api.sendMessage({ chat_id: chat.id, text: "📣 متن پیام همگانی رو بفرست (می‌تونی عکس/ویدیو/فایل هم بفرستی):", reply_markup: NAVKB }) }728 729  await db.bot.set("bc_text", txt)730  if (mdId) { await db.bot.set("bc_media", { type: mdType, file_id: mdId }) } else { await db.bot.del("bc_media") }731  await db.bot.set("bc_time", stamp)732  await db.bot.del("wstep:" + user.id)733 734  let job = await Bot.broadcast({ command: "bc_send", filters: { chatType: "private" } })735 736  return Bot.sendMessage(737    "📣 <b>ارسال همگانی شروع شد!</b>\n\n👥 تعداد کاربران: <b>" + (job ? job.totalTargetChats : "?") + "</b>\n🎫 کد ارسال: <code>" + (job ? job.broadcastId : "—") + "</code>\n\nℹ️ پلتفرم خودش ضدفلاد و بلاک‌شده‌ها رو هندل می‌کنه.",738    { parse_mode: "HTML" }739  )740}741 742// ─────────────── ۱۰) جست‌وجو (همه)743if (st === "search") {744  if (!txt) { return Api.sendMessage({ chat_id: chat.id, text: "🔎 عبارت جست‌وجو رو بفرست:", reply_markup: NAVKB }) }745  await db.bot.del("wstep:" + user.id)746 747  let ql = txt.toLowerCase()748  let plist = await db.bot.get("plist", [])749  let scan = plist.slice()750  let data = {}751  for (let ci2 = 0; ci2 < scan.length; ci2 += 30) {752    let ck2 = scan.slice(ci2, ci2 + 30)753    let cp2 = await db.bot.mget(ck2.map(x => "p:" + x))754    if (cp2) { for (let key2 in cp2) { data[key2] = cp2[key2] } }755  }756 757  let hits = []758  for (let id of scan) {759    let p = data["p:" + id]760    if (!p) { continue }761    if (((p.title || "") + " " + (p.desc || "")).toLowerCase().indexOf(ql) !== -1) { hits.push(id) }762  }763 764  if (!hits.length) {765    return Api.sendMessage({766      chat_id: chat.id,767      text: "🔍 برای «" + Libs.tgutil.escapeText(txt, "html") + "» چیزی پیدا نشد.",768      parse_mode: "HTML",769      reply_markup: { inline_keyboard: [[{ text: "📚 همه آموزش‌ها", callback_data: "files 0" , style: "primary" }]] }770    })771  }772 773  hits.reverse()774  let rows = []775  let hitsShown = hits.slice(0, 24)776  for (let id of hitsShown) {777    let p = data["p:" + id]778    let t = p.title ? p.title : "بدون عنوان"779    if (t.length > 32) { t = t.slice(0, 32) + "…" }780    rows.push([{ text: "📄 " + t, callback_data: "dl " + id }])781  }782  rows.push([{ text: "🏠 منوی اصلی", callback_data: "/start" , style: "primary" }])783 784  return Api.sendMessage({785    chat_id: chat.id,786    text: "🔍 <b>نتایج:</b> «" + Libs.tgutil.escapeText(txt, "html") + "» — تعداد: <b>" + hits.length + "</b>" + (hits.length > 24 ? "\n(۲۴ نتیجه‌ی اول — عبارت دقیق‌تری بنویس)" : ""),787    parse_mode: "HTML",788    reply_markup: { inline_keyboard: rows }789  })790}791 792// ─────────────── ۱۱) لینک دستی پست ری‌اکشن (ادمین)793if (isAdmin && st === "reactlink") {794  if (!txt) { return Api.sendMessage({ chat_id: chat.id, text: "🔗 لینک پست کانال رو بفرست (مثل https://t.me/mychannel/123):", reply_markup: NAVKB }) }795 796  if (txt === "-") {797    cfg.react_link = ""798  } else if (txt.indexOf("http") === 0) {799    cfg.react_link = txt.slice(0, 300)800  } else {801    return Api.sendMessage({ chat_id: chat.id, text: "⚠️ لینک باید با <code>http</code> شروع بشه. دوباره بفرست یا «-»:", parse_mode: "HTML", reply_markup: NAVKB })802  }803 804  await db.bot.set("cfg", cfg)805  await db.bot.del("wstep:" + user.id)806  return Api.sendMessage({807    chat_id: chat.id,808    text: (cfg.react_link ? "✅ لینک پست ری‌اکشن ثبت شد:\n" + Libs.tgutil.escapeText(cfg.react_link, "html") : "✅ لینک پاک شد."),809    parse_mode: "HTML",810    reply_markup: { inline_keyboard: [[{ text: "🔐 قفل‌ها", callback_data: "adm gates" , style: "primary" }]] }811  })812}813 814// ─────────────── ۱۲) پیام‌های ناشناس815// ادمین → مستقیم پنل (دیگه «متوجه نشدم» نمی‌گیره)816if (isAdmin) {817  await Api.sendMessage({ chat_id: chat.id, text: "🛠 پنل مدیریت رو باز کردم 👇", reply_markup: NAVOFF })818  return Bot.run({ command: "adm", ignoreMissingCommand: true })819}820 821// کاربری که تیکت باز داره → وضعیت پشتیبانی822let tid3 = await db.bot.get("tu:" + user.id, null)823let tk3 = tid3 ? await db.bot.get("tkt:" + tid3, null) : null824if (tk3 && tk3.status !== "closed") {825  let stateTx = (tk3.status === "answered" ? "✅ پاسخ پشتیبانی اومده — پیام‌ها رو ببین" : "📞 منتظر پاسخ پشتیبانی هستیم")826  return Api.sendMessage({827    chat_id: chat.id,828    text: "🎫 <b>تیکت #" + tid3 + "</b>\n\n" + stateTx + "\n\nاگه حرف جدیدی داری، به همین تیکت اضافه کن 👇",829    parse_mode: "HTML",830    reply_markup: {831      inline_keyboard: [832        [{ text: "📝 پیام جدید به پشتیبانی", callback_data: "support new" , style: "danger" }],833        [{ text: "📚 آموزش‌ها", callback_data: "files 0" , style: "primary" }, { text: "🏠 منو", callback_data: "/start" , style: "primary" }]834      ]835    }836  })837}838 839// کاربر عادی840Api.sendMessage({841  chat_id: chat.id,842  text: "🔎 چیزی که دنبالش بودی پیدا نشد.\n\nاز دکمه‌های زیر استفاده کن یا /start بزن 👇",843  reply_markup: {844    inline_keyboard: [845      [{ text: "📚 آموزش‌ها", callback_data: "files 0" , style: "primary" }],846      [{ text: "👤 پروفایل من", callback_data: "profile" , style: "primary" }, { text: "❓ راهنما", callback_data: "help" , style: "primary" }],847      [{ text: "📞 پشتیبانی", callback_data: "support" , style: "danger" }, { text: "🏠 منوی اصلی", callback_data: "/start" , style: "primary" }]848    ]849  }850})