SuperAPI AI Directory

gramio

General & other

View repository

Updated 10-15 21:55

About this skill

GramIO

GramIO is a modern, type-safe Telegram Bot API framework for TypeScript. It runs on Node.js, Bun, and Deno with full Bot API coverage, a composable plugin system, and first-class TypeScript support.

When to Use This Skill

  • Creating or modifying Telegram bots
  • Setting up bot commands, callbacks, inline queries, or reactions
  • Building keyboards (reply, inline, remove, force reply)
  • Formatting messages with entities (bold, italic, code, links, mentions)
  • Uploading/downloading files and media
  • Managing user sessions or multi-step conversation flows (scenes)
  • Writing custom plugins
  • Configuring webhooks or long polling
  • Handling payments with Telegram Stars
  • Broadcasting messages with rate limit handling
  • Building Telegram Mini Apps (TMA) with backend auth
  • Containerizing bots with Docker
  • Using standalone @gramio/types for custom Bot API wrappers
  • Writing and publishing custom plugins
  • Migrating bots from puregram, grammY, Telegraf, or node-telegram-bot-api to GramIO

Quick Start

npm create gramio bot-name
cd bot-name
npm run dev

Basic Pattern

import { Bot } from "gramio";

const bot = new Bot(process.env.BOT_TOKEN as string)
    .command("start", (context) => context.send("Hello!"))
    .onStart(({ info }) => console.log(`@${info.username} started`))
    .onError(({ context, kind, error }) => console.error(`[${kind}]`, error));

bot.start();

Introspection Tools

