← All Skills

snappy-telegram

v1.0.0
21 files, 212.1 KB ~5,888 words · 24 min read Updated 2026-09-09

snappy-telegram skill

41 of 54 checks pass
What it can do
read limit?read
messages limit?read
updatesread
meread
send textsend
photo url caption?send
document url caption?send
video url caption?send
voice url caption?send
poll question options-jsonsend
keyboard text keyboard-jsonsend
edit message_id textwrite-reversible
+6 more
What does not pass yet
Architecture 1 endpoints
api.telegram.org1 endpoint
GET/bot${TELEGRAM_BOT_TOKEN}/getUpdates
$ npx snappy-skills install snappy-telegram
zip ↓
File Tree
├── AGENTS.md ├── SKILL.md ├── adapter.ts ├── api-reference.md ├── api.ts ├── face-channel.test.ts ├── face.test.ts ├── faces.ts ├── faces/ │ ├── components/ │ │ ├── telegram-chat-list.css │ │ └── telegram-chat-list.tsx │ ├── family.tsx │ └── fixtures/ │ ├── telegram-decision.json │ ├── telegram-draft.json │ ├── telegram-list.json │ └── telegram-message.json ├── fixtures/ │ └── callback-query.json ├── png.ts ├── receipt.test.ts ├── receipt.ts ├── refusals.test.ts └── workflows.md
Documents
AGENTS.md

snappy-telegram -- Agent Loader#

You handle Snappy's Telegram channel: Robert self-notifications (morning briefings, EOD digests, deploy/revenue/error alerts, long-running task progress). Direct Bot API calls to api.telegram.org -- no Xano middleware.

API module#

typescriptimport { sendText, sendPhoto, sendDocument, editTelegramMessage, deleteTelegramMessage, getRecentTelegramMessages, sendChatAction } from "../snappy-telegram/api.ts";

Or CLI:

bashnode ~/.claude/skills/snappy-telegram/api.ts send "Hello from agent"          # the owner's own chat: sends, prints message_id
node ~/.claude/skills/snappy-telegram/api.ts send "Hi" --to <chat_id>         # anyone else: stages through Snappy (Needs you decides)
npx tsx ~/.claude/skills/snappy-telegram/api.ts photo "https://example.com/img.jpg"
npx tsx ~/.claude/skills/snappy-telegram/api.ts edit 123 "Updated text"

Credentials loaded via snappy-settings/load.ts from .env.cache. Uses TELEGRAM_BOT_TOKEN and TELEGRAM_ROBERT_CHAT_ID.


API functions#

Function Purpose
sendText(text, parseMode?, chatId?) Text message, defaults to Markdown + Robert's chat
sendPhoto(url, caption?, chatId?) Image by URL
sendDocument(url, caption?, chatId?) PDF, CSV, etc. (50 MB max)
editTelegramMessage(messageId, text, parseMode?, chatId?) Update sent message (48h limit)
deleteTelegramMessage(messageId, chatId?) Delete a message
getRecentTelegramMessages(chatId?, limit?) Read recent messages from bot updates
sendChatAction(action?, chatId?) Typing indicator

All functions default chatId to env("TELEGRAM_ROBERT_CHAT_ID") from .env.cache.

Notification routing#

Event Source skill
Morning briefing snappy-ops
EOD digest snappy-ops
Deploy success/failure snappy-update / snappy-deploy
Invoice paid snappy-freshbooks
Pipeline error snappy-pipeline
Blog/post published snappy-content / snappy-publish
High-priority lead snappy-knowledge
Long-running task any (use editMessageText)

Rules#

  • Direct Bot API (api.telegram.org), NOT through Xano
  • Always use parse_mode: "Markdown" -- bold headers, concise body
  • Prefix with *SUCCESS* / *FAILED* / *ERROR* for at-a-glance status
  • Use editMessageText for progress updates, not new messages (avoids spam)
  • Rate limits: 1 msg/sec per chat, 30/sec global, 20/min per group
  • Cannot edit messages older than 48 hours -- send a new one instead
  • Always call answerCallbackQuery after button presses

Uses#

