mcpField
G
gramio

gramio

github.com/gramiojsDevelopment7282 installsUpdated 1w ago
Repository

About gramio

GramIO is a TypeScript skill for building and maintaining Telegram bots with the GramIO framework. It provides implementation guidance and scripts that inspect installed package types and APIs for version-specific reference.

Key features

  1. 01Guidance for bot commands and update handlers
  2. 02Typed context, plugins, and scene patterns
  3. 03Message formatting and callback routing guidance
  4. 04Scripts to inspect installed API types
  5. 05Migration guidance for other bot frameworks

Use cases

  1. 01Creating Telegram bots with GramIO
  2. 02Adding commands, callbacks, or multi-step flows
  3. 03Checking installed Bot API and plugin signatures
  4. 04Migrating an existing bot to GramIO

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/gramiojs/documentation --skill gramio

After installing, type/gramioin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

metadata.json
examples/
basic.ts
callback-data.ts
callback-routing.ts
deep-links.ts
docker.ts
error-handling.ts
file-upload.ts
formatting.ts
keyboards.ts
onboarding.ts
rich-messages.ts
scene-builder.ts
scenes.ts
session.ts
telegram-stars.ts
testing.ts
tma.ts
ux-menu.ts
webhook.ts
wizard-scene.ts
examples/scene-composer-inheritance/
base.ts
index.ts
scene.ts
gramio-pick-username/
check-usernames.mjs
gramio-upgrade/
detect-versions.mjs
MIGRATIONS.md
plugins/
autoload.md
i18n.md
jsx.md

Details

From the project README

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

GramIO documentation: package installation

When creating or editing installation instructions on the GramIO VitePress site (docs/ and docs/ru/), always use ::: pm-add for package installation, for every package, including Wrappergram and optional dependencies:

::: pm-add wrappergram
:::

Preserve existing pm-add blocks. Never replace them with manual npm/yarn/pnpm/bun code-group blocks or individual install commands: the site's package-manager plugin generates those automatically. Apply the same convention in English and Russian. This rule is specific to the documentation site; GitHub README files, terminal commands and Dockerfiles use ordinary shell syntax.

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.

ToolUse 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

PluginPackagePurpose
Session@gramio/sessionPersistent per-user data storage
Scenes@gramio/scenesMulti-step conversation flows
Onboarding@gramio/onboardingDecorate-only — declarative tutorials / feature intros. Multi-flow concurrency, refusal ladder, scope-aware rendering. NOT a forms/wizard tool — use Scenes for that
I18n@gramio/i18nInternationalization (TS-native or Fluent)
Autoload@gramio/autoloadFile-based handler loading
Prompt@gramio/promptSingle-question prompts — in-memory only, not persistent. Use Scenes .ask() for anything that must survive restarts
Views@gramio/viewsRecommended for screen composition — reusable templates (programmatic + JSON), auto send/edit, keyboards, media, i18n
JSX@gramio/jsxJSX syntax for formatting + keyboards (no React)
Pagination@gramio/paginationPaginated inline-keyboard menus with fluent builder
Auto Retry@gramio/auto-retryRetry on 429 rate limits
Media Cache@gramio/media-cacheCache file_ids
Media Group@gramio/media-groupHandle album messages
Split@gramio/splitSplit long messages
Auto Answer CB@gramio/auto-answer-callback-queryAuto-answer callbacks
PostHog@gramio/posthogAnalytics + feature flags
OpenTelemetry@gramio/opentelemetryDistributed tracing and spans
Sentry@gramio/sentryError 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
TopicDescriptionReference
Bot ConfigurationConstructor, API options, proxy, test DC, debuggingbot-configuration
Bot APICalling methods, suppress, withRetries, type helpersbot-api
Context & Updatesderive, decorate, middleware, start/stop, type narrowingcontext
Triggerscommand, hears, callbackQuery, inlineQuery, reactiontriggers
Filterstype-safe context narrowing — filter-only .on(), filters.* predicates, guard()filters
Middleware Routinghandler priority, next(), overlapping CallbackData, centralized routingmiddleware-routing
Scene ↔ Composer inheritanceshare named .as("scoped") composer derives between bot-level handlers and Scene steps; file split to avoid circular importsscene-composer-inheritance
HooksonStart, onStop, onError, preRequest, onResponsehooks
Updates & Lifecyclestart/stop options, graceful shutdown (SIGINT/SIGTERM)updates
Troubleshootingsymptom → cause → fix: 409 conflict, broken formatting, spinner hangs, missing opt-in updates, scenes-without-session, snake_case access, un-awaited MediaUploadtroubleshooting
Features
TopicDescriptionReference
KeyboardsKeyboard, InlineKeyboard, layout helpers, stylingkeyboards
Formattingentity helpers, join (never native .join()!), variable composition, no parse_modeformatting
Rich MessagesMarkdown/structured block helpers, rich JSX, recursive uploads, drafts, stop-generation updatesrich-messages
UX Patternsbutton-first nav, /start anatomy, nested menus, toggles, destructive confirm, empty states, formatting hierarchy, command discoveryux-patterns
Deep Linksevery 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, footgunsdeep-links
FilesMediaUpload, MediaInput, download, Bun.file()files
CallbackDataType-safe callback data schemascallback-data
StorageIn-memory, Redis, Cloudflare adaptersstorage
Telegram StarsPayments, invoices, subscriptions, inline invoices, refunds, test modetelegram-stars
Types@gramio/types, type helpers, Proxy wrapper, declaration mergingtypes
Infrastructure
TopicDescriptionReference
WebhookFramework integration, tunneling, custom handlerswebhook
Rate LimitswithRetries, broadcasting, queuesrate-limits
DockerDockerfile, multi-stage build, Docker Composedocker
Local Bot API Serverself-host telegram-bot-api, cloud→local migration (logOut + api.baseURL), 2 GB uploads, token-less file downloadslocal-bot-api
tdlight (@gramio/tdlight)userbot/user mode, extra methods (searchMessages/getChats/votePoll), scheduling, proxies, extra object fields; method-name traps; Dockertdlight
TMAMini Apps, mkcert HTTPS, @gramio/init-data authtma
TestingEvent-driven bot testing, user actors, API mockingtesting
Migrations

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

