Skip to content

Bot

The Bot class is a subclass of discord.ext.commands.Bot and drives your bot's lifecycle: loading extensions, syncing application commands, running the scheduler, and — when enabled — hot-reloading on file changes.

from logging import info

from grace.bot import Bot


class TaskBot(Bot):
    async def on_ready(self):
        info(f"{self.user.name}:#{self.user.id} is online and ready to use!")

grace.bot.Bot

Bot(app, **kwargs)

Bases: Bot

This class is the core of the bot

This class is a subclass of discord.ext.commands.Bot and is the core of the bot. It is responsible for loading the extensions and syncing the commands.

The bot is instantiated with the application object and the intents.

Source code in grace/bot.py
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
def __init__(self, app: Application, **kwargs) -> None:
    self.app: Application = app
    self.config: SectionProxy = self.app.client
    self.scheduler: AsyncIOScheduler = AsyncIOScheduler()
    self.watcher: Watcher = Watcher(self.on_reload)

    command_prefix = kwargs.pop(
        "command_prefix", when_mentioned_or(self.config.get("prefix", "!"))
    )
    description: str = kwargs.pop("description", self.config.get("description"))
    intents: Intents = kwargs.pop("intents", Intents.default())

    super().__init__(
        command_prefix=command_prefix,
        description=description,
        intents=intents,
        **kwargs,
    )

app instance-attribute

app = app

config instance-attribute

config = self.app.client

scheduler instance-attribute

scheduler = AsyncIOScheduler()

watcher instance-attribute

watcher = Watcher(self.on_reload)

load_extensions async

load_extensions()
Source code in grace/bot.py
43
44
45
46
async def load_extensions(self) -> None:
    for module in self.app.extension_modules:
        info(f"Loading module '{module}'")
        await self.load_extension(module)

sync_commands async

sync_commands()
Source code in grace/bot.py
48
49
50
51
52
53
54
async def sync_commands(self) -> None:
    warning("Syncing application commands. This may take some time.")

    if guild_id := self.config.get("guild_id"):
        guild = DiscordObject(id=guild_id)
        self.tree.copy_global_to(guild=guild)
        await self.tree.sync(guild=guild)

invoke async

invoke(ctx)
Source code in grace/bot.py
56
57
58
59
60
61
62
async def invoke(self, ctx):
    if ctx.command:
        info(
            f"'{ctx.command}' has been invoked by {ctx.author} "
            f"({ctx.author.display_name})"
        )
    await super().invoke(ctx)

setup_hook async

setup_hook()
Source code in grace/bot.py
64
65
66
67
68
69
70
71
72
73
async def setup_hook(self) -> None:
    await self.load_extensions()

    if self.app.command_sync:
        await self.sync_commands()

    if self.app.watch:
        self.watcher.start()

    self.scheduler.start()

load_extension async

load_extension(name)
Source code in grace/bot.py
75
76
77
78
79
async def load_extension(self, name: str) -> None:  # type: ignore[override]
    try:
        await super().load_extension(name)
    except ExtensionAlreadyLoaded:
        warning(f"Extension '{name}' already loaded, skipping.")

unload_extension async

unload_extension(name)
Source code in grace/bot.py
81
82
83
84
85
async def unload_extension(self, name: str) -> None:  # type: ignore[override]
    try:
        await super().unload_extension(name)
    except ExtensionNotLoaded:
        warning(f"Extension '{name}' was not loaded, skipping.")

on_reload async

on_reload()
Source code in grace/bot.py
87
88
89
90
91
92
async def on_reload(self):
    for module in self.app.extension_modules:
        info(f"Reloading extension '{module}'")

        await self.unload_extension(module)
        await self.load_extension(module)

run

run(**kwargs)

Override the run method to handle the token retrieval

Source code in grace/bot.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
def run(self, **kwargs) -> None:  # type: ignore[override]
    """Override the `run` method to handle the token retrieval"""
    try:
        if self.app.token:
            super().run(self.app.token, **kwargs)
        else:
            critical(
                "Unable to find the token. Make sure your current"
                "directory contains an '.env' and that "
                "'DISCORD_TOKEN' is defined"
            )
    except LoginFailure as e:
        critical(f"Authentication failed : {e}")