- Hash inputs in timingSafeEqual to prevent length leak side-channel - Add quote escaping to escapeHtml for defense in depth - Normalize chatId to Number in parseKvKey for type consistency - Log Retry-After header on 429 rate limit responses - Slim README to focused overview, move details to docs/ - Add docs/: system-architecture, setup-guide, feature-decisions - Add documentation section and README guidelines to CLAUDE.md
3.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project
Telegram bot that forwards status.claude.com (Atlassian Statuspage) webhook notifications to subscribed users. Hosted on Cloudflare Workers.
Commands
npm run dev— Start local dev server (wrangler dev, emulates KV + Queues locally)npm run deploy— Deploy to Cloudflare Workersnpx wrangler deploy --dry-run— Verify build without deployingnode scripts/setup-bot.js— One-time: register bot commands + set Telegram webhook (interactive prompts)
No test framework configured yet. No linter configured.
Secrets (set via wrangler secret put)
BOT_TOKEN— Telegram bot tokenWEBHOOK_SECRET— Secret token in Statuspage webhook URL path
Architecture
Cloudflare Workers with two entry points exported from src/index.js:
fetch— Hono.js HTTP handler (routes below)queue— CF Queues consumer for fan-out message delivery
Routes
| Method | Path | Handler | Purpose |
|---|---|---|---|
| GET | / |
inline | Health check |
| POST | /webhook/telegram |
bot-commands.js |
grammY webhookCallback("cloudflare-mod") |
| POST | /webhook/status/:secret |
statuspage-webhook.js |
Receives Statuspage webhooks (URL secret) |
Data Flow
- Statuspage → Worker: Webhook POST → verify URL secret (timing-safe via
crypto-utils.js) → parse incident/component event → filter subscribers by type + component →sendBatchto CF Queue - Queue → Telegram: Consumer processes batches of 30 →
sendMessageviatelegram-api.jshelper → auto-removes blocked subscribers (403/400), retries on 429 - User → Bot: Telegram webhook → grammY handles
/help,/start,/stop,/status,/subscribe,/history,/uptimecommands → reads/writes KV
KV Storage
Per-subscriber keys (no read-modify-write races):
sub:{chatId}→{ types: ["incident", "component"], components: [] }sub:{chatId}:{threadId}→{ types: ["incident"], components: ["API"] }
kv-store.js handles key building/parsing with kv.list({ prefix: "sub:" }) pagination. Subscriber type/component data is stored as KV metadata so getSubscribersByType() uses only list() (O(1)) instead of individual get() calls. threadId can be 0 (General topic), so null checks use != null.
Component-Specific Subscriptions
Subscribers can filter to specific components via /subscribe component <name>. Empty components array = all components (default). Filtering applies to webhook notifications.
Supergroup Topic Support
Bot stores message_thread_id from the topic where /start was sent. Notifications and queue consumer include message_thread_id in sendMessage payload to target the correct topic.
Key Dependencies
- hono — HTTP routing framework
- grammy — Telegram Bot API framework (webhook mode for CF Workers)
- wrangler — CF Workers CLI (dev/deploy)
CF Bindings (wrangler.jsonc)
claude_status— KV namespaceclaude-status— Queue producer/consumer (batch size 30, max retries 3)
Documentation
Detailed docs live in docs/:
docs/setup-guide.md— Prerequisites, deployment, local devdocs/system-architecture.md— Entry points, data flow, KV schema, queue, securitydocs/feature-decisions.md— Evaluated features and rationale for decisions
README Guidelines
Keep README.md clean and focused: project intro, features, commands, quick start, and links to docs.
Move detailed setup, architecture, and decision records to docs/. Do not bloat the README with
step-by-step instructions or implementation details.