Files
arah2/bot/scripts/set-webhook.mjs
araharah-executor d600d6e71a fix(02-03): set-webhook auto-appends /telegram/webhook to a pathless URL
- Deploy walkthrough revealed a bare workers.dev origin setWebhook answers ok but drops updates at / (404)
- Normalize WEBHOOK_URL: no path -> append /telegram/webhook; invalid URL -> fail fast, never echo values (T-02-04)
- .dev.vars.example documents the path expectation
2026-10-22 19:56:24 +08:00

94 lines
3.5 KiB
JavaScript

#!/usr/bin/env node
/*
* set-webhook.mjs — one-time setWebhook registration (D-16, D-17).
* -----------------------------------------------------------------
* Points Telegram at the Worker's /telegram/webhook route and arms the
* secret-token channel: the `secret_token` value sent here is the one
* Telegram echoes back in X-Telegram-Bot-Api-Secret-Token on every update,
* which the Worker's webhook adapter constant-time-compares against
* env.WEBHOOK_SECRET (T-02-03).
*
* Inputs come from bot/.dev.vars (gitignored, D-15): BOT_TOKEN,
* WEBHOOK_URL, WEBHOOK_SECRET. The bot token is never echoed — on failure
* only the API's error_code/description are printed (T-02-04).
* WEBHOOK_URL is normalized: if the configured origin points at the
* workers.dev root (no path), /telegram/webhook is appended automatically —
* a bare origin passed the 2026-10-22 deploy walkthrough caused Telegram to
* answer ok but deliver nothing (updates routed to "/" → 404).
* Exit 0 only when Telegram answers ok:true.
*/
import { readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
function readDevVars() {
const devVarsPath = path.join(fileURLToPath(new URL(import.meta.url)), "..", "..", ".dev.vars");
let raw = "";
try {
raw = readFileSync(devVarsPath, "utf8");
} catch {
return {};
}
const vars = {};
for (const line of raw.split("\n")) {
const trimmed = line.trim();
if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
const eq = trimmed.indexOf("=");
if (eq === -1) continue;
vars[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
}
return vars;
}
const devVars = readDevVars();
const token = process.env.BOT_TOKEN ?? devVars.BOT_TOKEN;
const webhookUrl = process.env.WEBHOOK_URL ?? devVars.WEBHOOK_URL;
const secret = process.env.WEBHOOK_SECRET ?? devVars.WEBHOOK_SECRET;
const missing = ["BOT_TOKEN", "WEBHOOK_URL", "WEBHOOK_SECRET"].filter(
(name) => !{ BOT_TOKEN: token, WEBHOOK_URL: webhookUrl, WEBHOOK_SECRET: secret }[name],
);
if (missing.length > 0) {
console.error(`Missing ${missing.join(", ")} — set them in bot/.dev.vars (see .dev.vars.example; never commit).`);
process.exit(1);
}
// Auto-append the route path when the URL has none — prevents the recurring
// misconfiguration where a bare workers.dev origin is registered and the
// Worker answers ok:true but Telegram updates land on "/" (404, silent drop).
const WEBHOOK_ROUTE = "/telegram/webhook";
let normalizedUrl;
try {
const parsed = new URL(webhookUrl);
if (parsed.pathname === "/" || parsed.pathname === "") {
parsed.pathname = WEBHOOK_ROUTE;
normalizedUrl = parsed.toString();
console.log(`WEBHOOK_URL had no path — normalized to ...${WEBHOOK_ROUTE}`);
} else {
normalizedUrl = webhookUrl;
}
} catch {
console.error(`WEBHOOK_URL is not a valid URL — check bot/.dev.vars (value never echoed).`);
process.exit(1);
}
const payload = {
url: normalizedUrl,
secret_token: secret,
allowed_updates: ["message"],
};
const res = await fetch(`https://api.telegram.org/bot${token}/setWebhook`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(payload),
});
const body = await res.json();
if (!body.ok) {
console.error(`setWebhook failed: ${body.error_code ?? "?"} ${body.description ?? "unknown error"}`);
process.exit(1);
}
console.log(`setWebhook ok:true — updates will be delivered to the configured URL with the secret-token header.`);
process.exit(0);