Масштабирование I: Большая кодовая база
Как только ваш бот возрастёт, вы столкнетесь с проблемой структурирования кода. Естественно, вы можете разделить код по файлам.
Возможное решение
grammY еще довольно молод и пока не предоставляет официальных интеграций с DI-контейнерами. Подпишитесь на @grammyjs
_news , чтобы получить уведомление, как только мы начнем поддерживать это.
Вы вольны структурировать свой код так, как вам нравится, и не существует универсального решения. Тем не менее, простая и проверенная стратегия структурирования кода заключается в следующем.
- Группируйте вещи, которые семантически принадлежат друг другу, в одном файле (или, в зависимости от размера кода, директории). Каждая из этих частей раскрывает middleware, который будет обрабатывать назначенные сообщения.
- Централизованно создайте экземпляр бота, который объединит все middleware.
- (Необязательно) Предварительно отфильтруйте обновления централизованно и отправляйте их только в нужном направлении. Для этого вам может пригодиться
bot
(ссылка на API) или, как вариант, плагин router..route
Выполняемый пример, реализующий описанную выше стратегию, можно найти в репозитории бота для примера.
Пример структуры
Для очень простого бота, управляющего списком TODO, можно представить такую структуру.
src/
├── bot.ts
└── todo/
├── item.ts
└── list.ts
item
просто определяет некоторые вещи об элементах TODO, и эти части кода используются в list
.
В list
, вы можете сделать следующее:
export const lists = new Composer();
// Зарегистрируйте здесь несколько обработчиков, которые будут работать с вашим middleware обычным способом.
lists.on("message", async (ctx) => {/* ... */});
2
3
4
Обратите внимание, что если вы используете TypeScript, то при создании Composer вам нужно передать ваш пользовательский тип контекста. Например, вам нужно будет использовать
new Composer<My
.Context>()
Как вариант, вы можете использовать границы ошибок для обработки всех ошибок, возникающих внутри вашего модуля.
Теперь в bot
вы можете установить этот модуль следующим образом:
import { lists } from "./todo/list";
const bot = new Bot("");
bot.use(lists);
// ... здесь может быть множество других модулей как todo
bot.start();
2
3
4
5
6
7
8
Как вариант, вы можете использовать плагин router или bot
для объединения различных модулей, если вы можете заранее определить, какой middleware за это отвечает.
Однако помните, что точный способ структурирования вашего бота очень сложно назвать в общем виде. Как и всегда в программном обеспечении, делайте так, чтобы это имело наибольший смысл 😉
Определения типов для извлеченного middleware
Приведенная выше структура с использованием Composer работает хорошо. Однако иногда вы можете оказаться в ситуации, когда вам нужно извлечь обработчик в функцию, а не создавать новый Composer и добавлять в него логику. Это потребует от вас добавления правильных определений типов в обработчики, поскольку они больше не могут быть выведены через Composer.
grammY экспортирует определения типов для всех узких типов middleware, таких как middleware, который вы можете передавать обработчикам команд. Кроме того, он экспортирует определения типов для узких контекстных объектов, которые используются в этом middleware. Оба типа настраиваются вашим пользовательским контекстным объектом. Таким образом, обработчик команд будет иметь тип Command
и его контекстный объект Command
. Их можно использовать следующим образом.
import {
type CallbackQueryMiddleware,
type CommandContext,
type NextFunction,
} from "grammy";
function commandMiddleware(ctx: CommandContext<MyContext>, next: NextFunction) {
// обработка команд
}
const callbackQueryMiddleware: CallbackQueryMiddleware<MyContext> = (ctx) => {
// обработка запросов обратного вызова
};
bot.command(["start", "help"], commandMiddleware);
bot.callbackQuery("query-data", callbackQueryMiddleware);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {
type CallbackQueryMiddleware,
type CommandContext,
type NextFunction,
} from "https://deno.land/x/grammy@v1.30.0/mod.ts";
function commandMiddleware(ctx: CommandContext<MyContext>, next: NextFunction) {
// обработка команд
}
const callbackQueryMiddleware: CallbackQueryMiddleware<MyContext> = (ctx) => {
// обработка запросов обратного вызова
};
bot.command(["start", "help"], commandMiddleware);
bot.callbackQuery("query-data", callbackQueryMiddleware);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Ознакомьтесь со справочником псевдонимов типов, чтобы увидеть обзор всех псевдонимов типов, которые экспортирует grammY.