Прошло уже какое-то время с момента, когда я публиковал свой первый туториал по tgbotapi и пришло время начать уже серию статей, которая должна разложить по полочкам, как можно разбивать логику Telegram ботов (а потенциально, любых ботов 🙂 ) в целом и как это делать в вышеупомянутой библиотеке в связке с надстройкой PlaguBot.
Оглавление
С чего бы начать?
Начнем с того, какой инструментарий будет использован:
Все исходники туториалов будут доступны в этом репозитории, запуск описан там же, но если вкратце — просто замените значение по ключу botToken в конфиге репозитория и вызовите ./gradlew run --args="config.json". В идеале, нужно создать отдельный конфигурационный файл local.config.json и положить конфигурацию в него, потому что он не попадет в репозиторий, но если вы хотите использовать config.json — не забудьте вычистить это токен, если захотите пушить изменения в свой форк или репозиторий
О хитрости local.config.json
В рамках репозитория в .gitignore внесены две строчки:
local.* local.*/
Это значит, что все файлы и папки, начинающиейся с local. будут проигнорированы гитом. Таким образом, достаточно назвать вашу конфигурацию local.config.json и она точно не попадёт в репозиторий
Моё рабочее окружение
Для собственно кодинга я использую Intellij IDEAиз-под линукс (Elementary OS), но практика показывает, что обычно можно работать и под любыми другими системами (а обладатели Linux могут обойтись текстовым редактором и терминалом для сборки/запуска)
Код данного туториала доступен в модуле introduction, а конкретно нас больше всего будет интересовать создаваемый там плагин.
А? (Или как оно там работает)
PlaguBot устроен достаточно просто: он берёт путь до файла, превращает его в конфиг, берёт все плагины из конфига и дальше в два этапа загружает бота:
-
Этап загрузки конфигураций — на этом этапе в Koin DI необходимо объявить свои объекты и прочие настройки.
-
Этап конфигурирования поведения бота — тут мы уже настраиваем реакцию на сообщения от пользователей и прочее такое.
Теоретически, можно выгрузить все настройки в поля самого плагина, но…
это не очень соответствует общей парадигме плагинов. Плагины по своей сути это фабрики, которые должны при необходимости настраивать работу в рамках нескольких ботов
А теперь введение
Первое (@Serializable private class Config( val onStartCommandMessage: String )
Тут у нас приватный класс, который будет недоступен в других классах никак кроме На самом деле, первое, что мы делаем — определяем логгер для плагина logger создаст нам объект KSLog для автоматического логгирования с тэгом нашего плагина. Подробнее можно посмотреть тут. Отдельный логгер удобнее создавать так, поскольку logger — это метод-расширение к любому объекту, а посколько мы не хотим, чтобы тэги перепутались, лучше создать отдельный логгер. Кроме прочего, это еще и будет оптимальней в плане быстродействия 🙂 Вторая вещь, которую мы делаем — это переопределяем Для начала, на вход setupDI мы получаем три параметра: А теперь Собственно, вызов Во введении я старался максимально подробно расписать все пришедшие в голову важные моменты, поэтому с одной стороны информации много, а с другой — она вся очень скучная ? Ну и третья, самая сложная часть — это настройка бота. Собственно, код: Ну что ж, а вот здесь давайте построчно (1) На вход получаем два параметра: (2) (4 — 7) Если конфиг не был получен — ругаемся об этом в консоль и заканчиваем настройку плагина. (9) — Вот, в общем-то, и всё ? В данном туториале мы: Разобрались, как запускать ботов и конфигурировать их. Создали свой плагин для Научились отвечать пользователям на команду Кроме прочего, немного разобрались в терминологии и строении ботов в рамках этой серии туториалов. Для создания своего бота с нуля можно использовать вот этот шаблон, а если вы хотите использовать Спасибо за внимание к статье и буду рад комментариям/замечаниям/предложениям ?чёрной магии рефлексии. Собственно, в этом классе у нас есть только одно поле onStartCommandMessage , которое мы будем использовать далее для ответа пользователям.Почти?
private val log = loggerПояснение
setupDI метод для собственно десериализации конфига и его передачи в Koin.override fun Module.setupDI(database: Database, params: JsonObject) { single { /*1*/get<Json>()./*4*/decodeFromJsonElement(/*3*/Config.serializer(), /*2*/params["introduction"] ?: return@single null) } }
Module является ресивером (receiver) функции и доступен в её рамках как this.database: Database — собственно, Exposed Database, которую можно будет использовать для сохранения данных (но в этой части мы этого делать не будем).params: JsonObject — конфигурация PlaguBot’а как она была декодирована из конфигурационного файла.построчно разберем, что тут происходит:
get<Json>() — получаем объект типа Json для дальнейшей десериализации конфига.params["introduction"] ?: return@single null — берем элемент из PlaguBot конфига по ключу introduction, а если такого нет — возвращаем null (то есть нельзя создать конфиг).Config.serializer() — получаем сериализатор конфига (он доступен благодаря аннотации @Serializable).decodeFromJsonElement(сериализатор, наш элемент из конфига) — собственно, превращаем элемент из конфига в объект конфигурации нашего плагина.single скажет Koin вызвать этот код один раз и сохранить его результат.Уф, да чего так сложно/просто-то?
override suspend fun BehaviourContext.setupBotPlugin(koin: Koin) { val config = koin.getOrNull<Config>() if (config == null) { log.w("Plugin has been disabled due to absence of \"introduction\" field in config or some error during configuration loading") return } onCommand("start", initialFilter = { it.chat is PrivateChat }) { sendMessage( it.chat, config.onStartCommandMessage ) } }
BehaviourContext, сочетающий в себе контекст для асинхронности, бота и информацию об обновлениях, приходящих в бота; и koin: Koin — наш источник зависимостей и параметров, в том числе тех, что мы зарегистрировали в setupDI.val config = koin.getOrNull<Config>() — получение текущего конфига нашего плагина либо null — то самое отсутствие конфига в случае, если поле introduction было пропущено в json конфигурации.onCommand("start", initialFilter = { it.chat is PrivateChat }) — конфигурация ожидания команды start. Кроме того, тут используется фильтрация — блок, который будет описан далее, вызовется только для приватных сообщений. Подробнее можно почитать тут.sendMessage(it.chat, config.onStartCommandMessage) — отправка сообщений в чат, в котором нам пришла команда старта. Тут it — это CommonMessage<TextContent> — то есть обычное сообщение с текстом.Итоги
PlaguBot./start.tgbotapi без каких-либо лишних обвязок вроде Koin/PlaguBot/Exposed — можно воспользоваться этим шаблоном.
ссылка на оригинал статьи https://habr.com/ru/articles/672488/
Добавить комментарий