Install GramIO via create-gramio
mainTo scaffold a new GramIO project, use the create-gramio CLI tool. This will customize the project structure according to your needs.
npm create gramio@latest ./botrepository·main·Indexed 43 days ago
https://github.com/gramiojs/gramioA powerful, extensible, and type-safe TypeScript/JavaScript framework for building Telegram bots. GramIO supports Node.js, Bun, and Deno, providing a fluent API for handling commands, events, and middleware. It features a robust filtering system, automatic allowed updates generation via buildAllowedUpdates, and built-in utilities for text formatting and file management. Version 0.15.1.
To scaffold a new GramIO project, use the create-gramio CLI tool. This will customize the project structure according to your needs.
npm create gramio@latest ./botGramIO is composed of several specialized modules that you can import directly from the main package:
@gramio/callback-data: Tools for handling and creating Telegram callback data.@gramio/contexts: Provides the context objects used within handlers to interact with the Telegram API.@gramio/files: Utilities for managing file uploads and downloads.@gramio/format: Formatting utilities for messages.@gramio/keyboards: Classes and types for creating Inline Keyboards and Reply Keyboards.@gramio/types: Core TypeScript types for the Telegram Bot API.filters: Logic for filtering updates (e.g., by message text or user).plugin: Interface for extending the bot with custom plugins.The .group(groupedFn) method allows you to group handlers together. The groupedFn receives a Bot instance and returns an AnyBot.
Warning: If you use on or use inside a group and also at the plugin level, the group handlers are registered after the plugin-level handlers.
By default, webhookHandler adds updates to a queue and returns a response immediately to the web framework without waiting for the bot to finish processing. If you need to ensure the update is processed before responding (e.g., for certain web framework requirements or synchronous flows), you can enable shouldWait.
When shouldWait is enabled, the handler will wait for bot.updates.handleUpdate to complete. You can configure the timeout behavior using WebhookHandlerOptionsShouldWait.
timeout: The maximum time (in ms) to wait for the update to be handled.onTimeout: What to do if the timeout is reached. Use "throw" to raise an error or "return" to simply proceed.// Waiting for processing with a 5-second timeout
webhookHandler(bot, "fastify", {
shouldWait: {
timeout: 5000,
onTimeout: "return"
}
});webhookHandler(bot, "fastify", {
shouldWait: {
timeout: 5000,
onTimeout: "return"
}
});When setting up webhooks, Telegram can include a secret token in the X-Telegram-Bot-Api-Secret-Token header. This allows you to verify that the incoming request actually originated from Telegram.
GramIO's framework adapters are designed to look for this specific header. Note that in many Node.js runtimes, header names are normalized to lowercase, so the adapter internally looks for x-telegram-bot-api-secret-token to ensure compatibility.
GramIO provides a filters object used within .on() handlers to narrow down incoming updates (like messages, callback queries, etc.) to specific criteria. These filters act as type guards, meaning that when a filter matches, the context object is automatically narrowed to include the relevant properties (e.g., if you use filters.text, the context will be typed to include a text: string property).
Common categories of filters include:
photo, video, document, audio, sticker, voice, videoNote, animation, contact, location, poll, and media (any attachment).text, caption, dice, entities, captionEntities, quote, linkPreview, startPayload, authorSignature, replyInfo, giveaway, paidMedia, game, story, effectId, mediaGroup, venue.chat (by type), pm (private), group, supergroup, channel, senderChat (by type), from (by user ID), chatId (by chat ID), isBot, isPremium, isForum.service, topicMessage, mediaSpoiler, protectedContent, fromOffline.hasMessage, hasData, hasInlineMessageId, hasGameShortName.// Example: Matching a message that contains a photo and has specific text
bot.on(filters.every(filters.photo, filters.text), (ctx) => {
// ctx.attachment is now typed as Photo
// ctx.text is now typed as string
console.log(ctx.text);
});A Plugin is an object used to extend a Bot instance with custom logic, types, and handlers. You can create a new plugin using the Plugin class and then attach it to a bot using the .extend(plugin) method. This allows you to modularize your bot's functionality, including middleware, hooks, and custom error definitions.
When extending a bot, the plugin's handlers and hooks are merged into the bot instance. If you use group() within a plugin, those handlers are registered after the plugin-level handlers.
import { Plugin, Bot } from "gramio";
export class PluginError extends Error {
wow: "type" | "safe" = "type";
}
const plugin = new Plugin("gramio-example")
.error("PLUGIN", PluginError)
.derive(() => {
return {
some: ["derived", "props"] as const,
};
});
const bot = new Bot(process.env.TOKEN!)
.extend(plugin)
.onError(({ context, kind, error }) => {
if (context.is("message") && kind === "PLUGIN") {
console.log(error.wow);
}
})
.use((context) => {
console.log(context.some);
});To start using GramIO, create a new Bot instance by providing your Telegram Bot token. You can also pass an optional configuration object to customize the API behavior (like baseURL or useTest).
import { Bot } from "gramio";
const bot = new Bot("YOUR_BOT_TOKEN");
bot.start();GramIO allows you to extend the context using .derive(). This is useful for attaching shared data (like user information from a database) to every subsequent event handler. Once derived, the data is available on the context object with full type safety.
GramIO also provides utility functions for text formatting:
format: A tagged template literal for constructing messages.bold: Wraps text in bold formatting.code: Wraps text in code formatting.import { Bot, format, bold, code } from "gramio";
import { findOrRegisterUser } from "./utils";
const bot = new Bot(process.env.BOT_TOKEN as string)
.derive("message", async () => {
const user = await findOrRegisterUser();
return {
user,
};
})
.on("message", (context) => {
context.user; // typed
return context.send(format`
Hi, ${bold(context.user.name)}!
You balance: ${code(context.user.balance)}`);
});You can initialize a bot by instantiating the Bot class with your Telegram Bot Token. GramIO provides a fluent API to handle commands and events.
Key methods used in this example:
command(name, callback): Registers a handler for a specific command.onStart(callback): Registers a listener for when the bot starts.start(): Starts the bot polling/listening for updates.import { Bot } from "gramio";
const bot = new Bot(process.env.TOKEN as string)
.command("start", (context) => context.send("Hello!"))
.onStart(({ info }) => console.log(`✨ Bot ${info.username} was started!`));
bot.start();When instantiating a new Bot, you can provide a BotOptions object to configure the token, API behavior, and file handling.
Key configuration areas:
token: The Telegram bot token.info: (Optional) Pre-provide TelegramUser metadata to avoid an initial getMe request. This is highly recommended for serverless or horizontally scaled environments.api: Configure baseURL (defaults to https://api.telegram.org/bot), useTest for the Telegram test environment, and fetchOptions.files: Configuration for local Bot API servers, including baseURL, localDir, and mountDir for handling file downloads and serving.const bot = new Bot(token, {
api: { baseURL: "http://telegram-bot-api:8081/bot" },
files: {
// bot.getFileLink() and ctx.download() resolve to this (token-less) URL
baseURL: "http://telegram-bot-api:8080",
},
});The syncCommands method accepts SyncCommandsOptions to control how bot commands are synchronized with the Telegram API.
storage: An optional SyncStorage implementation to cache sync hashes. This prevents unnecessary API calls by only syncing changed command groups.cleanUnusedScopes: If true, deletes commands for scopes not declared by any command.exclude: An array of command names to exclude from syncing (in addition to those marked with hide: true).