В мире, где видеоигры так важны для многих людей, жизненно важно общение и общение вокруг игр. Discord предлагает как те, так и другие, в одной хорошо продуманной упаковке. В этом уроке вы узнаете, как создать бот Discord на Python, чтобы вы могли максимально использовать эту фантастическую платформу.
К концу этой статьи вы узнаете:
- Что такое Discord и почему он так ценен
- Как сделать бот Discord через портал разработчика
- Как создать Discord соединения
- Как обрабатывать события
- Как принимать команды и проверять предположения
- Как взаимодействовать с различными API Discord
Вы начнете с изучения, что такое Discord и почему это ценно.
Что такое Discord?
Discord – это голосовая и текстовая коммуникационная платформа для геймеров.
Игроки, стримеры и разработчики используют Discord для обсуждения игр, ответов на вопросы, общения в чате и многого другого. Здесь даже есть игровой магазин с критическими отзывами и сервисом подписки. Это почти универсальный магазин для игровых сообществ.
В то время как есть много вещей, которые вы можете построить с помощью API Discord , этот урок будет сфокусирован на конкретном результате обучения: как создать бота Discord в Python.
Что такое бот?
Dicord растет в популярности. Таким образом, автоматизированные процессы, такие как запрет несоответствующих пользователей и реагирование на запросы пользователей, жизненно важны для процветания и развития сообщества.
Автоматизированные программы, которые выглядят и действуют как пользователи и автоматически реагируют на события и команды в Discord, называются пользователями-ботами . Пользователи Discord ботов (или просто боты ) имеют почти неограниченное количество приложений .
Например, допустим, вы управляете новой гильдией Discord, и пользователь присоединяется впервые. Возбужденный, вы можете лично обратиться к этому пользователю и приветствовать его в своем сообществе. Вы также можете рассказать им о своих каналах или попросить их представиться.
Пользователь чувствует себя желанным гостем и получает удовольствие от обсуждений, происходящих в вашей гильдии, а они, в свою очередь, приглашают друзей.
Со временем ваше сообщество становится настолько большим, что больше не представляется возможным лично связаться с каждым новым участником, но вы все равно хотите отправить им что-нибудь, чтобы признать их новым членом гильдии.
С ботом можно автоматически реагировать на присоединение нового члена вашей гильдии. Вы даже можете настроить его поведение в зависимости от контекста и контролировать его взаимодействие с каждым новым пользователем.
Это здорово, но это только один маленький пример того, как бот может быть полезен. У вас так много возможностей для творчества с ботами, если вы знаете, как их создавать.
Примечание. Хотя Discord позволяет создавать ботов, которые имеют дело с голосовой связью, эта статья будет придерживаться текстовой части сервиса.
Есть два ключевых шага при создании бота:
- Создайте пользователя бота на Discord и зарегистрируйте его в гильдии.
- Напишите код, который использует API Discord и реализует поведение вашего бота.
В следующем разделе вы узнаете, как создать бот Discord на портале разработчиков Discord .
Как сделать Discord Bot на портале для разработчиков
Прежде чем вы сможете погрузиться в любой код Python для обработки событий и создания захватывающих автоматизаций, вам необходимо сначала создать несколько компонентов Discord:
- Аккаунт
- Приложение
- Бот
- Гильдия
Вы узнаете больше о каждой части в следующих разделах.
Создав все эти компоненты, вы свяжете их вместе, зарегистрировав бота в своей гильдии.
Вы можете начать, перейдя на Портал разработчиков Discord .
Создание учетной записи Discord
Первое, что вы увидите, это целевая страница, на которой вам нужно будет либо авторизоваться, если у вас уже есть аккаунт, либо создать новый аккаунт:

Если вам необходимо создать новую учетную запись, нажмите кнопку « Регистрация» под кнопкой « Войти» и введите данные своей учетной записи.
Важно: вам нужно будет подтвердить свою электронную почту, прежде чем вы сможете двигаться дальше.
Когда вы закончите, вы будете перенаправлены на домашнюю страницу портала разработчика, где вы создадите свое приложение.
Создание приложения
Приложение позволяет взаимодействовать с API , Discord, предоставляя маркеры аутентификации, назначение прав доступа, и так далее.
Чтобы создать новое приложение, выберите « Новое приложение» :

Далее вам будет предложено назвать ваше приложение. Выберите имя и нажмите « Создать» :

Поздравляем! Вы сделали заявку Discord. На появившемся экране вы можете увидеть информацию о вашем приложении:

Имейте в виду, что любая программа, взаимодействующая с API-интерфейсом Discord, требует приложения Discord, а не только ботов. API-интерфейсы, связанные с ботами, являются лишь частью общего интерфейса Discord.
Однако, поскольку в этом руководстве рассказывается, как создать бот Discord, перейдите на вкладку Bot в левом списке навигации.
Создание бота
Как вы узнали из предыдущих разделов, пользователь бота – тот, кто слушает и автоматически реагирует на определенные события и команды в Discord.
Чтобы ваш код действительно отображался в Discord, вам нужно создать пользователя бота. Для этого выберите Добавить бота :

Как только вы подтвердите, что хотите добавить бота в свое приложение, вы увидите нового пользователя бота на портале:

Обратите внимание, что по умолчанию пользователь вашего бота унаследует имя вашего приложения. Вместо этого обновите имя пользователя до чего-то более похожего на бота, например RealPythonTutorialBot
, и Сохранить изменения :

