Browse Source
shows setup_hook and async context manager use with examples approximating real world use casespull/8221/head
committed by
GitHub
1 changed files with 94 additions and 0 deletions
@ -0,0 +1,94 @@ |
|||
# This example covers advanced startup options and uses some real world examples for why you may need them. |
|||
|
|||
import asyncio |
|||
import logging |
|||
import logging.handlers |
|||
import os |
|||
|
|||
from typing import List, Optional |
|||
|
|||
import asyncpg # asyncpg is not a dependency of the discord.py, and is only included here for illustrative purposes. |
|||
import discord |
|||
from discord.ext import commands |
|||
from aiohttp import ClientSession |
|||
|
|||
|
|||
class CustomBot(commands.Bot): |
|||
def __init__( |
|||
self, |
|||
*args, |
|||
initial_extensions: List[str], |
|||
db_pool: asyncpg.Pool, |
|||
web_client: ClientSession, |
|||
testing_guild_id: Optional[int] = None, |
|||
**kwargs, |
|||
): |
|||
super().__init__(*args, **kwargs) |
|||
self.db_pool = db_pool |
|||
self.web_client = web_client |
|||
self.testing_guild_id = testing_guild_id |
|||
self.initial_extensions = initial_extensions |
|||
|
|||
async def setup_hook(self) -> None: |
|||
|
|||
# here, we are loading extensions prior to sync to ensure we are syncing interactions defined in those extensions. |
|||
|
|||
for extension in self.initial_extensions: |
|||
await self.load_extension(extension) |
|||
|
|||
# In overriding setup hook, |
|||
# we can do things that require a bot prior to starting to process events from the websocket. |
|||
# In this case, we are using this to ensure that once we are connected, we sync for the testing guild. |
|||
# You should not do this for every guild or for global sync, those should only be synced when changes happen. |
|||
if self.testing_guild_id: |
|||
guild = discord.Object(self.testing_guild_id) |
|||
# We'll copy in the global commands to test with: |
|||
self.tree.copy_global_to(guild=guild) |
|||
# followed by syncing to the testing guild. |
|||
await self.tree.sync(guild=guild) |
|||
|
|||
# This would also be a good place to connect to our database and |
|||
# load anything that should be in memory prior to handling events. |
|||
|
|||
|
|||
async def main(): |
|||
|
|||
# When taking over how the bot process is run, you become responsible for a few additional things. |
|||
|
|||
# 1. logging |
|||
|
|||
# for this example, we're going to set up a rotating file logger. |
|||
# for more info on setting up logging, |
|||
# see https://discordpy.readthedocs.io/en/latest/logging.html and https://docs.python.org/3/howto/logging.html |
|||
|
|||
logger = logging.getLogger('discord') |
|||
logger.setLevel(logging.INFO) |
|||
|
|||
handler = logging.handlers.RotatingFileHandler( |
|||
filename='discord.log', |
|||
encoding='utf-8', |
|||
maxBytes=32 * 1024 * 1024, # 32 MiB |
|||
backupCount=5, # Rotate through 5 files |
|||
) |
|||
dt_fmt = '%Y-%m-%d %H:%M:%S' |
|||
formatter = logging.Formatter('[{asctime}] [{levelname:<8}] {name}: {message}', dt_fmt, style='{') |
|||
handler.setFormatter(formatter) |
|||
logger.addHandler(handler) |
|||
|
|||
# One of the reasons to take over more of the process though |
|||
# is to ensure use with other libraries or tools which also require their own cleanup. |
|||
|
|||
# Here we have a web client and a database pool, both of which do cleanup at exit. |
|||
# We also have our bot, which depends on both of these. |
|||
|
|||
async with ClientSession() as our_client, asyncpg.create_pool(user='postgres', command_timeout=30) as pool: |
|||
# 2. We become responsible for starting the bot. |
|||
|
|||
exts = ['general', 'mod', 'dice'] |
|||
async with CustomBot(commands.when_mentioned, db_pool=pool, web_client=our_client, initial_extensions=exts) as bot: |
|||
|
|||
await bot.start(os.getenv('TOKEN', '')) |
|||
|
|||
|
|||
# For most use cases, after defining what needs to run, we can just tell asyncio to run it: |
|||
asyncio.run(main()) |
Loading…
Reference in new issue