diff --git a/bot/scripts/register-commands.mjs b/bot/scripts/register-commands.mjs index e8c4b71..6793abd 100644 --- a/bot/scripts/register-commands.mjs +++ b/bot/scripts/register-commands.mjs @@ -69,24 +69,38 @@ function menuFor(locale) { }); } -/* ————— exactly two API calls: default scope, then the ms scope (D-03/D-04) ————— */ +/* ————— scope set (device-gate fix 2026-10-24): Telegram resolves a chat's + * command menu from the NARROWEST scope that has ANY commands set — a stale + * all_private_chats set shadows the default scope entirely (the /cari-missing- + * from-menu incident). The script therefore arms every scope it may shadow: + * default (en) · default+ms · all_private_chats (en) · all_private_chats+ms + * and CLEARS the group scopes (leftover phase-2 experiments) so no junk set + * shadows anything elsewhere. ————— */ const API = `https://api.telegram.org/bot${token}/setMyCommands`; const JSON_HEADERS = { "content-type": "application/json" }; -const defaultPayload = { commands: menuFor("en"), bot_command_scope: { type: "default" } }; -const msPayload = { commands: menuFor("ms"), bot_command_scope: { type: "default" }, language_code: "ms" }; +const CALLS = [ + { label: "default (en)", body: { commands: menuFor("en"), bot_command_scope: { type: "default" } } }, + { label: "default (ms)", body: { commands: menuFor("ms"), bot_command_scope: { type: "default" }, language_code: "ms" } }, + { label: "all_private_chats (en)", body: { commands: menuFor("en"), bot_command_scope: { type: "all_private_chats" } } }, + { label: "all_private_chats (ms)", body: { commands: menuFor("ms"), bot_command_scope: { type: "all_private_chats" }, language_code: "ms" } }, + { label: "clear all_group_chats", body: { commands: [], bot_command_scope: { type: "all_group_chats" } } }, + { label: "clear all_chat_administrators", body: { commands: [], bot_command_scope: { type: "all_chat_administrators" } } }, +]; -const defaultRes = await fetch(API, { method: "POST", headers: JSON_HEADERS, body: JSON.stringify(defaultPayload) }); -const defaultBody = await defaultRes.json(); -console.log(`default scope -> ok:${defaultBody.ok} result:${JSON.stringify(defaultBody.result ?? null)}`); +let failed = null; +for (const call of CALLS) { + const res = await fetch(API, { method: "POST", headers: JSON_HEADERS, body: JSON.stringify(call.body) }); + const body = await res.json(); + console.log(`${call.label.padEnd(30)} -> ok:${body.ok} ${body.ok ? "" : JSON.stringify(body)}`); + if (!body.ok && failed === null) failed = body; +} -const msRes = await fetch(API, { method: "POST", headers: JSON_HEADERS, body: JSON.stringify(msPayload) }); -const msBody = await msRes.json(); -console.log(`ms scope -> ok:${msBody.ok} result:${JSON.stringify(msBody.result ?? null)}`); - -if (!defaultBody.ok || !msBody.ok) { - const firstError = !defaultBody.ok ? defaultBody : msBody; +if (!failed) { + console.log("all command scopes armed"); +} else { + const firstError = failed; console.error(`setMyCommands failed: ${firstError.error_code ?? "?"} ${firstError.description ?? "unknown error"}`); process.exit(1); }