This skill ships four Node.js scripts under tools/ that parse the installed @gramio/* and gramio packages on disk. Prefer these over URL-fetching or loading the telegram-api-index — each call returns one focused, version-accurate signature instead of a wall of docs.

Run from the user's project root (where node_modules/ lives). Tools print to stdout; errors and auto-correct hints go to stderr.

Tool Use it when
tools/get-bot-api-method.mjs <name> You need the signature of a Bot API method (e.g. sendMessage, createChatInviteLink) — returns JSDoc + params/return type from @gramio/types. --list shows all methods, --search <term> filters by name/description.
tools/get-bot-api-type.mjs <name> You need a Telegram type definition (e.g. Message, ChatInviteLink). Accepts short (Message) or full (TelegramMessage) names. Same --list / --search flags.
tools/get-context-getter.mjs <ClassName> You need to know what getters/methods a context exposes (MessageContext, CallbackQueryContext, User, Chat). Add --deep to pull in mixins + merged interfaces recursively. --search <name> finds every class that exposes a given getter/method (e.g. firstName lives on User, Chat, Contact, SharedUser).
tools/get-plugin.mjs <name> You need a plugin's entry function signature + what it derives onto context (e.g. session, scenes, i18n). --list shows every installed @gramio/* package.
node tools/get-bot-api-method.mjs sendMessage
node tools/get-bot-api-type.mjs InlineKeyboardMarkup
node tools/get-context-getter.mjs MessageContext --deep
node tools/get-context-getter.mjs --search chatId
node tools/get-plugin.mjs session

The scripts fuzzy-match (sendMesage → sendMessage) and suggest alternatives on miss. They require the relevant package to be installed — if not, they print the exact npm install hint.

Critical Concepts

  1. Callback routing — NEVER parse callback data manually. CallbackData.pack() produces a 6-character hash prefix (sha1-base64url of the schema name) + serialized payload — NOT a literal prefix like "nav:". Checks like ctx.data.startsWith("nav:") always fail at runtime. Use one of these four patterns, picked by shape of data:

    // Fixed string → exact string match
    bot.callbackQuery("refresh", (ctx) => ctx.answer("Refreshed"));
    
    // Pattern / variable slug → RegExp with capture groups
    bot.callbackQuery(/^user_(\d+)$/, (ctx) => {
        const [, id] = ctx.match!;
        ctx.answer(`User ${id}`);
    });
    
    // Structured data → CallbackData schema (preferred for multi-field payloads)
    import { CallbackData } from "gramio";
    const nav = new CallbackData("nav").enum("to", ["home", "history", "admin"]);
    bot.callbackQuery(nav, (ctx) => {
        ctx.queryData.to; // "home" | "history" | "admin" — fully typed
    });
    
    // Stale-safe unpack (when inline keyboard may outlive a schema change)
    const result = nav.safeUnpack(ctx.data!);
    if (!result.success) return ctx.answer("Button expired");
    result.data.to; // typed
    
    // ❌ NEVER — hashed prefix means string compare won't match,
    //    and you lose full type safety.
    if (ctx.data?.startsWith("nav:")) {
        const [, to] = ctx.data.slice(4).split(":");
        // ...
    }
    

    See callback-data and middleware-routing for overlapping-handler behavior across plugins.

  2. Method chaining — handlers, hooks, and plugins chain via .command(), .on(), .extend(), etc. Order matters: when two handlers match the same update, the first-registered one wins unless it calls next(). See middleware-routing.

  3. Type-safe context — context is automatically typed based on the update type. Use context.is("message") for type narrowing in generic handlers. After .derive()/.decorate()/.extend(plugin), new fields appear on the inferred context type automatically — never cast with ctx as unknown as { myField }. Export the bot's context type and reuse it (see context → "Context typing after derive").

  4. Context getters — always camelCase; never touch ctx.payload or ctx.update.* — every Telegram field is exposed as a camelCase getter (ctx.from, ctx.firstName, ctx.chatId, ctx.messageId, ctx.text, ctx.data, ctx.queryData). Both ctx.payload AND ctx.update are raw snake_case internal objects — treat them as off-limits in handler code.

  5. Plugin system — new Plugin("name").derive(() => ({ ... })) adds typed properties to context. Register via bot.extend(plugin).

  6. Hooks lifecycle — onStart → (updates with onError) → onStop. API calls: preRequest → call → onResponse/onResponseError.

  7. Error suppression — bot.api.method({ suppress: true }) returns error instead of throwing.

  8. Lazy plugins — async plugins (without await) load at bot.start(). Use await for immediate loading.

  9. Derive vs Decorate — .derive() runs per-update (computed), .decorate() injects static values once.

  10. Formatting — four critical rules (read formatting before writing any message text):

    • Never use parse_mode — format produces MessageEntity arrays, not HTML/Markdown strings. Adding parse_mode: "HTML" or "MarkdownV2" will break the message. GramIO passes entities automatically.
    • Never use native .join() on arrays of formatted values — it calls .toString() on each Formattable, silently destroying all styling. Always use the join helper: join(items, (x) => bold(x), "\n").
    • **Always wrap styled content in format\`** when composing or reusing — embedding a Formattablein a plain template literal (``${boldx}``) strips all entities. Useformat`${bold`x`}`` instead.
    • Never call .toString() on a FormattableString — pass it directly as the text:/caption: param to send, editMessageText, editMessageCaption, etc. Calling .toString() strips all entities. This is the #1 reason magic-links and formatted entities "stop working" after an edit.
  11. Scenes — step semantics and update-type filtering — context.scene.step.go(N) and context.scene.step.next() run the scene's middleware chain immediately, but each .step(updateName, handler) filters by context.is(updateName). If your current context is callback_query and the next step is .step("message", …), it will not fire — you must either send the UI directly before return step.next(), use .step(["message", "callback_query"], …), or render the prompt in onEnter / the current step's callback handler. Prefer .ask("field", zodSchema, "prompt") for single-value validated input. See scenes.

  12. InlineQueryResult builders — use InlineQueryResult.article(id, title, InputMessageContent.text(...)) and similar builder methods for inline results. bot.inlineQuery(/regex/, handler) routes inline queries. See triggers.

  13. Composing screens — prefer @gramio/views over inline ctx.send / ctx.editText — for any bot with more than a couple of screens, use the @gramio/views plugin to define reusable view templates and render them with context.render(view, params). It auto-detects send-vs-edit based on update type, centralises text/keyboards/media (incl. JSON-driven templates with {{param}} / {{$global}} interpolation and i18n via adapter factory), and eliminates the common failure mode of duplicating message bodies across ctx.send(...) and ctx.editText(...) call sites. The plain render-function pattern is fine for trivial menu-only bots (2-3 screens, no media, no i18n) — anything more graduates to @gramio/views. @gramio/views is marked work-in-progress (helper/adapter details may evolve), but the core shape (initViewsBuilder, .render(fn), context.render(...)) is stable and is the recommended direction for view composition in GramIO. See views.

  14. @gramio/prompt is in-memory only — NOT persistent. Use Scenes + .ask() for any multi-step / connect / onboarding flow. The Promise returned by context.prompt(...) / context.wait(...) lives in the current Node.js process. The moment the process restarts (deploy, crash, container reschedule, dyno cycling), the Promise is gone: the user's next message is not treated as an answer, no error is thrown, the bot silently "forgets" where the user was. This is fatal for OAuth-connect flows (Spotify/Last.fm/etc.), onboarding wizards, payment collection, any question-sequence that must survive a deploy. Scenes, by contrast, persist step index and collected answers in the configured storage (Redis / Cloudflare / custom) and rehydrate on the next update. Rule: @gramio/prompt is acceptable only for single-question, in-process prompts where losing the flow on restart is acceptable (e.g. throwaway confirmations, debug helpers). Every question-sequence that matters — and every "ask → validate → ask again" connect flow — belongs in a Scene with .ask("field", zodSchema, "prompt message"). .ask() kills the same firstTime branching and validation-retry boilerplate as prompt, plus it persists. Do not recommend @gramio/prompt as a simpler alternative to scenes for multi-step flows; that is the recommendation trap this concept exists to block. See scenes and prompt.

  15. Subagent delegation — skills do not auto-activate inside subagent sessions. When spawning a subagent that will write bot code, explicitly pass the relevant reference-file paths (e.g. skills/references/callback-data.md, skills/plugins/scenes.md, skills/references/formatting.md, skills/references/middleware-routing.md) in the agent prompt, or include the key rules inline.

  16. No any anywhere in examples — never write ctx: any, as any, <any>, or implicit-any parameters in any file under skills/ (examples, markdown code blocks, plugin docs). Skill examples are templates that AI copies verbatim into user bots; every any here multiplies into every downstream bot. Derive the proper type from ContextType<typeof bot, "update_name">, CallbackQueryShorthandContext<typeof bot, typeof schema>, or export a BotContext = typeof bot['_']['context'] alias. If a value is genuinely unknown at a system boundary, use unknown + narrowing. No exceptions, even in "what-not-to-do" snippets — use @ts-expect-error on the specific line with a comment instead of a broad any.

  17. Button-first UX — users tap, they don't type. Navigation belongs to inline keyboards, not slash commands. /start should be a short hero (bold title + blockquote description) with an inline keyboard of primary actions — not a wall of text listing /help, /settings, /delete. Nested menus need breadcrumbs in the title (⚙️ Settings · home › settings), a ◀ Back button on every non-home screen, and 🏠 Home anywhere deeper than two levels. Navigation clicks edit the current message (ctx.editText), never send new ones — new sends are for events (results, notifications), not navigation. Toggle buttons carry their state in the label (✅ Notifications / ⬜ Notifications) and one handler flips the session field + rerenders. Destructive actions always go through a confirm screen with the safe default on the left. Every callback handler starts with ctx.answer() so the spinner stops immediately — empty is fine for navigation, short text for toast feedback, { show_alert: true } only for errors the user must acknowledge. Commands exist for discovery (register with setMyCommands so they appear in Telegram's menu button), not as the primary UI. See ux-patterns for the full playbook and examples/ux-menu.ts for a worked example covering hero /start, nested menu, toggles, and destructive confirm.

  18. Run bun run check:skills before finishing any skill edit — any change to skills/**/*.ts or TypeScript code blocks in skills/**/*.md must typecheck cleanly against the currently installed gramio versions. The check:skills script runs tsc --noEmit over skills/examples/*.ts with strict mode. If it reports errors, fix them — don't ship. If a pre-existing example breaks because gramio's API evolved, update the example to match the current API (check node_modules/gramio/dist/index.d.ts and node_modules/@gramio/*/dist/index.d.ts for current signatures).

  19. Rich Messages are a separate send surface — use rich/markdownTable for the Markdown lane, blocks/table for native structured blocks, or @gramio/jsx/rich for rich JSX. Rich values route to sendRichMessage, not sendMessage. streamRichMessage is Markdown-chunk based; send native blocks directly with ctx.send / ctx.sendRichMessage. Uploads work recursively in rich_message.blocks / .media, but direct uploads are forbidden in sendRichMessageDraft. Handle user cancellation with bot.on("stopped_message_generation", ...). See rich-messages.

