diff options
| author | 2022-09-23 22:58:49 +0100 | |
|---|---|---|
| committer | 2022-09-23 22:58:49 +0100 | |
| commit | f9cc77f55a7bac9cff1f5674b36b3f17560f6bfe (patch) | |
| tree | 4d9a9684d6c0d8f1f749355353fbadb7fd89960b /bot/bot.py | |
| parent | Fix issue #1050 (#1097) (diff) | |
| parent | Remove all wait_until_guil_available as this is now done in bot-core (diff) | |
Merge pull request #1092 from python-discord/bot-core-migration
Diffstat (limited to 'bot/bot.py')
| -rw-r--r-- | bot/bot.py | 198 | 
1 files changed, 20 insertions, 178 deletions
| @@ -1,24 +1,20 @@ -import asyncio  import logging -import socket -from contextlib import suppress  from typing import Optional  import discord -from aiohttp import AsyncResolver, ClientSession, TCPConnector -from async_rediscache import RedisSession -from discord import DiscordException, Embed, Forbidden, Thread +from botcore import BotBase +from botcore.utils import scheduling +from discord import DiscordException, Embed  from discord.ext import commands -from discord.ext.commands import Cog, when_mentioned_or -from bot import constants +from bot import constants, exts  log = logging.getLogger(__name__) -__all__ = ("Bot", "bot") +__all__ = ("Bot", ) -class Bot(commands.Bot): +class Bot(BotBase):      """      Base bot instance. @@ -29,16 +25,6 @@ class Bot(commands.Bot):      name = constants.Client.name -    def __init__(self, redis_session: RedisSession, **kwargs): -        super().__init__(**kwargs) -        self.http_session = ClientSession( -            connector=TCPConnector(resolver=AsyncResolver(), family=socket.AF_INET) -        ) -        self._guild_available = asyncio.Event() -        self.redis_session = redis_session -        self.loop.create_task(self.check_channels()) -        self.loop.create_task(self.send_log(self.name, "Connected!")) -      @property      def member(self) -> Optional[discord.Member]:          """Retrieves the guild member object for the bot.""" @@ -47,60 +33,6 @@ class Bot(commands.Bot):              return None          return guild.me -    @Cog.listener() -    async def on_thread_join(self, thread: Thread) -> None: -        """ -        Try to join newly created threads. - -        Despite the event name being misleading, this is dispatched when new threads are created. -        We want our bots to automatically join threads in order to answer commands using their prefixes. -        """ -        if thread.me: -            # Already in this thread, return early -            return - -        with suppress(Forbidden): -            await thread.join() - -    async def close(self) -> None: -        """Close Redis session when bot is shutting down.""" -        await super().close() - -        if self.http_session: -            await self.http_session.close() - -        if self.redis_session: -            await self.redis_session.close() - -    def add_cog(self, cog: commands.Cog) -> None: -        """ -        Delegate to super to register `cog`. - -        This only serves to make the info log, so that extensions don't have to. -        """ -        super().add_cog(cog) -        log.info(f"Cog loaded: {cog.qualified_name}") - -    def add_command(self, command: commands.Command) -> None: -        """Add `command` as normal and then add its root aliases to the bot.""" -        super().add_command(command) -        self._add_root_aliases(command) - -    def remove_command(self, name: str) -> Optional[commands.Command]: -        """ -        Remove a command/alias as normal and then remove its root aliases from the bot. - -        Individual root aliases cannot be removed by this function. -        To remove them, either remove the entire command or manually edit `bot.all_commands`. -        """ -        command = super().remove_command(name) -        if command is None: -            # Even if it's a root alias, there's no way to get the Bot instance to remove the alias. -            return - -        self._remove_root_aliases(command) -        return command -      async def on_command_error(self, context: commands.Context, exception: DiscordException) -> None:          """Check command errors for UserInputError and reset the cooldown if thrown."""          if isinstance(exception, commands.UserInputError): @@ -108,34 +40,10 @@ class Bot(commands.Bot):          else:              await super().on_command_error(context, exception) -    async def check_channels(self) -> None: -        """Verifies that all channel constants refer to channels which exist.""" -        await self.wait_until_guild_available() - -        if constants.Client.debug: -            log.info("Skipping Channels Check.") -            return - -        all_channels_ids = [channel.id for channel in self.get_all_channels()] -        for name, channel_id in vars(constants.Channels).items(): -            if name.startswith("_"): -                continue -            if channel_id not in all_channels_ids: -                log.error(f'Channel "{name}" with ID {channel_id} missing') - -    async def send_log(self, title: str, details: str = None, *, icon: str = None) -> None: -        """Send an embed message to the devlog channel.""" -        await self.wait_until_guild_available() +    async def log_to_dev_log(self, title: str, details: str = None, *, icon: str = None) -> None: +        """Send an embed message to the dev-log channel."""          devlog = self.get_channel(constants.Channels.devlog) -        if not devlog: -            log.info(f"Fetching devlog channel as it wasn't found in the cache (ID: {constants.Channels.devlog})") -            try: -                devlog = await self.fetch_channel(constants.Channels.devlog) -            except discord.HTTPException as discord_exc: -                log.exception("Fetch failed", exc_info=discord_exc) -                return -          if not icon:              icon = self.user.display_avatar.url @@ -144,84 +52,18 @@ class Bot(commands.Bot):          await devlog.send(embed=embed) -    async def on_guild_available(self, guild: discord.Guild) -> None: -        """ -        Set the internal `_guild_available` event when PyDis guild becomes available. - -        If the cache appears to still be empty (no members, no channels, or no roles), the event -        will not be set. -        """ -        if guild.id != constants.Client.guild: -            return - -        if not guild.roles or not guild.members or not guild.channels: -            log.warning("Guild available event was dispatched but the cache appears to still be empty!") -            return +    async def setup_hook(self) -> None: +        """Default async initialisation method for discord.py.""" +        await super().setup_hook() -        self._guild_available.set() +        # This is not awaited to avoid a deadlock with any cogs that have +        # wait_until_guild_available in their cog_load method. +        scheduling.create_task(self.load_extensions(exts)) -    async def on_guild_unavailable(self, guild: discord.Guild) -> None: -        """Clear the internal `_guild_available` event when PyDis guild becomes unavailable.""" -        if guild.id != constants.Client.guild: +    async def invoke_help_command(self, ctx: commands.Context) -> None: +        """Invoke the help command or default help command if help extensions is not loaded.""" +        if "bot.exts.core.help" in ctx.bot.extensions: +            help_command = ctx.bot.get_command("help") +            await ctx.invoke(help_command, ctx.command.qualified_name)              return - -        self._guild_available.clear() - -    async def wait_until_guild_available(self) -> None: -        """ -        Wait until the PyDis guild becomes available (and the cache is ready). - -        The on_ready event is inadequate because it only waits 2 seconds for a GUILD_CREATE -        gateway event before giving up and thus not populating the cache for unavailable guilds. -        """ -        await self._guild_available.wait() - -    def _add_root_aliases(self, command: commands.Command) -> None: -        """Recursively add root aliases for `command` and any of its subcommands.""" -        if isinstance(command, commands.Group): -            for subcommand in command.commands: -                self._add_root_aliases(subcommand) - -        for alias in getattr(command, "root_aliases", ()): -            if alias in self.all_commands: -                raise commands.CommandRegistrationError(alias, alias_conflict=True) - -            self.all_commands[alias] = command - -    def _remove_root_aliases(self, command: commands.Command) -> None: -        """Recursively remove root aliases for `command` and any of its subcommands.""" -        if isinstance(command, commands.Group): -            for subcommand in command.commands: -                self._remove_root_aliases(subcommand) - -        for alias in getattr(command, "root_aliases", ()): -            self.all_commands.pop(alias, None) - - -_allowed_roles = [discord.Object(id_) for id_ in constants.MODERATION_ROLES] - -_intents = discord.Intents.default()  # Default is all intents except for privileged ones (Members, Presences, ...) -_intents.bans = False -_intents.integrations = False -_intents.invites = False -_intents.typing = False -_intents.webhooks = False - -redis_session = RedisSession( -    address=(constants.RedisConfig.host, constants.RedisConfig.port), -    password=constants.RedisConfig.password, -    minsize=1, -    maxsize=20, -    use_fakeredis=constants.RedisConfig.use_fakeredis, -    global_namespace="sir-lancebot" -) -loop = asyncio.get_event_loop() -loop.run_until_complete(redis_session.connect()) - -bot = Bot( -    redis_session=redis_session, -    command_prefix=when_mentioned_or(constants.Client.prefix), -    activity=discord.Game(name=f"Commands: {constants.Client.prefix}help"), -    allowed_mentions=discord.AllowedMentions(everyone=False, roles=_allowed_roles), -    intents=_intents, -) +        await ctx.send_help(ctx.command) | 
