[](https://www.npmjs.org/package/@gramio/prompt)
[](https://jsr.io/@gramio/prompt)
[](https://jsr.io/@gramio/prompt)
Плагин, который предоставляет методы [Prompt](#prompt) и [Wait](#wait)
### Установка
::: code-group
```bash [npm]
npm install @gramio/prompt
```
```bash [yarn]
yarn add @gramio/prompt
```
```bash [pnpm]
pnpm add @gramio/prompt
```
```bash [bun]
bun install @gramio/prompt
```
:::
## Использование
```ts
import { Bot, format, bold } from "gramio";
import { prompt } from "@gramio/prompt";
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(prompt())
.command("start", async (context) => {
const answer = await context.prompt(
"message",
format`Как вас ${bold`зовут`}?`
);
return context.send(`✨ Ваше имя: ${answer.text}`);
})
.onStart(console.log);
bot.start();
```
## Prompt
### Prompt с текстом + параметрами
```ts
const answer = await context.prompt("Как вас зовут?");
// или с SendMessageParams
const answer = await context.prompt("Правда или ложь?", {
reply_markup: new Keyboard().text("правда").row().text("ложь"),
});
```
ответ - это объект `MessageContext` или `CallbackQueryContext`
### Prompt с текстом + параметрами и указанным событием
```ts
const answer = await context.prompt("message", "Как вас зовут?");
const answer = await context.prompt("callback_query", "Правда или ложь?", {
reply_markup: new InlineKeyboard()
.text("правда", "true")
.row()
.text("ложь", "false"),
});
```
ответ - это объект `CallbackQueryContext`
### Валидация
Можно указать обработчик в параметрах для проверки ответа пользователя.
Если обработчик вернёт false, сообщение будет отправлено повторно.
```ts
const answer = await context.prompt(
"message",
"Введите строку, содержащую русскую букву",
{
validate: (context) => /[а-яА-Я]/.test(context.text),
//... и другие SendMessageParams
}
);
```
### Трансформация
```ts
const name = await context.prompt(
"message",
format`Как вас ${bold`зовут`}?`,
{
transform: (context) => context.text || context.caption || "",
}
);
```
name имеет тип `string`
## Wait
### Ожидание следующего события от пользователя
```ts
const answer = await context.wait();
```
ответ - это объект `MessageContext` или `CallbackQueryContext`
### Ожидание события от пользователя с игнорированием других типов событий
```ts
const answer = await context.wait("message");
```
ответ - это объект `CallbackQueryContext`
### Ожидание события с отфильтрованными ответами
Можно указать обработчик в параметрах для проверки ответа пользователя.
Если обработчик вернёт `false`, **сообщение** будет проигнорировано
```ts
const answer = await context.wait((context) => /[а-яА-Я]/.test(context.text));
// или в сочетании с событием
const answer = await context.wait("message", (context) =>
/[а-яА-Я]/.test(context.text)
);
```
### Ожидание события с проверкой и трансформацией ответа
Можно указать обработчик в параметрах для **трансформации** ответа пользователя.
```ts
const answer = await context.wait((context) => /[а-яА-Я]/.test(context.text));
// или в сочетании с событием
const answer = await context.wait("message", {
validate: (context) => /[а-яА-Я]/.test(context.text),
transform: (context) => c.text || "",
});
```
ответ имеет тип `string`
---
---
url: /ru/plugins/official/autoload.md
---
# Плагин автозагрузки
[](https://www.npmjs.org/package/@gramio/autoload)
[](https://jsr.io/@gramio/autoload)
[](https://jsr.io/@gramio/autoload)
Плагин автозагрузки команд для GramIO с поддержкой [`Bun.build`](#использование-bun-build).
### Установка
::: code-group
```bash [npm]
npm install @gramio/autoload
```
```bash [yarn]
yarn add @gramio/autoload
```
```bash [pnpm]
pnpm add @gramio/autoload
```
```bash [bun]
bun install @gramio/autoload
```
:::
## Использование
> [полный пример](https://github.com/gramiojs/autoload/tree/main/example)
> [!IMPORTANT]
> Пожалуйста, прочитайте о [Ленивой загрузке плагинов](https://gramio.dev/ru/plugins/official/autoload.html)
## Регистрация плагина
```ts twoslash
// index.ts
import { Bot } from "gramio";
import { autoload } from "@gramio/autoload";
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(await autoload())
.onStart(console.log);
bot.start();
export type BotType = typeof bot;
```
## Создание команды
```ts
// commands/command.ts
import type { BotType } from "..";
export default (bot: BotType) =>
bot.command("start", (context) => context.send("привет!"));
```
## Опции
| Ключ | Тип | По умолчанию | Описание |
| ----------------- | -------------------------------------------------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------ |
| pattern? | string \| string[] | "\*\*\/\*.{ts,js,cjs,mjs}" | [Шаблоны Glob]() |
| path? | string | "./commands" | Путь к папке |
| import? | string \| (file: any) => string | "default" | Импорт конкретного `export` из файла |
| failGlob? | boolean | true | Бросать ошибку, если не найдены совпадения |
| skipImportErrors? | boolean | false | Пропускать импорты, где нужный `export` не определён |
| onLoad? | (params: { absolute: string; relative: string }) => unknown | | Хук, вызываемый при загрузке файла |
| onFinish? | (paths: { absolute: string; relative: string }[]) => unknown; | | Хук, вызываемый после загрузки всех файлов |
| fdir? | [Options](https://github.com/thecodrr/fdir/blob/HEAD/documentation.md#method-chaining-alternative) | | Настройки для [fdir](https://github.com/thecodrr/fdir) |
| picomatch? | [PicomatchOptions](https://github.com/micromatch/picomatch?tab=readme-ov-file#picomatch-options) | | Настройки для [picomatch](https://www.npmjs.com/package/picomatch) |
### Использование [Bun build](https://bun.sh/docs/bundler)
Вы можете использовать этот плагин с [`Bun.build`](https://bun.sh/docs/bundler) благодаря [esbuild-plugin-autoload](https://github.com/kravetsone/esbuild-plugin-autoload)!
```ts
// @filename: build.ts
import { autoload } from "esbuild-plugin-autoload"; // также поддерживается импорт по умолчанию
await Bun.build({
entrypoints: ["src/index.ts"],
target: "bun",
outdir: "out",
plugins: [autoload("./src/commands")],
}).then(console.log);
```
Затем соберите с помощью `bun build.ts` и запустите с помощью `bun out/index.ts`.
### Использование [Bun compile](https://bun.sh/docs/bundler/executables)
Вы можете собрать, а затем скомпилировать в [единый исполняемый бинарный файл](https://bun.sh/docs/bundler/executables)
```ts
import { autoload } from "esbuild-plugin-autoload"; // также поддерживается импорт по умолчанию
await Bun.build({
entrypoints: ["src/index.ts"],
target: "bun",
outdir: "out",
plugins: [autoload("./src/commands")],
}).then(console.log);
await Bun.$`bun build --compile out/index.js`;
```
> [!WARNING]
> Вы не можете использовать это в режиме `bun build --compile` без дополнительного шага ([Issue с функционалом](https://github.com/oven-sh/bun/issues/11895))
[Узнать больше](https://github.com/kravetsone/esbuild-plugin-autoload)
---
---
url: /ru/plugins/official/auto-answer-callback-query.md
---
# Плагин автоматического ответа на запросы обратного вызова
[](https://www.npmjs.org/package/@gramio/auto-answer-callback-query)
[](https://jsr.io/@gramio/auto-answer-callback-query)
[](https://jsr.io/@gramio/auto-answer-callback-query)
Этот плагин автоматически отвечает на события `callback_query` методом `answerCallbackQuery`, если вы этого еще не сделали.
### Установка
::: code-group
```bash [npm]
npm install @gramio/auto-answer-callback-query
```
```bash [yarn]
yarn add @gramio/auto-answer-callback-query
```
```bash [pnpm]
pnpm add @gramio/auto-answer-callback-query
```
```bash [bun]
bun install @gramio/auto-answer-callback-query
```
:::
```ts
import { Bot, InlineKeyboard } from "gramio";
import { autoAnswerCallbackQuery } from "@gramio/auto-answer-callback-query";
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(autoAnswerCallbackQuery())
.command("start", (context) =>
context.send("Привет!", {
reply_markup: new InlineKeyboard()
.text("тест", "test")
.text("тест2", "test2"),
})
)
.callbackQuery("test", () => {
// Плагин вызовет метод answerCallbackQuery, так как вы его не вызвали
return context.send("Привет");
})
.callbackQuery("test2", (context) => {
// вы уже ответили, поэтому плагин не будет пытаться ответить
return context.answer("ПРИВЕТ");
});
```
### Параметры
Вы можете передать параметры для метода [answerCallbackQuery](https://core.telegram.org/bots/api#answercallbackquery)
```ts
bot.extend(
autoAnswerCallbackQuery({
text: "Автоматический ответ",
show_alert: true,
})
);
```
> [!IMPORTANT]
> Этот плагин перехватывает методы `context.answerCallbackQuery` (и `context.answer`), чтобы понять, был ли уже отправлен ответ на запрос обратного вызова. Старайтесь не использовать напрямую метод `bot.api.answerCallbackQuery` в контексте - это может помешать корректной работе плагина.
---
---
url: /ru/plugins/official/auto-retry.md
---
# Плагин автоматического повтора
[](https://www.npmjs.org/package/@gramio/auto-retry)
[](https://jsr.io/@gramio/auto-retry)
[](https://jsr.io/@gramio/auto-retry)
Плагин, который ловит ошибки с полем `retry_after` (**ошибки превышения лимита запросов**), **ждёт** указанное время и **повторяет** API-запрос.
### Установка
::: code-group
```bash [npm]
npm install @gramio/auto-retry
```
```bash [yarn]
yarn add @gramio/auto-retry
```
```bash [pnpm]
pnpm add @gramio/auto-retry
```
```bash [bun]
bun install @gramio/auto-retry
```
:::
### Использование
```ts
import { Bot } from "gramio";
import { autoRetry } from "@gramio/auto-retry";
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(autoRetry())
.command("start", async (context) => {
for (let index = 0; index < 100; index++) {
await context.reply(`сообщение ${index}`);
}
})
.onStart(console.log);
bot.start();
```
---
---
url: /ru/plugins/official/media-cache.md
---
# Плагин кэширования медиа
[](https://www.npmjs.org/package/@gramio/media-cache)
[](https://jsr.io/@gramio/media-cache)
[](https://jsr.io/@gramio/media-cache)
Плагин `Media cache` для [GramIO](https://gramio.dev/).
Этот плагин кэширует отправленные `file_id` и предотвращает повторную загрузку файлов.
На данный момент **sendMediaGroup** не кэшируется.
## Использование
```ts
import { Bot } from "gramio";
import { mediaCache } from "@gramio/media-cache";
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(mediaCache())
.command("start", async (context) => {
return context.sendDocument(
await MediaUpload.url(
"https://raw.githubusercontent.com/gramiojs/types/main/README.md"
)
);
})
.onStart(console.log);
bot.start();
```
---
---
url: /ru/plugins/official/media-group.md
---
# Плагин медиа-групп
[](https://www.npmjs.org/package/@gramio/media-group)
[](https://jsr.io/@gramio/media-group)
[](https://jsr.io/@gramio/media-group)
Этот плагин собирает `mediaGroup` из сообщений (**1** вложение = **1** сообщение), используя **задержку**, если `mediaGroupId` присутствует в **MessageContext**. Далее плагин передает только **первое** сообщение по цепочке обработчиков с ключом `mediaGroup`, содержащим **массив** всех сообщений этой **mediaGroup** (включая **первое** сообщение).
```ts
import { Bot } from "gramio";
import { mediaGroup } from "@gramio/media-group";
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(mediaGroup())
.on("message", async (context) => {
if (!context.mediaGroup) return;
return context.send(
`Подпись из первого сообщения - ${context.caption}. Медиа-группа содержит ${context.mediaGroup.length} вложений`
);
})
.onStart(({ info }) => console.log(`✨ Бот ${info.username} запущен!`));
bot.start();
```
### Установка
::: code-group
```bash [npm]
npm install @gramio/media-group
```
```bash [yarn]
yarn add @gramio/media-group
```
```bash [pnpm]
pnpm add @gramio/media-group
```
```bash [bun]
bun install @gramio/media-group
```
:::
### Настройка
Вы можете изменить время задержки в миллисекундах, просто указав его при вызове:
```typescript
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(mediaGroup(1000)) // ждём 1 секунду сообщений с mediaGroupId (обновляется с каждым новым сообщением)
.on("message", async (context) => {
if (!context.mediaGroup) return;
return context.send(
`Подпись из первого сообщения - ${context.caption}. Медиа-группа содержит ${context.mediaGroup.length} вложений`
);
})
.onStart(({ info }) => console.log(`✨ Бот ${info.username} запущен!`));
bot.start();
```
По умолчанию это `150 мс`.
---
---
url: /ru/plugins/official/session.md
---
# Плагин сессий
[](https://www.npmjs.org/package/@gramio/session)
[](https://jsr.io/@gramio/session)
[](https://jsr.io/@gramio/session)
Плагин сессий для GramIO.
**Пока не оптимизирован и находится в разработке.**
### Установка
::: code-group
```bash [npm]
npm install @gramio/session
```
```bash [yarn]
yarn add @gramio/session
```
```bash [pnpm]
pnpm add @gramio/session
```
```bash [bun]
bun install @gramio/session
```
:::
## Использование
```ts twoslash
import { Bot } from "gramio";
import { session } from "@gramio/session";
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(
session({
key: "sessionKey",
initial: () => ({ apple: 1 }),
})
)
.on("message", (context) => {
context.send(`🍏 количество яблок: ${++context.sessionKey.apple}`);
// ^?
})
.onStart(console.log);
bot.start();
```
Этот плагин можно использовать с любым хранилищем ([Подробнее](/ru/storages/index))
### Пример с Redis
[Больше информации](https://github.com/gramiojs/storages/tree/master/packages/redis)
```ts
import { Bot } from "gramio";
import { session } from "@gramio/session";
import { redisStorage } from "@gramio/storage-redis";
const bot = new Bot(process.env.BOT_TOKEN as string)
.extend(
session({
key: "sessionKey",
initial: () => ({ apple: 1 }),
storage: redisStorage(),
})
)
.on("message", (context) => {
context.send(`🍏 количество яблок: ${++context.sessionKey.apple}`);
})
.onStart(console.log);
bot.start();
```
### TypeScript
Чтобы **типизировать** данные сессии, укажите тип как `ReturnType` начальной функции.
```ts
interface MySessionData {
apple: number;
some?: "maybe-empty";
}
bot.extend(
session({
key: "sessionKey",
initial: (): MySessionData => ({ apple: 1 }),
})
);
```
---
---
url: /ru/plugins/official/scenes.md
---
# @gramio/scenes
[](https://www.npmjs.org/package/@gramio/scenes)
[](https://jsr.io/@gramio/scenes)
[](https://jsr.io/@gramio/scenes)
API может немного измениться, но мы уже активно используем его на боевых проектах.
# Использование
```ts twoslash
import { Bot } from "gramio";
import { scenes, Scene } from "@gramio/scenes";
export const greetingScene = new Scene("greeting")
.params<{ test: boolean }>()
.step("message", (context) => {
if (context.scene.step.firstTime)
return context.send("Привет! Как тебя зовут?");
if (!context.text) return context.send("Пожалуйста, напиши своё имя");
return context.scene.update({
name: context.text,
});
})
.step("message", (context) => {
if (context.scene.step.firstTime)
return context.send("Сколько тебе лет?");
const age = Number(context.text);
if (!age || Number.isNaN(age) || age < 0)
return context.send("Пожалуйста, укажи возраст корректно");
return context.scene.update({
age,
});
})
.step("message", async (context) => {
await context.send(
`Рад познакомиться! Теперь я знаю, что тебя зовут ${
context.scene.state.name
} и тебе ${context.scene.state.age} лет. ${
context.scene.params.test
? "Также у тебя есть параметр test!"
: ""
}`
);
return context.scene.exit();
});
const bot = new Bot(process.env.TOKEN as string)
.extend(scenes([greetingScene]))
.command("start", async (context) => {
return context.scene.enter(greetingScene, {
test: true,
});
});
```
> [!WARNING]
> Будьте внимательны. Первый шаг сцены должен так же включать в себя событие из которого вы вошли в сцену. (например если по нажатию InlineButton - `callback_query`)
### Общее состояние между шагами
```ts twoslash
import { Scene } from "@gramio/scenes";
const testScene = new Scene("test")
.step("message", async (context) => {
if (context.scene.step.firstTime || context.text !== "1")
return context.send("1");
return context.scene.update({
messageId: context.id,
some: "привет!" as const,
});
})
.step("message", async (context) => {
if (context.scene.step.firstTime || context.text !== "2")
return context.send("2");
console.log(context.scene.state.messageId);
});
```
## Использование хранилища
```ts
import { redisStorage } from "@gramio/storage-redis";
const bot = new Bot(process.env.TOKEN as string)
.extend(
scenes([testScene], {
storage: redisStorage(),
})
)
.command("start", async (context) => {
return context.scene.enter(someScene, {
test: true,
});
});
```
[Подробнее о хранилищах](/ru/storages/)
### step
Это функция — шаг сцены. Он выполняется только когда текущий id шага сцены совпадает с порядком зарегистрированного шага.
```ts
const testScene = new Scene("test")
// Для одного события
.step("message", async (context) => {
if (context.scene.step.firstTime)
return context.send("Первое сообщение");
return context.scene.exit();
})
// Для конкретных событий
.step(["message", "callback_query"], async (context) => {
if (context.scene.step.firstTime)
return context.send(
"Второе сообщение после сообщения пользователя"
);
if (context.is("callback_query"))
return context.answer("Вы нажали на кнопку");
return context.scene.exit();
})
// Для всех событий
.step((context) => {
console.log(context);
return context.scene.exit();
});
```
> [!NOTE]
> Если пользователь создаёт событие, которое не зарегистрировано в шаге, оно будет проигнорировано этим шагом (но зарегистрированные для него обработчики событий будут вызваны).
### on
Этот метод позволяет зарегистрировать обработчик событий для сцены.
```ts
const guessRandomNumberScene = new Scene("guess-random-number")
.params<{ randomNumber: number }>()
.on("message", async (context, next) => {
// Это условие нужно, чтобы обработчик не срабатывал при firstTime так как context будет одинаковым с предыдущим шагом
if (context.scene.step.firstTime) return next();
return await Promise.all([context.delete(), next()]);
})
.step(["message", "callback_query"], async (context) => {
if (context.scene.step.firstTime)
return context.send("Попробуй угадать число от 1 до 10");
if (!context.is("message"))
return context.answer("Пожалуйста, отправьте число сообщением");
const number = Number(context.text);
if (
Number.isNaN(number) ||
number !== context.scene.params.randomNumber
)
return; // Обработчик выше удалит отправленное пользователем сообщение
return Promise.all([
context.send(
format(
`Поздравляю! Ты угадал число ${bold(
context.scene.params.randomNumber
)}!`
)
),
context.scene.exit(),
]);
});
```
Обратите внимание: обработчик применяется только ко всем шагам, объявленным после него (или к следующим .on), а не к предыдущим.
```ts
new Scene("test")
.on(...) // Вызывается для всех шагов
.step(...)
.on(...) // Вызывается только после достижения второго шага
.step(...)
```
## Контекст сцены
### update
`update` - это функция, которая обновляет данные в сцене и при этом сохраняет их типы.
```ts twoslash
import { Scene } from "@gramio/scenes";
const testScene = new Scene("test")
.step("message", async (context) => {
if (context.scene.step.firstTime)
return context.send("Первое сообщение");
if (!context.text) return context.delete();
return context.scene.update({
message: context.text,
});
})
.step("message", async (context) => {
return context.send(context.scene.state.message);
// ^?
});
```
### state
`state` - это объект, который содержит все данные, которые были собраны в этой сцене.
(смотрите пример выше)
### params
`params` - это объект, который содержит все данные, которые были переданы в сцену при её входе.
```ts twoslash
import { Bot } from "gramio";
import { scenes, Scene } from "@gramio/scenes";
const testScene = new Scene("test")
// тут мы указываем тип параметров сцены
.params<{ test: boolean }>()
.step("message", async (context) => {
return context.send(context.scene.params.test);
// ^?
});
const bot = new Bot(process.env.TOKEN as string)
.extend(scenes([testScene]))
.command("start", async (context) => {
return context.scene.enter(testScene, {
test: true,
});
});
```
### reenter
`reenter` - это функция, которая позволяет перезайти в сцену в первый шаг потеряв [state](#state).
```ts
const testScene = new Scene("test")
.on("message", async (context, next) => {
if (context.text === "/start") return context.scene.reenter();
return next();
})
.step("message", async (context) => {
if (context.scene.step.firstTime) return context.send("Привет!");
return context.send("Пока!");
});
```
### Контекст шага
Тут находится вся информация о текущем шаге сцены.
Он хранится в `context.scene.step`.
#### firstTime
`firstTime` - это флаг, который указывает, является ли текущее выполнение шага первым.
```ts
const testScene = new Scene("test").step("message", async (context) => {
if (context.scene.step.firstTime) return context.send("Первое сообщение");
if (context.text !== "следующее")
return context.send(
"Последующие сообщения до того как напишут «следующее»"
);
return Promise.all([
context.send("Последнее сообщение"),
context.scene.exit(),
]);
});
```
#### next
`next` - это функция, которая передаёт управление следующему шагу сцены.
```ts
const testScene = new Scene("test").step("message", async (context) => {
if (context.scene.step.firstTime) return context.send("Первое сообщение");
return context.scene.next();
});
```
#### previous
`previous` - это функция, которая передаёт управление предыдущему шагу сцены.
```ts
const testScene = new Scene("test").step("message", async (context) => {
if (context.scene.step.firstTime) return context.send("Первое сообщение");
return context.scene.previous();
});
```
#### go
`go` - это функция, которая передаёт управление конкретному шагу сцены.
```ts
const testScene = new Scene("test").step("message", async (context) => {
if (context.scene.step.firstTime) return context.send("Первое сообщение");
return context.scene.go(5);
});
```
#### id
`id` - это идентификатор шага сцены.
```ts
const testScene = new Scene("test").step("message", async (context) => {
if (context.scene.step.firstTime)
return context.send(`Шаг ${context.scene.step.id}`);
return context.scene.exit();
});
```
#### previousId
`previousId` - это идентификатор предыдущего шага сцены.
Сохраняется id последнего шага при вызове [#go](#go), [#previous](#previous), [#next](#next).
```ts
const testScene = new Scene("test")
.step("message", async (context) => {
if (context.scene.step.firstTime)
return context.send(
`из шага ${context.scene.step.previousId} в шаг ${context.scene.step.id}`
);
return context.scene.exit();
})
.step("message", async (context) => {
return context.scene.step.go(1);
});
```
## scenesDerives
Иногда нужно управлять сценами до выполнения шагов сцены плагином, но после получения сцены из хранилища.
По умолчанию функция `scenes()` передаёт необходимые данные последующим обработчикам, если пользователь не находится в сцене.
С помощью `scenesDerives()` вы можете получить эти данные раньше и управлять ими.
```ts twoslash
import { Scene } from "@gramio/scenes";
const testScene = new Scene("test").state<{
simple: string;
example: number[];
}>();
// ---cut---
import { scenes, scenesDerives, type AnyScene } from "@gramio/scenes";
import { Bot } from "gramio";
import { redisStorage } from "@gramio/storage-redis";
const storage = redisStorage();
const scenesList: AnyScene[] = [testScene];
const bot = new Bot(process.env.TOKEN as string)
.extend(
scenesDerives(scenesList, {
withCurrentScene: true,
storage,
})
)
.on("message", (context, next) => {
if (context.text === "/start" && context.scene.current) {
if (context.scene.current.is(testScene)) {
console.log(context.scene.current.state);
return context.scene.current.step.previous();
} else return context.scene.current.reenter();
}
return next();
})
.extend(
scenes(scenesList, {
storage,
})
)
.command("start", async (context) => {
return context.scene.enter(testScene, {
test: true,
});
});
```
> [!IMPORTANT]
> Одно и то же **хранилище** и **список сцен** нужно использовать и в `scenes()`, и в опциях `scenesDerives()`.
По умолчанию при регистрации плагина `scenes()` используется `inMemoryStorage`. Поэтому если вам нужно использовать `scenesDerives()` для управления сценами, необходимо явно объявить `inMemoryStorage` и явно указать его в опциях как для `scenesDerives()`, так и для `scenes()`.
```ts
import { inMemoryStorage } from "@gramio/storage";
const storage = inMemoryStorage(); // Хранит в памяти процесса и будет стёрто при перезапуске
const bot = new Bot(process.env.TOKEN as string)
.extend(scenes([testScene], { storage }))
// ...
.extend(scenesDerives([testScene], { storage }));
```
---
---
url: /ru/plugins/index.md
---
# Обзор
Пожалуйста, ознакомьтесь с нашими официальными плагинами:
- [Сцены](/ru/plugins/official/scenes)
- [I18n](/ru/plugins/official/i18n)
- [Автозагрузка](/ru/plugins/official/autoload)
- [Автоответ на callback запросы](/ru/plugins/official/auto-answer-callback-query)
- [Сессия](/ru/plugins/official/session)
- [Авто-повтор](/ru/plugins/official/auto-retry)
- [Кеш медиа](/ru/plugins/official/media-cache)
- [Медиа группы](/ru/plugins/official/media-group)
- [Prompt](/ru/plugins/official/prompt)
---
---
url: /ru/keyboards/inline-keyboard.md
---
# Inline Keyboard
Inline Keyboard прикрепляется к сообщению. Представляет собой [встроенную клавиатуру](https://core.telegram.org/bots/features#inline-keyboards), которая появляется рядом с сообщением, к которому она принадлежит.
Смотрите также [API Reference](https://jsr.io/@gramio/keyboards/doc/~/InlineKeyboard) и [как отвечать на нажатия](/ru/triggers/callback-query).
## Импорт
### С GramIO
```ts twoslash
import { InlineKeyboard } from "gramio";
```
### Без GramIO
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
```
## Кнопки ([Документация](https://core.telegram.org/bots/api/#inlinekeyboardbutton))
Кнопки - это методы, которые собирают inline клавиатуру для вас.
### text
Текстовая кнопка с данными, которые будут отправлены в [callback query](https://core.telegram.org/bots/api/#callbackquery) боту при нажатии кнопки, 1-64 байта.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().text("какой-то текст", "payload");
// или
new InlineKeyboard().text("какой-то текст", {
json: "payload",
}); // использует JSON.stringify
```
### url
HTTP или tg:// URL, который будет открыт при нажатии на кнопку. Ссылки `tg://user?id=` можно использовать для упоминания пользователя по их идентификатору без использования имени пользователя, если это разрешено их настройками конфиденциальности.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().url("GitHub", "https://github.com/gramiojs/gramio");
```
### webApp
Описание [Веб-приложения](https://core.telegram.org/bots/webapps), которое будет запущено, когда пользователь нажмет на кнопку. Веб-приложение сможет отправить произвольное сообщение от имени пользователя, используя метод [answerWebAppQuery](https://core.telegram.org/bots/api/#answerwebappquery). Доступно только в приватных чатах между пользователем и ботом.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().webApp("какой-то текст", "https://...");
```
### copy
Тип кнопки, которая копирует указанный текст в буфер обмена.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().copy(
"Скопируй меня",
"Добро пожаловать в буфер обмена Gboard, любой скопированный вами текст будет сохранен здесь. Нажмите на клип для вставки его в текстовое поле. Используйте значок редактирования, чтобы закрепить, добавить или удалить клипы. Нажмите и удерживайте клип, чтобы закрепить его. Незакрепленные клипы будут удалены через 1 час."
);
```
### login
Эта кнопка inline-клавиатуры используется для автоматической авторизации пользователя. Служит отличной заменой [Telegram Login Widget](https://core.telegram.org/widgets/login), когда пользователь приходит из Telegram. Все, что нужно пользователю, — это нажать на кнопку и подтвердить, что он хочет войти в систему:
Приложения Telegram поддерживают эти кнопки начиная с [версии 5.7](https://telegram.org/blog/privacy-discussions-web-bots#meet-seamless-web-bots).
Пример бота: [@discussbot](https://t.me/discussbot)
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().login("какой-то текст", "https://...");
// или
new InlineKeyboard().login("какой-то текст", {
url: "https://...",
request_write_access: true,
});
```
Подробнее о параметрах в [документации](https://core.telegram.org/bots/api/#loginurl)
### pay
Отправляет [Кнопку оплаты](https://core.telegram.org/bots/api/#payments).
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().pay("5 монет");
```
> [!WARNING]
> Этот тип кнопки **всегда** должен быть первой кнопкой в первом ряду и может использоваться только в сообщениях со счетом.
### switchToChat
Нажатие на кнопку предложит пользователю выбрать один из своих чатов, открыть этот чат и вставить имя пользователя бота и указанный inline-запрос в поле ввода.
По умолчанию пустой, в этом случае будет вставлено только имя пользователя бота.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().switchToChat("Выберите чат");
// или
new InlineKeyboard().switchToChat("Выберите чат", "InlineQuery");
```
### switchToChosenChat
Нажатие на кнопку предложит пользователю выбрать один из своих чатов указанного типа, открыть этот чат и вставить имя пользователя бота и указанный inline-запрос в поле ввода.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().switchToChosenChat("Выберите чат");
// или
new InlineKeyboard().switchToChosenChat("Выберите чат", "InlineQuery");
// или
new InlineKeyboard().switchToChosenChat("Выберите чат", {
query: "InlineQuery",
allow_channel_chats: true,
allow_group_chats: true,
allow_bot_chats: true,
allow_user_chats: true,
});
```
Подробнее о параметрах в [документации](https://core.telegram.org/bots/api/#switchinlinequerychosenchat)
### switchToCurrentChat
Нажатие на кнопку вставит имя пользователя бота и указанный inline-запрос в поле ввода текущего чата. Может быть пустым, в этом случае будет вставлено только имя пользователя бота.
Это предлагает пользователю быстрый способ открыть вашего бота в режиме инлайн в том же чате - отлично подходит для выбора чего-либо из нескольких вариантов.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().switchToChosenChat("Открыть инлайн режим");
// или
new InlineKeyboard().switchToChosenChat("Открыть инлайн режим", "InlineQuery");
```
### game
Описание игры, которая будет запущена, когда пользователь нажмет на кнопку.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard().game("текст", {}); // ??? нет параметров...
```
> [!WARNING]
> Этот тип кнопки **всегда** должен быть первой кнопкой в первом ряду.
## Помощники
Методы, которые помогают вам создать клавиатуру.
### row
Добавляет `разрыв строки`. Вызовите этот метод, чтобы следующие добавленные кнопки были на новой строке.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard()
.text("первая строка", "payload")
.row()
.text("вторая строка", "payload");
```
### columns
Позволяет ограничить количество столбцов в клавиатуре.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard()
.columns(1)
.text("первая строка", "payload")
.text("вторая строка", "payload")
.text("третья строка", "payload");
```
### wrap
Кастомный обработчик, который управляет переносом строк.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard()
.wrap(({ button }) => button.callback_data === "2")
.text("первая строка", "1")
.text("первая строка", "1")
.text("вторая строка", "2");
```
обработчик имеет вид
```ts
(options: { button: T; index: number; row: T[]; rowIndex: number }) => boolean;
```
### pattern
Массив с количеством столбцов в каждой строке. Позволяет установить «шаблон».
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard()
.pattern([1, 3, 2])
.text("1", "payload")
.text("2", "payload")
.text("2", "payload")
.text("2", "payload")
.text("3", "payload")
.text("3", "payload");
```
### filter
Обработчик, который помогает фильтровать кнопки клавиатуры.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard()
.filter(({ button }) => button.callback_data !== "hidden")
.text("кнопка", "pass")
.text("кнопка", "hidden")
.text("кнопка", "pass");
```
### add
Позволяет добавить несколько кнопок в _сыром_ формате.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
const labels = ["какие-то", "кнопки"];
new InlineKeyboard()
.add({ text: "сырая кнопка", callback_data: "payload" })
.add(
InlineKeyboard.text("сырая кнопка через InlineKeyboard.text", "payload")
)
.add(...labels.map((x) => InlineKeyboard.text(x, `${x}payload`)));
```
### addIf
Позволяет динамически подставлять кнопки в зависимости от чего-либо.
```ts twoslash
// @noErrors
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
const labels = ["какие-то", "кнопки"];
const isAdmin = true;
new InlineKeyboard()
.addIf(1 === 2, { text: "сырая кнопка", callback_data: "payload" })
.addIf(
isAdmin,
InlineKeyboard.text("сырая кнопка через InlineKeyboard.text", "payload")
)
.addIf(
({ index, rowIndex }) => rowIndex === index,
...labels.map((x) => InlineKeyboard.text(x, `${x}payload`))
);
```
обработчик имеет вид
```ts
(options: { rowIndex: number; index: number }) => boolean;
```
### matrix
Позволяет создать матрицу кнопок.
```ts twoslash
// @noErrors
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
import { randomInt } from "node:crypto";
const bomb = [randomInt(0, 9), randomInt(0, 9)] as const;
new InlineKeyboard().matrix(10, 10, ({ rowIndex, index }) =>
InlineKeyboard.text(
rowIndex === bomb[0] && index === bomb[1] ? "💣" : "ㅤ",
"payload"
)
);
```
Результатом является клавиатура с бомбой на случайной кнопке.
обработчик имеет вид
```ts
(options: { index: number; rowIndex: number }) => T;
```
### combine
Позволяет объединять клавиатуры. Объединяются только клавиатуры. Вам нужно вызвать метод `.row()` для переноса строки после объединения.
```ts twoslash
import { InlineKeyboard } from "@gramio/keyboards";
// ---cut---
new InlineKeyboard()
.combine(new InlineKeyboard().text("первая строка", "payload"))
.row()
.combine(
new InlineKeyboard()
.text("вторая строка", "payload")
.row()
.text("третья строка", "payload")
);
```
---
---
url: /ru/keyboards/keyboard.md
---
# Keyboard
Эта клавиатура отображается под полем ввода и также известна как Reply/Custom Keyboard.
Представляет собой [кастомную клавиатуру](https://core.telegram.org/bots/features#keyboards) с вариантами ответа (смотрите [Введение в ботов](https://core.telegram.org/bots/features#keyboards) для деталей и примеров).
См. также [API Reference](https://jsr.io/@gramio/keyboards/doc/~/Keyboard)
## Импорт
### С GramIO
```ts twoslash
import { Keyboard } from "gramio";
```
### Без GramIO
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
```
## Кнопки ([Документация](https://core.telegram.org/bots/api/#keyboardbutton))
Кнопки - это методы, которые собирают клавиатуру для вас.
### text
Текстовая кнопка. Будет отправлена как сообщение при нажатии на кнопку.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().text("текст кнопки");
```
### requestUsers
Кнопка запроса пользователей. Нажатие на кнопку откроет список подходящих пользователей. Идентификаторы выбранных пользователей будут отправлены боту в служебном сообщении `users_shared`. Доступно только в приватных чатах. `Второй параметр` - это 32-битный идентификатор запроса, который будет получен обратно в объекте [UsersShared](https://core.telegram.org/bots/api/#usersshared). Должен быть уникальным в пределах сообщения.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().requestUsers("текст кнопки", 228, {
user_is_premium: true,
});
```
Подробнее о параметрах в [документации](https://core.telegram.org/bots/api/#keyboardbuttonrequestusers)
### requestChats
Кнопка запроса чатов. Нажатие на кнопку откроет список подходящих чатов. Нажатие на чат отправит его идентификатор боту в служебном сообщении `chat_shared`. Доступно только в приватных чатах. `Второй параметр` - это 32-битный идентификатор запроса, который будет получен обратно в объекте [ChatShared](https://core.telegram.org/bots/api/#chatshared). Должен быть уникальным в пределах сообщения.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().requestChat("gramio", 255, {
chat_is_forum: true,
});
```
> [!WARNING]
> По умолчанию параметр `chat_is_channel` установлен в false!
Подробнее о параметрах в [документации](https://core.telegram.org/bots/api/#keyboardbuttonrequestchat)
### requestPoll
Кнопка запроса опроса. Нажатие на кнопку откроет список подходящих пользователей. Идентификаторы выбранных пользователей будут отправлены боту в служебном сообщении `users_shared`. Доступно только в приватных чатах.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().requestPoll("текст кнопки", "quiz");
```
Подробнее о параметрах в [документации](https://core.telegram.org/bots/api/#keyboardbuttonpolltype)
### requestLocation
Кнопка запроса местоположения пользователя. Текущее местоположение пользователя будет отправлено при нажатии на кнопку. Доступно только в приватных чатах.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().requestLocation("текст кнопки");
```
### requestContact
Кнопка запроса контакта. Номер телефона пользователя будет отправлен как контакт при нажатии на кнопку. Доступно только в приватных чатах.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().requestContact("текст кнопки");
```
### webApp
Кнопка webApp. Описанное [Веб-приложение](https://core.telegram.org/bots/webapps) будет запущено при нажатии на кнопку. Веб-приложение сможет отправить служебное сообщение `web_app_data`. Доступно только в приватных чатах.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().webApp("текст кнопки", "https://...");
```
## Параметры ([Документация](https://core.telegram.org/bots/api/#replykeyboardmarkup))
Эти параметры отвечают за настройки кнопок.
### resized
Запрашивает у клиентов изменение размера клавиатуры по вертикали для оптимального размещения (например, уменьшить клавиатуру, если есть всего две строки кнопок). Если `false`, то кастомная клавиатура всегда имеет такую же высоту, как и стандартная клавиатура приложения. По умолчанию `true`.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().text("какой-то текст").resized(); // для включения
new Keyboard().text("какой-то текст").resized(false); // для отключения
```
> [!WARNING]
> Размер кнопок по умолчанию изменяется! Чтобы отменить это, используйте `.resized(false)`
### oneTime
Запрашивает у клиентов скрытие клавиатуры, как только она была использована. Клавиатура все еще будет доступна, но клиенты автоматически отобразят обычную буквенную клавиатуру в чате - пользователь может нажать специальную кнопку в поле ввода, чтобы снова увидеть кастомную клавиатуру. По умолчанию `false`.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().text("какой-то текст").oneTime(); // для включения
new Keyboard().text("какой-то текст").oneTime(false); // для отключения
```
### persistent
Запрашивает у клиентов всегда показывать клавиатуру, когда обычная клавиатура скрыта. По умолчанию `false`, в этом случае кастомная клавиатура может быть скрыта и открыта с помощью значка клавиатуры. По умолчанию `false`.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().text("какой-то текст").persistent(); // для включения
new Keyboard().text("какой-то текст").persistent(false); // для отключения
```
### selective
Используйте этот параметр, если вы хотите показать клавиатуру только определенным пользователям.
Цели:
1. пользователи, которые упоминаются в _тексте_ объекта [Message](https://core.telegram.org/bots/api/#message).
2. если сообщение бота является ответом на сообщение в том же чате и теме форума, отправитель исходного сообщения.
_Пример:_ Пользователь запрашивает изменение языка бота, бот отвечает на запрос клавиатурой для выбора нового языка. Другие пользователи в группе не видят клавиатуру. По умолчанию `false`.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().text("какой-то текст").selective(); // для включения
new Keyboard().text("какой-то текст").selective(false); // для отключения
```
### placeholder
Заполнитель, отображаемый в поле ввода, когда клавиатура активна. 1-64 символа. По умолчанию `не отображается`.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().text("какой-то текст").placeholder("какой-то текст"); // для включения
new Keyboard().text("какой-то текст").placeholder(); // для отключения
```
## Помощники
Методы, которые помогают вам создать клавиатуру.
### row
Добавляет `разрыв строки`. Вызовите этот метод, чтобы следующие добавленные кнопки были на новой строке.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard().text("первая строка").row().text("вторая строка");
```
### columns
Позволяет ограничить количество столбцов в клавиатуре.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard()
.columns(1)
.text("первая строка")
.text("вторая строка")
.text("третья строка");
```
### wrap
Кастомный обработчик, который управляет переносом строк.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard()
.wrap(({ button }) => button.text === "вторая строка")
.text("первая строка")
.text("первая строка")
.text("вторая строка");
```
обработчик имеет вид
```ts
(options: { button: T; index: number; row: T[]; rowIndex: number }) => boolean;
```
### pattern
Массив с количеством столбцов в каждой строке. Позволяет установить «шаблон».
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard()
.pattern([1, 3, 2])
.text("1")
.text("2")
.text("2")
.text("2")
.text("3")
.text("3");
```
### filter
Обработчик, который помогает фильтровать кнопки клавиатуры.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard()
.filter(({ button }) => button.text !== "скрытая")
.text("проходит")
.text("скрытая")
.text("проходит");
```
### add
Позволяет добавить несколько кнопок в _сыром_ формате.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
const labels = ["какие-то", "кнопки"];
new Keyboard()
.add({ text: "сырая кнопка" })
.add(Keyboard.text("сырая кнопка через Keyboard.text"))
.add(...labels.map((x) => Keyboard.text(x)));
```
### addIf
Позволяет динамически подставлять кнопки в зависимости от чего-либо.
```ts twoslash
// @noErrors
import { Keyboard } from "@gramio/keyboards";
// ---cut---
const labels = ["какие-то", "кнопки"];
const isAdmin = true;
new Keyboard()
.addIf(1 === 2, { text: "сырая кнопка" })
.addIf(isAdmin, Keyboard.text("сырая кнопка через Keyboard.text"))
.addIf(
({ index, rowIndex }) => rowIndex === index,
...labels.map((x) => Keyboard.text(x))
);
```
обработчик имеет вид
```ts
(options: { rowIndex: number; index: number }) => boolean;
```
### matrix
Позволяет создать матрицу кнопок.
```ts twoslash
// @noErrors
import { Keyboard } from "@gramio/keyboards";
// TODO: remove no errors
// ---cut---
import { randomInt } from "node:crypto";
const bomb = [randomInt(0, 9), randomInt(0, 9)] as const;
new Keyboard().matrix(10, 10, ({ rowIndex, index }) =>
Keyboard.text(rowIndex === bomb[0] && index === bomb[1] ? "💣" : "ㅤ")
);
```
Результатом является клавиатура с бомбой на случайной кнопке.
обработчик имеет вид
```ts
(options: { index: number; rowIndex: number }) => T;
```
### combine
Позволяет объединять клавиатуры. Объединяются только клавиатуры. Вам нужно вызвать метод `.row()` для переноса строки после объединения.
```ts twoslash
import { Keyboard } from "@gramio/keyboards";
// ---cut---
new Keyboard()
.combine(new Keyboard().text("первая"))
.row()
.combine(new Keyboard().text("вторая").row().text("третья"));
```
---
---
url: /ru/keyboards/overview.md
---
# Обзор
[`@gramio/keyboards`](https://github.com/gramiojs/keyboards) - это встроенный пакет GramIO. Вы также можете использовать его вне этого фреймворка, так как он не зависит от него.
Смотрите также [API Reference](https://jsr.io/@gramio/keyboards/doc).
### Установка (не требуется для пользователей GramIO)
::: code-group
```bash [npm]
npm install @gramio/keyboards
```
```bash [yarn]
yarn add @gramio/keyboards
```
```bash [pnpm]
pnpm add @gramio/keyboards
```
```bash [bun]
bun install @gramio/keyboards
```
:::
## Использование
::: code-group
```ts twoslash [с GramIO]
import { Keyboard } from "gramio";
const keyboard = new Keyboard()
.text("первая строка")
.row()
.text("вторая строка");
```
```ts twoslash [без GramIO]
import { Keyboard } from "@gramio/keyboards";
const keyboard = new Keyboard()
.text("первая строка")
.row()
.text("вторая строка")
.build();
```
:::
### Отправка через [GramIO](https://gramio.dev/)
```ts
import { Bot, Keyboard } from "gramio"; // импорт из пакета GramIO!!
const bot = new Bot(process.env.BOT_TOKEN as string);
const data = ["Apple", "Realme", "Tesla", "Xiaomi"];
bot.on("message", (ctx) => {
return ctx.send("тест", {
reply_markup: new Keyboard()
.columns(1)
.text("простая клавиатура")
.add(...data.map((x) => Keyboard.text(x)))
.filter(({ button }) => button.text !== "Tesla"),
});
});
bot.start();
```
### Отправка через [Grammy](https://grammy.dev/)
```ts
import { Keyboard } from "@gramio/keyboards";
import { Bot } from "grammy";
const bot = new Bot(process.env.BOT_TOKEN as string);
const data = ["Apple", "Realme", "Tesla", "Xiaomi"];
bot.on("message", (ctx) => {
return ctx.reply("тест", {
reply_markup: new Keyboard()
.columns(1)
.text("простая клавиатура")
.add(...data.map((x) => Keyboard.text(x)))
.filter(({ button }) => button.text !== "Tesla")
.build(),
});
});
bot.start();
```
### Отправка через [Telegraf](https://github.com/telegraf/telegraf)
> [!WARNING] > `Telegraf` не поддерживает последнюю версию Bot API
```ts
import { Keyboard } from "@gramio/keyboards";
import { Telegraf } from "telegraf";
const bot = new Telegraf(process.env.BOT_TOKEN as string);
const data = ["Apple", "Realme", "Tesla", "Xiaomi"];
bot.on("message", (ctx) => {
return ctx.reply("тест", {
reply_markup: new Keyboard()
.columns(1)
.text("простая клавиатура")
.add(...data.map((x) => Keyboard.text(x)))
.filter(({ button }) => button.text !== "Tesla")
.build(),
});
});
bot.launch();
```
### Отправка через [node-telegram-bot-api](https://www.npmjs.com/package/node-telegram-bot-api)
> [!WARNING] > `node-telegram-bot-api` не поддерживает последнюю версию Bot API, и типы написаны плохо, поэтому типы могут не совпадать
```ts
import { Keyboard } from "@gramio/keyboards";
import TelegramBot from "node-telegram-bot-api";
const bot = new TelegramBot(process.env.TOKEN as string, { polling: true });
const data = ["Apple", "Realme", "Tesla", "Xiaomi"];
bot.on("message", (msg) => {
return bot.sendMessage(msg.chat.id, "тест", {
reply_markup: new Keyboard()
.columns(1)
.text("простая клавиатура")
.add(...data.map((x) => Keyboard.text(x)))
.filter(({ button }) => button.text !== "Tesla")
.build(),
});
});
```
### Отправка через [puregram](https://puregram.cool/)
> [!WARNING] > `puregram` не поддерживает последнюю версию Bot API
```ts
import { Telegram } from "puregram";
import { Keyboard } from "@gramio/keyboards";
const bot = new Telegram({
token: process.env.TOKEN as string,
});
const data = ["Apple", "Realme", "Tesla", "Xiaomi"];
bot.on("message", (ctx) => {
return ctx.send("тест", {
reply_markup: new Keyboard()
.columns(1)
.text("простая клавиатура")
.add(...data.map((x) => Keyboard.text(x)))
.filter(({ button }) => button.text !== "Tesla")
.build(),
});
});
bot.updates.startPolling();
```
#### Результат
```json
{
"keyboard": [
[
{
"text": "простая клавиатура"
}
],
[
{
"text": "Apple"
}
],
[
{
"text": "Realme"
}
],
[
{
"text": "Xiaomi"
}
]
],
"one_time_keyboard": false,
"is_persistent": false,
"selective": false,
"resize_keyboard": true
}
```

---
---
url: /ru/tma/init-data.md
---
# Валидация WebAppInitData
`@gramio/init-data` — это библиотека на TypeScript для безопасной проверки, разбора и генерации строк инициализации (init data) Telegram Web App. Она предоставляет набор утилит для работы с init data Telegram Mini App, обеспечивая целостность и подлинность пользовательских данных, передаваемых из Telegram-клиента на ваш сервер. Библиотека не зависит от фреймворка.
Основные возможности:
- Проверка подлинности init data Telegram Web App с помощью токена бота
- Разбор и получение структурированных данных пользователя и чата из строки init data
- Генерация валидных строк init data для тестирования и документации
- Строгие типы TypeScript для всех структур и методов
[Документация и справочник](https://jsr.io/@gramio/init-data@latest/doc)
## Установка
::: code-group
```bash [npm]
npm install @gramio/init-data
```
```bash [yarn]
yarn add @gramio/init-data
```
```bash [pnpm]
pnpm add @gramio/init-data
```
```bash [bun]
bun install @gramio/init-data
```
:::
# getBotTokenSecretKey
Получает секретный ключ из токена вашего Telegram-бота. Этот ключ необходим для проверки хэша строки init data, отправленной из Telegram Web App. Всегда используйте этот метод для генерации ключа перед валидацией или подписью init data, если хотите максимальную производительность. Если не передавать ключ в методы проверки, он будет вычисляться **при каждом вызове**.
```ts
import { getBotTokenSecretKey } from "@gramio/init-data";
const botToken = process.env.BOT_TOKEN as string;
const secretKey = getBotTokenSecretKey(botToken);
// используйте secretKey далее
```
# validateAndParseInitData
Проверяет подлинность строки init data Telegram Web App с помощью секретного ключа или токена бота. Если данные валидны, возвращает объект [WebAppInitData](https://core.telegram.org/bots/webapps#webappinitdata). Если данные некорректны — возвращает `false`.
```ts
import {
validateAndParseInitData,
getBotTokenSecretKey,
} from "@gramio/init-data";
const botToken = process.env.BOT_TOKEN as string;
const secretKey = getBotTokenSecretKey(botToken);
const initData = req.headers["x-init-data"] as string;
const result = validateAndParseInitData(initData, secretKey);
if (!result) {
// Данные невалидны или подделаны
}
// Доступ к данным пользователя и чата
const userId = result.user?.id;
const chatId = result.chat?.id;
```
# validateInitData
Проверяет подлинность строки init data Telegram Web App с помощью секретного ключа или токена бота. Возвращает `true`, если данные валидны, иначе — `false`. Этот метод только проверяет данные и **не разбирает** их. Используйте, если нужно только проверить подлинность без извлечения информации о пользователе или чате.
```ts
import { validateInitData, getBotTokenSecretKey } from "@gramio/init-data";
const botToken = process.env.BOT_TOKEN as string;
const secretKey = getBotTokenSecretKey(botToken);
const initData = req.headers["x-init-data"] as string;
const isValid = validateInitData(initData, secretKey);
if (!isValid) {
// Данные невалидны или подделаны
}
// Если true — строке init data можно доверять
```
# parseInitData
Разбирает строку init data Telegram Web App и возвращает объект [WebAppInitData](https://core.telegram.org/bots/webapps#webappinitdata). Этот метод **не выполняет проверку** подлинности или целостности — используйте его только после проверки через `validateInitData` или `validateAndParseInitData`.
```ts
import { parseInitData } from "@gramio/init-data";
const initData = req.headers["x-init-data"] as string;
const parsed = parseInitData(initData);
// Доступ к данным пользователя и чата
const userId = parsed.user?.id;
const chatId = parsed.chat?.id;
```
# signInitData
Генерирует валидную строку init data из объекта данных и секретного ключа или токена бота. Полезно для тестирования, документации или генерации примеров для клиентов API.
```ts
import {
signInitData,
getBotTokenSecretKey,
type WebAppUser,
} from "@gramio/init-data";
const botToken = process.env.BOT_TOKEN as string;
const secretKey = getBotTokenSecretKey(botToken);
const data = {
user: {
id: 1,
first_name: "durov",
username: "durov",
},
} satisfies WebAppUser;
const signedInitData = signInitData(data, secretKey);
// Используйте signedInitData как валидную строку init data для тестов
```
# Пример: интеграция с Elysia
Вы можете использовать `@gramio/init-data` с любым backend-фреймворком. Ниже — бонус: пример интеграции с Elysia для типобезопасной аутентификации.
```ts
twoslash;
import {
validateAndParseInitData,
signInitData,
getBotTokenSecretKey,
} from "@gramio/init-data";
import { Elysia, t } from "elysia";
const secretKey = getBotTokenSecretKey(process.env.BOT_TOKEN as string);
export const authElysia = new Elysia({
name: "auth",
})
.guard({
headers: t.Object({
"x-init-data": t.String({
examples: [
signInitData(
{
user: {
id: 1,
first_name: "durov",
username: "durov",
},
},
secretKey
),
],
}),
}),
response: {
401: t.Literal("UNAUTHORIZED"),
},
})
.resolve(({ headers, error }) => {
const result = validateAndParseInitData(
headers["x-init-data"],
secretKey
);
if (!result || !result.user)
return error("Unauthorized", "UNAUTHORIZED");
return {
tgId: result.user.id,
user: result.user,
};
})
.as("plugin");
const app = new Elysia()
.get("hello", "world")
.use(authElysia)
.get("/user", ({ user }) => {
user;
// ^?
});
```
---
---
url: /ru/guides/for-beginners/4.md
---
# Soon
---
---
url: /ru/files/usage-without-gramio.md
---
# Использование без GramIO
## Напишите свою собственную типо-безопасную обертку для Telegram Bot API с поддержкой загрузки файлов!
```ts twoslash
// @noErrors
import type {
APIMethods,
APIMethodParams,
TelegramAPIResponse,
} from "@gramio/types";
import { isMediaUpload, convertJsonToFormData } from "@gramio/files";
const TBA_BASE_URL = "https://api.telegram.org/bot";
const TOKEN = "";
const api = new Proxy({} as APIMethods, {
get:
(_target: APIMethods, method: T) =>
async (params: APIMethodParams) => {
const reqOptions: Parameters[1] = {
method: "POST",
};
if (params && isMediaUpload(method, params)) {
const formData = await convertJsonToFormData(method, params);
reqOptions.body = formData;
} else {
reqOptions.headers = {
"Content-Type": "application/json",
};
reqOptions.body = JSON.stringify(params);
}
const response = await fetch(
`${TBA_BASE_URL}${TOKEN}/${method}`,
reqOptions
);
const data = (await response.json()) as TelegramAPIResponse;
if (!data.ok) throw new Error(`Произошла ошибка в ${method}`);
return data.result;
},
});
```
---
---
url: /ru/files/download.md
---
# Скачивание
Стандартный путь скачивания файлов выглядит так:
- Вызвать `getFile` с параметром `file_id`
- Извлечь `file_path` из ответа
- Составить ссылку следующего вида `https://api.telegram.org/file/bot/`
- Отправить запрос и скачать запрашиваемый медиафайл
- ? Возможно, сохранить файл в файловой системе ?
но в нашем случае это выглядит более удобно и проще.
## Через методы контекста
```ts
bot.on("message", async (context) => {
if (!context.document) return;
// скачать в ./file-name
await context.download(context.document.fileName || "file-name");
// получить ArrayBuffer
const buffer = await context.download();
return context.send("Спасибо!");
});
```
> [!IMPORTANT]
>
> **Одно сообщение** содержит только **одно вложение**. Поэтому для скачивания вложения можно просто использовать метод `context.download`
> Но вы можете работать с медиагруппами, используя плагин [media-group](/ru/plugins/official/media-group) и итерацию по массиву `context.mediaGroup`.
## Через метод экземпляра бота
```ts
const chat = await bot.api.getChat({ chat_id: "@not_found" });
if (!chat.photo?.big_file_id) return;
// скачать в ./not_found_chat_photo.png
await bot.downloadFile(chat.photo.big_file_id, "not_found_chat_photo.png");
// получить ArrayBuffer
const buffer = await bot.downloadFile(chat.photo.big_file_id);
```
---
---
url: /ru/guides/for-beginners/2.md
---
# Первое знакомство
#### Что сделаем?
- Создадим аккаунт боту
- Установим и настроим проект
- Запустим бота, который отвечает на команду `/start`
- Научимся работать с «переменными окружения» и `--watch` режимом
### Создание аккаунта телеграм боту
Для начала, создайте своего бота и получите `токен`. Вы можете сделать это воспользовавшись ботом [@BotFather](https://t.me/BotFather).
Отправьте ему команду `/newbot` и следуйте инструкциям пока вы не получите токен вида `110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw`.
### Установка
Для начала нам необходим создать проект и установить туда `GramIO` с помощью `npm`. Создаём папку и прописываем в ней `npm init` для создания package.json для нашего проекта. Так же укажите `"type": "module"` (TODO: рассказать зачем) чтобы конфиг выглядел примерно так:
```json
{
"name": "bot-test",
"type": "module",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": ""
}
```
Следующим шагом мы установим необходимую нам зависимость - GramIO.
```bash
npm install gramio # или npm i gramio
```
### Первый бот
Давайте напишем логику нашему первому боту. Создайте файл `index.js` и напишите в нём:
```js
import { Bot } from "gramio";
const bot = new Bot("12345678:AaBbCcDdEeFfGgHh").command("start", (context) =>
context.send("Вы написали /start")
);
bot.start();
```
где `12345678:AaBbCcDdEeFfGgHh` это токен нашего бота.
Этот код можно разобрать на части -
- `import { Bot } from "gramio";` - это [импорт](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Statements/import) главного класса нашей библиотеки.
- `const bot = new Bot("12345678:AaBbCcDdEeFfGgHh")` - создаём переменную `bot` и инициализируем класс передав ему токен нашего бота
- `.command("start", (context) =>
???);` - Регистрируем нашу команду `/start` для которой будет вызываться эта функция`
- `context.send("Вы написали /start")` - с помощью этого метода отправляем в тот же чат указанное сообщение
- `bot.start();` - а благодаря этому запускаем прослушивание событий с помощью Long-Polling (пока не важно)
Запустим этот код с помощью `node index.js` и проверим работу бота.
// TODO: картинка
### Что за `.send`?
Это метод контекста `Message`, который позволяет отправить сообщение в тот же чат откуда и пришло исходное сообщение. Благодаря этому мы можем не описать полностью в какой чат нам нужно отправлять сообщение, но всё же если нужно мы также можем воспользоваться обычными методами из [API](https://core.telegram.org/bots/api).
Вы можете отправить запрос к API и вне обработчиков событий.
```js
import { Bot } from "gramio";
const bot = new Bot("12345678:AaBbCcDdEeFfGgHh");
bot.api.sendMessage({
chat_id: "id нужного чата",
text: "Вы написали /start",
});
```
А вот в обработчиках вам понадобиться обратиться к `context.bot` переменную которая является нашем инициализированным классом бота
```js
import { Bot } from "gramio";
const bot = new Bot("12345678:AaBbCcDdEeFfGgHh").command("start", (context) =>
context.bot.api.sendMessage({
chat_id: "id нужного чата",
text: "Вы написали /start",
})
);
bot.start();
```
### Конфигурация нашего бота
Сразу же стоит запомнить что хранить так токен - зло. Рекомендуется хранить секреты в переменных окружения, поэтому давайте создадим файл `.env` в нашем проекте и перенесём токен туда.
```dotenv
BOT_TOKEN=12345678:AaBbCcDdEeFfGgHh
```
Объявление переменных окружения в этом файле до нельзя простое, ведь это всего-лишь пара `ключ=значение`.
Хорошо, файл создан, но как обращаться к этим «переменным окружения»? Для этого существует объект `process.env` в котором в нашей программе и будут лежать эти переменные, но стоит помнить что файл `.env` существует для удобства и не все программы читают его по умолчанию. Например в Node.js нам необходимо указать его в аргументе `--env-file`.
Давайте модифицируем наш код, чтобы использовать переменные окружения.
```js
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN).command("start", (context) =>
context.send("Вы написали /start")
);
bot.start();
```
И запустим указав наш `.env` файл. ([дока](https://nodejs.org/en/learn/command-line/how-to-read-environment-variables-from-nodejs))
```bash
node --env-file=.env index.js
```
а ещё давайте добавим `--watch` режим, который будет перезапускать наш скрипт при изменениях в файле!
```bash
node --watch --env-file=.env index.js
```
Так же давайте для удобства вынесем эту команду в наш `package.json`.
для этого добавьте `scripts` объект и в нём `dev` с содержимым нашей команды. В итоге это выглядит примерно так:
```json
{
"name": "bot-test",
"type": "module",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"dev": "node --watch --env-file=.env index.js"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"gramio": "^0.0.40"
}
}
```
Теперь мы можем её вызывать так - `npm run dev`.
---
---
url: /ru/triggers/hears.md
---
# Метод Hears
Метод `hears` в GramIO позволяет настроить ответы на определенные сообщения. Вы можете определить, какие сообщения должен прослушивать ваш бот и как он должен обрабатывать эти сообщения при их обнаружении. Метод гибкий и может работать с регулярными выражениями, точными строками или пользовательскими функциями.
## Основное использование
### Использование сопоставителя `Regular Expression` (Регулярное выражение)
Вы можете настроить бота на прослушивание сообщений, соответствующих определенному шаблону, используя регулярные выражения. Это полезно, когда вы хотите, чтобы ваш бот отвечал на сообщения, соответствующие определенной структуре.
```ts
bot.hears(/hello (.*)/i, async (context) => {
if (context.args) {
await context.send(`Привет, ${context.args[1]}!`);
}
});
```
В этом примере бот прослушивает сообщения, начинающиеся со слова "привет", за которым следует любой текст. Когда такое сообщение получено, бот извлекает текст после "привет" и отправляет персонализированное приветствие.
### Использование сопоставителя `String` (Строка)
Если вы хотите, чтобы ваш бот отвечал на конкретное слово или фразу, вы можете использовать строковый триггер.
```ts
bot.hears("hello", async (context) => {
await context.send("Добро пожаловать! Введите 'помощь', чтобы увидеть доступные команды.");
});
```
В этом случае бот прослушивает точное слово "старт" и отвечает приветственным сообщением.
### Использование сопоставителя `Function` (Функция)
Для более сложных сценариев вы можете использовать функцию для определения триггера. Эта функция проверяет контекст сообщения и решает, запускать ли ответ.
```ts
bot.hears(
(context) => context.user.role === "admin",
async (context) => {
await context.send("Здравствуйте, Администратор! Чем я могу вам помочь?");
}
);
```
Здесь бот проверяет, является ли пользователь администратором. Если да, он отправляет специальное сообщение для администраторов.
---
---
url: /ru/types/index.md
---
# Типы Telegram Bot API
> Автоматически сгенерированные и опубликованные типы Telegram Bot API
[Справочник по типам API](https://jsr.io/@gramio/types/doc)
### Версионирование
Типы 7.7.x соответствуют Telegram Bot API 7.7
## Использование в качестве [пакета NPM](https://www.npmjs.com/package/@gramio/types)
```ts twoslash
import type { APIMethods, APIMethodReturn } from "@gramio/types";
type SendMessageReturn = Awaited>;
// ^? type SendMessageReturn = TelegramMessage
type GetMeReturn = APIMethodReturn<"getMe">;
// ^? type GetMeReturn = TelegramUser
```
### Автоматическое обновление пакета
Эта библиотека автоматически обновляется до последней версии Telegram Bot API в случае изменений благодаря CI/CD!
Если GitHub Action завершается с ошибкой, это означает, что в Bot API нет изменений.
## Импорты
- `index` - экспортирует всё в разделе
- `methods` - экспортирует `APIMethods`, который описывает функции API
- `objects` - экспортирует объекты с префиксом `Telegram` (например, [Update](https://core.telegram.org/bots/api/#update))
- `params` - экспортирует параметры, которые используются в `methods`
### Создайте свою собственную обертку Telegram Bot API с проверкой типов
```ts twoslash
import type {
APIMethods,
APIMethodParams,
TelegramAPIResponse,
} from "@gramio/types";
const TBA_BASE_URL = "https://api.telegram.org/bot";
const TOKEN = "";
const api = new Proxy({} as APIMethods, {
get:
(_target: APIMethods, method: T) =>
async (params: APIMethodParams) => {
const response = await fetch(`${TBA_BASE_URL}${TOKEN}/${method}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});
const data = (await response.json()) as TelegramAPIResponse;
if (!data.ok) throw new Error(`Some error occurred in ${method}`);
return data.result;
},
});
api.sendMessage({
chat_id: 1,
text: "message",
});
```
#### Использование с [`@gramio/keyboards`](https://github.com/gramiojs/keyboards)
```typescript twoslash
import type {
APIMethods,
APIMethodParams,
TelegramAPIResponse,
} from "@gramio/types";
const TBA_BASE_URL = "https://api.telegram.org/bot";
const TOKEN = "";
const api = new Proxy({} as APIMethods, {
get:
(_target: APIMethods, method: T) =>
async (params: APIMethodParams) => {
const response = await fetch(`${TBA_BASE_URL}${TOKEN}/${method}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});
const data = (await response.json()) as TelegramAPIResponse;
if (!data.ok) throw new Error(`Some error occurred in ${method}`);
return data.result;
},
});
// ---cut---
import { Keyboard } from "@gramio/keyboards";
// код из примера выше
api.sendMessage({
chat_id: 1,
text: "message with keyboard",
reply_markup: new Keyboard().text("button text"),
});
```
### С поддержкой загрузки файлов
См. [files/usage-without-gramio](/ru/files/usage-without-gramio)
## Генерация типов вручную
Требуется - [`rust`](https://www.rust-lang.org/)
1. Клонируйте [этот репозиторий](https://github.com/gramiojs/types) и откройте его
```bash
git clone https://github.com/gramiojs/types.git
```
2. Клонируйте [репозиторий](https://github.com/ark0f/tg-bot-api) с генератором схемы Telegram Bot API
```bash
git clone https://github.com/ark0f/tg-bot-api.git
```
3. Запустите генератор схемы JSON в папке `cloned`
```bash
cd tg-bot-api && cargo run --package gh-pages-generator --bin gh-pages-generator -- dev && cd ..
```
4. Запустите генерацию кода типов из `корня` проекта
```bash
bun generate
```
или, если вы не используете `bun`, используйте `tsx`
```bash
npx tsx src/index.ts
```
5. Готово! Проверьте типы Telegram Bot API в папке `out`!
---
---
url: /ru/keyboards/remove-keyboard.md
---
# Remove Keyboard
При получении сообщения с этим объектом, клиенты Telegram удалят текущую кастомную клавиатуру и отобразят стандартную буквенную клавиатуру. По умолчанию кастомные клавиатуры отображаются до тех пор, пока бот не отправит новую клавиатуру. Исключение составляют одноразовые клавиатуры, которые скрываются сразу после нажатия пользователем на кнопку (см. [ReplyKeyboardMarkup](https://core.telegram.org/bots/api/#replykeyboardmarkup)).
Смотрите также [API Reference](https://jsr.io/@gramio/keyboards/doc/~/RemoveKeyboard).
## Импорт
### С GramIO
```ts twoslash
import { RemoveKeyboard } from "gramio";
```
### Без GramIO
```ts twoslash
import { RemoveKeyboard } from "@gramio/keyboards";
```
## Параметры ([Документация](https://core.telegram.org/bots/api/#replykeyboardremove))
Эти параметры отвечают за настройки удаления кнопок
### selective
Используйте этот параметр, если вы хотите удалить клавиатуру только для определенных пользователей.
Цели:
1. пользователи, которые упоминаются в _тексте_ объекта [Message](https://core.telegram.org/bots/api/#message).
2. если сообщение бота является ответом на сообщение в том же чате и теме форума, отправитель исходного сообщения.
Пример: Пользователь голосует в опросе, бот возвращает сообщение с подтверждением в ответ на голос и удаляет клавиатуру для этого пользователя, при этом продолжая показывать клавиатуру с вариантами опроса пользователям, которые еще не голосовали.
```ts twoslash
import { RemoveKeyboard } from "@gramio/keyboards";
// ---cut---
new RemoveKeyboard().selective(); // для включения
new RemoveKeyboard().selective(false); // для отключения
```
---
---
url: /ru/formatting/index.md
---
# Форматирование сообщений
[`@gramio/format`](https://github.com/gramiojs/format) - это встроенный пакет GramIO. Вы также можете использовать его вне этого фреймворка, так как он не зависит от него.
Смотрите также [API Reference](https://jsr.io/@gramio/format/doc).
## Format
Шаблонный литерал, который помогает создавать сущности сообщений для форматирования текста.
Используйте его, если хотите удалить все отступы в начале каждой строки. (как [common-tags#stripindents](https://www.npmjs.com/package/common-tags#stripindents) или [dedent](https://www.npmjs.com/package/dedent))
> [!IMPORTANT]
> Для форматирования с **массивами** используйте его с помощником [`join`](#join)
```ts twoslash
import { format, bold, link, italic, Bot } from "gramio";
const bot = new Bot("");
// ---cut---
format`какой-то текст`;
// или
format`${bold`Хмм...`} ${link(
"GramIO",
"https://github.com/gramiojs/gramio"
)}?`;
```
Давайте отправим что-нибудь...
```ts twoslash
import { format, bold, link, italic, spoiler, Bot } from "gramio";
const bot = new Bot("");
// ---cut---
bot.api.sendMessage({
chat_id: 12321,
text: format`${bold`Hi!`}
Can ${italic("you")} help ${spoiler`me`}?
Can you give me ${link("a star", "https://github.com/gramiojs/gramio")}?`,
});
```

## FormatSaveIndents
Шаблонный литерал, который помогает создавать сущности сообщений для форматирования текста.
Используйте его, если хотите сохранить все отступы.
**ПРИМЕЧАНИЕ**: для форматирования с **массивами** используйте его с помощником [`join`](#join)
```ts twoslash
import { formatSaveIndents, bold, link, italic, spoiler, Bot } from "gramio";
const bot = new Bot("");
// ---cut---
bot.api.sendMessage({
chat_id: 12321,
text: formatSaveIndents`${bold`Hi!`}
Can ${italic("you")} help ${spoiler`me`}?
Can you give me ${link("a star", "https://github.com/gramiojs/gramio")}?`,
});
```

## Сущности
### **Жирный**
Форматирует текст как **жирный**. Нельзя комбинировать с `code` и `pre`.
```ts twoslash
import { format, bold } from "@gramio/format";
// ---cut---
format`Format text as ${bold`bold`}`;
```

### _Курсив_
Форматирует текст как _курсив_. Нельзя комбинировать с `code` и `pre`.
```ts twoslash
import { format, italic } from "@gramio/format";
// ---cut---
format`Format text as ${italic`italic`}`;
```

### Подчеркнутый
Форматирует текст как подчеркнутый. Нельзя комбинировать с `code` и `pre`.
```ts twoslash
import { format, underline } from "@gramio/format";
// ---cut---
format`Format text as ${underline`underlined`}`;
```

### ~~Зачеркнутый~~
Форматирует текст как ~~зачеркнутый~~. Нельзя комбинировать с `code` и `pre`.
```ts twoslash
import { format, strikethrough } from "@gramio/format";
// ---cut---
format`Format text as ${strikethrough`strikethrough`}`;
```

### Спойлер
Форматирует текст как спойлер. Нельзя комбинировать с `code` и `pre`.
```ts twoslash
import { format, spoiler } from "@gramio/format";
// ---cut---
format`Format text as ${spoiler`spoiler`}`;
```

> ### Цитата
Форматирует текст как цитату. Не может быть вложенной.
```ts twoslash
import { format, blockquote } from "@gramio/format";
// ---cut---
format`Format text as ${blockquote`quote`}`;
```

> ### Раскрывающаяся цитата
Форматирует текст как раскрывающуюся цитату. Не может быть вложенной.
```ts twoslash
import { format, expandableBlockquote } from "@gramio/format";
function loremIpsum(options: { count: number }): string {
return "";
}
// ---cut---
format`Format text as ${expandableBlockquote(loremIpsum({ count: 20 }))}`;
```

### `Код`
Форматирует текст как `код`. Удобно для скопированных элементов. Нельзя комбинировать с любым другим форматированием.
```ts twoslash
import { format, code } from "@gramio/format";
// ---cut---
format`Format text as ${code`code`}`;
```

### `Pre`
Форматирует текст как `pre`. Нельзя комбинировать с любым другим форматированием. ([Поддерживаемые языки](https://github.com/TelegramMessenger/libprisma#supported-languages))
```ts twoslash
import { format, pre } from "@gramio/format";
// ---cut---
format`Format text as ${pre`pre`}`;
// или с указанием языка
format`Format text as ${pre(`console.log("pre with language")`, "js")}`;
```

### [Ссылка](https://github.com/gramiojs/gramio)
Форматирует текст как [ссылку](https://github.com/gramiojs/gramio). Нельзя комбинировать с `code` и `pre`.
```ts twoslash
import { format, link } from "@gramio/format";
// ---cut---
format`Format text as ${link("link", "https://github.com/gramiojs/gramio")}`;
```

### [Упоминание](https://github.com/gramiojs/gramio)
Форматирует текст как [упоминание](https://github.com/gramiojs/gramio). Нельзя комбинировать с `code` и `pre`.
```ts twoslash
import { format, mention } from "@gramio/format";
// ---cut---
format`Format text as ${mention("mention", {
id: 12312312,
is_bot: false,
first_name: "GramIO",
})}`;
```

### 🄲 🅄 🅂 🅃 🄾 🄼 ㅤ🄴 🄼 🄾 🄹 🄸
Вставляет пользовательский эмодзи по его id.
```ts twoslash
import { format, customEmoji } from "@gramio/format";
// ---cut---
format`text with emoji - ${customEmoji("⚔️", "5222106016283378623")}`;
```
> [!WARNING]
> Сущности пользовательских эмодзи могут использоваться только ботами, которые приобрели дополнительные имена пользователей на [Fragment](https://fragment.com/).
## Помощники
### Join
Помощник для отличной работы с форматируемыми массивами. ([].join разрушает стилизацию)
Разделитель по умолчанию - `, `
```ts twoslash
import { format, join, bold } from "@gramio/format";
// ---cut---
format`${join(["test", "other"], (x) => format`${bold(x)}`, "\n")}`;
```
---
---
url: /ru/storages/index.md
---
# Хранилища
Это специальные хранилища данных. Их основное применение — в плагинах (например, [сессии](/ru/plugins/official/session)).
## Адаптеры
GramIO имеет множество готовых адаптеров, но вы также можете написать свой собственный!
- [В памяти (In Memory)](#в-памяти) (`@gramio/storage`)
- [Redis](#redis) (`@gramio/storage-redis`)
## Как написать свой собственный адаптер хранилища
Написать свой адаптер очень просто!
Достаточно вернуть объект с необходимыми методами и использовать методы выбранного вами решения для адаптера (например, `ioredis`).
```ts
import type { Storage } from "@gramio/storage";
import ThirdPartyStorage, { type ThirdPartyStorageOptions } from "some-library";
export interface MyOwnStorageOptions extends ThirdPartyStorageOptions {
/** добавить новое свойство в параметры */
some?: number;
}
export function myOwnStorage(options: MyOwnStorageOptions = {}): Storage {
const storage = new ThirdPartyStorage(options);
return {
async get(key) {
const data = await storage.get(key);
return data ? JSON.parse(data) : undefined;
},
async has(key) {
return storage.has(key);
},
async set(key, value) {
await storage.set(key, JSON.stringify(value));
},
async delete(key) {
return storage.delete(key);
},
};
}
```
> [!IMPORTANT]
> Если вы хотите опубликовать свой адаптер, мы рекомендуем следовать **соглашению** и назвать его начиная с `gramio-storage` и добавить ключевые слова `gramio` + `gramio-storage` в ваш **package.json**
## Как использовать адаптеры хранилища в своем плагине
Работать с адаптерами хранилища в вашем плагине также очень просто!
Всё, что нам нужно, уже есть в `@gramio/storage`.
```ts
import { Plugin } from "gramio";
import { type Storage, inMemoryStorage } from "@gramio/storage";
export interface MyOwnPluginOptions {
storage?: Storage;
}
export function myOwnPlugin(options: MyOwnPluginOptions = {}) {
// используем хранилище в памяти по умолчанию
const storage = options.storage ?? inMemoryStorage();
return new Plugin("gramio-example");
}
```
> [!IMPORTANT]
> Вы можете создать шаблон этого примера с помощью [create-gramio-plugin](/ru/plugins/how-to-write.html#scaffolding-the-plugin)
## Список
## В памяти
[](https://www.npmjs.org/package/@gramio/storage)
[](https://www.npmjs.org/package/@gramio/storage)
[](https://jsr.io/@gramio/storage)
[](https://jsr.io/@gramio/storage)
##### Установка
::: code-group
```bash [npm]
npm install @gramio/storage
```
```bash [yarn]
yarn add @gramio/storage
```
```bash [pnpm]
pnpm add @gramio/storage
```
```bash [bun]
bun install @gramio/storage
```
:::
##### Использование
1. С использованием стандартного [Map](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Map)
```ts twoslash
import { inMemoryStorage } from "@gramio/storage";
const storage = inMemoryStorage();
```
2. Предоставление своего собственного [Map](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Map)
```ts twoslash
import { inMemoryStorage, type InMemoryStorageMap } from "@gramio/storage";
const map: InMemoryStorageMap = new Map();
const storage = inMemoryStorage(map);
```
## Redis
[](https://www.npmjs.org/package/@gramio/storage-redis)
[](https://www.npmjs.org/package/@gramio/storage-redis)
[](https://jsr.io/@gramio/storage-redis)
[](https://jsr.io/@gramio/storage-redis)
##### Установка
::: code-group
```bash [npm]
npm install @gramio/storage-redis
```
```bash [yarn]
yarn add @gramio/storage-redis
```
```bash [pnpm]
pnpm add @gramio/storage-redis
```
```bash [bun]
bun install @gramio/storage-redis
```
:::
##### Использование
1. Предоставление параметров ioredis для функции `redisStorage`
```ts twoslash
import { redisStorage } from "@gramio/storage-redis";
const storage = redisStorage({
host: process.env.REDIS_HOST,
});
```
2. Предоставление экземпляра ioredis для функции `redisStorage`
```ts twoslash
import { redisStorage } from "@gramio/storage-redis";
import { Redis } from "ioredis";
const redis = new Redis({
host: process.env.REDIS_HOST,
});
const storage = redisStorage(redis);
```
##### Советы
- Вы можете установить переменную окружения `DEBUG` в `ioredis:*` для вывода отладочной информации:
```bash
DEBUG=ioredis:* npm run start
```
и это будет выглядеть так:
```bash
ioredis:redis write command[::1:6379]: 0 -> get([ '@gramio/scenes:617580375' ]) +187ms
ioredis:redis write command[::1:6379]: 0 -> set([ '@gramio/scenes:617580375', '{"name":"scene-name","state":{},"stepId":0,"previousStepId":0,"firstTime":false}' ]) +1ms
```
- Для проверки того, какие данные хранятся в Redis, мы рекомендуем использовать графические клиенты, такие как [AnotherRedisDesktopManager](https://github.com/qishibo/AnotherRedisDesktopManager).
---
---
url: /ru/hooks/on-error.md
---
# onError (Обработка ошибок)
Бывает, что в middleware возникают ошибки, и нам нужно их обрабатывать.
Для этого был создан хук `onError`.
```ts twoslash
import { Bot } from "gramio";
const bot = new Bot("");
// ---cut---
bot.on("message", () => {
bot.api.sendMessage({
chat_id: "@not_found",
text: "Чат не существует....",
});
});
bot.onError(({ context, kind, error }) => {
if (context.is("message")) return context.send(`${kind}: ${error.message}`);
});
```
### Добавление хука только для определенных контекстов
```ts
bot.onError("message", ({ context, kind, error }) => {
return context.send(`${kind}: ${error.message}`);
});
// или массив
bot.onError(["message", "message_reaction"], ({ context, kind, error }) => {
return context.send(`${kind}: ${error.message}`);
});
```
## Типы ошибок
### Пользовательские
Вы можете отловить ошибку определенного класса, унаследованного от Error.
```ts twoslash
import { Bot, format, bold } from "gramio";
// ---cut---
export class NoRights extends Error {
needRole: "admin" | "moderator";
constructor(role: "admin" | "moderator") {
super();
this.needRole = role;
}
}
const bot = new Bot(process.env.BOT_TOKEN as string)
.error("NO_RIGHTS", NoRights)
.onError("message", ({ context, kind, error }) => {
if (kind === "NO_RIGHTS")
return context.send(
format`У вас недостаточно прав! Вам нужно иметь роль «${bold(
error.needRole
// ^^^^^^^^
)}».`
);
});
bot.on("message", (context) => {
if (context.text === "ban") throw new NoRights("admin");
});
```
> [!IMPORTANT]
> Мы рекомендуем следовать **соглашению** и называть типы ошибок в формате **SCREAMING_SNAKE_CASE**
### Telegram
Эта ошибка является результатом неудачного запроса к Telegram Bot API.
```ts twoslash
import { Bot } from "gramio";
const bot = new Bot("");
// ---cut---
bot.onError(({ context, kind, error }) => {
if (kind === "TELEGRAM" && error.method === "sendMessage") {
error.params; // это параметры sendMessage
}
});
```
### Unknown
Эта ошибка - любая неизвестная ошибка, будь то ваш класс или просто Error.
```ts twoslash
import { Bot } from "gramio";
const bot = new Bot("");
// ---cut---
bot.onError(({ context, kind, error }) => {
if (kind === "UNKNOWN") {
console.log(error.message);
}
});
```
---
---
url: /ru/hooks/on-response.md
---
# onResponse
Этот хук вызывается `после получения успешного ответа` от Telegram Bot API.
## Параметры
Объект с:
- method - название метода API
- params - параметры метода API
- response - ответ
## Пример
```ts twoslash
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string).onResponse((context) => {
console.log("ответ для", context.method, context.response);
});
```
### Добавление хука только для определенных методов API
```ts
bot.onResponse("sendMessage", (context) => {
console.log("ответ для sendMessage", context.response);
});
// или массив
bot.onResponse(["sendMessage", "sendPhoto"], (context) => {
console.log("ответ для", context.method, context.response);
});
```
---
---
url: /ru/hooks/on-response-error.md
---
# onResponseError
Этот хук вызывается `после получения ответа с ошибкой` от Telegram Bot API.
## Параметры
[TelegramError](https://jsr.io/@gramio/core@latest/doc/~/TelegramError)
## Пример
```ts twoslash
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string).onResponseError(
(context) => {
console.log("Ошибка для", context.method, context.message);
}
);
```
### Добавление хука только для определенных методов API
```ts
bot.onResponseError("sendMessage", (context) => {
console.log("Ошибка для sendMessage", context.message);
});
// или массив
bot.onResponseError(["sendMessage", "sendPhoto"], (context) => {
console.log("Ошибка для", context.method, context.message);
});
```
---
---
url: /ru/hooks/on-start.md
---
# onStart
Этот хук вызывается при `запуске` бота.
## Параметры
```ts
{
plugins: string[];
info: TelegramUser;
updatesFrom: "webhook" | "long-polling";
}
```
- plugins - массив плагинов
- info - информация об аккаунте бота
- updatesFrom - webhook/polling
## Пример
```ts twoslash
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string).onStart(
({ plugins, info, updatesFrom }) => {
console.log(`список плагинов - ${plugins.join(", ")}`);
console.log(`имя пользователя бота @${info.username}`);
console.log(`обновления из ${updatesFrom}`);
}
);
bot.start();
```
---
---
url: /ru/hooks/on-stop.md
---
# onStop
Этот хук вызывается при остановке бота.
## Параметры
```ts
{
plugins: string[];
info: TelegramUser;
}
```
- plugins - массив плагинов
- info - информация об аккаунте бота
## Пример
```ts twoslash
// @noErrors
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string).onStop(
({ plugins, info }) => {
console.log(`список плагинов - ${plugins.join(", ")}`);
console.log(`имя пользователя бота @${info.username}`);
}
);
bot.start();
bot.stop();
```
---
---
url: /ru/hooks/pre-request.md
---
# preRequest
Этот хук вызывается `перед отправкой запроса` в Telegram Bot API (позволяет нам влиять на отправляемые параметры).
## Параметры
- method - название метода API
- params - параметры метода API
> [!IMPORTANT]
> Возврат контекста из обработчика хука обязателен!
## Пример
```ts twoslash
import { Bot } from "gramio";
const bot = new Bot(process.env.BOT_TOKEN as string).preRequest((context) => {
if (context.method === "sendMessage") {
context.params.text = "измененные параметры";
}
return context;
});
bot.start();
```
### Добавление хука только для определенных методов API
```ts
bot.preRequest("sendMessage", (context) => {
context.params.text = "измененные параметры";
return context;
});
// или массив
bot.preRequest(["sendMessage", "sendPhoto"], (context) => {
if (context.method === "sendMessage") {
context.params.text = "измененные параметры";
} else context.params.caption = "метод sendPhoto";
return context;
});
```
---
---
url: /ru/hooks/overview.md
---
# Хуки
Система хуков позволяет нам подключаться к жизненному циклу API-запроса/контекста и каким-то образом влиять на него.
Ниже приведены хуки, доступные в GramIO:
- [onStart](/ru/hooks/on-start) - вызывается при запуске бота
- [onStop](/ru/hooks/on-stop) - вызывается при остановке бота
- [onError](/ru/hooks/on-error) - вызывается при возникновении ошибки в контекстах
- [preRequest](/ru/hooks/pre-request) - вызывается перед отправкой запроса в Telegram Bot API (позволяет нам влиять на отправляемые параметры)
- [onResponse](/ru/hooks/on-response) - вызывается при получении ответа на API-запрос (только в случае успеха)
- [onResponseError](/ru/hooks/on-response-error) - вызывается при получении ответа на API-запрос (только в случае ошибки)