Official Plugins

Plugin Package Purpose
Session @gramio/session Persistent per-user data storage
Scenes @gramio/scenes Multi-step conversation flows
Onboarding @gramio/onboarding Decorate-only — declarative tutorials / feature intros. Multi-flow concurrency, refusal ladder, scope-aware rendering. NOT a forms/wizard tool — use Scenes for that
I18n @gramio/i18n Internationalization (TS-native or Fluent)
Autoload @gramio/autoload File-based handler loading
Prompt @gramio/prompt Single-question prompts — in-memory only, not persistent. Use Scenes .ask() for anything that must survive restarts
Views @gramio/views Recommended for screen composition — reusable templates (programmatic + JSON), auto send/edit, keyboards, media, i18n
JSX @gramio/jsx JSX syntax for formatting + keyboards (no React)
Pagination @gramio/pagination Paginated inline-keyboard menus with fluent builder
Auto Retry @gramio/auto-retry Retry on 429 rate limits
Media Cache @gramio/media-cache Cache file_ids
Media Group @gramio/media-group Handle album messages
Split @gramio/split Split long messages
Auto Answer CB @gramio/auto-answer-callback-query Auto-answer callbacks
PostHog @gramio/posthog Analytics + feature flags
OpenTelemetry @gramio/opentelemetry Distributed tracing and spans
Sentry @gramio/sentry Error tracking + performance monitoring

