From 510da5190a0f499397c9419fa3b125233bed566c Mon Sep 17 00:00:00 2001 From: Chris Lovering Date: Tue, 28 Jun 2022 22:36:22 +0100 Subject: Use BotBase from bot core --- bot/bot.py | 166 ++++++------------------------------------------------------- 1 file changed, 14 insertions(+), 152 deletions(-) (limited to 'bot/bot.py') diff --git a/bot/bot.py b/bot/bot.py index c7b87a65..221bfd62 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -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): @@ -144,84 +76,14 @@ 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. + async def setup_hook(self) -> None: + """Default async initialisation method for discord.py.""" + await super().setup_hook() - 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 + await self.check_channels() - self._guild_available.set() - - 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: - return + # 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)) - 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 self.send_log(self.name, "Connected!") -- cgit v1.2.3