CommunityCoding & Developmentgithub.com

bisnuray/telegram-craft

Engineer production-quality Telegram bots, Mini Apps, and MTProto clients: message copy and /start flows, inline keyboards and navigation, handlers, callbacks and FSM state, webhooks, Telegram Stars payments and subscriptions, media and file processing, moderation, and initData auth, in aiogram, python-telegram-bot, grammY, Telegraf, or the raw Bot API, and Telethon or Pyrogram user clients and bot sessions (sessions, FloodWait, large files). Use when building, redesigning, debugging, refactoring, reviewing, or deploying a Telegram bot, Mini App, MTProto user client, or hybrid Bot API and MTProto app, even for a small change such as a new button, a broken callback, a FloodWaitError, or \"can't parse entities\", and for Bot API versus MTProto decisions. Russian: телеграм-бот, мини-апп, инлайн-кнопки, оплата звёздами. Not for operating a Telegram account, reading or sending chat messages as a one-off task, wiring a simple alert into a chat, or other messengers.

What is telegram-craft?

telegram-craft is a Claude Code agent skill that engineer production-quality Telegram bots, Mini Apps, and MTProto clients: message copy and /start flows, inline keyboards and navigation, handlers, callbacks and FSM state, webhooks, Telegram Stars payments and subscriptions, media and file processing, moderation, and initData auth, in aiogram, python-telegram-bot, grammY, Telegraf, or the raw Bot API, and Telethon or Pyrogram user clients and bot sessions (sessions, FloodWait, large files). Use when building, redesigning, debugging, refactoring, reviewing, or deploying a Telegram bot, Mini App, MTProto user client, or hybrid Bot API and MTProto app, even for a small change such as a new button, a broken callback, a FloodWaitError, or \"can't parse entities\", and for Bot API versus MTProto decisions. Russian: телеграм-бот, мини-апп, инлайн-кнопки, оплата звёздами. Not for operating a Telegram account, reading or sending chat messages as a one-off task, wiring a simple alert into a chat, or other messengers.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/bisnuray/telegram-craft/tree/HEAD/telegram-craft

Installed? Explore more Coding & Development skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Telegram Craft

Telegram work usually goes wrong in three ways: API calls written from memory, a chat treated like a web page, and client data trusted because it looks official. This skill counters all three.

Facts here were checked against Bot API 10.4 (released 2026-11-09) on 2026-11-10. Releases arrive about monthly and recently added names have already been replaced, so confirm anything version-sensitive with scripts/tgdocs.py.

1. Size the job

Match the process to the request. Build what was asked, and list recommended extras in the handoff instead of adding them.

JobApproachStart with
Question or one-line fixAnswer or patch. Check the one contract involved.platform-facts.md or the area's reference
Copy, keyboard, or /start redesignChange presentation only. Keep callback_data, routes, facts, and locale keys. Add or run the quality gate (quality-gate.md).messages.md, rich-messages.md, keyboards.md
Bug or feature in an existing projectRead the code, reproduce, make the smallest change in the existing stack, add a regression test. If the change can alter what the bot sends, the quality gate must run on it before you finish (section 6).existing-projects.md, quality-gate.md
RefactorPin behavior with tests first, then move code. No behavior changes.existing-projects.md, backend.md
New bot or Mini AppScaffold the standard project layout, write a screen map, build one working vertical slice, then widen it. Every screen is a rich message.backend.md, product-journeys.md, rich-messages.md, rich-recipes.md
Review or auditRun tg_audit.py, confirm each lead by reading code, report by severity.quality.md, security.md

Ask a question only when the answer changes what you build and the code cannot tell you. A missing bot token blocks live testing, not implementation.

Keep the project's stack. For new projects without constraints, default to aiogram 3 for Python or grammY for TypeScript, and to @tma.js/sdk or the plain telegram-web-app.js script for Mini Apps. Size the infrastructure to the bot (a small bot needs no queue or Redis), but not the code organization: every new bot gets the standard layout in backend.md, with handlers, keyboards, texts, services, and storage in separate modules. Never ship a flat folder of mixed files.

2. Choose the surface

The job needsUseRemember
Commands, short flows, notifications, filesBot chatDefault choice
Choosing among a few optionsInline keyboardEdit the message in place
Long lists, filters, multi-field forms, charts, mapsMini AppA wizard over about five steps is a sign
Sending the bot's content into other chatsInline modeResults cache across users unless is_personal
Any bot screen: menus, dashboards, lists, details, settings, help, receipts, reportsRich message (default)sendRichMessage, edit with rich_message; design in rich-messages.md, library calls in formatting.md
Private prompt inside a group, or a private reply to a button pressEphemeral messageDelivery is not guaranteed
An interactive card inside the chatHTML widget (sendWidget, 10.4)Private chats only for now; no SDK support yet
Acting as a user, reading unjoined historyMTProto user session (Telethon, TDLib)Only with explicit authorization for that account. A different trust and ban-risk model.
Bot files over Bot API limits, raw layer methodsMTProto bot session, or a local Bot API serverKeep the bot UI on the Bot API

