From 2abdd895ea74ca0bfdefbc362afc2c18a28995b2 Mon Sep 17 00:00:00 2001 From: Harsha Kodali Date: Mon, 18 May 2026 01:01:10 +0530 Subject: [PATCH 1/2] feat: add /status command to verify Better Stack connectivity Makes a live GET request to the Better Stack incidents API to confirm the bot token is valid and the API is reachable. Returns HTTP status, policy ID (restricted for unauthorized users), and allowlist state. Also adds /status to /help command list. Closes #12 --- bot.ts | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/bot.ts b/bot.ts index ba2ace1..0e90640 100644 --- a/bot.ts +++ b/bot.ts @@ -179,6 +179,7 @@ bot.command('help', (ctx: Context) => { return ctx.reply( `*AlphaFi ASIR Bot — Available Commands*\n\n` + `\`/alert \` — Trigger the Better Stack escalation policy and phone the on-call team.\n` + + `\`/status\` — Check if the bot is running and connected to Better Stack.\n` + `\`/help\` — Show this message.\n\n` + `*Notes:*\n` + `• Only authorized users can trigger alerts.\n` + @@ -188,6 +189,58 @@ bot.command('help', (ctx: Context) => { ); }); +bot.command('status', async (ctx: Context) => { + const statusMsg = await ctx.reply('⏳ Checking Better Stack connectivity...'); + + try { + const response = await axios.get(`${BETTER_STACK_URL}?per_page=1`, { + headers: HEADERS, + timeout: 10000, + }); + + const authorized = ALLOWED_USERS.length === 0 || ALLOWED_USERS.includes(ctx.from!.id.toString()); + const authLine = ALLOWED_USERS.length === 0 + ? '⚠️ *Auth:* No allowlist set — all users can trigger alerts' + : `🔒 *Auth:* Allowlist active (${ALLOWED_USERS.length} user${ALLOWED_USERS.length === 1 ? '' : 's'})`; + + const policyLine = authorized + ? `📋 *Policy ID:* \`${POLICY_ID}\`` + : `📋 *Policy ID:* _restricted_`; + + log.info({ userId: ctx.from!.id.toString(), httpStatus: response.status }, '/status check passed'); + + await ctx.telegram.editMessageText( + ctx.chat!.id, + statusMsg.message_id, + undefined, + `✅ *ASIR Bot Status*\n\n` + + `🤖 *Bot:* Running\n` + + `🌐 *Better Stack API:* Connected (HTTP ${response.status})\n` + + `${policyLine}\n` + + `${authLine}`, + { parse_mode: 'Markdown' }, + ); + } catch (error) { + const axiosError = error as { response?: { status?: number }; message?: string }; + const detail = axiosError.response?.status + ? `HTTP ${axiosError.response.status}` + : (axiosError.message ?? 'unknown error'); + + log.error({ userId: ctx.from!.id.toString(), detail }, '/status check failed'); + + await ctx.telegram.editMessageText( + ctx.chat!.id, + statusMsg.message_id, + undefined, + `❌ *ASIR Bot Status*\n\n` + + `🤖 *Bot:* Running\n` + + `🌐 *Better Stack API:* Unreachable (${escapeMarkdown(detail)})\n\n` + + `_The bot cannot trigger alerts until connectivity is restored._`, + { parse_mode: 'Markdown' }, + ); + } +}); + bot.catch((err: unknown, ctx: Context) => { log.error({ updateType: ctx.updateType, err }, 'Unhandled bot error'); }); From 00fed42cb0fa484d25cb48dfaecce5b43992a101 Mon Sep 17 00:00:00 2001 From: Harsha Kodali Date: Mon, 18 May 2026 14:15:32 +0530 Subject: [PATCH 2/2] fix(bot): gate /status behind ALLOWED_USERS and add cooldown - Block unauthorized users before making any API call (mirrors /alert) - Add 2-minute cooldown per user via a namespaced key (status:) to prevent API hammering - Remove now-redundant authorized check and policyLine conditional - Remove unreachable dead code (axiosError.message ?? 'unknown error') --- bot.ts | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/bot.ts b/bot.ts index 0e90640..62bf90d 100644 --- a/bot.ts +++ b/bot.ts @@ -190,6 +190,22 @@ bot.command('help', (ctx: Context) => { }); bot.command('status', async (ctx: Context) => { + const userId = ctx.from!.id.toString(); + const rawUserLabel = ctx.from!.username ? `@${ctx.from!.username}` : ctx.from!.first_name; + + if (ALLOWED_USERS.length > 0 && !ALLOWED_USERS.includes(userId)) { + log.warn({ userId, userLabel: rawUserLabel }, 'Unauthorized /status access attempt'); + return ctx.reply('🚫 Error: You are not authorized to use this command.'); + } + + const lastCheck = cooldowns.get(`status:${userId}`); + const now = Date.now(); + if (lastCheck && now - lastCheck < COOLDOWN_MS) { + const remainingSeconds = Math.ceil((COOLDOWN_MS - (now - lastCheck)) / 1000); + return ctx.reply(`⏳ Slow down! You can check status again in ${remainingSeconds}s.`); + } + cooldowns.set(`status:${userId}`, now); + const statusMsg = await ctx.reply('⏳ Checking Better Stack connectivity...'); try { @@ -198,16 +214,13 @@ bot.command('status', async (ctx: Context) => { timeout: 10000, }); - const authorized = ALLOWED_USERS.length === 0 || ALLOWED_USERS.includes(ctx.from!.id.toString()); const authLine = ALLOWED_USERS.length === 0 ? '⚠️ *Auth:* No allowlist set — all users can trigger alerts' : `🔒 *Auth:* Allowlist active (${ALLOWED_USERS.length} user${ALLOWED_USERS.length === 1 ? '' : 's'})`; - const policyLine = authorized - ? `📋 *Policy ID:* \`${POLICY_ID}\`` - : `📋 *Policy ID:* _restricted_`; + const policyLine = `📋 *Policy ID:* \`${POLICY_ID}\``; - log.info({ userId: ctx.from!.id.toString(), httpStatus: response.status }, '/status check passed'); + log.info({ userId, httpStatus: response.status }, '/status check passed'); await ctx.telegram.editMessageText( ctx.chat!.id, @@ -224,9 +237,9 @@ bot.command('status', async (ctx: Context) => { const axiosError = error as { response?: { status?: number }; message?: string }; const detail = axiosError.response?.status ? `HTTP ${axiosError.response.status}` - : (axiosError.message ?? 'unknown error'); + : axiosError.message; - log.error({ userId: ctx.from!.id.toString(), detail }, '/status check failed'); + log.error({ userId, detail }, '/status check failed'); await ctx.telegram.editMessageText( ctx.chat!.id, @@ -234,7 +247,7 @@ bot.command('status', async (ctx: Context) => { undefined, `❌ *ASIR Bot Status*\n\n` + `🤖 *Bot:* Running\n` + - `🌐 *Better Stack API:* Unreachable (${escapeMarkdown(detail)})\n\n` + + `🌐 *Better Stack API:* Unreachable (${escapeMarkdown(detail ?? '')})\n\n` + `_The bot cannot trigger alerts until connectivity is restored._`, { parse_mode: 'Markdown' }, );