Теперь бот готов и готов к работе, но куда?
Пользователь бота бесполезен, если он не взаимодействует с другими пользователями. Далее вы создадите гильдию, чтобы ваш бот мог взаимодействовать с другими пользователями.
Создание гильдии
Гильдии (или сервера , как его часто называют в пользовательском интерфейсе Discord) представляет собой специфическую группу каналов , где пользователи собираются общаться.
Примечание. Хотя гильдия и сервер взаимозаменяемы, в данной статье термин « гильдия» будет использоваться главным образом потому, что API придерживаются одного и того же термина. Термин сервер будет использоваться только при обращении к гильдии в графическом интерфейсе.
Например, скажем, вы хотите создать пространство, где пользователи могут собираться вместе и рассказывать о вашей последней игре. Вы бы начали с создания гильдии. Тогда в вашей гильдии у вас может быть несколько каналов, таких как:
- Общие обсуждения: канал для пользователей, чтобы говорить о том, что они хотят
- Спойлеры, Осторожно: канал для пользователей, которые закончили вашу игру, чтобы поговорить обо всех концах игры.
- Объявления: канал для вас, чтобы объявить об обновлениях игры и для пользователей, чтобы обсудить их
Создав свою гильдию, вы пригласите других пользователей заполнить ее.
Итак, чтобы создать гильдию, зайдите на домашнюю страницу Discord :

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

Это представит две опции: Создать сервер и Присоединиться к серверу . В этом случае выберите Создать сервер и введите имя для вашей гильдии:

Когда вы закончите создание своей гильдии, вы сможете увидеть пользователей справа и каналы слева:

Последний шаг на Discord – регистрация вашего бота в вашей новой гильдии.
Добавление бота в гильдию
Бот не может принимать приглашения, как обычный пользователь. Вместо этого вы добавите своего бота, используя протокол OAuth2.
Техническая деталь: OAuth2 – это протокол для работы с авторизацией, где служба может предоставить клиентскому приложению ограниченный доступ на основе учетных данных приложения и разрешенных областей.
Для этого вернитесь на портал разработчика и выберите страницу OAuth2 на левой навигационной панели:

В этом окне вы увидите Генератор URL OAuth2.
Этот инструмент генерирует URL-адрес авторизации, который обращается к API-интерфейсу Discord OAuth2 и авторизует доступ к API с использованием учетных данных вашего приложения.
В этом случае вы захотите предоставить пользователю бота вашего приложения доступ к API Discord с использованием учетных данных OAuth2 вашего приложения.
Чтобы сделать это, прокрутите вниз и выберите бота из опций SCOPES , а администратора – из BOT PERMISSIONS :

Теперь Discord сгенерировал URL авторизации вашего приложения с выбранной областью и разрешениями.
Отказ от ответственности: хотя мы используем Администратора для целей этого учебного пособия, вы должны быть максимально детализированы при предоставлении разрешений в реальных приложениях.
Выберите « Копировать» рядом с URL-адресом, который был сгенерирован для вас, вставьте его в браузер и выберите свою гильдию в раскрывающемся списке:

Нажмите Авторизоваться , и все готово!
Примечание: вы можете получить reCAPTCHA, прежде чем двигаться дальше. Если это так, вам нужно доказать, что вы человек.
Если вы вернетесь в свою гильдию, то увидите, что бот был добавлен:

В итоге вы создали:
- Приложение , что ваш бот будет использовать для аутентификации Discord API
- Бот пользователь , который вы будете использовать для взаимодействия с другими пользователями и событиями в вашей гильдии
- Гильдии , в которой ваша учетная запись пользователя , а пользователь бот будет активен
- Discord счета , с которым вы создали все остальное , и что вы будете использовать для взаимодействия с ботом
Теперь вы знаете, как создать бот Discord с помощью портала разработчика. Далее идут забавные вещи: реализация вашего бота на Python!
Как сделать Discord Bot в Python
Поскольку вы изучаете, как создать бот Discord с Python, вы будете использовать его discord.py
.
discord.py
библиотека Python, которая исчерпывающе и эффективно реализует API-интерфейсы Discord. Это включает в себя использование Python реализации Async IO .
Начните с установки discord.py
с pip
:
$ pip install -U discord.py
Теперь, когда вы установили discord.py
, вы будете использовать его для создания вашего первого подключения к Discord!
Создание Discord Connection
Первым шагом в реализации вашего бота является создание подключения к Discord. С помощью discord.py
этого вы создаете экземпляр Client
:
# bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
client = discord.Client()
@client.event
async def on_ready():
print(f'{client.user} has connected to Discord!')
client.run(TOKEN)
A Client
– это объект, представляющий связь с Discord. A Client
обрабатывает события, отслеживает состояние и обычно взаимодействует с API-интерфейсами Discord.
Здесь вы создали Client
и реализовали его on_ready()
обработчик событий, который обрабатывает событие, когда Client
он установил соединение с Discord и завершил подготовку данных, отправленных Discord, таких как состояние входа в систему, данные гильдии и канала и т. Д.
Другими словами, on_ready()
будет вызван (и ваше сообщение будет напечатано), как только client
будет готов к дальнейшим действиям. Вы узнаете больше о обработчиках событий позже в этой статье.
Когда вы работаете с такими секретами, как токен Discord, рекомендуется записать его в свою программу из переменной среды. Использование переменных среды помогает вам:
- Избегайте помещения секретов в систему контроля версий
- Используйте разные переменные для среды разработки и производства без изменения кода
Хотя вы могли бы export DISCORD_TOKEN={your-bot-token}
, более простое решение – сохранить .env
файл на всех машинах, на которых будет выполняться этот код. Это не только проще, так как вам не придется export
каждый раз очищать свою оболочку, но и защищает вас от сохранения ваших секретов в истории вашей оболочки.
Создайте файл с именем .env
в том же каталоге, что и bot.py
:
# .env
DISCORD_TOKEN={your-bot-token}
Вам нужно заменить {your-bot-token}
с ботами маркером, который вы можете получить, перейдя обратно на Bot страницу на Developer Portal и нажав Copy под TOKEN раздела:

