Discord.py
lastmod 2025-02-14created 2024-03-0610 edits
discord.py is a modern, easy to use, feature-rich, and async-ready API wrapper for Discord.
Features
- Modern Pythonic API using async/await syntax
- Sane rate limit handling that prevents 429 errors
- Implements the entire Discord API
- Command extension to aid with bot creation
- Easy to use with an object-oriented design
- Optimised for both speed and memory
Install
Example
# -*- coding: utf-8 -*-
from argparse import Namespace
from discord import Intents
from discord.ext.commands import Bot
from discord.ext.commands.context import Context as CommandContext
from overrides import override
from api.aio.run import aio_run
from api.apps.discord.config import DiscordConfig
from api.arguments import version as version
from api.context.context import CommonContext
from api.logging.logging import logger
class DiscordContext(CommonContext):
def __init__(self, args: Namespace):
self._config = DiscordConfig.from_namespace(args)
super().__init__(self._config)
self._version = version()
self._intents = Intents.default()
self._intents.typing = False
self._intents.presences = False
self._intents.message_content = True
self._bot = bot = Bot(
command_prefix="$",
intents=self._intents,
sync_command=True,
application_id=self._config.discord_application_id,
)
@bot.command(help="Shows version number")
async def version(ctx: CommandContext) -> None:
await self.on_version(ctx)
@override
async def on_mq_connect(self) -> None:
logger.info("Connection to redis was successful!")
@override
async def on_mq_subscribe(self, channel: bytes, data: bytes) -> None:
logger.info(f"Recv sub msg channel: {channel!r} -> {data!r}")
@override
async def on_mq_done(self) -> None:
logger.warning("Redis task is done")
async def on_version(self, ctx: CommandContext) -> None:
await ctx.send(self._version)
async def main(self) -> None:
await self.open_common_context()
try:
await self._bot.start(token=self._config.discord_token)
finally:
await self.close_common_context()
def run(self) -> None:
aio_run(self.main(), self._config.use_uvloop)
Event
- API Reference # Event Reference
- 4. About events - Making a Discord bot with Python
- Discord bot (Python) 06 | Learning discord.py event functions and interacting with them.
- python - How to make Discord.py respond to a single message event twice - Stack Overflow
Example:
@bot.event
async def on_message(message):
if message.author == bot.user:
return
# ignore all channels but the specified one
if message.channel.id == requestsChannelID:
# record request
with open('requests.txt', 'a') as f:
print(repr(message.content), file=f)
reply = await message.channel.send('Got it!')
# wait for 10 seconds, then delete message and reply
await asyncio.sleep(10)
await message.delete()
await reply.delete()
else:
# process other commands (if there are none, delete this line)
await bot.process_commands(message)
File download
@client.event
async def on_message(message):
if len(message.attachments) > 0:
attachment = message.attachments[0]
if (
attachment.filename.endswith(".jpg")
or attachment.filename.endswith(".jpeg")
or attachment.filename.endswith(".png")
or attachment.filename.endswith(".webp")
or attachment.filename.endswith(".gif")
):
img_data = requests.get(attachment.url).content
with open("image_name.jpg", "wb") as handler:
handler.write(img_data)
elif (
"https://images-ext-1.discordapp.net" in message.content
or "https://tenor.com/view/" in message.content
):
print(message.content)
Checks
To register a check for a command, we would have two ways of doing so. The first is using the check() decorator. For example:
async def is_owner(ctx):
return ctx.author.id == 316026178463072268
@bot.command(name='eval')
@commands.check(is_owner)
async def _eval(ctx, *, code):
"""A bad example of an eval command"""
await ctx.send(eval(code))
This would only evaluate the command if the function is_owner returns True. Sometimes we re-use a check often and want to split it into its own decorator. To do that we can just add another level of depth: content_copy
def is_owner():
async def predicate(ctx):
return ctx.author.id == 316026178463072268
return commands.check(predicate)
@bot.command(name='eval')
@is_owner()
async def _eval(ctx, *, code):
"""A bad example of an eval command"""
await ctx.send(eval(code))