telegram-telethon/SKILL.md
--- name: telegram-telethon description: This skill should be used for comprehensive Telegram automation via Telethon API. Use for sending/receiving messages, monitoring chats, running a background daemon that triggers Claude Code sessions, managing channels/groups, and downloading media. Triggers on "telegram daemon", "monitor telegram", "telegram bot", "spawn Claude from telegram", or any Telethon-related request. IMPORTANT: Use `draft` command for "драфт/draft", use `send` for "отправь/send";
npx skillsauth add glebis/claude-skills telegram-telethonInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| User says | Claude does | Clarify? |
|-----------|-------------|----------|
| "драфт", "draft", "сделай драфт" | draft | No |
| "отправь", "пошли", "send" | send | No |
| "напиши сообщение" (ambiguous) | Ask what user wants | Yes |
draft command immediatelysend command immediatelyUser: "сделай драфт для lv: привет"
Claude: Uses draft --chat "lv" --text "привет" immediately
User: "отправь сообщение Маше: встретимся в 5?"
Claude: Uses send --chat "Маша" --text "встретимся в 5?" immediately
User: "напиши сообщение для Маши: встретимся в 5?" Claude: Asks "Создать драфт или сразу отправить?"
Full Telethon API wrapper with daemon mode and Claude Code integration. Supports interactive setup, background message monitoring, and automatic Claude session spawning per chat.
telegram-telethon/
├── SKILL.md # This file
├── pyproject.toml # Installable Python package
├── scripts/
│ ├── tg.py # Main CLI (messages, media, drafts, etc.)
│ └── tgd.py # Daemon controller
├── src/telegram_telethon/ # Importable package
│ ├── core/ # auth, config
│ ├── modules/ # messages, media
│ ├── daemon/ # runner, handlers, claude_bridge
│ └── utils/ # formatting
└── tests/ # pytest unit + integration tests
Scripts import from src/telegram_telethon. Install the package in editable mode so tg.py/tgd.py can resolve imports:
cd telegram-telethon
pip install -e .
# or with dev tools (pytest, coverage):
pip install -e ".[dev]"
telegram SkillThe separate telegram skill (single-script telegram_fetch.py backed by telegram_dl) overlaps on list/recent/search/send/edit/download/thread but differs:
With the publish/markdown/schedule ports now complete, telegram-telethon is a superset of telegram on everything except the external telegram_dl auth dependency. Use this skill for:
publish — draft→channel workflow (frontmatter, media albums, post-publish move + index update, post-flight lint)--markdown on send / publish — markdown→Telegram HTML conversion--schedule on send / publish — ISO / relative / natural-language scheduled deliverydelete / forward / mark-read, local draft / drafts / draft-send, lint-channel, non-interactive auth setup.Run setup wizard on first use:
python3 scripts/tg.py setup
This guides through:
For use from Claude Code or scripts without TTY:
# Step 1: Provide credentials and trigger code send
python3 scripts/tg.py setup --api-id 12345678 --api-hash abc123... --phone +1234567890
# Step 2: User receives code on phone, then complete auth
python3 scripts/tg.py setup --api-id 12345678 --api-hash abc123... --phone +1234567890 --code 12345
# If 2FA enabled, add password
python3 scripts/tg.py setup --api-id 12345678 --api-hash abc123... --phone +1234567890 --code 12345 --password mypassword
The script auto-detects TTY and switches between interactive/non-interactive modes.
# Check connection status
python3 scripts/tg.py status
# List chats
python3 scripts/tg.py list
# Get recent messages from a chat
python3 scripts/tg.py recent "John Doe" --limit 20
# Search messages
python3 scripts/tg.py search "meeting notes"
# Configure daemon triggers interactively
python3 scripts/tg.py daemon-config
# Start daemon (foreground with logs)
python3 scripts/tgd.py start --foreground
# Start daemon (background)
python3 scripts/tgd.py start
# View daemon logs
python3 scripts/tgd.py logs
# List all chats
python3 scripts/tg.py list [--limit 30] [--search "term"]
# Fetch recent messages
python3 scripts/tg.py recent [CHAT] [--limit 50] [--days 7] [--format markdown|json] [--output file.md]
# Search messages by content
python3 scripts/tg.py search QUERY [--chat "Chat Name"] [--limit 50] [--format markdown|json]
# Fetch unread messages
python3 scripts/tg.py unread [--chat "Chat Name"] [--format markdown|json]
# Fetch forum thread
python3 scripts/tg.py thread CHAT_ID THREAD_ID [--limit 100]
# Send message
python3 scripts/tg.py send --chat "Chat Name" --text "Message text" [--reply-to MSG_ID] [--file path] [--topic TOPIC_ID] [--markdown] [--html] [--schedule "+1h" | "tomorrow 10:00" | "2026-04-10T09:30"]
# Edit message
python3 scripts/tg.py edit --chat "Chat Name" --message-id MESSAGE_ID --text "New text"
# Delete messages
python3 scripts/tg.py delete --chat "Chat Name" --message-ids 123 456 789 [--no-revoke]
# Forward messages
python3 scripts/tg.py forward --from "Source" --to "Dest" --message-ids 123 456
# Mark messages as read
python3 scripts/tg.py mark-read --chat "Chat Name" [--max-id MSG_ID]
# Save/update a draft message
python3 scripts/tg.py draft --chat "Chat Name" --text "Draft text" [--reply-to MSG_ID] [--no-preview]
# Clear a draft (save empty text)
python3 scripts/tg.py draft --chat "Chat Name" --text ""
# Clear all drafts
python3 scripts/tg.py draft --clear-all
# List all drafts
python3 scripts/tg.py drafts [--limit 50]
# Send a draft as a message (clears the draft)
python3 scripts/tg.py draft-send --chat "Chat Name"
Note: Use "me" as the chat name to target Saved Messages (your own chat). The literal name "Saved Messages" doesn't work as it's localized differently per user.
# Download media from chat
python3 scripts/tg.py download "Chat Name" [--limit 5] [--output-dir ~/Downloads] [--message-id ID] [--type voice|video|photo]
# Transcribe a single voice message (MESSAGE_ID required)
python3 scripts/tg.py transcribe "Chat Name" MESSAGE_ID [--method telegram|groq|whisper]
# Batch-transcribe recent voice messages (omit MESSAGE_ID, use --batch)
python3 scripts/tg.py transcribe "Chat Name" --batch [--limit 10] [--method telegram|groq|whisper]
End-to-end publish workflow: parse a draft markdown file, resolve the destination channel from folder structure or frontmatter channel:, upload media (single file or album), post-process to move the draft to published/ and insert an entry in the channel index.
# Dry-run preview
python3 scripts/tg.py publish --draft "Channels/klodkot/drafts/20260416-post.md" --dry-run
# Publish now
python3 scripts/tg.py publish --draft "20260416-post" # slug works too
# Publish scheduled for later
python3 scripts/tg.py publish --draft "..." --schedule "tomorrow 10:00"
The result JSON includes published, channel, message_id, media_count, moved_to, and — crucially — lint_warnings when the final body contains leaked markdown/HTML that Telegram wouldn't render. Post-publish bookkeeping failures (e.g. index write error) surface as warnings but don't roll back the send.
Pass --markdown to convert a markdown-flavored message into Telegram HTML before sending:
python3 scripts/tg.py send --chat "@mychannel" --markdown \
--text $'## Release\n\n**v2** ships _today_. See [docs](https://example.com).\n\n* fast\n* stable'
Rules (applied in order): ## Header → bold line; * item / - item at line start → → item; **bold** → <b>; _italic_ → <i>; [text](url) → <a href>. Pre-existing HTML passes through unchanged, so the flag is safe to add to content that was already authored as HTML.
Pair with lint-channel below to catch cases where --markdown was forgotten.
Pass --html to send text that already contains Telegram-compatible HTML tags (<b>, <i>, <a href>, <code>, <pre>, <u>, <s>, <tg-spoiler>, <blockquote>):
python3 scripts/tg.py send --chat "@mychannel" --html \
--text '<b>Release v2</b> ships today. See <a href="https://example.com">docs</a>.'
Unlike --markdown (which converts markdown syntax to HTML), --html sends the text as-is with parse_mode='html'. Use --html when you have already authored HTML content or when programmatically building messages with tags.
Pass --schedule with one of three formats (naive times default to Europe/Berlin):
# Relative: send in one hour
python3 scripts/tg.py send --chat "@mychannel" --text "..." --schedule "+1h"
# Natural: send tomorrow morning
python3 scripts/tg.py send --chat "@mychannel" --text "..." --schedule "tomorrow 09:30"
# Absolute: send at a specific time
python3 scripts/tg.py send --chat "@mychannel" --text "..." --schedule "2026-04-20T15:00"
The response includes "scheduled_for": "<iso datetime>" when the message is queued for later. Telegram displays scheduled messages in the chat's scheduled-messages view.
Scan a channel (or a single message) for unrendered markdown/HTML that leaked into the raw message text — i.e. the sender forgot --markdown or the HTML conversion failed, so readers see literal **bold**, <b>…</b>, [text](url), or ## Header in the post.
# Scan last 50 messages in @mychannel
python3 scripts/tg.py lint-channel --chat "@mychannel"
# Scan last 200 messages
python3 scripts/tg.py lint-channel --chat "@mychannel" --limit 200
# Lint a single message by ID
python3 scripts/tg.py lint-channel --chat "@mychannel" --message-id 1234
# Machine-readable output for pipelines / CI
python3 scripts/tg.py lint-channel --chat "@mychannel" --json
The detector lives in modules/lint.py as a pure function (detect_unrendered_markup(text, entities)), so it can also be called directly on drafts or wired into a post-flight check after publishing. Content inside MessageEntityCode/MessageEntityPre spans is ignored (inline code / code blocks are expected to contain raw characters).
--to-daily and --to-person are flags on the read commands (recent, search, unread), not standalone subcommands:
# Append recent messages to today's daily note (Daily/YYYYMMDD.md in the active vault)
python3 scripts/tg.py recent "Chat Name" --to-daily
# Append search results to today's daily note
python3 scripts/tg.py search "query" --to-daily
# Append recent messages to a person's note
python3 scripts/tg.py recent "Chat Name" --to-person "Person Name"
The target vault path is resolved by the formatting helpers in utils/formatting.py; there are currently no --vault or --section overrides on the CLI.
The skill supports three transcription methods with automatic fallback:
GROQ_API_KEY environment variable)pip install openai-whisper)# Use Telegram's transcription (Premium feature)
python3 scripts/tg.py transcribe "Chat" 123
# Force Groq transcription
python3 scripts/tg.py transcribe "Chat" 123 --method groq
# Force local Whisper
python3 scripts/tg.py transcribe "Chat" 123 --method whisper
The daemon monitors Telegram for messages matching configured triggers and can:
Triggers are stored in ~/.config/telegram-telethon/daemon.yaml:
triggers:
# Respond to /claude command in DMs
- chat: "@myusername"
pattern: "^/claude (.+)$"
action: claude
reply_mode: inline
# Respond to @Bot mentions in a group
- chat: "AI Assistants"
pattern: "@Bot (.+)$"
action: claude
reply_mode: new
# Simple ping-pong in any chat
- chat: "*"
pattern: "^/ping$"
action: reply
reply_text: "pong"
claude:
allowed_tools:
- Read
- Edit
- Bash
- WebFetch
max_turns: 10
timeout: 300
queue:
max_concurrent: 1
timeout: 600
| Field | Description |
|-------|-------------|
| chat | Chat name, @username, or * for all chats |
| pattern | Regex pattern (capture group 1 becomes Claude prompt) |
| action | claude, reply, or ignore |
| reply_mode | inline (reply to message) or new (separate message) |
| reply_text | Static text for reply action |
When action is claude:
claude -p "..." --output-format jsonsessions.json--resume <session_id>Claude sessions are saved to ~/.config/telegram-telethon/sessions.json:
To reset: delete chat entry from sessions.json or configure a /reset trigger.
Respond to all DMs to yourself:
triggers:
- chat: "@yourusername"
pattern: "(.+)"
action: claude
reply_mode: inline
Only respond when @mentioned:
triggers:
- chat: "Dev Team"
pattern: "@AssistantBot (.+)"
action: claude
reply_mode: inline
triggers:
- chat: "*"
pattern: "^/ask (.+)"
action: claude
reply_mode: inline
- chat: "*"
pattern: "^/ping$"
action: reply
reply_text: "pong"
- chat: "Noisy Group"
pattern: ".*"
action: ignore
~/.config/telegram-telethon/
├── config.yaml # API credentials (api_id, api_hash, phone)
├── daemon.yaml # Daemon triggers and Claude config
├── session.session # Telethon session file
├── sessions.json # Claude session persistence
└── daemon.log # Daemon log file
# Install with dev dependencies
cd telegram-telethon
pip install -e ".[dev]"
# Run all tests
pytest
# Run with coverage
pytest --cov=telegram_telethon
# Run specific test file
pytest tests/unit/test_claude_bridge.py -v
Mapping natural-language asks to commands:
| User says | Command |
|-----------|---------|
| "Is Telegram connected?" | status |
| "What chats do I have?" | list |
| "Find chat named X exactly" | list --search "X" (increase --limit if not found) |
| "Show recent messages from John" | recent "John" --limit 20 |
| "Messages from the last week in Group Y" | recent "Group Y" --days 7 |
| "Search Telegram for 'deadline'" | search "deadline" |
| "Unread messages from Group Z" | unread --chat "Group Z" |
| "Mark Group Z as read" | mark-read --chat "Group Z" |
| "Get thread 174 in Lab" | thread <chat_id> 174 --limit 100 |
| "Send 'hi' to John" / "отправь John: hi" | send --chat "John" --text "hi" |
| "Post a markdown-formatted note to @channel" | send --chat "@channel" --markdown --text "..." |
| "Schedule this for tomorrow at 10am" | send --chat "..." --text "..." --schedule "tomorrow 10:00" |
| "Send this in an hour" | send --chat "..." --text "..." --schedule "+1h" |
| "Reply thanks to message 12345" | send --chat "..." --text "thanks" --reply-to 12345 |
| "Send image.jpg to John" | send --chat "John" --file image.jpg |
| "Save a draft for John: hi" / "сделай драфт" | draft --chat "John" --text "hi" |
| "List my drafts" | drafts |
| "Send the draft for John" | draft-send --chat "John" |
| "Delete messages 123, 456 from John" | delete --chat "John" --message-ids 123 456 |
| "Forward msg 789 from John to Maria" | forward --from "John" --to "Maria" --message-ids 789 |
| "Edit message 76 in @channel" | edit --chat "@channel" --message-id 76 --text "..." |
| "Download last 5 voice notes from John" | download "John" --type voice --limit 5 |
| "Transcribe voice message 512 from John" | transcribe "John" 512 |
| "Batch-transcribe recent voices from John" | transcribe "John" --batch --limit 10 |
| "Add John's messages to daily note" | recent "John" --to-daily |
| "Add messages to a person's note" | recent "Chat" --to-person "Person Name" |
| "Publish this draft to the klodkot channel" | publish --draft "20260416-post" |
| "Preview a draft before publishing" | publish --draft "..." --dry-run |
| "Publish this at 10am tomorrow" | publish --draft "..." --schedule "tomorrow 10:00" |
| "Check if @mychannel has unrendered markup" | lint-channel --chat "@mychannel" |
| "Lint message 1234 in @mychannel" | lint-channel --chat "@mychannel" --message-id 1234 |
| "Start the Telegram daemon" | python3 scripts/tgd.py start (or --foreground) |
| "Show daemon logs" | python3 scripts/tgd.py logs |
| "Configure daemon triggers" | daemon-config |
Saved Messages: Use "me" (not "Saved Messages") — the label is localized per user.
| Issue | Solution |
|-------|----------|
| "Config not found" | Run python3 scripts/tg.py setup |
| "Session expired" | Delete session.session and re-run setup |
| ModuleNotFoundError: telegram_telethon | Run pip install -e . from the skill directory |
| "Claude timeout" | Increase timeout in daemon.yaml |
| "Queue full" | Reduce request rate or wait |
| "No trigger matched" | Check pattern regex and chat name match |
| Chat not found by name | Increase --limit on list (default 30); may not be in recent dialogs |
development
--- name: agency-docs-updater description: End-to-end pipeline for publishing Claude Code lab meetings. Accepts optional args: date (YYYYMMDD, "yesterday", "today") and lab number (e.g. "04"). Examples: "yesterday 04", "20260420 05", "04" (today, lab 04), "" (today, auto-detect lab). --- # Agency Docs Updater Execute ALL steps automatically in sequence. Only pause if a step fails and cannot be recovered. Read `references/learnings.md` before starting for known pitfalls. **Configuration**: pat
tools
This skill should be used when applying proper typography to prose text or files in Russian, English, German, or French — smart quotes per locale («ёлочки», “curly”, „Gänsefüßchen“, « guillemets »), correct dashes (тире, em/en dash, Gedankenstrich, tiret), non-breaking spaces, ranges, ellipsis, and French espaces insécables before ! ? ; :. Fully deterministic via a pinned typograf-based CLI; never apply these rules by hand. Triggers on "типографика", "typograf", "оттипографь", "smart quotes", "fix typography", "неразрывные пробелы".
development
This skill should be used when inspecting or applying advanced OpenType features of a font (woff2/otf/ttf) — ligatures, stylistic sets (ss01–ss20), character variants (cvXX), texture healing, slashed zero, tabular/oldstyle figures, fractions, small caps, case-sensitive forms — and generating the CSS to enable them. Interviews the user via cenno to pick features. Triggers on "OpenType features", "font features", "stylistic sets", "ligatures", "texture healing", "tabular figures", "what can this font do".
tools
--- name: pre-session-portrait description: Build a compressed, visualizable "portrait" of a consulting/coaching client before a session, so the paid hour is spent solving, not scoping. Runs a 7-lens JTBD-inspired interview (where / how / what / problem / ideal / tension / jobs-to-be-done) that takes rich open answers in and compresses them to an 11-field YAML portrait out. Delivers three ways: raw paste-into-a-clean-chat prompt, a secret GitHub gist link, or a Codex CLI one-liner. Use when prep