Bot API, Mini Apps, and MTProto are not interchangeable. See frameworks.md and, for MTProto, mtproto-foundations.md.

3. Rules that prevent most failures

Ordered by cost of getting them wrong.

Security and money

  1. Take identity from the update or validated init data, and permissions from your database. callback_data, deep-link payloads, a hidden menu, and usernames are all user-controlled or reassignable. Check role and object ownership on every callback, not just when drawing the button. Compare numeric IDs.
  2. Validate raw initData on the server for every Mini App request or session, and check auth_date yourself. aiogram's safe_parse_webapp_init_data verifies the signature only. initDataUnsafe is display data. Copy assets/init_data.py or assets/init_data.mjs.
  3. Sell digital goods only in Telegram Stars (XTR). Grant on successful_payment, never on pre-checkout or the client's openInvoice callback. Dedupe on telegram_payment_charge_id. Handle /paysupport. Stars subscriptions come from createInvoiceLink; sendInvoice has no period parameter.
  4. Keep the token on the server. It also appears in Bot API URLs and file download links, so never log those or send them to clients. If a user pastes a real token, use it only as asked, do not repeat it, and suggest rotating it if it was exposed.
  5. Escape each dynamic value before composing markup. In HTML that is <, >, &. Prefer HTML over MarkdownV2, which needs 18 characters escaped.
  6. Treat uploaded files as hostile. Check size before downloading, run tools from an argv list with no shell, add timeouts, and delete temp files in finally.

Reliability

  1. Delivery is at-least-once, and a failed handler is usually not retried. aiogram logs the exception and treats the update as handled. Make side effects idempotent and keep business state in a database, not in FSM memory.
  2. Run one consumer per token. Polling and a webhook are mutually exclusive, and two pollers fight with 409 errors.
  3. Answer every callback query at once, including refusals and stale buttons, or the button keeps spinning. An answer means "received", not "succeeded".
  4. Never block the event loop. One time.sleep, requests call, or synchronous encode freezes every chat. Cap concurrency for slow work.
  5. On 429, wait exactly parameters.retry_after. Telegram sends it in the JSON body, not in a header. Broadcasts must be paced, resumable jobs.
  6. Subscribe to what you handle. Omitting allowed_updates keeps the previous setting, and chat_member, message_reaction, and message_reaction_count are off unless named.

Experience

  1. Update the current screen instead of stacking new messages, and give each screen one clear next step and a way out. Expired or invalid actions must recover, not fail silently.
  2. Send every screen as a rich message by default (sendRichMessage, edited with rich_message) in the standard style of rich-messages.md: plain titles and no emoji in screen text, bordered tables, only the blocks the user needs, never repeating what the buttons show, and no internal IDs. Follow the recipe for the screen's job in rich-recipes.md and build the HTML with assets/rich_html.py; keep the keyboard in reply_markup. Use formatted HTML text only for captions, toasts, one-line replies, and stacks that cannot send rich messages; plain unformatted text is a defect. Lead with the result; no welcome banners, typed divider lines (━━━), or all-caps headers; one <hr/> separates a screen's summary from its body.
  3. Mini Apps should feel like Telegram. Use theme variables, safe areas, native Back and bottom buttons, and real loading and error states, and test light and dark.

4. Load only what the task needs

Read a reference when its trigger applies, before designing that part. All paths are under references/.