FromDescriptionReference
grammYFlavor generics → derive/extend, named API params, conversations → scenes, checklistmigration-from-grammy
puregramSymbol mapping, API comparisons, checklist for puregram → GramIO refactormigration-from-puregram
TelegrafSymbol mapping, context typing, Scenes/WizardScene, webhook differences, checklistmigration-from-telegraf
node-telegram-bot-apiSymbol mapping, middleware concepts, keyboard builders, session, checklistmigration-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
PluginDescriptionReference
SessionPer-user data, Redis supportsession
ScenesMulti-step flows, state, navigationscenes
OnboardingDecorative tutorials with multi-flow concurrency — pair with Scenes for formsonboarding
I18nTS-native and Fluent internationalizationi18n
AutoloadFile-based handler discoveryautoload
PromptSend + wait for response — in-memory only, lost on restart; use Scenes .ask() for persistent question sequencesprompt
ViewsRecommended pattern — @gramio/views plugin (templates, JSON, i18n, auto send/edit); plain render-fn fallback for trivial botsviews
JSXJSX syntax for formatting + keyboards (no React runtime)jsx
PaginationFluent paginated inline keyboards (prev/next/first/last, page info)pagination
OpenTelemetryDistributed tracing, spans, instrumentationopentelemetry
SentryError tracking, performance monitoringsentry
Othersauto-retry, media-cache, media-group, split, posthogother
Plugin DevelopmentWriting custom plugins, derive/decorate/error, lazy loadingplugin-development
Examples
ExampleDescriptionFile
Basic botCommands, hooks, error handlingbasic.ts
KeyboardsReply, inline, columns, conditionalkeyboards.ts
UX menuHero /start, nested menu with breadcrumbs + Back, toggle buttons, destructive confirm stepux-menu.ts
CallbackDataType-safe callback schemascallback-data.ts
FormattingEntity types, join helper, variable composition, parse_mode anti-patternformatting.ts
File uploadPath, URL, buffer, media groupsfile-upload.ts
Error handlingCustom errors, suppress, scopederror-handling.ts
WebhookFramework integrationwebhook.ts
SessionCounters, settings, Redissession.ts
ScenesRegistration flow with stepsscenes.ts
Wizard sceneCallback-driven scene, mixed callback+message steps, global exitwizard-scene.ts
Scene composer inheritance3-file package: named scoped composer + Scene.extend + file split for circular-import-safe layoutscene-composer-inheritance/
Callback routingCentralized router, shared nav CallbackData across featurescallback-routing.ts
Telegram StarsPayments, invoices, refundstelegram-stars.ts
TMAElysia server, init-data auth, webhooktma.ts
DockerGraceful shutdown, webhook/polling toggledocker.ts
TestingUser simulation, API mocking, error testingtesting.ts
Deep links/start payload routing (ref / order / opaque token / inline-mode redirect) + my_chat_member admin-rights verification for ?startchannel/?startgroup linksdeep-links.ts

Comments

Markdown supported