В Контур.Маркете огромное количество кассовой техники. Когда ее забирают домой, то нигде не отмечаются. Поэтоу порой непонятно, где находится нужный тебе терминал эквайринга или ККТ. Приходится писать в чатики и объявлять технику в розыск. Бот создан для того, чтобы записывать технику на себя было легко и удобно - и чтобы у всех была актуальная инфа о том, где техника.
Доступные команды выводятся при первом запуске бота (/start) и при вызове команды /help.
- /all - выводит весь список устройств с пагинацией
- /categories - список устройств в конкретной категории
- /mine - устройства, которые пользователь записал на себя
- /wishlist - устройства, за которыми пользователь стоит в очереди (когда очередь придет, в чате появится сообщение об этом)
В переменной среды прописаны администраторы бота. Администраторам доступны те же команды + ряд специальных возможностей.
- /add - команда для добавления устройств (по одному или файлом csv)
- /users - для поиска по пользователям (также можно изменить почту пользователя, если он ввел неправильную; оставить про пользователя комментарий; удалить пользователя)
- При поиске устройств доступна команда /edit, которая позволяет отредактировать информацию об устройстве, записать его на пользователя и списать с пользователя, а также удалить устройство
Если обычный пользователь попытается ввести недоступные ему команды, все, что он увидит - сообщение о том, что устройство не найдено.
На самом деле, есть еще скрытая роль разработчика - есть особые команды для получения логов из файлов, массового удаления устройств, дропа БД, скачивания устройств в виде файла, скачивания всех важных объектов со всеми полями. Для подтверждения подобных действий требуется код, который прописан в переменных среды и доступен только обладателям определенных прав в проекте на гитлабе.
При записи устройства на пользователя есть возможность указать дату возврата. В этом случае бот напомнит за день, что скоро пора возвращать технику обратно. А также будет напоминать в последующие дни.
Для пользователей бот запущен в контуровском кубернетес, куда деплоится при помощи гитлаба (.gitlab-ci.yml в корне репозитория) и хельма (values.yaml в папке deploy).
Для простоты бот использует polling. Потому что для вебкуха нужно открывать доступ вовне, а значит, проходить безопасности и подкручивать кубернетес - в нашем случае особого смысла в этом нет.
Локально бота удобно запускать через docker compose (чтобы поднимать сразу самого бота, Postgres, Redis и воркера). В корне проекта есть конфиг compose.yaml.
Для этого вам нужно:
- Сходить в телеграме в бота BotFather, создать своего тестового бота и сохранить токен.
- Создать в src папку secrets и засунуть в нее файлы со всеми секретными переменными, которые нужны для запуска контейнеров (посмотрите в compose.yaml). Например, файл token.txt, в котором содержимое - только токен.
- Поставить докер, если его нет (в т.ч. доступен на Windows и Mac)
- Запустить командой docker compose up --build
Для взаимодействия с Postgres:
- Asyncpg - драйвер для асинхронного подключения к Postgres.
- SQLAlchemy - в качестве ORM (тоже, естественно, в асинхронном режиме). Эта штука соединяет мир наших классов и мир таблиц в базе данных. Т.е. мы описываем модели понятным нам спообом, с подсветкой синтаксиса - и эти модели используются для создания таблиц, просмотра, изменения и удаления данных. В нашем случае основные модели - Resource, Visitor, Record, Category, Action. У них есть primary и foreign keys, они связаны между собой при помощи relations, связанные поля автоматически обновляются, некоторые поля автоматически заполняются (created_at, updated_at) - все это делать с алхимией очень удобно.
- Alembic - для миграций. Алембик создан теми же людьми, что и SQLAlchemy, и работает как надстройка над первой библиотекой. Когда наше состояние моделей уходит вперед от состояния БД - мы можем одной командой сгенерировать миграцию. В большинстве случаев это будет корректная миграция, которая позволяет делать апгрейд и даунгрейд. В нашем случае апгрейд происходит прямо при запуске контейнера (хотя бывают разные подходы, и это дискуссионная тема). При желании или для тестирования локальной базы - можно откатывать и накатывать миграции вручную.
Для взаимодействия с телеграмом используется aiogram. В частности такие фичи:
- Хэндлеры. Для старта с aiogram достаточно навесить на функцию с параметром message: Message декоратор, например @router.message(Command("wishlist")). Создать сам этот router, засунуть его в dispatcher, указать параметры запуска бота - и запустить. И за счет асинхронности сразу будет возможность за небольшое количество ресурсов обработать много запросов.
- FSM. Когда пользователь заходит в сценарий - мы сохраняем его состояние. Поэтому можем провести его по нужному маршруту - например, по добавлению нового устройства (ввод 9 полей с валидацией и соответствующими подсказками). А если он будет вводить какую-то неожиданную фигню - не перейдем в следующий стейт, а переспросим его. По дефолту стейты хранятся в памяти, а в нашем проекте - в редисе (чтобы состояние не терялось при перезапуске бота). В проекте переиспользуются типичные переходы между стейтами - в модуле fsmhelper.
- Клавиатуры. ReplyKeyboard - используется для выбора нужного варианта и заменяет сообщение от пользователя. InlineKeyboard - для просмотра результатов, например, для выбора страницы в выводе. Кстати, пагинации из коробки нет, она реализована самостоятельно + написано большое количество тестов.
Подробности смотрите в документации aiogram. А еще есть прикольный гайд.
Для запуска бота на сервере в режиме вебхука - используется aiohttp (ну точнее использовался - сейчас, как упомянуто выше, бот запускается в режиме поллинга). Как вы понимаете, все эти библиотеки неплохо между собой дружат: asyncio, aiohttp, aiogram - поэтому многие вещи работают из коробки легко и гладко.
Также в проекте есть:
- pydantic-settings - чтобы собирать разрозненные переменные среды в удобные конфиги. Нужные значения подтягиваются в конфиг из переменных среды и файлов с секретами - и сразу валидируются привычными средствами pydantic. Если переменных не хватает или они указаны неправильно, приложение сразу выбросит ошибку - а не тогда, когда мы попытаемся эти переменные использовать. В этом проекте переменных среды реально много, поэтому если бы этой библиотеки не было, ее надо бы было придумать.
- charset_normalizer - майкрософт очень коварный, поэтому мы заранее не знаем, в какой кодировке придет файл .csv с устройствами. Пришлось заюзать эту библиотеку и вставить костыль, потому что она плохо определяет cp1251 (считает маковской кодировкой).
- arq - для запуска воркера, который отправляет напоминания о необходимости сдать устройство. Сейчас используется не на всю мощь: есть возможность в зависимости от событий класть вызов функций в редис - и чтоб воркер доставал их из редиса и исполнял.
- redis - для хранения стейтов и результатов работы воркера.