tg-alerts · git:20260403.e09f017 · 2026-04-03 · sha256 cd63b07912c9156e

tg-alerts git:20260403.e09f017A

Immutable. This exact content is served forever at /api/v1/blob/cd63b07912c9156e.

---
name: tg-alerts
description: Use when adding Telegram error notifications to any project. Guides through bot creation with @BotFather, chat/channel/forum-topic ID discovery, alert service implementation with deduplication and graceful failure, and framework-specific integration for Python (async/sync) and Node.js.
---

# Telegram Error Alerts

Adds operational error alerts to any project via a dedicated Telegram bot. Alerts go to a private channel, group, or forum topic — not to end users. Battle-tested pattern with deduplication, HTML formatting, and crash-proof error handling.

## When to Use

- User asks to add error notifications, Telegram alerts, or operational monitoring
- Setting up a new deployment and want to know when things break
- NOT for: user-facing bot messages or interactive Telegram features

## Setup Flow

This skill is **interactive**. Each phase involves asking the user questions or guiding them through Telegram actions.

```dot
digraph flow {
    rankdir=LR;
    "Assess\nProject" -> "Create\nBot" -> "Get\nChat ID" -> "Generate\nCode" -> "Integrate" -> "Test";
}
```

---

### Phase 1: Assess Project

Detect from codebase or ASK:
1. **Language** — Python / Node.js / TypeScript / other
2. **Framework** — FastAPI, Django, Flask, Express, NestJS, Hono, etc.
3. **Async or sync** — determines implementation variant
4. **Already has a Telegram bot?** — alert bot MUST be separate from any main bot

---

### Phase 2: Create Alert Bot

Guide the user through these Telegram steps:

> 1. Open Telegram, find **@BotFather**
> 2. Send `/newbot`
> 3. Name: something like **"MyApp Alerts"**
> 4. Username: something like `myapp_alerts_bot`
> 5. BotFather replies with a token — **copy it**
> 6. Save as `ALERT_BOT_TOKEN` in `.env`

ASK the user to confirm they have the token before proceeding.

---

### Phase 3: Get Chat ID

**ASK:** "Where should alerts go?"

| Target | Best for | Complexity |
|--------|----------|------------|
| **Private chat** | Solo dev | Easiest |
| **Group** | Small team | Easy |
| **Channel** | Read-only broadcast | Medium |
| **Forum topic** | Organized by category | Medium |

Guide based on their choice:

#### Option A: Private Chat

> 1. Open Telegram, find your new alert bot
> 2. Send it any message (e.g., "hello")
> 3. Run in terminal:

```bash
curl -s "https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates" | python3 -m json.tool
```

> 4. Find `"chat": {"id": 123456789}` — that positive number is your `ALERT_CHAT_ID`

#### Option B: Group

> 1. Create a Telegram group (or use existing)
> 2. Add the alert bot to the group
> 3. Send any message in the group
> 4. Run the `getUpdates` curl above
> 5. Find `chat.id` — **negative number** like `-1001234567890`

#### Option C: Channel

> 1. Create a channel (or use existing)
> 2. Add the alert bot as **administrator** (needs "Post Messages" permission)
> 3. Post any message in the channel
> 4. Run `getUpdates`
> 5. Find `"channel_post"` -> `"chat"` -> `"id"` — negative, starts with `-100`

#### Option D: Forum Topic

> 1. Create a group -> Settings -> enable **Topics**
> 2. Add the bot to the group
> 3. Create a topic (e.g., "Errors")
> 4. Send a message **inside that topic**
> 5. Run `getUpdates`
> 6. Find TWO values:
>    - `chat.id` -> `ALERT_CHAT_ID`
>    - `message_thread_id` -> `ALERT_THREAD_ID`

#### Verify IDs

**Always test** before writing code:

```bash
curl -s -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" \
  -d chat_id=<CHAT_ID> -d "text=Test alert" -d parse_mode=HTML
```

For forum topics, add `-d message_thread_id=<THREAD_ID>`.

If the message appears in Telegram, proceed to Phase 4.

**Troubleshooting empty `getUpdates`:**
- Send another message, then run curl immediately (updates expire)
- For channels: bot must be **admin**, not just member
- For groups: send `/setprivacy` to @BotFather -> set to **Disabled**