Telegram Bot API Reference Pages

GramIO docs include a dedicated reference page for every Telegram Bot API method and type:

  • Methods: https://gramio.dev/telegram/methods/{methodName} — e.g. sendMessage, createChatInviteLink, answerCallbackQuery
  • Types: https://gramio.dev/telegram/types/{typeName} — e.g. Message, ChatInviteLink, InlineKeyboard

Each page contains: GramIO TypeScript examples, parameter details, error table with causes and fixes, tips & gotchas, and related links. When a user asks about a specific Telegram API method or type, you can fetch or reference the corresponding page for accurate GramIO-specific usage.

Tip for LLMs: Any GramIO docs page can be fetched as clean Markdown by appending .md to the URL: https://gramio.dev/telegram/methods/sendMessage.md — clean Markdown instead of HTML. This works for all sections of the docs, not just API pages.

These pages are not included in this skill by default — fetch them on demand when the user asks about a specific method/type.

To quickly find which methods exist — use the pre-built index: telegram-api-index. It lists all 165+ Bot API methods with short descriptions in one file. Load it when you need to discover a method name or confirm one exists before fetching a full page.

References

Core

Topic Description Reference
Bot Configuration Constructor, API options, proxy, test DC, debugging bot-configuration
Bot API Calling methods, suppress, withRetries, type helpers bot-api
Context & Updates derive, decorate, middleware, start/stop, type narrowing context
Triggers command, hears, callbackQuery, inlineQuery, reaction triggers
Filters type-safe context narrowing — filter-only .on(), filters.* predicates, guard() filters
Middleware Routing handler priority, next(), overlapping CallbackData, centralized routing middleware-routing
Scene ↔ Composer inheritance share named .as("scoped") composer derives between bot-level handlers and Scene steps; file split to avoid circular imports scene-composer-inheritance
Hooks onStart, onStop, onError, preRequest, onResponse hooks
Updates & Lifecycle start/stop options, graceful shutdown (SIGINT/SIGTERM) updates
Troubleshooting symptom → cause → fix: 409 conflict, broken formatting, spinner hangs, missing opt-in updates, scenes-without-session, snake_case access, un-awaited MediaUpload troubleshooting

Features

Topic Description Reference
Keyboards Keyboard, InlineKeyboard, layout helpers, styling keyboards
Formatting entity helpers, join (never native .join()!), variable composition, no parse_mode formatting
Rich Messages Markdown/structured block helpers, rich JSX, recursive uploads, drafts, stop-generation updates rich-messages
UX Patterns button-first nav, /start anatomy, nested menus, toggles, destructive confirm, empty states, formatting hierarchy, command discovery ux-patterns
Deep Links every t.me/<bot>?... link family — ?start=, ?startgroup=, ?startchannel=, ?startapp=, ?startattach=, ?game= — with the handler / context field each one lands in, payload encoding rules, admin= permission tokens, footguns deep-links
Files MediaUpload, MediaInput, download, Bun.file() files
CallbackData Type-safe callback data schemas callback-data
Storage In-memory, Redis, Cloudflare adapters storage
Telegram Stars Payments, invoices, subscriptions, inline invoices, refunds, test mode telegram-stars
Types @gramio/types, type helpers, Proxy wrapper, declaration merging types

