diff --git a/bot.ts b/bot.ts index ba2ace1..62bf90d 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,71 @@ 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 { + const response = await axios.get(`${BETTER_STACK_URL}?per_page=1`, { + headers: HEADERS, + timeout: 10000, + }); + + 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 = `📋 *Policy ID:* \`${POLICY_ID}\``; + + log.info({ userId, 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; + + log.error({ userId, 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'); });