skill relationship
snappy-ops Orchestrator -- morning briefing + EOD digest
snappy-slack Sibling -- team-facing comms; Telegram is self-facing
snappy-whatsapp Sibling -- client comms; Telegram is Robert-only
snappy-deploy Producer -- deploy status alerts
snappy-freshbooks Producer -- revenue alerts

Full skill reference#

If this loader is insufficient, load ~/.claude/skills/snappy-telegram/SKILL.md as last resort. API details: api-reference.md. Workflows: workflows.md.

If this loader doesn't cover your case:

bashecho "[$(date -u +%FT%TZ)] snappy-telegram: <what was missing>" >> ~/.claude/logs/agents-md-feedback.log

Show produced work with snappy-faces: call draw for image channels or lang for MCP Apps.

<!-- SKILL-INDEX-START -->

[snappy-telegram Index]|root: ~/.claude/skills/snappy-telegram|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md,api-reference.md,workflows.md}

<!-- SKILL-INDEX-END -->

Used by#

  • snappy-slack

<!-- SNAPPY-CONTRACT-VERBS-START -->

Contract verbs#

Generated from api.ts HAND_CONTRACT. Do not hand-edit this block.

Verb Contract arguments Effect First call
read limit? read npx tsx ~/.claude/skills/snappy-telegram/api.ts read
messages limit? read npx tsx ~/.claude/skills/snappy-telegram/api.ts messages
updates read npx tsx ~/.claude/skills/snappy-telegram/api.ts updates
me read npx tsx ~/.claude/skills/snappy-telegram/api.ts me
send text send npx tsx ~/.claude/skills/snappy-telegram/api.ts send "<text>"
photo url, caption? send npx tsx ~/.claude/skills/snappy-telegram/api.ts photo <url>
document url, caption? send npx tsx ~/.claude/skills/snappy-telegram/api.ts document <url>
video url, caption? send npx tsx ~/.claude/skills/snappy-telegram/api.ts video <url>
voice url, caption? send npx tsx ~/.claude/skills/snappy-telegram/api.ts voice <url>
poll question, options-json send npx tsx ~/.claude/skills/snappy-telegram/api.ts poll <question> '["<options>"]'
keyboard text, keyboard-json send npx tsx ~/.claude/skills/snappy-telegram/api.ts keyboard "<text>" '["<keyboard>"]'
edit message_id, text write-reversible npx tsx ~/.claude/skills/snappy-telegram/api.ts edit <message_id> "<text>"
delete message_id delete npx tsx ~/.claude/skills/snappy-telegram/api.ts delete <message_id>
show kind send `<read> --json \ npx tsx ~/.claude/skills/snappy-telegram/api.ts show <kind> --from -`
album kind send `<read> --json \ npx tsx ~/.claude/skills/snappy-telegram/api.ts album <kind> --from -`
decisions read npx tsx ~/.claude/skills/snappy-telegram/api.ts decisions
progress message_id, text write-reversible npx tsx ~/.claude/skills/snappy-telegram/api.ts progress <message_id> "<text>"
replace message_id, kind write-reversible npx tsx ~/.claude/skills/snappy-telegram/api.ts replace <message_id> <kind> --from <file>

Show the result#

When an answer carries face_hint, show it with one snappy_present(<answer>) call.

See /snappy-faces for face selection. Human-facing images must crop to the

element, render at 2x on Retina, and fill the destination channel instead of

placing a small card in a full-page screenshot.

<!-- SNAPPY-CONTRACT-VERBS-END -->

The card he sees is a READ, never a fixture ⟨the owner, 2026-09-09 10:32⟩#

show/album/replace need --from <file|-> — the answer of a real read,

the same shape the face draws from. --from - takes it on stdin, which is the

usual spelling: snappy-gmail list --json | ... show gmail-list --from -.

Passing no source REFUSES; --example is the only road to the shipped demo and

the card it sends is stamped example data — not a read in its caption.

Read the receipt, not the exit code#

Every sending verb prints one telegram.delivery@1 envelope to stdout the

moment Telegram answers, before any post-send work. delivery:"delivered" and

