В этой статье мы поговорим о том как сделать простого бота в Discord на Python с базой данных SQlite3.
Функционал бота будет следующим:
1. Вывод пинга:
- Бот отвечает на команду /пинг, отправляя обратно сообщение с текущим пингом на запущенном сервере.
2. Выдача роли:
- Бот может выдавать роль пользователю по команде /выдать_роль. Находит упомянутого пользователя и добавляет ему указанную роль.
3. Удаление роли:
- Бот осуществляет отзыв роли у пользователя командой /забрать_роль. Находит упомянутого пользователя и убирает у него указанную роль.
4. Смена никнейма:
- Бот может менять никнейм пользователя по команде /изменить_ник. Находит упомянутого пользователя и меняет его никнейм на новый.
5. Вывод баланса:
- Бот получает данные о баланса пользователя по его ID с помощью
команды /баланс. Данные получаются из базы данных и вставляются если их нет.
После написания кода бота, мы развернем его облаке Amvera буквально за пару минут и три команды в IDE, используя GitOps-подход. Это позволит осуществлять обновления проекта существенно проще, чем при использовании классического VPS.
Создание бота
Для создания бота нам нужно зайти на сайт разработчиков Discord. Нажимаем New application. Вводим имя вашего бота, подтверждаем согласие и нажимаем Create.
После создания переходим во вкладку Bot, листаем чуть ниже и включаем Intents
Теперь нам нужно добавить бота на сервер, для этого нам нужна ссылка авторизации. Переходим во вкладку OAuth2. В разделе SCOPES выбираем bot
далее нам нужно выбрать разрешения, листаем ещё вниз и выбираем Administrator
Теперь мы можем получить ссылку для авторизации бота на сервере. Листаем ещё вниз и копируем ссылку
import disnake
from disnake.ext import commands
import sqlite3
Скопированную ссылку нужно вставить в поисковую строку браузера. Теперь мы можем добавить бота на сервер
После добавления бота мы можем перейти к написанию его кода.
Написание кода с использованием disnake библиотеки
Для начала установим библиотеку disnake. Альтернативой является использование библиотеки discord.py, но disnake больше подходит для новичков из-за своей простоты.
Переходим в командную строку и пишем команду pip install disnake. Теперь можно приступить к написанию кода.
Создаем файл bot.py и открываем его.
-
Импортируем библиотеки
import disnake from disnake.ext import commands import sqlite
-
Подключаем интенты и создаем экземпляр бота
intents = disnake.Intents.default() intents.message_content = True bot = commands.Bot(command_prefix='+', intents=intents)
-
Подключаем базу данных
conn = sqlite3.connect('/data/bot.db') cursor = conn.cursor()
-
Создаем событие, которое выведет в консоль готовность бота когда он будет запущен, а так же создадим таблицу в базе данных
@bot.event async def on_ready(): cursor.execute("""CREATE TABLE IF NOT EXISTS users ( id BIGINT cash INT )""") conn.commit() print(f'{bot.user.name} ready!')
Мы сделали основу бота, теперь можно приступить к созданию команд.
-
Создадим обычную текстовую команду выводящую задержку бота
@bot.command() async def ping(ctx): await ctx.reply(f'Понг! {round(bot.latency * 1000)} мс')
-
Создадим слэш-команду с обращением к базе данных
@bot.slash_command(name='баланс', description='Вывод баланса пользователя') async def balance(interaction): user_id = interaction.author.id user_cash = cursor.execute('SELECT cash FROM users WHERE id = ?', (user_id,)).fetchone()[0] if user_cash is None: cursor.execute("INSERT INTO users VALUES (?, ?)", (user_id, 0)) conn.commit() await interaction.response.send_message(f'Ваш баланс: {user_cash}') else: await interaction.response.send_message(f'Ваш баланс: {user_cash}')
-
Теперь сделаем команду для выдачи и отзыва роли пользователю
@bot.slash_command(name='выдать_роль', description='Выдача роли пользователю') async def give_role(interaction, member: disnake.Member, role: disnake.Role): await member.add_roles(role) await interaction.response.send_message('Роль выдана!') @bot.slash_command(name='забрать_роль', description='Отзыв роли у пользователя') async def take_role(interaction, member: disnake.Member, role: disnake.Role): await member.remove_roles(role) await interaction.response.send_message('Роль отозвана!')
-
Осталась команда изменения ника
@bot.slash_command(name='изменить_ник', description='Изменяет никнейм пользователя') async def set_nick(interaction, member: disnake.Member, nick: str): await member.edit(nick=nick) await interaction.response.send_message('Никнейм изменен!')
-
Когда все команды добавлены, в самый конец файла требуется добавить
bot.run('TOKEN')
-
Итоговый файл нашего бота
import disnake
from disnake.ext import commands
import sqlite3
conn = sqlite3.connect('/data/bot.db')
cursor = conn.cursor()
intents = disnake.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='+', intents=intents)
@bot.event
async def on_ready():
cursor.execute("""CREATE TABLE IF NOT EXISTS users (
id BIGINT,
cash INT )""")
conn.commit()
print(f'{bot.user.name} ready!')
@bot.command()
async def ping(ctx):
await ctx.reply(f'Понг! {round(bot.latency * 1000)} мс')
@bot.slash_command(name='баланс', description='Вывод баланса пользователя')
async def balance(interaction):
user_id = interaction.author.id
user_cash = cursor.execute('SELECT cash FROM users WHERE id = ?', (user_id,)).fetchone()[0]
if user_cash is None:
cursor.execute("INSERT INTO users VALUES (?, ?)", (user_id, 0))
conn.commit()
await interaction.response.send_message(f'Ваш баланс: {user_cash}')
else:
await interaction.response.send_message(f'Ваш баланс: {user_cash}')
@bot.slash_command(name='выдать_роль', description='Выдача роли пользователю')
async def give_role(interaction, member: disnake.Member, role: disnake.Role):
await member.add_roles(role)
await interaction.response.send_message('Роль выдана!')
@bot.slash_command(name='забрать_роль', description='Забор роли у пользователя')
async def take_role(interaction, member: disnake.Member, role: disnake.Role):
await member.remove_roles(role)
await interaction.response.send_message('Роль убрана!')
@bot.slash_command(name='изменить_ник', description='Изменяет никнейм пользователя')
async def set_nick(interaction, member: disnake.Member, nick: str):
await member.edit(nick=nick)
await interaction.response.send_message('Никнейм изменен!')
bot.run('TOKEN')
Подготовка к деплою
Деплой мы произведем в облаке Amvera.
Встроенный функционал CI/CD Amvera позволит нам накатывать обновления простой командой git push amvera master и максимально упростить процесс развертывания в сравнении с классической VPS.
Но перед деплоем нам необходимо создать конфигурационный файл amvera.yml и файл с зависимостями requirements.txt
Файл amvera.yaml
Используем генератор для создания этого файла
Выбираем окружение Python
Далее вводим версию Python, в нашем случае 3.8
Указываем путь к файлу с зависимостями, requirements.txt
Вводим имя вашего основного файла, к примеру bot.py
Нажимаем Generate YAML
Вот так выглядит файл конфигурации:
meta:
environment: python
toolchain:
name: pip
version: 3.8
build:
requirementsPath: requirements.txt
run:
scriptName: bot.py
persistenceMount: /data
containerPort: 80
Следует обратить внимание, что порт в конфигурации должен соответствовать порту в коде. Это поможет избежать 502 ошибки.
И так как мы используем SQLite3, сохраняем ее в постоянном хранилище /data. Это позволит избежать ее утери при пересборке проекта.
Файл requirements.txt
Для его создания, используем командную строку.
Открываем ее и вводим команду pip freeze > requirements.txt
Переходим к папке которая была указана в командной строке и забираем оттуда файл с зависимостями.
Вот так выглядит файл с зависимостями:
aiohttp==3.9.5
aiosignal==1.3.1
async-timeout==4.0.3
attrs==23.2.0
disnake==2.9.2
frozenlist==1.4.1
idna==3.7
multidict==6.0.5
psycopg2==2.9.9
yarl==1.9.4
Примечание: в файл будут записаны все библиотеки которые у вас установлены, будьте осторожнее и не загружайте лишние библиотеки в проект. Лучше составить данный файл руками.
Деплой через интерфейс
Нажимаем на кнопку создать.
Вводим название для проекта, выбираем тип сервиса - приложение и выбираем тарифный план
Нажимаем далее
Выбираем файлы которые нужно закинуть в проект и перемещаем их в это окошко.
Нажимаем на кнопку далее
У нас появится вот это окно
Тут ничего делать не надо так как мы закинули файл amvera.yaml и все настраивается автоматически.
Теперь нам нужно добавить секрет - токен нашего бот в bot.run() мы указали просто слово TOKEN, теперь нужно добавить переменную как секрет. Для этого заходим во вкладку переменные в проекте и нажимаем создать секрет. В поле название вводим нашу переменную TOKEN, а в поле значение, вводим сам токен
Нажимаем кнопку завершить и начнется сборка проекта.
Если вы все сделали правильно, то после сборки начнется запуск.
Если вы сделал все правильно то ваше приложение будет запущено.
Деплой через Git
Деплой через git push является альтернативным способом развернуть приложение. Он чуть сложнее, но в дальнейшем позволит обновлять наш проект тремя командами в терминале не переходя на сайт облака, что намного удобнее.
Создаем папку и закидываем туда все файлы. Открываем командную строку, и переходим в нашу папку с помощью команды cd “путь к папке”. Создаем репозиторий с помощью команды git init. Заходим в Amvera и создаем проект.
На данном этапе мы выбираем метод “Через Git” и нажимаем отмена.
Теперь нам нужно подключиться к существующему репозиторию, для этого копируем команду ниже и вставляем в командную строку.
После чего вводим команды для добавления файлов и создания коммита:
git add .
git commit -m "initial commit"
Теперь нам остается запушить все файлы и сборка начнется автоматически. Для этого вводим команду: git push amvera master
Результат
Если вы все сделали правильно, то после сборки начнется запуск.
Если же выдает ошибку, полезно ознакомиться с Логом сборки и Логом приложения и посмотреть частые ошибки в документации сервиса.
Приложение запущено, а значит наш бот должен появиться в сети
Теперь мы можем ввести команды
В этой статье мы поговорили о том как сделать и задеплоить python бота в Discord, научили работать его с базой данных и взаимодействовать с участниками сервера и развернули его в облаке Amvera, используя GitOps-подход.
Данный пример содержит самый базовый функционал, практически "hello world", но надеюсь, даст первое представление о работе discord-ботов.