Оглядываясь на bot.py
код, вы заметите библиотеку под названием dotenv
. Эта библиотека удобна для работы с .env
файлами. load_dotenv()
загружает переменные окружения из .env
файла в переменные окружения вашей оболочки, чтобы вы могли использовать их в своем коде.
Установить dotenv
с помощью pip
:
$ pip install -U python-dotenv
Наконец, client.run()
запускается ваш Client
токен вашего бота.
Теперь, когда вы настроили оба bot.py
и .env
, вы можете запустить свой код:
$ python bot.py
RealPythonTutorialBot#9643 has connected to Discord!
Большой! Вы Client
подключились к Discord, используя токен вашего бота. В следующем разделе вы будете опираться на это Client
, взаимодействуя с другими API-интерфейсами Discord.
Взаимодействие с API Discord
Используя Client
, у вас есть доступ к широкому спектру API Discord.
Например, предположим, что вы хотели записать имя и идентификатор гильдии, в которой вы зарегистрировали своего бота, на консоль.
Сначала вам нужно добавить новую переменную среды:
# .env
DISCORD_TOKEN={your-bot-token}
DISCORD_GUILD={your-guild-name}
Не забывайте, что вам нужно заменить два заполнителя фактическими значениями:
{your-bot-token}
{your-guild-name}
Помните, что вызовы Discord on_ready()
, которые вы использовали ранее, после Client
установления соединения и подготовки данных. Таким образом, вы можете положиться на данные гильдии, доступные внутри on_ready()
:
# bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
client = discord.Client()
@client.event
async def on_ready():
for guild in client.guilds:
if guild.name == GUILD:
break
print(
f'{client.user} is connected to the following guild:\n'
f'{guild.name}(id: {guild.id})'
)
client.run(TOKEN)
Здесь вы просматривали данные гильдии, которые отправила Discord client
, а именно client.guilds
. Затем вы нашли гильдию с соответствующим именем и напечатали отформатированную строку для stdout
.
Примечание. Несмотря на то, что в этом уроке вы можете быть уверены, что ваш бот подключен только к одной гильдии (так client.guilds[0]
будет проще), важно понимать, что пользователь бота может быть подключен ко многим гильдиям.
Следовательно, более надежное решение состоит в том, чтобы просмотреть client.guilds
то, что вы ищете.
Запустите программу, чтобы увидеть результаты:
$ python bot.py
RealPythonTutorialBot#9643 is connected to the following guild:
RealPythonTutorialServer(id: 571759877328732195)
Большой! Вы можете увидеть имя вашего бота, имя вашего сервера и идентификационный номер сервера.
Еще одна интересная часть данных, которую вы можете извлечь из гильдии, это список пользователей, которые являются членами гильдии:
# bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
client = discord.Client()
@client.event
async def on_ready():
for guild in client.guilds:
if guild.name == GUILD:
break
print(
f'{client.user} is connected to the following guild:\n'
f'{guild.name}(id: {guild.id})\n'
)
members = '\n - '.join([member.name for member in guild.members])
print(f'Guild Members:\n - {members}')
client.run(TOKEN)
Проходя по кругу guild.members
, вы вытягивали имена всех членов гильдии и печатали их с форматированной строкой.
Когда вы запустите программу, вы должны увидеть хотя бы имя учетной записи, с которой вы создали гильдию, и имя самого пользователя бота:
$ python bot.py
RealPythonTutorialBot#9643 is connected to the following guild:
RealPythonTutorialServer(id: 571759877328732195)
Guild Members:
- aronq2
- RealPythonTutorialBot
Эти примеры едва касаются поверхности API, доступных на Discord, обязательно ознакомьтесь с их документацией, чтобы увидеть все, что они могут предложить.
Далее вы узнаете о некоторых служебных функциях и о том, как они могут упростить эти примеры.
Использование служебных функций
Давайте еще раз посмотрим на пример из последнего раздела, где вы напечатали имя и идентификатор гильдии бота:
# bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
client = discord.Client()
@client.event
async def on_ready():
for guild in client.guilds:
if guild.name == GUILD:
break
print(
f'{client.user} is connected to the following guild:\n'
f'{guild.name}(id: {guild.id})'
)
client.run(TOKEN)
Вы можете очистить этот код с помощью некоторых утилит, доступных в discord.py
.
discord.utils.find()
это одна утилита, которая может улучшить простоту и удобочитаемость этого кода, заменив for
цикл на интуитивно понятную абстрактную функцию:
# bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
client = discord.Client()
@client.event
async def on_ready():
guild = discord.utils.find(lambda g: g.name == GUILD, client.guilds)
print(
f'{client.user} is connected to the following guild:\n'
f'{guild.name}(id: {guild.id})'
)
client.run(TOKEN)
find()
принимает функцию, называемую предикатом , которая идентифицирует некоторую характеристику элемента в итерируемом элементе, который вы ищете. Здесь вы использовали определенный тип анонимной функции, называемой лямбда-выражением , в качестве предиката.
В этом случае вы пытаетесь найти гильдию с тем же именем, которое вы сохранили в DISCORD_GUILD
переменной среды. Найдя find()
элемент в итерируемом объекте, который удовлетворяет предикату, он вернет элемент. Это по сути эквивалентно break
утверждению в предыдущем примере, но чище.
discord.py
даже абстрагировал эту концепцию на один шаг вперед с помощью get()
утилиты :
# bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
GUILD = os.getenv('DISCORD_GUILD')
client = discord.Client()
@client.event
async def on_ready():
guild = discord.utils.get(client.guilds, name=GUILD)
print(
f'{client.user} is connected to the following guild:\n'
f'{guild.name}(id: {guild.id})'
)
client.run(TOKEN)
get()
принимает итерируемые и некоторые ключевые аргументы. Аргументы ключевого слова представляют атрибуты элементов в итерируемых элементах, которые должны быть удовлетворены для get()
возврата элемента.
В этом примере вы указали name=GUILD
атрибут, который должен быть удовлетворен.
Техническая деталь: на get()
самом деле использует attrs
ключевое слово arguments для построения предиката, который затем использует для вызова find()
.
Теперь, когда вы изучили основы взаимодействия с API – интерфейсами, вы будете нырять немного глубже в функцию , которую вы использовали для доступа к ним: on_ready()
.
Отвечая на события
Вы уже узнали, что on_ready()
это событие. На самом деле, вы могли заметить, что client.event
декоратор идентифицирует его как таковой в коде .
Но что такое событие?
Событие является то , что происходит на Discord , которые вы можете использовать , чтобы вызвать реакцию в вашем коде. Ваш код будет прослушивать, а затем отвечать на события.
Используя пример, который вы уже видели, on_ready()
обработчик событий обрабатывает событие, Client
которое установило соединение с Discord и подготовило его данные ответа.
Таким образом, когда Discord запускает событие, discord.py
маршрутизирует данные события в соответствующий обработчик событий на вашем подключенном Client
.
Есть два способа discord.py
реализовать обработчик событий:
- Использование
client.event
декоратора - Создание подкласса
Client
и переопределение его методов-обработчиков
Вы уже видели реализацию с использованием декоратора. Далее рассмотрим, как создать подкласс Client
:
# bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
class CustomClient(discord.Client):
async def on_ready(self):
print(f'{self.user} has connected to Discord!')
client = CustomClient()
client.run(TOKEN)
Здесь, как и раньше, вы создали client
переменную и вызвали ее .run()
с токеном Discord. На самом деле Client
все по-другому. Вместо использования обычного базового класса client
используется экземпляр класса CustomClient
с переопределенной on_ready()
функцией.
Нет никакой разницы между двумя стилями реализации событий, но этот учебник будет в основном использовать версию декоратора, потому что он выглядит аналогично тому, как вы реализуете Bot
команды, о чем мы немного поговорим.
Техническая деталь: независимо от того, как вы реализуете свой обработчик событий, одна вещь должна быть последовательной: все обработчики событий discord.py
должны быть сопрограммами .
Теперь, когда вы узнали, как создать обработчик событий, давайте рассмотрим несколько различных примеров обработчиков, которые вы можете создать.
Приветствуя новых людей
Ранее вы видели пример ответа на событие, когда член вступает в гильдию. В этом примере пользователь вашего бота может отправить им сообщение, приветствуя их в сообществе Discord.
Теперь вы реализуете это поведение в своем Client
, используя обработчики событий, и проверяете его поведение в Discord:
# bot.py
import os
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
client = discord.Client()
@client.event
async def on_ready():
print(f'{client.user.name} has connected to Discord!')
@client.event
async def on_member_join(member):
await member.create_dm()
await member.dm_channel.send(
f'Hi {member.name}, welcome to my Discord server!'
)
client.run(TOKEN)
Как и раньше, вы обрабатывали on_ready()
событие, печатая имя пользователя бота в отформатированной строке. Новым, однако, является реализация on_member_join()
обработчика событий.
on_member_join()
, как следует из названия, обрабатывает событие вступления нового члена в гильдию.
В этом примере вы использовали member.create_dm()
для создания прямого канала сообщений. Затем вы использовали этот канал для .send()
прямого сообщения этому новому участнику.
Техническая информация: обратите внимание на await
ключевое слово перед member.create_dm()
и member.dm_channel.send()
.
await
приостанавливает выполнение окружающей сопрограммы до тех пор, пока не завершится выполнение каждой сопрограммы.
Теперь давайте проверим новое поведение вашего бота.
Сначала запустите новую версию bot.py
и подождите, пока on_ready()
событие не сработает, записав ваше сообщение по адресу stdout
:
$ python bot.py
RealPythonTutorialBot has connected to Discord!
Теперь перейдите в Discord , войдите в систему и перейдите к своей гильдии, выбрав ее в левой части экрана:

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