Infrastructure

Topic Description Reference
Webhook Framework integration, tunneling, custom handlers webhook
Rate Limits withRetries, broadcasting, queues rate-limits
Docker Dockerfile, multi-stage build, Docker Compose docker
Local Bot API Server self-host telegram-bot-api, cloud→local migration (logOut + api.baseURL), 2 GB uploads, token-less file downloads local-bot-api
tdlight (@gramio/tdlight) userbot/user mode, extra methods (searchMessages/getChats/votePoll), scheduling, proxies, extra object fields; method-name traps; Docker tdlight
TMA Mini Apps, mkcert HTTPS, @gramio/init-data auth tma
Testing Event-driven bot testing, user actors, API mocking testing

Migrations

Load when the user wants to migrate an existing bot to GramIO.

From Description Reference
grammY Flavor generics → derive/extend, named API params, conversations → scenes, checklist migration-from-grammy
puregram Symbol mapping, API comparisons, checklist for puregram → GramIO refactor migration-from-puregram
Telegraf Symbol mapping, context typing, Scenes/WizardScene, webhook differences, checklist migration-from-telegraf
node-telegram-bot-api Symbol mapping, middleware concepts, keyboard builders, session, checklist migration-from-ntba

Upgrading an existing GramIO project across gramio/@gramio/* versions (not migrating from another framework) → use the standalone gramio-upgrade skill (skills/gramio-upgrade/SKILL.md). It detects installed versions, looks up the breaking changes / deprecations / new features between current and target in its version-by-version ledger (skills/gramio-upgrade/MIGRATIONS.md), and applies the edits.

Plugins

Plugin Description Reference
Session Per-user data, Redis support session
Scenes Multi-step flows, state, navigation scenes
Onboarding Decorative tutorials with multi-flow concurrency — pair with Scenes for forms onboarding
I18n TS-native and Fluent internationalization i18n
Autoload File-based handler discovery autoload
Prompt Send + wait for response — in-memory only, lost on restart; use Scenes .ask() for persistent question sequences prompt
Views Recommended pattern — @gramio/views plugin (templates, JSON, i18n, auto send/edit); plain render-fn fallback for trivial bots views
JSX JSX syntax for formatting + keyboards (no React runtime) jsx
Pagination Fluent paginated inline keyboards (prev/next/first/last, page info) pagination
OpenTelemetry Distributed tracing, spans, instrumentation opentelemetry
Sentry Error tracking, performance monitoring sentry
Others auto-retry, media-cache, media-group, split, posthog other
Plugin Development Writing custom plugins, derive/decorate/error, lazy loading plugin-development

Examples

Example Description File
Basic bot Commands, hooks, error handling basic.ts
Keyboards Reply, inline, columns, conditional keyboards.ts
UX menu Hero /start, nested menu with breadcrumbs + Back, toggle buttons, destructive confirm step ux-menu.ts
CallbackData Type-safe callback schemas callback-data.ts
Formatting Entity types, join helper, variable composition, parse_mode anti-pattern formatting.ts
File upload Path, URL, buffer, media groups file-upload.ts
Error handling Custom errors, suppress, scoped error-handling.ts
Webhook Framework integration webhook.ts
Session Counters, settings, Redis session.ts
Scenes Registration flow with steps scenes.ts
Wizard scene Callback-driven scene, mixed callback+message steps, global exit wizard-scene.ts
Scene composer inheritance 3-file package: named scoped composer + Scene.extend + file split for circular-import-safe layout scene-composer-inheritance/
Callback routing Centralized router, shared nav CallbackData across features callback-routing.ts
Telegram Stars Payments, invoices, refunds telegram-stars.ts
TMA Elysia server, init-data auth, webhook tma.ts
Docker Graceful shutdown, webhook/polling toggle docker.ts
Testing User simulation, API mocking, error testing testing.ts
Deep links /start payload routing (ref / order / opaque token / inline-mode redirect) + my_chat_member admin-rights verification for ?startchannel/?startgroup links deep-links.ts