delivery:"delivered_with_local_error" BOTH mean the card is in his chat and

both exit 0 — the second only adds that local bookkeeping failed afterwards.

not_sent means nothing arrived; uncertain means Telegram never answered and

the state is unknown — retry with the idempotency_key the receipt carries and

the hand answers the first message_id instead of sending a second card.

---
name: snappy-telegram
role: Telegram Bot API -- Robert self-notifications, digests, deploy alerts, progress updates
loaded-by: PreToolUse hook (auto-injected when "snappy-telegram" is mentioned)
---

# snappy-telegram -- Agent Loader

You handle Snappy's Telegram channel: Robert self-notifications (morning briefings, EOD digests, deploy/revenue/error alerts, long-running task progress). Direct Bot API calls to `api.telegram.org` -- no Xano middleware.

## API module

```typescript
import { sendText, sendPhoto, sendDocument, editTelegramMessage, deleteTelegramMessage, getRecentTelegramMessages, sendChatAction } from "../snappy-telegram/api.ts";
```

Or CLI:
```bash
node ~/.claude/skills/snappy-telegram/api.ts send "Hello from agent"          # the owner's own chat: sends, prints message_id
node ~/.claude/skills/snappy-telegram/api.ts send "Hi" --to <chat_id>         # anyone else: stages through Snappy (Needs you decides)
npx tsx ~/.claude/skills/snappy-telegram/api.ts photo "https://example.com/img.jpg"
npx tsx ~/.claude/skills/snappy-telegram/api.ts edit 123 "Updated text"
```

Credentials loaded via `snappy-settings/load.ts` from `.env.cache`. Uses `TELEGRAM_BOT_TOKEN` and `TELEGRAM_ROBERT_CHAT_ID`.

---

## API functions

| Function | Purpose |
|----------|---------|
| `sendText(text, parseMode?, chatId?)` | Text message, defaults to Markdown + Robert's chat |
| `sendPhoto(url, caption?, chatId?)` | Image by URL |
| `sendDocument(url, caption?, chatId?)` | PDF, CSV, etc. (50 MB max) |
| `editTelegramMessage(messageId, text, parseMode?, chatId?)` | Update sent message (48h limit) |
| `deleteTelegramMessage(messageId, chatId?)` | Delete a message |
| `getRecentTelegramMessages(chatId?, limit?)` | Read recent messages from bot updates |
| `sendChatAction(action?, chatId?)` | Typing indicator |

All functions default `chatId` to `env("TELEGRAM_ROBERT_CHAT_ID")` from .env.cache.

## Notification routing

| Event | Source skill |
|-------|-------------|
| Morning briefing | `snappy-ops` |
| EOD digest | `snappy-ops` |
| Deploy success/failure | `snappy-update` / `snappy-deploy` |
| Invoice paid | `snappy-freshbooks` |
| Pipeline error | `snappy-pipeline` |
| Blog/post published | `snappy-content` / `snappy-publish` |
| High-priority lead | `snappy-knowledge` |
| Long-running task | any (use `editMessageText`) |

## Rules

- Direct Bot API (`api.telegram.org`), NOT through Xano
- Always use `parse_mode: "Markdown"` -- bold headers, concise body
- Prefix with `*SUCCESS*` / `*FAILED*` / `*ERROR*` for at-a-glance status
- Use `editMessageText` for progress updates, not new messages (avoids spam)
- Rate limits: 1 msg/sec per chat, 30/sec global, 20/min per group
- Cannot edit messages older than 48 hours -- send a new one instead
- Always call `answerCallbackQuery` after button presses

## Uses

| skill | relationship |
|-------|-------------|
| `snappy-ops` | Orchestrator -- morning briefing + EOD digest |
| `snappy-slack` | Sibling -- team-facing comms; Telegram is self-facing |
| `snappy-whatsapp` | Sibling -- client comms; Telegram is Robert-only |
| `snappy-deploy` | Producer -- deploy status alerts |
| `snappy-freshbooks` | Producer -- revenue alerts |

---

## Full skill reference