---

### Phase 4: Generate Alert Service

Use the appropriate reference implementation from this skill's directory:

| Stack | Reference File |
|-------|---------------|
| Python async (FastAPI, aiohttp) | `references/alert_service_async.py` |
| Python sync (Django, Flask) | `references/alert_service_sync.py` |
| Node.js / TypeScript | `references/alert_service_node.ts` |

Read the reference file and **adapt** to the project:
- Match code style, naming conventions, import patterns
- Use the project's existing HTTP client if available
- Place where services/utils live in the project structure
- Add env vars to the project's config system

---

### Phase 5: Framework Integration

Wire alert service into the project's error handling. The pattern:

1. **Initialize on startup** (only if env vars present)
2. **Attach logging handler** to root logger (captures all `logger.error()` / `console.error()`)
3. **Add unhandled exception handler** specific to the framework
4. **Send lifecycle alert** on startup and shutdown

| Framework | Unhandled Exception Hook | Startup Hook |
|-----------|------------------------|-------------|
| FastAPI | `@app.exception_handler(Exception)` | `lifespan` context manager |
| Django | Custom middleware + 500 handler | `AppConfig.ready()` |
| Flask | `@app.errorhandler(Exception)` | App factory / `before_first_request` |
| Express | `app.use((err, req, res, next) => ...)` | After `app.listen()` |
| NestJS | `@Catch()` exception filter | `onModuleInit()` |

---

### Phase 6: Environment Variables

Add to `.env` and `.env.example`:

```env
# Telegram operational alerts (optional)
# ALERT_BOT_TOKEN=123456:ABC-DEF...
# ALERT_CHAT_ID=-1001234567890
# ALERT_THREAD_ID=42
```

Ensure the config treats these as **optional** — alerting gracefully disables when not configured:
```
alerting_enabled = bool(ALERT_BOT_TOKEN and ALERT_CHAT_ID)
```

---

### Phase 7: Test

1. Start the app with alert env vars set
2. Check for **startup lifecycle alert** in Telegram
3. Trigger an intentional error (e.g., hit a broken endpoint)
4. Verify **error alert** appears with traceback
5. Trigger **same error again** — verify dedup blocks the duplicate
6. Stop the app — verify **shutdown alert**

---

## Alert Message Format

Consistent across all stacks — use Telegram HTML parse mode:

```
🔴 <b>Error</b>
<pre>Env: production
Error: ValueError
Context: [module.name] error message

Traceback (most recent call last):
  ...</pre>

🟡 <b>Warning</b>
<pre>Env: production
Some warning message</pre>

🟢
<pre>Application started
Env: production</pre>
```

---

## Critical Design Rules

Non-negotiable for every implementation:

1. **Alert failures NEVER crash the app** — wrap all sending in try/except or try/catch
2. **Log alert failures at DEBUG** — ERROR would trigger the handler again -> infinite loop
3. **Deduplicate by exception location** — key: `{ExcType}:{lineno}@{filename}`. One alert per crash site
4. **Truncate to 3500 chars** — Telegram limit is 4096; stay well under
5. **HTML-escape all dynamic content** — tracebacks contain `<>&` breaking Telegram HTML
6. **Separate bot** — never reuse the main app bot for alerts
7. **Include environment** — every alert shows dev/staging/prod
8. **Make alerting optional** — app runs normally without alert env vars
9. **Fire-and-forget** — async: `create_task()`, sync: daemon thread. Never block requests
10. **Cap dedup set at ~10k** — clear when full to prevent unbounded memory growth

## Common Mistakes

| Mistake | Consequence | Fix |
|---------|-------------|-----|
| Same bot for app + alerts | Alerts fail when app bot is down | Separate bot token |
| No dedup | Hundreds of identical alerts | Dedup by exc type + location |
| `await send_alert()` in request path | Slow requests if Telegram lags | Fire-and-forget |
| Log send failure at ERROR | Infinite alert loop | Log at DEBUG |
| No truncation | Telegram silently drops message | Truncate to 3500 chars |
| Raw HTML in traceback | Broken formatting | `html.escape()` all content |
| Alert required for startup | App won't start without Telegram | Optional via `alerting_enabled` |
| `getUpdates` returns empty | Can't find chat ID | Send message first, curl immediately |