Теперь, скопировав ссылку приглашения, создайте новую учетную запись и присоединитесь к гильдии, используя ссылку приглашения:

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

Когда вы выберете его, вы увидите личное сообщение от вашего пользователя бота:

Отлично! Ваш бот-пользователь теперь взаимодействует с другими пользователями с минимальным кодом.
Далее вы узнаете, как отвечать на конкретные сообщения пользователей в чате.
Отвечая на сообщения
Давайте добавим к предыдущей функциональности вашего бота, обработав on_message()
событие.
on_message()
происходит, когда сообщение публикуется на канале, к которому у вашего бота есть доступ. В этом примере вы ответите на сообщение '99!'
одной строчкой из телешоу « Бруклинские девять-девять» :
@client.event
async def on_message(message):
if message.author == client.user:
return
brooklyn_99_quotes = [
'I\'m the human form of the 💯 emoji.',
'Bingpot!',
(
'Cool. Cool cool cool cool cool cool cool, '
'no doubt no doubt no doubt no doubt.'
),
]
if message.content == '99!':
response = random.choice(brooklyn_99_quotes)
await message.channel.send(response)
Большая часть этого обработчика событий просматривает message.content
, проверяет, равно ли оно '99!'
и отвечает, отправляя случайную кавычку на канал сообщения, если она есть.
Другая часть важна:
if message.author == client.user:
return
Поскольку Client
невозможно определить разницу между пользователем бота и учетной записью обычного пользователя, ваш on_message()
обработчик должен защитить от потенциально рекурсивного случая, когда бот отправляет сообщение, которое он сам может обработать.
Для иллюстрации, скажем, вы хотите, чтобы ваш бот слушал, как пользователи рассказывают друг другу 'Happy Birthday'
. Вы можете реализовать свой on_message()
обработчик следующим образом:
@client.event
async def on_message(message):
if 'happy birthday' in message.content.lower():
await message.channel.send('Happy Birthday! 🎈🎉')
Помимо потенциально спамовой природы этого обработчика событий, он также имеет разрушительный побочный эффект. Сообщение, которым отвечает бот, содержит то же сообщение, которое он собирается обработать!
Итак, если один человек на канале скажет другому «С Днем Рождения», тогда бот тоже включится… снова… и снова… и снова:

Вот почему важно сравнивать message.author
их с client.user
(пользователем вашего бота) и игнорировать любые его собственные сообщения.
Итак, давайте исправим bot.py
:
# bot.py
import os
import random
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
client = discord.Client()
@client.event
async def on_ready():
print(f'{client.user.name} has connected to Discord!')
@client.event
async def on_member_join(member):
await member.create_dm()
await member.dm_channel.send(
f'Hi {member.name}, welcome to my Discord server!'
)
@client.event
async def on_message(message):
if message.author == client.user:
return
brooklyn_99_quotes = [
'I\'m the human form of the 💯 emoji.',
'Bingpot!',
(
'Cool. Cool cool cool cool cool cool cool, '
'no doubt no doubt no doubt no doubt.'
),
]
if message.content == '99!':
response = random.choice(brooklyn_99_quotes)
await message.channel.send(response)
client.run(TOKEN)
Не забудьте import random
в верхней части модуля, так как on_message()
обработчик использует random.choice()
.
Запустите программу:
$ python bot.py
RealPythonTutorialBot has connected to Discord!
Наконец, отправляйтесь в Discord, чтобы проверить это:

Большой! Теперь, когда вы увидели несколько различных способов обработки некоторых распространенных событий Discord, вы узнаете, как обращаться с ошибками, которые могут вызывать обработчики событий.
Обработка исключений
Как вы уже видели, discord.py
это система, управляемая событиями. Этот акцент на событиях распространяется даже на исключения. Когда один обработчик события вызываетException
, Discord вызывает on_error()
.
Поведение по умолчанию on_error()
для записи сообщения об ошибке и трассировки стека stderr
. Чтобы проверить это, добавьте специальный обработчик сообщений в on_message()
:
# bot.py
import os
import random
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
client = discord.Client()
@client.event
async def on_ready():
print(f'{client.user.name} has connected to Discord!')
@client.event
async def on_member_join(member):
await member.create_dm()
await member.dm_channel.send(
f'Hi {member.name}, welcome to my Discord server!'
)
@client.event
async def on_message(message):
if message.author == client.user:
return
brooklyn_99_quotes = [
'I\'m the human form of the 💯 emoji.',
'Bingpot!',
(
'Cool. Cool cool cool cool cool cool cool, '
'no doubt no doubt no doubt no doubt.'
),
]
if message.content == '99!':
response = random.choice(brooklyn_99_quotes)
await message.channel.send(response)
elif message.content == 'raise-exception':
raise discord.DiscordException
client.run(TOKEN)
Новый raise-exception
обработчик сообщений позволяет вам вызывать DiscordException
команду включения.
Запустите программу и введите raise-exception
в канал Discord:

Теперь вы должны увидеть, Exception
что поднял ваш on_message()
обработчик в консоли:
$ python bot.py
RealPythonTutorialBot has connected to Discord!
Ignoring exception in on_message
Traceback (most recent call last):
File "/Users/alex.ronquillo/.pyenv/versions/discord-venv/lib/python3.7/site-packages/discord/client.py", line 255, in _run_event
await coro(*args, **kwargs)
File "bot.py", line 42, in on_message
raise discord.DiscordException
discord.errors.DiscordException
Исключение было перехвачено обработчиком ошибок по умолчанию, поэтому вывод содержит сообщение Ignoring exception in on_message
. Давайте исправим это, обработав эту конкретную ошибку. Для этого вы поймаете DiscordException
и запишите его в файл .
on_error()
Обработчик события принимает в event
качестве первого аргумента. В этом случае мы ожидаем, что event
будет 'on_message'
. Он также принимает *args
и **kwargs
как гибкие, позиционные и ключевые аргументы, передаваемые исходному обработчику событий.
Итак, поскольку он on_message()
принимает один аргумент, message
мы ожидаем, args[0]
что он будет message
отправлен пользователем в канале Discord:
@client.event
async def on_error(event, *args, **kwargs):
with open('err.log', 'a') as f:
if event == 'on_message':
f.write(f'Unhandled message: {args[0]}\n')
else:
raise
Если Exception
возникла в on_message()
обработчике событий, вы .write()
отформатировали строку в файл err.log
. Если возникает другое событие Exception
, мы просто хотим, чтобы наш обработчик повторно вызвал исключение, чтобы вызвать поведение по умолчанию.
Запустите bot.py
и отправьте raise-exception
сообщение еще раз, чтобы просмотреть вывод в err.log
:
$ cat err.log
Unhandled message: <Message id=573845548923224084 pinned=False author=<Member id=543612676807327754 name='alexronquillo' discriminator='0933' bot=False nick=None guild=<Guild id=571759877328732195 name='RealPythonTutorialServer' chunked=True>>>
Вместо только трассировки стека, у вас есть более информативная ошибка, показывающая, message
что вызвало on_message()
поднять DiscordException
, сохраненный в файл для более длительного хранения.
Техническая информация: если вы хотите учесть фактические данные Exception
при написании сообщений об ошибках err.log
, вы можете использовать функции из sys
, например exc_info()
.
Теперь, когда у вас есть некоторый опыт работы с различными событиями и взаимодействия с Discord API , вы узнаете о подклассе Client
под названием Bot
, который реализует некоторые полезные, личинка конкретной функциональности.
Подключение бота
A Bot
является подклассом, Client
который добавляет немного дополнительной функциональности, которая полезна при создании пользователей ботов. Например, объект Bot
может обрабатывать события и команды, вызывать проверки правильности и многое другое.
Прежде чем перейти к функциям, специфичным для Bot
, преобразуйте bot.py
в использование Bot
вместо Client
:
# bot.py
import os
import random
from dotenv import load_dotenv
# 1
from discord.ext import commands
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
# 2
bot = commands.Bot(command_prefix='!')
@bot.event
async def on_ready():
print(f'{bot.user.name} has connected to Discord!')
bot.run(TOKEN)
Как видите, Bot
можете обрабатывать события так же, как это Client
делают. Однако обратите внимание на различия между Client
и Bot
:
Bot
импортируется изdiscord.ext.commands
модуля.Bot
Инициализатор требуетcommand_prefix
, что вы узнаете больше в следующем разделе.
Библиотека расширений ext
предлагает несколько интересных компонентов, которые помогут вам создать Discord Bot
. Одним из таких компонентов является Command
.
Использование Bot
команд
В общем, команда – это порядок, который пользователь дает боту, чтобы он что-то делал. Команды отличаются от событий, потому что они:
- Произвольно определено
- Напрямую вызывается пользователем
- Гибкий, с точки зрения их интерфейса
С технической точки зрения, a Command
– это объект, который оборачивает функцию, которая вызывается текстовой командой в Discord. Текстовая команда должна начинаться с command_prefix
, определенного Bot
объектом.
Давайте посмотрим на старое событие, чтобы лучше понять, как оно выглядит:
# bot.py
import os
import random
import discord
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
client = discord.Client()
@client.event
async def on_message(message):
if message.author == client.user:
return
brooklyn_99_quotes = [
'I\'m the human form of the 💯 emoji.',
'Bingpot!',
(
'Cool. Cool cool cool cool cool cool cool, '
'no doubt no doubt no doubt no doubt.'
),
]
if message.content == '99!':
response = random.choice(brooklyn_99_quotes)
await message.channel.send(response)
client.run(TOKEN)
Здесь вы создали on_message()
обработчик событий, который принимает message
строку и сравнивает ее с заранее определенным параметром: '99!'
.
Используя Command
, вы можете преобразовать этот пример, чтобы быть более конкретным:
# bot.py
import os
import random
from discord.ext import commands
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
bot = commands.Bot(command_prefix='!')
@bot.command(name='99')
async def nine_nine(ctx):
brooklyn_99_quotes = [
'I\'m the human form of the 💯 emoji.',
'Bingpot!',
(
'Cool. Cool cool cool cool cool cool cool, '
'no doubt no doubt no doubt no doubt.'
),
]
response = random.choice(brooklyn_99_quotes)
await ctx.send(response)
bot.run(TOKEN)
Есть несколько важных характеристик, которые следует понимать при использовании Command
:
- Вместо того, чтобы использовать
bot.event
как прежде, вы используетеbot.command()
, передавая команду invocation (name
) в качестве аргумента. - Функция теперь будет вызываться только когда
!99
упоминается в чате. Это отличается отon_message()
события, которое было выполнено каждый раз, когда пользователь отправлял сообщение, независимо от содержимого. - Команде должен предшествовать восклицательный знак (
!
), потому что это то,command_prefix
что вы определили в инициализаторе для вашегоBot
. - Любая
Command
функция (технически называемая acallback
) должна принимать как минимум один вызываемый параметрctx
, который являетсяContext
окружением вызванногоCommand
.
A Context
содержит данные, такие как канал и гильдия, которые пользователь вызвал Command
из.
Запустите программу:
$ python bot.py
Теперь, когда ваш бот запущен, вы можете отправиться в Discord, чтобы опробовать новую команду:

С точки зрения пользователя, практическое отличие состоит в том, что префикс помогает формализовать команду, а не просто реагирует на конкретное on_message()
событие.
Это дает и другие большие преимущества. Например, вы можете вызвать help
команду, чтобы увидеть все команды, которые вы Bot
обрабатываете:

Если вы хотите добавить описание к вашей команде, чтобы help
сообщение было более информативным, просто передайте help
описание .command()
декоратору:
# bot.py
import os
import random
from discord.ext import commands
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
bot = commands.Bot(command_prefix='!')
@bot.command(name='99', help='Responds with a random quote from Brooklyn 99')
async def nine_nine(ctx):
brooklyn_99_quotes = [
'I\'m the human form of the 💯 emoji.',
'Bingpot!',
(
'Cool. Cool cool cool cool cool cool cool, '
'no doubt no doubt no doubt no doubt.'
),
]
response = random.choice(brooklyn_99_quotes)
await ctx.send(response)
bot.run(TOKEN)
Теперь, когда пользователь вызывает help
команду, ваш бот представит описание вашей команды:

Имейте в виду, что все эти функции существуют только для Bot
подкласса, но не для Client
суперкласса.
Command
имеет еще одну полезную функциональность: возможность использовать Converter
для изменения типов своих аргументов.
Автоматическое преобразование параметров
Еще одним преимуществом использования команд является возможность преобразования параметров.
Иногда требуется, чтобы параметр был определенного типа, но аргументы Command
функции по умолчанию являются строками. A Converter
позволяет вам преобразовать эти параметры в тип, который вы ожидаете.
Например, если вы хотите создать Command
для своего бота пользователя, который будет имитировать бросание костей (зная, что вы уже узнали), вы можете определить его так:
@bot.command(name='roll_dice', help='Simulates rolling dice.')
async def roll(ctx, number_of_dice, number_of_sides):
dice = [
str(random.choice(range(1, number_of_sides + 1)))
for _ in range(number_of_dice)
]
await ctx.send(', '.join(dice))
Вы решили roll
принять два параметра:
- Количество игральных костей
- Количество сторон на кубик
Затем вы украсили его, .command()
чтобы вы могли вызвать его с помощью !roll_dice
команды. Наконец, вы .send()
результаты в сообщении обратно в channel
.
Хотя это выглядит правильно, это не так. К сожалению, если вы запустите bot.py
и вызовете !roll_dice
команду на своем канале Discord, вы увидите следующую ошибку:
$ python bot.py
Ignoring exception in command roll_dice:
Traceback (most recent call last):
File "/Users/alex.ronquillo/.pyenv/versions/discord-venv/lib/python3.7/site-packages/discord/ext/commands/core.py", line 63, in wrapped
ret = await coro(*args, **kwargs)
File "bot.py", line 40, in roll
for _ in range(number_of_dice)
TypeError: 'str' object cannot be interpreted as an integer
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/alex.ronquillo/.pyenv/versions/discord-venv/lib/python3.7/site-packages/discord/ext/commands/bot.py", line 860, in invoke
await ctx.command.invoke(ctx)
File "/Users/alex.ronquillo/.pyenv/versions/discord-venv/lib/python3.7/site-packages/discord/ext/commands/core.py", line 698, in invoke
await injected(*ctx.args, **ctx.kwargs)
File "/Users/alex.ronquillo/.pyenv/versions/discord-venv/lib/python3.7/site-packages/discord/ext/commands/core.py", line 72, in wrapped
raise CommandInvokeError(exc) from exc
discord.ext.commands.errors.CommandInvokeError: Command raised an exception: TypeError: 'str' object cannot be interpreted as an integer
Другими словами, range()
не может принять str
в качестве аргумента. Вместо этого это должно быть int
. В то время как вы можете привести каждое значение к a int
, есть лучший способ: вы можете использовать a Converter
.
В discord.py
, a Converter
определяется с помощью аннотаций функций Python 3 :
@bot.command(name='roll_dice', help='Simulates rolling dice.')
async def roll(ctx, number_of_dice: int, number_of_sides: int):
dice = [
str(random.choice(range(1, number_of_sides + 1)))
for _ in range(number_of_dice)
]
await ctx.send(', '.join(dice))
Вы добавили : int
аннотации к двум параметрам, которые, как вы ожидаете, будут иметь тип int
. Попробуйте команду еще раз:

С этим небольшим изменением ваша команда работает! Разница в том, что теперь вы конвертируете аргументы команды int
, что делает их совместимыми с логикой вашей функции.
Примечание: A Converter
может быть любым вызываемым, а не просто типом данных. Аргумент будет передан в вызываемый объект, а возвращаемое значение будет передано в Command
.
Далее вы узнаете об Check
объекте и о том, как он может улучшить ваши команды.
Проверка предикатов команд
A Check
– это предикат, который оценивается перед выполнением a, Command
чтобы гарантировать, что Context
окружение Command
вызова допустимо.
В предыдущем примере вы сделали нечто подобное, чтобы убедиться, что пользователь, отправивший сообщение, которое обрабатывает бот, сам не был пользователем бота:
if message.author == client.user:
return
commands
Расширение предоставляет более чистое и более удобный механизм для выполнения такой проверки, а именно с использованием Check
объектов.
Чтобы продемонстрировать, как это работает, предположим, что вы хотите поддержать команду, !create_channel <channel_name>
которая создает новый канал. Однако вы только хотите разрешить администраторам создавать новые каналы с помощью этой команды.
Во-первых, вам нужно создать новую роль члена в администраторе. Зайдите в гильдию Discord и выберите меню {Имя сервера} → Настройки сервера :

Затем выберите Роли из списка навигации слева:

Наконец, выберите знак + рядом с ROLES, введите имя admin
и выберите Сохранить изменения :

Теперь вы создали admin
роль, которую вы можете назначить конкретным пользователям. Далее вы обновитесь bot.py
до Check
роли пользователя, прежде чем позволить ему инициировать команду:
# bot.py
import os
import discord
from discord.ext import commands
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
bot = commands.Bot(command_prefix='!')
@bot.command(name='create-channel')
@commands.has_role('admin')
async def create_channel(ctx, channel_name='real-python'):
guild = ctx.guild
existing_channel = discord.utils.get(guild.channels, name=channel_name)
if not existing_channel:
print(f'Creating a new channel: {channel_name}')
await guild.create_text_channel(channel_name)
bot.run(TOKEN)
В bot.py
, у вас есть новая Command
функция, create_channel()
которая называется необязательной channel_name
и создает этот канал. create_channel()
также украшен Check
призванным has_role()
.
Вы также используете, discord.utils.get()
чтобы не создавать канал с тем же именем, что и существующий канал.
Если вы запустите эту программу как есть и !create-channel
наберете свой канал Discord, вы увидите следующее сообщение об ошибке:
$ python bot.py
Ignoring exception in command create-channel:
Traceback (most recent call last):
File "/Users/alex.ronquillo/.pyenv/versions/discord-venv/lib/python3.7/site-packages/discord/ext/commands/bot.py", line 860, in invoke
await ctx.command.invoke(ctx)
File "/Users/alex.ronquillo/.pyenv/versions/discord-venv/lib/python3.7/site-packages/discord/ext/commands/core.py", line 691, in invoke
await self.prepare(ctx)
File "/Users/alex.ronquillo/.pyenv/versions/discord-venv/lib/python3.7/site-packages/discord/ext/commands/core.py", line 648, in prepare
await self._verify_checks(ctx)
File "/Users/alex.ronquillo/.pyenv/versions/discord-venv/lib/python3.7/site-packages/discord/ext/commands/core.py", line 598, in _verify_checks
raise CheckFailure('The check functions for command {0.qualified_name} failed.'.format(self))
discord.ext.commands.errors.CheckFailure: The check functions for command create-channel failed.
Это CheckFailure
говорит о том, что has_role('admin')
не удалось. К сожалению, эта ошибка печатается только в stdout
. Было бы лучше сообщить об этом пользователю в канале. Для этого добавьте следующее событие:
@bot.event
async def on_command_error(ctx, error):
if isinstance(error, commands.errors.CheckFailure):
await ctx.send('You do not have the correct role for this command.')
Это событие обрабатывает событие ошибки из команды и отправляет информативное сообщение об ошибке обратно в оригинал Context
вызванного Command
.
Попробуйте все это снова, и вы должны увидеть ошибку в канале Discord:

Большой! Теперь, чтобы решить проблему, вам нужно дать себе роль администратора :

С ролью администратора ваш пользователь передаст Check
и сможет создавать каналы с помощью команды.
Примечание. Помните, что для назначения роли у вашего пользователя должны быть правильные разрешения. Самый простой способ убедиться в этом – войти в систему с пользователем, с которым вы создали гильдию.
При !create-channel
повторном вводе вы успешно создадите канал real-python :

Также обратите внимание, что вы можете передать необязательный channel_name
аргумент, чтобы присвоить каналу имя, которое вы хотите!
В последнем примере вы объединили a Command
, event, a Check
и даже get()
утилиту для создания полезного бота Discord!
Вывод
Поздравляем! Теперь вы узнали, как создать бот Discord в Python. Вы можете создавать ботов для взаимодействия с пользователями в созданных вами гильдиях или даже ботов, которых другие пользователи могут приглашать для взаимодействия со своими сообществами. Ваши боты смогут отвечать на сообщения и команды и множество других событий.
В этом уроке вы узнали основы создания своего собственного бота Discord. Теперь вы знаете:
- Что такое Discord
- Почему
discord.py
так ценно - Как сделать бот Discord на портале разработчиков
- Как создать Discord соединение в Python
- Как обрабатывать события
- Как создать
Bot
соединение - Как использовать команды ботов, проверки и конвертеры
Чтобы узнать больше о мощной discord.py
библиотеке и поднять ботов на новый уровень, прочитайте их обширную документацию . Кроме того, теперь, когда вы знакомы с API-интерфейсами Discord в целом, у вас есть лучшая основа для создания других типов приложений Discord.