Шпаргалка
Быстрый справочник по самым распространённым паттернам GramIO. Нажмите на любой заголовок, чтобы перейти к полной документации.
На этой странице: Настройка · Команды · Hears · Любое обновление · Derive и Decorate · Guard · Инлайн клавиатура · Callback Query · Обычная клавиатура · Форматирование · Файлы · Session · Scenes · I18n · Обработка ошибок · Хуки · Плагины · Написать плагин · Inline Query · Autoload · Тестирование · Webhook
Настройка
npm create gramio my-botimport { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string);
bot.start();Команды
bot.command("start", (ctx) => ctx.send("Привет!"));
// С аргументами: /say привет мир → ctx.args = "привет мир"
bot.command("say", (ctx) => ctx.send(ctx.args ?? "ничего"));Hears
// Точная строка
bot.hears("привет", (ctx) => ctx.send("Привет!"));
// Regex — ctx.args содержит массив совпадений
bot.hears(/^hello (.+)/i, (ctx) => ctx.send(`Привет, ${ctx.args?.[1]}!`));
// Функция-предикат
bot.hears(
(ctx) => ctx.text?.startsWith("?"),
(ctx) => ctx.send("Вопрос!"),
);Прослушивание любых обновлений
bot.on("message", (ctx) => ctx.send("Получил твоё сообщение!"));
bot.on(["message", "edited_message"], (ctx) =>
ctx.send("Новое или отредактированное!"),
);Derive и Decorate
derive выполняется при каждом запросе; decorate — один раз при запуске.
// На каждый запрос: обогащает ctx свежими данными
const bot = new Bot(token)
.derive(async (ctx) => ({
user: await db.getUser(ctx.from?.id),
}))
.on("message", (ctx) => {
ctx.user; // полностью типизировано ✅
});// На тип обновления: только для конкретных событий
const bot = new Bot(token)
.derive("message", async (ctx) => ({
isAdmin: await db.isAdmin(ctx.from.id),
}))
.on("message", (ctx) => {
ctx.isAdmin; // ✅ — типизировано только в "message" обработчиках
});// При старте: без накладных расходов на каждый запрос
const bot = new Bot(token)
.decorate({ db, redis, config })
.on("message", (ctx) => {
ctx.db.query("..."); // ✅ — один и тот же экземпляр каждый раз
});Guard
Останавливает цепочку middleware, если условие не выполнено. Последующие обработчики выполняются только если guard пропускает.
// Переиспользуемый guard для администраторов
const adminOnly = bot.guard(
(ctx) => ctx.from?.id === ADMIN_ID,
// опциональный обработчик отклонения:
(ctx) => ctx.send("Только для администраторов."),
);
adminOnly.command("ban", (ctx) => ctx.send("Пользователь заблокирован."));// Сужение типа обновления — фильтр для сообщений с текстом
const textOnly = bot.guard((ctx) => ctx.is("message") && !!ctx.text);
textOnly.on("message", (ctx) => {
ctx.text; // string — сужен guard'ом ✅
});Инлайн клавиатура
import { InlineKeyboard, CallbackData } from "gramio";
// Простые текстовые кнопки
const keyboard = new InlineKeyboard()
.text("Да ✅", "yes")
.text("Нет ❌", "no")
.row()
.url("GitHub", "https://github.com/gramiojs/gramio");
ctx.send("Выберите:", { reply_markup: keyboard });// Типобезопасные callback данные
const actionData = new CallbackData("action").number("id");
ctx.send("Выберите:", {
reply_markup: new InlineKeyboard()
.text("Пункт 1", actionData.pack({ id: 1 }))
.text("Пункт 2", actionData.pack({ id: 2 })),
});Callback Query
// По строке
bot.callbackQuery("yes", (ctx) => ctx.editText("Вы сказали да!"));
// Типобезопасные CallbackData
const actionData = new CallbackData("action").number("id");
bot.callbackQuery(actionData, (ctx) => {
ctx.send(`Вы выбрали ID: ${ctx.queryData.id}`);
// ^? number
});Обычная клавиатура
import { Keyboard } from "gramio";
const keyboard = new Keyboard()
.text("Вариант А")
.text("Вариант Б")
.row()
.requestLocation("Поделиться геолокацией 📍")
.resized();
ctx.send("Выберите:", { reply_markup: keyboard });Удалить клавиатуру
import { RemoveKeyboard } from "gramio";
ctx.send("Клавиатура удалена.", { reply_markup: new RemoveKeyboard() });Форматирование сообщений
import { format, bold, italic, link, code, pre, spoiler } from "gramio";
ctx.send(format`
${bold`Привет!`} Добро пожаловать в ${link("GramIO", "https://gramio.dev")}.
Вот немного ${italic`стилизованного`} текста и ${spoiler`сюрприз`}.
${code("инлайн код")} или блок:
${pre("const x = 1;", "typescript")}
`);Отправка файлов
import { MediaUpload } from "@gramio/files";
// С диска
ctx.sendDocument(await MediaUpload.path("./report.pdf"));
// По URL
ctx.sendPhoto(await MediaUpload.url("https://example.com/cat.png"));
// Из Buffer
ctx.sendDocument(await MediaUpload.buffer(buffer, "file.pdf"));
// По file_id (уже загружен в Telegram)
ctx.sendPhoto("AgACAgIAAxk...");Session
import { session } from "@gramio/session";
const bot = new Bot(process.env.BOT_TOKEN as string).extend(
session({
key: "session",
initial: () => ({ count: 0 }),
}),
);
bot.command("count", (ctx) => {
ctx.session.count++;
// ^? { count: number }
ctx.send(`Счётчик: ${ctx.session.count}`);
});Scenes (диалоги)
import { Scene, scenes } from "@gramio/scenes";
import { session } from "@gramio/session";
const loginScene = new Scene("login")
.step("message", (ctx) => {
if (ctx.scene.step.firstTime) return ctx.send("Введите email:");
return ctx.scene.update({ email: ctx.text });
})
.step("message", (ctx) =>
ctx.send(`Зарегистрировано: ${ctx.scene.state.email}`),
);
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(session())
.extend(scenes([loginScene]));
bot.command("login", (ctx) => ctx.scene.enter(loginScene));I18n
import {
defineI18n,
type LanguageMap,
type ShouldFollowLanguage,
} from "@gramio/i18n";
import { format, bold } from "gramio";
const en = {
welcome: (name: string) => format`Hello, ${bold(name)}!`,
items: (count: number) => `You have ${count} item${count === 1 ? "" : "s"}`,
} satisfies LanguageMap;
const ru = {
welcome: (name: string) => format`Привет, ${bold(name)}!`,
items: (count: number) =>
`У вас ${count} предмет${count === 1 ? "" : "ов"}`,
} satisfies ShouldFollowLanguage<typeof en>; // должен совпадать с ключами/сигнатурами en
const i18n = defineI18n({ primaryLanguage: "en", languages: { en, ru } });
const bot = new Bot(token).derive((ctx) => ({
t: i18n.buildT(ctx.from?.language_code ?? "en"),
}));
bot.command("start", (ctx) =>
ctx.send(ctx.t("welcome", ctx.from?.firstName ?? "незнакомец")),
);Обработка ошибок
// Перехват всех ошибок
bot.onError(({ context, kind, error }) => {
console.error(kind, error.message);
if (context.is("message")) context.send("Что-то пошло не так.");
});
// Только для определённых типов обновлений
bot.onError("message", ({ context, kind, error }) => {
context.send(`${kind}: ${error.message}`);
});
// Кастомные типизированные ошибки
class NoRights extends Error {
constructor(public role: "admin" | "moderator") {
super();
}
}
const bot = new Bot(process.env.BOT_TOKEN as string)
.error("NO_RIGHTS", NoRights)
.onError(({ kind, error, context }) => {
if (kind === "NO_RIGHTS" && context.is("message"))
context.send(`Вам нужна роль «${error.role}».`);
});Хуки
bot.onStart((info) => console.log(`@${info.username} запущен!`));
bot.onStop(() => console.log("Завершаю работу..."));
// Перехват каждого API запроса
bot.preRequest((ctx) => {
console.log("Вызываю", ctx.method);
return ctx;
});
// Просмотр каждого ответа
bot.onResponse((ctx) => {
console.log(ctx.method, "→", ctx.response);
return ctx;
});Использование плагина
import { autoAnswerCallbackQuery } from "@gramio/auto-answer-callback-query";
bot.extend(autoAnswerCallbackQuery());Написать плагин
import { Plugin } from "gramio";
const myPlugin = new Plugin("my-plugin").derive("message", (ctx) => ({
isAdmin: ctx.from?.id === 123456789,
}));
bot.extend(myPlugin);
bot.on("message", (ctx) => {
if (ctx.isAdmin) ctx.send("Привет, босс!");
// ^? boolean
});Inline Query
bot.inlineQuery("cats", async (ctx) => {
await ctx.answer(
[
ctx.buildInlineQueryResult.article({
id: "1",
title: "Факт о кошках",
input_message_content: {
message_text: "Кошки мурлычут на частоте 25 Гц.",
},
}),
],
{ cache_time: 30 },
);
});Autoload обработчиков
import { autoload } from "@gramio/autoload";
// Автоматически загружает все файлы из ./src/commands/**/*.ts
const bot = new Bot(process.env.BOT_TOKEN as string).extend(autoload());// src/commands/start.ts
import type { Bot } from "gramio";
export default (bot: Bot) => bot.command("start", (ctx) => ctx.send("Привет!"));Тестирование
import { describe, expect, it } from "bun:test";
import { Bot } from "gramio";
import { TelegramTestEnvironment } from "@gramio/test";
const bot = new Bot("test");
bot.command("start", (ctx) => ctx.send("Привет!"));
const env = new TelegramTestEnvironment(bot);
const user = env.createUser({ first_name: "Алиса" });
// Симулируем команду /start
await user.sendMessage("/start");
// Проверяем ответ
expect(env.apiCalls[0].method).toBe("sendMessage");
expect(env.apiCalls[0].params.text).toBe("Привет!");Webhook
В GramIO нет встроенного HTTP-сервера — подключите свой фреймворк и используйте webhookHandler:
import { Bot, webhookHandler } from "gramio";
import Fastify from "fastify";
const bot = new Bot(process.env.BOT_TOKEN as string);
const fastify = Fastify();
fastify.post("/webhook", webhookHandler(bot, "fastify"));
fastify.listen({ port: 3000, host: "::" });
bot.start({
webhook: { url: "https://example.com/webhook" },
});Все поддерживаемые фреймворки → (Hono, Express, Elysia, Koa, Bun.serve, Deno.serve, node:http)