Boty Discord

Четыре продакшн-бота, обслуживающих серверы наших брендов. Тикеты с пингом команды, приветствие новых участников, проверка адресов e-mail и наведение порядка в ролях - все подогнано под конкретный сервер. Они снимают нагрузку с поддержки и следят за гигиеной сообщества без ручной работы.

Boty Discord
TL;DR

Четыре продакшн-бота, каждый подогнан под конкретный сервер: тикетинг с пингом нужной команды, приветствия и онбординг, проверка e-mail при входе, порядок в ролях. Все стоят на едином ядре и архитектуре когов, поэтому каждый сервер берёт только те функции, которые ему действительно нужны. Новая функция - это новый cog, а не очередной if в монолите.

Введение

Сообщества наших брендов живут в Discord, а не на сайте. Именно туда попадает новый клиент, там он открывает тикет, там ждёт роль, дающую доступ к нужным каналам. При нескольких сотнях участников ручное управление тикетами, приветствиями и ролями просто перестаёт складываться - кто-то должен быть на месте, смотреть и кликать, а это не масштабируется с числом людей.

Вместо того чтобы писать одного универсального бота на всё, мы построили одно ядро и набор модулей, из которых каждый бот собирается отдельно. Четыре сервера, четыре роли, четыре конфигурации - но одно место, где живёт логика. Этот кейс о том, почему такое разделение окупилось больше, чем удобный вначале монолит.

Почему не один бот на всё

Заманчиво написать одного бота, который делает всю работу на всех серверах. Проблема в том, что у каждого сервера немного разные потребности: другая команда для пинга при тикете, другой путь входа нового участника, другие роли и другой порог проверки. Универсальный бот, который должен обслужить их все, со временем превращается в новогоднюю ёлку из флагов, которую никто не хочет трогать, потому что неясно, что ещё от них зависит.

Вторая ловушка - это общее состояние. Один процесс, обслуживающий четыре сервера сразу, - это единая точка отказа: ошибка в логике тикетов может положить приветствия на совсем другом сервере. И чем больше неиспользуемого кода везёт сервер, тем труднее что-либо в нём безопасно изменить.

Одно ядро, много когов

Мы пошли в другую сторону: одно ядро с модулями, которые в экосистеме Discord называют когами, и каждый бот - это набор включённых когов. Сервер тикетов берёт cog тикетов; сервер, которому он не нужен, просто его не включает.

Монолит против ядра с когами

АспектОдин бот на всёЯдро с когами
Новая функцияочередной if внутриновый, изолированный cog
Сервер без данной функциивсё равно везёт её кодпросто не включает её
Отказ одного модуляриск для всегоограничен когом
Правка логикипоиск в монолитеодно место, везде где cog включён

Четыре бота, четыре роли

На практике экосистема - это четыре отдельных бота, каждый из которых делает одну вещь как надо. Бот тикетов открывает ветки обращений и пингует нужную команду, чтобы обращение не застряло в пустоте. Приветственный бот проводит нового участника через первые шаги. Бот проверки подтверждает адрес e-mail при входе. Бот ролей следит за гигиеной ролей и назначает их автоматически.

Это разделение не косметическое. У каждого бота свой объём прав и своё состояние, поэтому отказ одного не тянет за собой остальных, а изменение в одном не требует думать о трёх других. Общим является только ядро - то, как cog встраивается в бота, как читает конфигурацию и как общается с API.

Четыре бота, четыре роли

БотЧто делаетКлючевой cog
Тикетыоткрывает ветки обращений и пингует нужную командуtickets
Приветствиеонбординг и первые шаги нового участникаwelcome
Проверкаподтверждение адреса e-mail при входеmailcheck
Ролиавтоматическое назначение и гигиена ролейroles

Как выглядит cog

Cog - это замкнутая единица: собственные команды, собственные слушатели, собственное состояние. Вы встраиваете его в бота одной строкой и так же отсоединяете. Нет глобального переплетения - cog тикетов ничего не знает о coge ролей и не должен. Ниже фрагмент проверки: новый участник получает роль ожидания и приглашение подтвердить адрес, прежде чем увидит остальной сервер.

verify.py · python
class VerifyCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    @commands.Cog.listener()
    async def on_member_join(self, member):
        pendingRole = member.guild.get_role(self.pendingRoleId)
        await member.add_roles(pendingRole)
        await self.sendVerifyPrompt(member)

Имена вроде on_member_join или add_roles задаёт API Discord, и они остаются как есть - остальные идентификаторы идут по нашей конвенции. Это граница, на которой приходится идти за библиотекой, а не за собственным стилем, и единственное место, где смешиваются две конвенции именования.

i
Примечание

Проверка e-mail при входе - не украшение. Самая дешёвая защита от волны фейковых аккаунтов - это порог, который массово регистрирующиеся боты должны пройти: подтверждение реального адреса отсеивает большинство из них ещё до того, как они увидят каналы. Роль ожидания держит нового участника за дверью до подтверждения.

Что остаётся на стороне сопровождения

Самый большой выигрыш не виден на сервере, а в том, как эти боты сопровождаются. Правка в логике тикетов идёт в одном месте и попадает всюду, где этот cog включён - вы не переписываете её четыре раза и не забываете про один сервер. Ни один сервер не везёт код, который не использует, поэтому его конфигурация ровно настолько сложна, насколько сложны его реальные потребности.

4
продакшн-бота
1
общее ядро вместо четырёх отдельных
n
когов, включаемых на сервер

Добавить пятый сервер не означает писать пятого бота с нуля. Его собирают из того, что уже есть: берёте подходящие коги, выставляете конфигурацию, и всё. В этом и разница между четырьмя отдельными проектами и одной экосистемой, которая растёт за счёт добавления модулей, а не умножения кода.

Больше проектов

Другие работы из той же категории - посмотрите, как мы решаем похожие задачи.

Есть похожий проект?

Напишите нам - смета бесплатна и приходит в течение часа.