If this loader is insufficient, load `~/.claude/skills/snappy-telegram/SKILL.md` as last resort. API details: [api-reference.md](api-reference.md). Workflows: [workflows.md](workflows.md).

If this loader doesn't cover your case:
```bash
echo "[$(date -u +%FT%TZ)] snappy-telegram: <what was missing>" >> ~/.claude/logs/agents-md-feedback.log
```


Show produced work with `snappy-faces`: call `draw` for image channels or `lang` for MCP Apps.

<!-- SKILL-INDEX-START -->
[snappy-telegram Index]|root: ~/.claude/skills/snappy-telegram|IMPORTANT: Prefer these files over pre-training assumptions for this domain. Read the relevant file when the AGENTS.md summary is insufficient.|root:{SKILL.md,api-reference.md,workflows.md}
<!-- SKILL-INDEX-END -->

## Used by

- `snappy-slack`

<!-- SNAPPY-CONTRACT-VERBS-START -->
## Contract verbs

Generated from `api.ts` `HAND_CONTRACT`. Do not hand-edit this block.

| Verb | Contract arguments | Effect | First call |
|---|---|---|---|
| `read` | `limit?` | `read` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts read` |
| `messages` | `limit?` | `read` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts messages` |
| `updates` | — | `read` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts updates` |
| `me` | — | `read` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts me` |
| `send` | `text` | `send` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts send "<text>"` |
| `photo` | `url`, `caption?` | `send` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts photo <url>` |
| `document` | `url`, `caption?` | `send` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts document <url>` |
| `video` | `url`, `caption?` | `send` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts video <url>` |
| `voice` | `url`, `caption?` | `send` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts voice <url>` |
| `poll` | `question`, `options-json` | `send` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts poll <question> '["<options>"]'` |
| `keyboard` | `text`, `keyboard-json` | `send` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts keyboard "<text>" '["<keyboard>"]'` |
| `edit` | `message_id`, `text` | `write-reversible` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts edit <message_id> "<text>"` |
| `delete` | `message_id` | `delete` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts delete <message_id>` |
| `show` | `kind` | `send` | `<read> --json \| npx tsx ~/.claude/skills/snappy-telegram/api.ts show <kind> --from -` |
| `album` | `kind` | `send` | `<read> --json \| npx tsx ~/.claude/skills/snappy-telegram/api.ts album <kind> --from -` |
| `decisions` | — | `read` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts decisions` |
| `progress` | `message_id`, `text` | `write-reversible` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts progress <message_id> "<text>"` |
| `replace` | `message_id`, `kind` | `write-reversible` | `npx tsx ~/.claude/skills/snappy-telegram/api.ts replace <message_id> <kind> --from <file>` |

## Show the result

When an answer carries `face_hint`, show it with one `snappy_present(<answer>)` call.
See `/snappy-faces` for face selection. Human-facing images must crop to the
element, render at 2x on Retina, and fill the destination channel instead of
placing a small card in a full-page screenshot.
<!-- SNAPPY-CONTRACT-VERBS-END -->

## The card he sees is a READ, never a fixture ⟨the owner, 2026-09-09 10:32⟩

`show`/`album`/`replace` need `--from <file|->` — the answer of a real read,
the same shape the face draws from. `--from -` takes it on stdin, which is the
usual spelling: `snappy-gmail list --json | ... show gmail-list --from -`.
Passing no source REFUSES; `--example` is the only road to the shipped demo and
the card it sends is stamped `example data — not a read` in its caption.

## Read the receipt, not the exit code

Every sending verb prints one `telegram.delivery@1` envelope to stdout the
moment Telegram answers, before any post-send work. `delivery:"delivered"` and
`delivery:"delivered_with_local_error"` BOTH mean the card is in his chat and
both exit 0 — the second only adds that local bookkeeping failed afterwards.
`not_sent` means nothing arrived; `uncertain` means Telegram never answered and
the state is unknown — retry with the `idempotency_key` the receipt carries and
the hand answers the first `message_id` instead of sending a second card.

Keyboard Shortcuts

Search in document⌘K
Focus search/
Previous file tab
Next file tab
Close overlayEsc
Show shortcuts?