From 220c3bbc2afe5c5e696e8b9fa59b35a9138948ee Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Wed, 18 Aug 2021 12:45:46 -0400 Subject: [PATCH 01/16] chore: [temp fix] add members intent --- modmail/bot.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modmail/bot.py b/modmail/bot.py index deb66464..a1d2a428 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -4,6 +4,7 @@ import arrow from aiohttp import ClientSession +from discord import Intents from discord.ext import commands from modmail.config import CONFIG @@ -26,7 +27,12 @@ def __init__(self, **kwargs): self.config = CONFIG self.http_session: t.Optional[ClientSession] = None self.start_time = arrow.utcnow() - super().__init__(command_prefix=commands.when_mentioned_or(self.config.bot.prefix), **kwargs) + intents = Intents( + guilds=True, messages=True, reactions=True, typing=True, members=True, emojis_and_stickers=True + ) + super().__init__( + command_prefix=commands.when_mentioned_or(self.config.bot.prefix), intents=intents, **kwargs + ) async def create_session(self) -> None: """Create an aiohttp client session.""" From 9db00afa24d60d402b19dbff0efd1edd8e545ab5 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Wed, 18 Aug 2021 23:27:31 -0400 Subject: [PATCH 02/16] [launch]: refactor bot creation and bot run --- modmail/__main__.py | 5 +- modmail/bot.py | 130 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 115 insertions(+), 20 deletions(-) diff --git a/modmail/__main__.py b/modmail/__main__.py index 4b4aaa84..3a98cc25 100644 --- a/modmail/__main__.py +++ b/modmail/__main__.py @@ -17,10 +17,7 @@ def main() -> None: """Run the bot.""" - bot = ModmailBot() - bot.load_extensions() - bot.load_plugins() - log.notice("Running the bot.") + bot = ModmailBot.create() bot.run(bot.config.bot.token) diff --git a/modmail/bot.py b/modmail/bot.py index a1d2a428..0e09bb7a 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -1,10 +1,13 @@ import asyncio import logging +import signal import typing as t import arrow +import discord from aiohttp import ClientSession -from discord import Intents +from discord import Activity, AllowedMentions, Intents +from discord.client import _cleanup_loop from discord.ext import commands from modmail.config import CONFIG @@ -20,32 +23,127 @@ class ModmailBot(commands.Bot): Has an aiohttp.ClientSession and a ModmailConfig instance. """ - main_task: asyncio.Task logger: ModmailLogger = logging.getLogger(__name__) def __init__(self, **kwargs): self.config = CONFIG - self.http_session: t.Optional[ClientSession] = None - self.start_time = arrow.utcnow() - intents = Intents( - guilds=True, messages=True, reactions=True, typing=True, members=True, emojis_and_stickers=True - ) + self.start_time: t.Optional[arrow.Arrow] = None # arrow.utcnow() super().__init__( - command_prefix=commands.when_mentioned_or(self.config.bot.prefix), intents=intents, **kwargs + **kwargs, ) + self.http_session: ClientSession = ClientSession(loop=self.loop) - async def create_session(self) -> None: - """Create an aiohttp client session.""" - self.http_session = ClientSession() + @classmethod + def create(cls, *args, **kwargs) -> "ModmailBot": + """ + Create a ModmailBot instance. - async def start(self, *args, **kwargs) -> None: + This configures our instance with all of our custom options, without making __init__ unusable. """ - Start the bot. + intents = Intents( + guilds=True, + messages=True, + reactions=True, + typing=True, + members=True, + emojis_and_stickers=True, + ) + # start with an invisible status while we load everything + status = discord.Status.invisible + activity = Activity(type=discord.ActivityType.listening, name="users dming me!") + # listen to messages mentioning the bot or matching the prefix + # ! NOTE: This needs to use the configuration system to get the prefix from the db once it exists. + prefix = commands.when_mentioned_or(CONFIG.bot.prefix) + description = "Modmail bot by discord-modmail." + # allow commands not caring of the case + case_insensitive_commands = True + # allow only user mentions by default. + # ! NOTE: This may change in the future to allow roles as well + allowed_mentions = AllowedMentions(everyone=False, users=True, roles=False, replied_user=True) + return cls( + case_insensitive=case_insensitive_commands, + description=description, + status=status, + activity=activity, + allowed_mentions=allowed_mentions, + command_prefix=prefix, + intents=intents, + **kwargs, + ) - This just serves to create the http session. + def run(self, *args, **kwargs) -> None: """ - await self.create_session() - await super().start(*args, **kwargs) + + Start up our instance of the bot. Since this method is blocking, it must be called last. + + This method sets up the bot, loads extensions and plugins, and then executes the main task. + + A blocking call that abstracts away the event loop + initialisation from you. + If you want more control over the event loop then this + function should not be used. Use :meth:`start` coroutine + or :meth:`connect` + :meth:`login`. + Roughly Equivalent to: :: + try: + loop.run_until_complete(start(*args, **kwargs)) + except KeyboardInterrupt: + loop.run_until_complete(close()) + # cancel all tasks lingering + finally: + loop.close() + .. warning:: + This function must be the last function to call due to the fact that it + is blocking. That means that registration of events or anything being + called after this function call will not execute until it returns. + """ + loop = self.loop + + try: + loop.add_signal_handler(signal.SIGINT, lambda: loop.stop()) + # this one we may want to get rid of, depending on certain things, and just hard stop instead. + loop.add_signal_handler(signal.SIGTERM, lambda: loop.stop()) + except NotImplementedError: + pass + + async def runner() -> None: + try: + # set start time to when we started the bot + self.start_time = arrow.utcnow() + # we want to load extensions before we log in, so that any issues in them are discovered + # before we connect to discord + self.load_extensions() + # next, we log in to discord, to ensure that we are able to connect to discord + await self.login(*args) + # now that we're logged in and gotten a connection, we load all of the plugins + self.load_plugins() + # alert the user that we're done loading everything + self.logger.notice("Loaded all extensions, and plugins. Starting bot.") + # finally, we enter the main loop + await self.connect(**kwargs) + finally: + if not self.is_closed(): + await self.close() + + def stop_loop_on_completion(f) -> None: # noqa: ANN001 + loop.stop() + + future = asyncio.ensure_future(runner(), loop=loop) + future.add_done_callback(stop_loop_on_completion) + try: + loop.run_forever() + except KeyboardInterrupt: + self.logger.info("Received signal to terminate bot and event loop.") + finally: + future.remove_done_callback(stop_loop_on_completion) + self.logger.info("Cleaning up tasks.") + _cleanup_loop(loop) + + if not future.cancelled(): + try: + return future.result() + except KeyboardInterrupt: + # I am unsure why this gets raised here but suppress it anyway + return None async def close(self) -> None: """Safely close HTTP session, unload plugins and extensions when the bot is shutting down.""" From 1d9388302bfcbfb979fe89953df955ab02f5da16 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Wed, 18 Aug 2021 23:35:00 -0400 Subject: [PATCH 03/16] chore: make status update on_ready --- modmail/bot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modmail/bot.py b/modmail/bot.py index 0e09bb7a..d82bb503 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -228,3 +228,4 @@ def remove_cog(self, cog: str) -> None: async def on_ready(self) -> None: """Send basic login success message.""" self.logger.info("Logged in as %s", self.user) + await self.change_presence(status=discord.Status.online, activity=self.activity) From 472302cf421837e92a012284dea94ff7c54632c1 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Wed, 18 Aug 2021 23:53:25 -0400 Subject: [PATCH 04/16] chore: move creating the clientSession to the run meth --- modmail/bot.py | 5 ++++- tests/docs.md | 6 ------ tests/test_bot.py | 19 +++++-------------- 3 files changed, 9 insertions(+), 21 deletions(-) diff --git a/modmail/bot.py b/modmail/bot.py index d82bb503..d15c1101 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -31,7 +31,7 @@ def __init__(self, **kwargs): super().__init__( **kwargs, ) - self.http_session: ClientSession = ClientSession(loop=self.loop) + self.http_session: t.Optional[ClientSession] = None @classmethod def create(cls, *args, **kwargs) -> "ModmailBot": @@ -107,6 +107,9 @@ def run(self, *args, **kwargs) -> None: async def runner() -> None: try: + # create the aiohttp session + self.http_session = ClientSession(loop=self.loop) + self.logger.trace("Created ClientSession.") # set start time to when we started the bot self.start_time = arrow.utcnow() # we want to load extensions before we log in, so that any issues in them are discovered diff --git a/tests/docs.md b/tests/docs.md index a08cee67..54ff01a8 100644 --- a/tests/docs.md +++ b/tests/docs.md @@ -12,12 +12,6 @@ Ensure we can make a ModmailBot instance. **Markers:** - asyncio - dependency (name=create_bot) -### test_bot_aiohttp -Test aiohttp client session creates and closes without warnings. - -**Markers:** -- asyncio -- dependency (depends=['create_bot']) ### test_bot_close Ensure bot closes without error. diff --git a/tests/test_bot.py b/tests/test_bot.py index 8210be09..d5074a6f 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -4,6 +4,8 @@ - import module - create a bot object """ +import asyncio + import pytest from modmail.bot import ModmailBot @@ -13,9 +15,9 @@ @pytest.mark.asyncio async def test_bot_creation(): """Ensure we can make a ModmailBot instance.""" - bot = ModmailBot() + bot = ModmailBot(command_prefix="!") assert isinstance(bot, ModmailBot) - + assert bot.command_prefix == "!" # cleanup await bot.close() @@ -27,21 +29,10 @@ def bot() -> ModmailBot: ModmailBot instance. """ - bot: ModmailBot = ModmailBot() + bot: ModmailBot = ModmailBot.create(loop=asyncio.get_event_loop()) return bot -@pytest.mark.dependency(depends=["create_bot"]) -@pytest.mark.asyncio -async def test_bot_aiohttp(bot): - """Test aiohttp client session creates and closes without warnings.""" - import aiohttp - - await bot.create_session() - assert isinstance(bot.http_session, aiohttp.ClientSession) - await bot.close() - - @pytest.mark.dependency(depends=["create_bot"]) @pytest.mark.asyncio async def test_bot_close(bot): From c9e7d6ea3f4938b96028107e4c3eb5020e83e50e Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Wed, 18 Aug 2021 23:58:43 -0400 Subject: [PATCH 05/16] changes: add bot creation and running notes --- docs/changelog.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index c058072f..d74475a5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Refactored bot creation and bot running (#56) + - Bot creation is now a class method, `ModmailBot.create()` + - Running the bot is still the same method, but it loads extensions and plugins now. + + ## [0.1.0] - 2021-08-13 ### Added From ccc0a4be72185a38cd81994dd46a360bce29226c Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 09:42:53 -0400 Subject: [PATCH 06/16] chore: remove discord py docstring --- modmail/bot.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/modmail/bot.py b/modmail/bot.py index d15c1101..97876ee3 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -76,25 +76,10 @@ def run(self, *args, **kwargs) -> None: Start up our instance of the bot. Since this method is blocking, it must be called last. - This method sets up the bot, loads extensions and plugins, and then executes the main task. - - A blocking call that abstracts away the event loop - initialisation from you. - If you want more control over the event loop then this - function should not be used. Use :meth:`start` coroutine - or :meth:`connect` + :meth:`login`. - Roughly Equivalent to: :: - try: - loop.run_until_complete(start(*args, **kwargs)) - except KeyboardInterrupt: - loop.run_until_complete(close()) - # cancel all tasks lingering - finally: - loop.close() - .. warning:: - This function must be the last function to call due to the fact that it - is blocking. That means that registration of events or anything being - called after this function call will not execute until it returns. + This method does several things, it loads extensions and plugins, + and then executes the main task. + + This method was copied from discord.py and modified to suit our needs. """ loop = self.loop From c2275baa8aabe0150e4bcac01250ece59fa969d7 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:00:59 -0400 Subject: [PATCH 07/16] chore: refactor the loop runner to be bot.start() --- modmail/bot.py | 52 ++++++++++++++++++++++++++++---------------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/modmail/bot.py b/modmail/bot.py index 97876ee3..e280a09f 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -71,6 +71,34 @@ def create(cls, *args, **kwargs) -> "ModmailBot": **kwargs, ) + async def start(self, token: str, reconnect: bool = True) -> None: + """ + Start the bot. + + This function is called by the run method, and finishes the set up of the bot that needs an + asyncrhonous event loop running, before connecting the bot to discord. + """ + try: + # create the aiohttp session + self.http_session = ClientSession(loop=self.loop) + self.logger.trace("Created ClientSession.") + # set start time to when we started the bot + self.start_time = arrow.utcnow() + # we want to load extensions before we log in, so that any issues in them are discovered + # before we connect to discord + self.load_extensions() + # next, we log in to discord, to ensure that we are able to connect to discord + await self.login(token) + # now that we're logged in and gotten a connection, we load all of the plugins + self.load_plugins() + # alert the user that we're done loading everything + self.logger.notice("Loaded all extensions, and plugins. Starting bot.") + # finally, we enter the main loop + await self.connect(reconnect=reconnect) + finally: + if not self.is_closed(): + await self.close() + def run(self, *args, **kwargs) -> None: """ @@ -90,32 +118,10 @@ def run(self, *args, **kwargs) -> None: except NotImplementedError: pass - async def runner() -> None: - try: - # create the aiohttp session - self.http_session = ClientSession(loop=self.loop) - self.logger.trace("Created ClientSession.") - # set start time to when we started the bot - self.start_time = arrow.utcnow() - # we want to load extensions before we log in, so that any issues in them are discovered - # before we connect to discord - self.load_extensions() - # next, we log in to discord, to ensure that we are able to connect to discord - await self.login(*args) - # now that we're logged in and gotten a connection, we load all of the plugins - self.load_plugins() - # alert the user that we're done loading everything - self.logger.notice("Loaded all extensions, and plugins. Starting bot.") - # finally, we enter the main loop - await self.connect(**kwargs) - finally: - if not self.is_closed(): - await self.close() - def stop_loop_on_completion(f) -> None: # noqa: ANN001 loop.stop() - future = asyncio.ensure_future(runner(), loop=loop) + future = asyncio.ensure_future(self.start(), loop=loop) future.add_done_callback(stop_loop_on_completion) try: loop.run_forever() From b9d8153b5d3a48797995f8d76b52a5a578fa2ba4 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:09:05 -0400 Subject: [PATCH 08/16] chore: set initial status to online --- modmail/bot.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modmail/bot.py b/modmail/bot.py index e280a09f..4d331e42 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -48,8 +48,7 @@ def create(cls, *args, **kwargs) -> "ModmailBot": members=True, emojis_and_stickers=True, ) - # start with an invisible status while we load everything - status = discord.Status.invisible + status = discord.Status.online activity = Activity(type=discord.ActivityType.listening, name="users dming me!") # listen to messages mentioning the bot or matching the prefix # ! NOTE: This needs to use the configuration system to get the prefix from the db once it exists. @@ -222,4 +221,3 @@ def remove_cog(self, cog: str) -> None: async def on_ready(self) -> None: """Send basic login success message.""" self.logger.info("Logged in as %s", self.user) - await self.change_presence(status=discord.Status.online, activity=self.activity) From 298131bf3ca7d754f1e0e46cef867f04df1558c5 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:11:58 -0400 Subject: [PATCH 09/16] chore: make intents a global, move description and case_insensitive to the constructor --- modmail/bot.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/modmail/bot.py b/modmail/bot.py index 4d331e42..23d412b8 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -15,6 +15,15 @@ from modmail.utils.extensions import EXTENSIONS, NO_UNLOAD, walk_extensions from modmail.utils.plugins import PLUGINS, walk_plugins +INTENTS = Intents( + guilds=True, + messages=True, + reactions=True, + typing=True, + members=True, + emojis_and_stickers=True, +) + class ModmailBot(commands.Bot): """ @@ -40,33 +49,22 @@ def create(cls, *args, **kwargs) -> "ModmailBot": This configures our instance with all of our custom options, without making __init__ unusable. """ - intents = Intents( - guilds=True, - messages=True, - reactions=True, - typing=True, - members=True, - emojis_and_stickers=True, - ) status = discord.Status.online activity = Activity(type=discord.ActivityType.listening, name="users dming me!") # listen to messages mentioning the bot or matching the prefix # ! NOTE: This needs to use the configuration system to get the prefix from the db once it exists. prefix = commands.when_mentioned_or(CONFIG.bot.prefix) - description = "Modmail bot by discord-modmail." - # allow commands not caring of the case - case_insensitive_commands = True # allow only user mentions by default. # ! NOTE: This may change in the future to allow roles as well allowed_mentions = AllowedMentions(everyone=False, users=True, roles=False, replied_user=True) return cls( - case_insensitive=case_insensitive_commands, - description=description, + case_insensitive=True, + description="Modmail bot by discord-modmail.", status=status, activity=activity, allowed_mentions=allowed_mentions, command_prefix=prefix, - intents=intents, + intents=INTENTS, **kwargs, ) From 7b6a52176b9c0ebf94b7768644e6e236238c7479 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:12:28 -0400 Subject: [PATCH 10/16] chore: comment the signal handlers --- modmail/bot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modmail/bot.py b/modmail/bot.py index 23d412b8..c07f2827 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -109,6 +109,7 @@ def run(self, *args, **kwargs) -> None: loop = self.loop try: + # adds signal handlers so the loop is safely stopped loop.add_signal_handler(signal.SIGINT, lambda: loop.stop()) # this one we may want to get rid of, depending on certain things, and just hard stop instead. loop.add_signal_handler(signal.SIGTERM, lambda: loop.stop()) From 254c8aa709550725589ccea015139ea7d2f5d840 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:18:04 -0400 Subject: [PATCH 11/16] chore: readd args and kwargs to calling start() --- modmail/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modmail/bot.py b/modmail/bot.py index c07f2827..4e5f5575 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -119,7 +119,7 @@ def run(self, *args, **kwargs) -> None: def stop_loop_on_completion(f) -> None: # noqa: ANN001 loop.stop() - future = asyncio.ensure_future(self.start(), loop=loop) + future = asyncio.ensure_future(self.start(*args, **kwargs), loop=loop) future.add_done_callback(stop_loop_on_completion) try: loop.run_forever() From 268362f977098d6bca233e99d02ad282fbbc79f4 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:18:55 -0400 Subject: [PATCH 12/16] chore[bot]: get rid of bot.create() method --- modmail/__main__.py | 2 +- modmail/bot.py | 12 +----------- tests/test_bot.py | 6 ++---- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/modmail/__main__.py b/modmail/__main__.py index 3a98cc25..39599e20 100644 --- a/modmail/__main__.py +++ b/modmail/__main__.py @@ -17,7 +17,7 @@ def main() -> None: """Run the bot.""" - bot = ModmailBot.create() + bot = ModmailBot() bot.run(bot.config.bot.token) diff --git a/modmail/bot.py b/modmail/bot.py index 4e5f5575..26535090 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -37,18 +37,8 @@ class ModmailBot(commands.Bot): def __init__(self, **kwargs): self.config = CONFIG self.start_time: t.Optional[arrow.Arrow] = None # arrow.utcnow() - super().__init__( - **kwargs, - ) self.http_session: t.Optional[ClientSession] = None - @classmethod - def create(cls, *args, **kwargs) -> "ModmailBot": - """ - Create a ModmailBot instance. - - This configures our instance with all of our custom options, without making __init__ unusable. - """ status = discord.Status.online activity = Activity(type=discord.ActivityType.listening, name="users dming me!") # listen to messages mentioning the bot or matching the prefix @@ -57,7 +47,7 @@ def create(cls, *args, **kwargs) -> "ModmailBot": # allow only user mentions by default. # ! NOTE: This may change in the future to allow roles as well allowed_mentions = AllowedMentions(everyone=False, users=True, roles=False, replied_user=True) - return cls( + super().__init__( case_insensitive=True, description="Modmail bot by discord-modmail.", status=status, diff --git a/tests/test_bot.py b/tests/test_bot.py index d5074a6f..854c4ece 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -15,9 +15,7 @@ @pytest.mark.asyncio async def test_bot_creation(): """Ensure we can make a ModmailBot instance.""" - bot = ModmailBot(command_prefix="!") - assert isinstance(bot, ModmailBot) - assert bot.command_prefix == "!" + bot = ModmailBot() # cleanup await bot.close() @@ -29,7 +27,7 @@ def bot() -> ModmailBot: ModmailBot instance. """ - bot: ModmailBot = ModmailBot.create(loop=asyncio.get_event_loop()) + bot: ModmailBot = ModmailBot(loop=asyncio.get_event_loop()) return bot From e04c0f60e8e5189d4b97552680e06a89c47d06f4 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:22:43 -0400 Subject: [PATCH 13/16] changes: document bot.start() --- docs/changelog.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index d74475a5..ab0ba54c 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -10,8 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Refactored bot creation and bot running (#56) - - Bot creation is now a class method, `ModmailBot.create()` - Running the bot is still the same method, but it loads extensions and plugins now. + - `bot.start()` can also be used if already in a running event loop. Keep in mind using it will require + handling loop errors, as run() does this automatically. ## [0.1.0] - 2021-08-13 From f69011ea545edd0bdebcb8571a24c37e40fe0aed Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:23:50 -0400 Subject: [PATCH 14/16] nit: change intents global to REQUIRED_INTENTS --- modmail/bot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modmail/bot.py b/modmail/bot.py index 26535090..dcdd3081 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -15,7 +15,7 @@ from modmail.utils.extensions import EXTENSIONS, NO_UNLOAD, walk_extensions from modmail.utils.plugins import PLUGINS, walk_plugins -INTENTS = Intents( +REQUIRED_INTENTS = Intents( guilds=True, messages=True, reactions=True, @@ -54,7 +54,7 @@ def __init__(self, **kwargs): activity=activity, allowed_mentions=allowed_mentions, command_prefix=prefix, - intents=INTENTS, + intents=REQUIRED_INTENTS, **kwargs, ) From 4029d628b4fbd7db680a4f3655a723b7a1b22be4 Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:39:12 -0400 Subject: [PATCH 15/16] nit: don't pass the event loop in tests --- tests/test_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_bot.py b/tests/test_bot.py index 854c4ece..cd220cac 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -27,7 +27,7 @@ def bot() -> ModmailBot: ModmailBot instance. """ - bot: ModmailBot = ModmailBot(loop=asyncio.get_event_loop()) + bot: ModmailBot = ModmailBot() return bot From fc72d37ad3145739a97b12e5e3f08e93aa35bf2f Mon Sep 17 00:00:00 2001 From: onerandomusername Date: Thu, 19 Aug 2021 10:45:12 -0400 Subject: [PATCH 16/16] minor: update docstrings to be more specific --- modmail/bot.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/modmail/bot.py b/modmail/bot.py index dcdd3081..1ba67b52 100644 --- a/modmail/bot.py +++ b/modmail/bot.py @@ -69,14 +69,23 @@ async def start(self, token: str, reconnect: bool = True) -> None: # create the aiohttp session self.http_session = ClientSession(loop=self.loop) self.logger.trace("Created ClientSession.") - # set start time to when we started the bot + # set start time to when we started the bot. + # This is now, since we're about to connect to the gateway. + # This should also be before we load any extensions, since if they have a load time, it should + # be after the bot start time. self.start_time = arrow.utcnow() # we want to load extensions before we log in, so that any issues in them are discovered - # before we connect to discord + # before we connect to discord. This keeps us from connecting to the gateway a lot if we have a + # problem with an extension. self.load_extensions() # next, we log in to discord, to ensure that we are able to connect to discord + # This only logs in to discord and gets a gateway, it does not connect to the websocket await self.login(token) - # now that we're logged in and gotten a connection, we load all of the plugins + # now that we're logged in and ensured we can have connection, we load all of the plugins + # The reason to wait until we know we have a gateway we can connect to, even though we have not + # signed in yet, is in some cases, a plugin may be poorly made and mess up if it is loaded but + # the bot never connects to discord. Putting this below the login ensures that we don't load if + # we don't have a gateway. self.load_plugins() # alert the user that we're done loading everything self.logger.notice("Loaded all extensions, and plugins. Starting bot.")