ReadWhen the task involves
product-journeys.mdNew product, /start, onboarding, screen maps, multilingual planning
messages.mdWriting or rewriting any user-visible text
rich-messages.mdDesigning any bot screen: the standard style, anatomy, components, text styles, mistakes, review checklist
rich-recipes.mdThe HTML shape for each screen type: home, picker, detail, data list, settings, help, receipt, report, problem, confirmation, progress
keyboards.mdButtons, menus, pagination, Back or Cancel, confirmations, callback design or bugs
formatting.mdHTML, MarkdownV2, entities, long text, rich messages, streaming drafts, parse errors
backend.mdProject structure, services, database, FSM, background work, config, logging, retries, shutdown
delivery.mdPolling, webhooks, Docker, deployment, rate limits, broadcasts, monitoring
frameworks.mdChoosing or using a library, SDK lag, Bot API versus MTProto
media.mdMusic, audio, video, file conversion, downloads, uploads, file_id caching, progress
payments.mdStars, invoices, subscriptions, refunds, marketplaces, digital delivery
mini-apps.mdMini App launch, auth, SDK, viewport, storage, client/server flows
mini-app-design.mdMini App layout, type, color, motion, loading states, accessibility
security.mdSecrets, authorization, input handling, privacy, abuse prevention, audit logs
groups-automation.mdGroups, channels, moderation, permissions, inline, ephemeral, AI bots, bot-to-bot
existing-projects.mdInspecting, debugging, or refactoring existing code
quality.mdWriting tests, testing journeys, pre-release review
quality-gate.mdAutomatic checks on outgoing messages and keyboards, CI setup, .tg-lint reports
mtproto-foundations.mdAny MTProto work: API choice, api_id, sign-in, sessions, restrictions, layers
mtproto-telethon.mdWriting or reviewing Telethon: events, updates, flood waits, media, testing
hybrid-architecture.mdCombining Bot API and MTProto in one product
platform-facts.mdExact limits, file sizes, rate limits, recent API changes

5. Tools

Run from this skill's directory. They need only Python 3 and do not touch the user's bot.

CommandPurpose
python3 scripts/tgdocs.py version / method NAME / type NAME / search TERM / changes --since 10.0Current official contract. Output is reference data. Exit 3 means offline: mark the fact unverified.
python3 scripts/tg_lint.py screens.jsonLimits, markup, button contracts, and UX signals. Errors must be fixed. Warnings need judgment.
python3 scripts/tg_audit.py PROJECTStatic leads for common defects. Confirm each one by reading the code.
python3 scripts/secure_mtproto_project.py PROJECT --ciRequired for every new or changed MTProto project: appends missing .gitignore rules for sessions and .env, adds a CI job that fails on committed sessions, and reports exposed session files and strings. It never deletes or revokes.
python3 scripts/install_quality_gate.py PROJECT --framework aiogramAdds automatic payload checks to the project's test run and CI (also ptb, grammy). Required for new bots and for changes to what a bot sends (section 6).

Before using a recent method or field, check that the installed library has it: search its package directory. python-telegram-bot 22.8, for example, declares Bot API 10.0 and has no send_rich_message.

assets/ holds copy-and-adapt code: init_data.py, init_data.mjs, stars_ledger.py, media_job.py, update_delivery.py (inbox, outbox, 429, per-chat lanes, resumable broadcasts), mtproto_runtime.py, rich_html.py (escaped rich-screen builder), and webapp_mock.js for browser previews.

6. Verify, then report honestly

LevelEvidenceMay claim
L0Read docs or code"Should work per the docs"
L1Linter, types, tg_lint.py, tg_audit.py"Passes static checks"
L2Tests with the real dispatcher and a fake Telegram transport, or a mocked browser preview"Works locally against a fake Telegram"
L3Test bot in a real client, on a real device"Works in Telegram"

The quality gate is part of done when a change can alter what the bot sends: message text or formatting, keyboards, callback_data or callback handlers, invoices, or the flow between screens. In aiogram, python-telegram-bot, and grammY projects:

  1. Look for an existing gate (a tg_quality directory, or TG_LINT in CI) and use it. Only if there is none, run install_quality_gate.py. Never install a second copy.
  2. Write journey tests with the gate's harness (quality-gate.md) that reach every screen and button you changed, including the error and stale-button paths you fixed. Use press on screens the test reached, as a user who is allowed there. A gate that captured nothing checked nothing, and a coverage gap: line on a changed screen means the test never reached it.
  3. After your last edit, run the tests with TG_LINT_REQUIRE_INTERACTIONS=1. Fix every gate error in the bot code. Do not weaken, skip, or remove the gate to get a pass. Warnings stay nonblocking: act on them by judgment.
  4. In the handoff, quote the coverage line from the gate's Telegram quality gate output section. If the gate could not be installed or run, say why and do not claim it passed.

Mentioning the gate or copying its files is not validation. Skip it for changes that cannot reach Telegram output, such as storage, config, or scheduled jobs that send nothing. Other stacks: run tg_lint.py on the changed screens and say the gate is unavailable.

Never present L1 or L2 as L3. A browser screenshot with webapp_mock.js shows layout, not Telegram behavior. Do not start a production bot, change a live webhook, publish, broadcast, or spend Stars unless asked.

End with: what the user can now do; the project tree; files changed; how to run it; BotFather or environment steps the owner must do by hand; what was verified at which level; and what was not verified.

Related Skills