From d913a91531ba6414741d745303f89cb687cf345b Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 7 Dec 2019 19:29:33 -0800 Subject: Subclass Bot --- bot/__main__.py | 26 ++------------------------ bot/bot.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 24 deletions(-) create mode 100644 bot/bot.py diff --git a/bot/__main__.py b/bot/__main__.py index ea7c43a12..84bc7094b 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -1,18 +1,11 @@ -import asyncio -import logging -import socket - import discord -from aiohttp import AsyncResolver, ClientSession, TCPConnector -from discord.ext.commands import Bot, when_mentioned_or +from discord.ext.commands import when_mentioned_or from bot import patches -from bot.api import APIClient, APILoggingHandler +from bot.bot import Bot from bot.constants import Bot as BotConfig, DEBUG_MODE -log = logging.getLogger('bot') - bot = Bot( command_prefix=when_mentioned_or(BotConfig.prefix), activity=discord.Game(name="Commands: !help"), @@ -20,18 +13,6 @@ bot = Bot( max_messages=10_000, ) -# Global aiohttp session for all cogs -# - Uses asyncio for DNS resolution instead of threads, so we don't spam threads -# - Uses AF_INET as its socket family to prevent https related problems both locally and in prod. -bot.http_session = ClientSession( - connector=TCPConnector( - resolver=AsyncResolver(), - family=socket.AF_INET, - ) -) -bot.api_client = APIClient(loop=asyncio.get_event_loop()) -log.addHandler(APILoggingHandler(bot.api_client)) - # Internal/debug bot.load_extension("bot.cogs.error_handler") bot.load_extension("bot.cogs.filtering") @@ -77,6 +58,3 @@ if not hasattr(discord.message.Message, '_handle_edited_timestamp'): patches.message_edited_at.apply_patch() bot.run(BotConfig.token) - -# This calls a coroutine, so it doesn't do anything at the moment. -# bot.http_session.close() # Close the aiohttp session when the bot finishes running diff --git a/bot/bot.py b/bot/bot.py new file mode 100644 index 000000000..05734ac1d --- /dev/null +++ b/bot/bot.py @@ -0,0 +1,30 @@ +import asyncio +import logging +import socket + +import aiohttp +from discord.ext import commands + +from bot import api + +log = logging.getLogger('bot') + + +class Bot(commands.Bot): + """A subclass of `discord.ext.commands.Bot` with an aiohttp session and an API client.""" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Global aiohttp session for all cogs + # - Uses asyncio for DNS resolution instead of threads, so we don't spam threads + # - Uses AF_INET as its socket family to prevent https related problems both locally and in prod. + self.http_session = aiohttp.ClientSession( + connector=aiohttp.TCPConnector( + resolver=aiohttp.AsyncResolver(), + family=socket.AF_INET, + ) + ) + + self.api_client = api.APIClient(loop=asyncio.get_event_loop()) + log.addHandler(api.APILoggingHandler(self.api_client)) -- cgit v1.2.3 From 6fe61e5919cb541a1651312a01ddf7e7f10d0f86 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 7 Dec 2019 20:11:50 -0800 Subject: Change all Bot imports to use the subclass --- bot/cogs/alias.py | 3 ++- bot/cogs/antimalware.py | 3 ++- bot/cogs/antispam.py | 3 ++- bot/cogs/bot.py | 3 ++- bot/cogs/clean.py | 3 ++- bot/cogs/defcon.py | 3 ++- bot/cogs/doc.py | 5 +++-- bot/cogs/duck_pond.py | 3 ++- bot/cogs/error_handler.py | 3 ++- bot/cogs/eval.py | 3 ++- bot/cogs/extensions.py | 3 ++- bot/cogs/filtering.py | 3 ++- bot/cogs/free.py | 3 ++- bot/cogs/help.py | 3 ++- bot/cogs/information.py | 3 ++- bot/cogs/jams.py | 5 +++-- bot/cogs/logging.py | 3 ++- bot/cogs/moderation/__init__.py | 3 +-- bot/cogs/moderation/infractions.py | 3 ++- bot/cogs/moderation/management.py | 3 ++- bot/cogs/moderation/modlog.py | 3 ++- bot/cogs/moderation/scheduler.py | 3 ++- bot/cogs/moderation/superstarify.py | 3 ++- bot/cogs/off_topic_names.py | 3 ++- bot/cogs/reddit.py | 3 ++- bot/cogs/reminders.py | 3 ++- bot/cogs/security.py | 4 +++- bot/cogs/site.py | 3 ++- bot/cogs/snekbox.py | 3 ++- bot/cogs/sync/__init__.py | 3 +-- bot/cogs/sync/cog.py | 3 ++- bot/cogs/sync/syncers.py | 7 ++++--- bot/cogs/tags.py | 3 ++- bot/cogs/token_remover.py | 3 ++- bot/cogs/utils.py | 3 ++- bot/cogs/verification.py | 3 ++- bot/cogs/watchchannels/__init__.py | 3 +-- bot/cogs/watchchannels/bigbrother.py | 3 ++- bot/cogs/watchchannels/talentpool.py | 3 ++- bot/cogs/watchchannels/watchchannel.py | 3 ++- bot/cogs/wolfram.py | 7 ++++--- bot/interpreter.py | 4 +++- tests/helpers.py | 4 +++- 43 files changed, 92 insertions(+), 52 deletions(-) diff --git a/bot/cogs/alias.py b/bot/cogs/alias.py index 5190c559b..4ee5a2aed 100644 --- a/bot/cogs/alias.py +++ b/bot/cogs/alias.py @@ -3,8 +3,9 @@ import logging from typing import Union from discord import Colour, Embed, Member, User -from discord.ext.commands import Bot, Cog, Command, Context, clean_content, command, group +from discord.ext.commands import Cog, Command, Context, clean_content, command, group +from bot.bot import Bot from bot.cogs.extensions import Extension from bot.cogs.watchchannels.watchchannel import proxy_user from bot.converters import TagNameConverter diff --git a/bot/cogs/antimalware.py b/bot/cogs/antimalware.py index 602819191..03c1e28a1 100644 --- a/bot/cogs/antimalware.py +++ b/bot/cogs/antimalware.py @@ -1,8 +1,9 @@ import logging from discord import Embed, Message, NotFound -from discord.ext.commands import Bot, Cog +from discord.ext.commands import Cog +from bot.bot import Bot from bot.constants import AntiMalware as AntiMalwareConfig, Channels, URLs log = logging.getLogger(__name__) diff --git a/bot/cogs/antispam.py b/bot/cogs/antispam.py index 1340eb608..88912038a 100644 --- a/bot/cogs/antispam.py +++ b/bot/cogs/antispam.py @@ -7,9 +7,10 @@ from operator import itemgetter from typing import Dict, Iterable, List, Set from discord import Colour, Member, Message, NotFound, Object, TextChannel -from discord.ext.commands import Bot, Cog +from discord.ext.commands import Cog from bot import rules +from bot.bot import Bot from bot.cogs.moderation import ModLog from bot.constants import ( AntiSpam as AntiSpamConfig, Channels, diff --git a/bot/cogs/bot.py b/bot/cogs/bot.py index ee0a463de..a2edb7576 100644 --- a/bot/cogs/bot.py +++ b/bot/cogs/bot.py @@ -5,8 +5,9 @@ import time from typing import Optional, Tuple from discord import Embed, Message, RawMessageUpdateEvent, TextChannel -from discord.ext.commands import Bot, Cog, Context, command, group +from discord.ext.commands import Cog, Context, command, group +from bot.bot import Bot from bot.constants import Channels, DEBUG_MODE, Guild, MODERATION_ROLES, Roles, URLs from bot.decorators import with_role from bot.utils.messages import wait_for_deletion diff --git a/bot/cogs/clean.py b/bot/cogs/clean.py index dca411d01..3365d0934 100644 --- a/bot/cogs/clean.py +++ b/bot/cogs/clean.py @@ -4,8 +4,9 @@ import re from typing import Optional from discord import Colour, Embed, Message, User -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group +from bot.bot import Bot from bot.cogs.moderation import ModLog from bot.constants import ( Channels, CleanMessages, Colours, Event, diff --git a/bot/cogs/defcon.py b/bot/cogs/defcon.py index bedd70c86..f062a7546 100644 --- a/bot/cogs/defcon.py +++ b/bot/cogs/defcon.py @@ -6,8 +6,9 @@ from datetime import datetime, timedelta from enum import Enum from discord import Colour, Embed, Member -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group +from bot.bot import Bot from bot.cogs.moderation import ModLog from bot.constants import Channels, Colours, Emojis, Event, Icons, Roles from bot.decorators import with_role diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py index e5b3a4062..7df159fd9 100644 --- a/bot/cogs/doc.py +++ b/bot/cogs/doc.py @@ -17,6 +17,7 @@ from requests import ConnectTimeout, ConnectionError, HTTPError from sphinx.ext import intersphinx from urllib3.exceptions import ProtocolError +from bot.bot import Bot from bot.constants import MODERATION_ROLES, RedirectOutput from bot.converters import ValidPythonIdentifier, ValidURL from bot.decorators import with_role @@ -147,7 +148,7 @@ class InventoryURL(commands.Converter): class Doc(commands.Cog): """A set of commands for querying & displaying documentation.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.base_urls = {} self.bot = bot self.inventories = {} @@ -506,7 +507,7 @@ class Doc(commands.Cog): return tag.name == "table" -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Doc cog load.""" bot.add_cog(Doc(bot)) log.info("Cog loaded: Doc") diff --git a/bot/cogs/duck_pond.py b/bot/cogs/duck_pond.py index 2d25cd17e..879071d1b 100644 --- a/bot/cogs/duck_pond.py +++ b/bot/cogs/duck_pond.py @@ -3,9 +3,10 @@ from typing import Optional, Union import discord from discord import Color, Embed, Member, Message, RawReactionActionEvent, User, errors -from discord.ext.commands import Bot, Cog +from discord.ext.commands import Cog from bot import constants +from bot.bot import Bot from bot.utils.messages import send_attachments log = logging.getLogger(__name__) diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py index 49411814c..cf90e9f48 100644 --- a/bot/cogs/error_handler.py +++ b/bot/cogs/error_handler.py @@ -14,9 +14,10 @@ from discord.ext.commands import ( NoPrivateMessage, UserInputError, ) -from discord.ext.commands import Bot, Cog, Context +from discord.ext.commands import Cog, Context from bot.api import ResponseCodeError +from bot.bot import Bot from bot.constants import Channels from bot.decorators import InChannelCheckFailure diff --git a/bot/cogs/eval.py b/bot/cogs/eval.py index 00b988dde..5daec3e39 100644 --- a/bot/cogs/eval.py +++ b/bot/cogs/eval.py @@ -9,8 +9,9 @@ from io import StringIO from typing import Any, Optional, Tuple import discord -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group +from bot.bot import Bot from bot.constants import Roles from bot.decorators import with_role from bot.interpreter import Interpreter diff --git a/bot/cogs/extensions.py b/bot/cogs/extensions.py index bb66e0b8e..4d77d8205 100644 --- a/bot/cogs/extensions.py +++ b/bot/cogs/extensions.py @@ -6,8 +6,9 @@ from pkgutil import iter_modules from discord import Colour, Embed from discord.ext import commands -from discord.ext.commands import Bot, Context, group +from discord.ext.commands import Context, group +from bot.bot import Bot from bot.constants import Emojis, MODERATION_ROLES, Roles, URLs from bot.pagination import LinePaginator from bot.utils.checks import with_role_check diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 1e7521054..2e54ccecb 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -5,8 +5,9 @@ from typing import Optional, Union import discord.errors from dateutil.relativedelta import relativedelta from discord import Colour, DMChannel, Member, Message, TextChannel -from discord.ext.commands import Bot, Cog +from discord.ext.commands import Cog +from bot.bot import Bot from bot.cogs.moderation import ModLog from bot.constants import ( Channels, Colours, diff --git a/bot/cogs/free.py b/bot/cogs/free.py index 82285656b..bbc9f063b 100644 --- a/bot/cogs/free.py +++ b/bot/cogs/free.py @@ -3,8 +3,9 @@ from datetime import datetime from operator import itemgetter from discord import Colour, Embed, Member, utils -from discord.ext.commands import Bot, Cog, Context, command +from discord.ext.commands import Cog, Context, command +from bot.bot import Bot from bot.constants import Categories, Channels, Free, STAFF_ROLES from bot.decorators import redirect_output diff --git a/bot/cogs/help.py b/bot/cogs/help.py index 9607dbd8d..6385fa467 100644 --- a/bot/cogs/help.py +++ b/bot/cogs/help.py @@ -6,10 +6,11 @@ from typing import Union from discord import Colour, Embed, HTTPException, Message, Reaction, User from discord.ext import commands -from discord.ext.commands import Bot, CheckFailure, Cog as DiscordCog, Command, Context +from discord.ext.commands import CheckFailure, Cog as DiscordCog, Command, Context from fuzzywuzzy import fuzz, process from bot import constants +from bot.bot import Bot from bot.constants import Channels, STAFF_ROLES from bot.decorators import redirect_output from bot.pagination import ( diff --git a/bot/cogs/information.py b/bot/cogs/information.py index 530453600..56bd37bec 100644 --- a/bot/cogs/information.py +++ b/bot/cogs/information.py @@ -9,10 +9,11 @@ from typing import Any, Mapping, Optional import discord from discord import CategoryChannel, Colour, Embed, Member, Role, TextChannel, VoiceChannel, utils from discord.ext import commands -from discord.ext.commands import Bot, BucketType, Cog, Context, command, group +from discord.ext.commands import BucketType, Cog, Context, command, group from discord.utils import escape_markdown from bot import constants +from bot.bot import Bot from bot.decorators import InChannelCheckFailure, in_channel, with_role from bot.utils.checks import cooldown_with_role_bypass, with_role_check from bot.utils.time import time_since diff --git a/bot/cogs/jams.py b/bot/cogs/jams.py index be9d33e3e..0c82e7962 100644 --- a/bot/cogs/jams.py +++ b/bot/cogs/jams.py @@ -4,6 +4,7 @@ from discord import Member, PermissionOverwrite, utils from discord.ext import commands from more_itertools import unique_everseen +from bot.bot import Bot from bot.constants import Roles from bot.decorators import with_role @@ -13,7 +14,7 @@ log = logging.getLogger(__name__) class CodeJams(commands.Cog): """Manages the code-jam related parts of our server.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @commands.command() @@ -108,7 +109,7 @@ class CodeJams(commands.Cog): ) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Code Jams cog load.""" bot.add_cog(CodeJams(bot)) log.info("Cog loaded: CodeJams") diff --git a/bot/cogs/logging.py b/bot/cogs/logging.py index c92b619ff..44c771b42 100644 --- a/bot/cogs/logging.py +++ b/bot/cogs/logging.py @@ -1,8 +1,9 @@ import logging from discord import Embed -from discord.ext.commands import Bot, Cog +from discord.ext.commands import Cog +from bot.bot import Bot from bot.constants import Channels, DEBUG_MODE diff --git a/bot/cogs/moderation/__init__.py b/bot/cogs/moderation/__init__.py index 7383ed44e..0cbdb3aa6 100644 --- a/bot/cogs/moderation/__init__.py +++ b/bot/cogs/moderation/__init__.py @@ -1,7 +1,6 @@ import logging -from discord.ext.commands import Bot - +from bot.bot import Bot from .infractions import Infractions from .management import ModManagement from .modlog import ModLog diff --git a/bot/cogs/moderation/infractions.py b/bot/cogs/moderation/infractions.py index 2713a1b68..7478e19ef 100644 --- a/bot/cogs/moderation/infractions.py +++ b/bot/cogs/moderation/infractions.py @@ -7,6 +7,7 @@ from discord.ext import commands from discord.ext.commands import Context, command from bot import constants +from bot.bot import Bot from bot.constants import Event from bot.decorators import respect_role_hierarchy from bot.utils.checks import with_role_check @@ -25,7 +26,7 @@ class Infractions(InfractionScheduler, commands.Cog): category = "Moderation" category_description = "Server moderation tools." - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): super().__init__(bot, supported_infractions={"ban", "kick", "mute", "note", "warning"}) self.category = "Moderation" diff --git a/bot/cogs/moderation/management.py b/bot/cogs/moderation/management.py index abfe5c2b3..feae00b7c 100644 --- a/bot/cogs/moderation/management.py +++ b/bot/cogs/moderation/management.py @@ -9,6 +9,7 @@ from discord.ext import commands from discord.ext.commands import Context from bot import constants +from bot.bot import Bot from bot.converters import InfractionSearchQuery from bot.pagination import LinePaginator from bot.utils import time @@ -36,7 +37,7 @@ class ModManagement(commands.Cog): category = "Moderation" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @property diff --git a/bot/cogs/moderation/modlog.py b/bot/cogs/moderation/modlog.py index 0df752a97..35ef6cbcc 100644 --- a/bot/cogs/moderation/modlog.py +++ b/bot/cogs/moderation/modlog.py @@ -10,8 +10,9 @@ from dateutil.relativedelta import relativedelta from deepdiff import DeepDiff from discord import Colour from discord.abc import GuildChannel -from discord.ext.commands import Bot, Cog, Context +from discord.ext.commands import Cog, Context +from bot.bot import Bot from bot.constants import Channels, Colours, Emojis, Event, Guild as GuildConstant, Icons, URLs from bot.utils.time import humanize_delta from .utils import UserTypes diff --git a/bot/cogs/moderation/scheduler.py b/bot/cogs/moderation/scheduler.py index 3e0968121..937113ef4 100644 --- a/bot/cogs/moderation/scheduler.py +++ b/bot/cogs/moderation/scheduler.py @@ -7,10 +7,11 @@ from gettext import ngettext import dateutil.parser import discord -from discord.ext.commands import Bot, Context +from discord.ext.commands import Context from bot import constants from bot.api import ResponseCodeError +from bot.bot import Bot from bot.constants import Colours, STAFF_CHANNELS from bot.utils import time from bot.utils.scheduling import Scheduler diff --git a/bot/cogs/moderation/superstarify.py b/bot/cogs/moderation/superstarify.py index 9b3c62403..7631d9bbe 100644 --- a/bot/cogs/moderation/superstarify.py +++ b/bot/cogs/moderation/superstarify.py @@ -6,9 +6,10 @@ import typing as t from pathlib import Path from discord import Colour, Embed, Member -from discord.ext.commands import Bot, Cog, Context, command +from discord.ext.commands import Cog, Context, command from bot import constants +from bot.bot import Bot from bot.utils.checks import with_role_check from bot.utils.time import format_infraction from . import utils diff --git a/bot/cogs/off_topic_names.py b/bot/cogs/off_topic_names.py index 78792240f..18d9cfb01 100644 --- a/bot/cogs/off_topic_names.py +++ b/bot/cogs/off_topic_names.py @@ -4,9 +4,10 @@ import logging from datetime import datetime, timedelta from discord import Colour, Embed -from discord.ext.commands import BadArgument, Bot, Cog, Context, Converter, group +from discord.ext.commands import BadArgument, Cog, Context, Converter, group from bot.api import ResponseCodeError +from bot.bot import Bot from bot.constants import Channels, MODERATION_ROLES from bot.decorators import with_role from bot.pagination import LinePaginator diff --git a/bot/cogs/reddit.py b/bot/cogs/reddit.py index 0d06e9c26..c76fcd937 100644 --- a/bot/cogs/reddit.py +++ b/bot/cogs/reddit.py @@ -6,9 +6,10 @@ from datetime import datetime, timedelta from typing import List from discord import Colour, Embed, TextChannel -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group from discord.ext.tasks import loop +from bot.bot import Bot from bot.constants import Channels, ERROR_REPLIES, Emojis, Reddit as RedditConfig, STAFF_ROLES, Webhooks from bot.converters import Subreddit from bot.decorators import with_role diff --git a/bot/cogs/reminders.py b/bot/cogs/reminders.py index 81990704b..b805b24c5 100644 --- a/bot/cogs/reminders.py +++ b/bot/cogs/reminders.py @@ -8,8 +8,9 @@ from typing import Optional from dateutil.relativedelta import relativedelta from discord import Colour, Embed, Message -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group +from bot.bot import Bot from bot.constants import Channels, Icons, NEGATIVE_REPLIES, POSITIVE_REPLIES, STAFF_ROLES from bot.converters import Duration from bot.pagination import LinePaginator diff --git a/bot/cogs/security.py b/bot/cogs/security.py index 316b33d6b..45d0eb2f5 100644 --- a/bot/cogs/security.py +++ b/bot/cogs/security.py @@ -1,6 +1,8 @@ import logging -from discord.ext.commands import Bot, Cog, Context, NoPrivateMessage +from discord.ext.commands import Cog, Context, NoPrivateMessage + +from bot.bot import Bot log = logging.getLogger(__name__) diff --git a/bot/cogs/site.py b/bot/cogs/site.py index 683613788..1d7bd03e4 100644 --- a/bot/cogs/site.py +++ b/bot/cogs/site.py @@ -1,8 +1,9 @@ import logging from discord import Colour, Embed -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group +from bot.bot import Bot from bot.constants import URLs from bot.pagination import LinePaginator diff --git a/bot/cogs/snekbox.py b/bot/cogs/snekbox.py index 55a187ac1..1ea61a8da 100644 --- a/bot/cogs/snekbox.py +++ b/bot/cogs/snekbox.py @@ -5,8 +5,9 @@ import textwrap from signal import Signals from typing import Optional, Tuple -from discord.ext.commands import Bot, Cog, Context, command, guild_only +from discord.ext.commands import Cog, Context, command, guild_only +from bot.bot import Bot from bot.constants import Channels, Roles, URLs from bot.decorators import in_channel from bot.utils.messages import wait_for_deletion diff --git a/bot/cogs/sync/__init__.py b/bot/cogs/sync/__init__.py index d4565f848..0da81c60e 100644 --- a/bot/cogs/sync/__init__.py +++ b/bot/cogs/sync/__init__.py @@ -1,7 +1,6 @@ import logging -from discord.ext.commands import Bot - +from bot.bot import Bot from .cog import Sync log = logging.getLogger(__name__) diff --git a/bot/cogs/sync/cog.py b/bot/cogs/sync/cog.py index aaa581f96..90d4c40fe 100644 --- a/bot/cogs/sync/cog.py +++ b/bot/cogs/sync/cog.py @@ -3,10 +3,11 @@ from typing import Callable, Iterable from discord import Guild, Member, Role from discord.ext import commands -from discord.ext.commands import Bot, Cog, Context +from discord.ext.commands import Cog, Context from bot import constants from bot.api import ResponseCodeError +from bot.bot import Bot from bot.cogs.sync import syncers log = logging.getLogger(__name__) diff --git a/bot/cogs/sync/syncers.py b/bot/cogs/sync/syncers.py index 2cc5a66e1..14cf51383 100644 --- a/bot/cogs/sync/syncers.py +++ b/bot/cogs/sync/syncers.py @@ -2,7 +2,8 @@ from collections import namedtuple from typing import Dict, Set, Tuple from discord import Guild -from discord.ext.commands import Bot + +from bot.bot import Bot # These objects are declared as namedtuples because tuples are hashable, # something that we make use of when diffing site roles against guild roles. @@ -52,7 +53,7 @@ async def sync_roles(bot: Bot, guild: Guild) -> Tuple[int, int, int]: Synchronize roles found on the given `guild` with the ones on the API. Arguments: - bot (discord.ext.commands.Bot): + bot (bot.bot.Bot): The bot instance that we're running with. guild (discord.Guild): @@ -169,7 +170,7 @@ async def sync_users(bot: Bot, guild: Guild) -> Tuple[int, int, None]: Synchronize users found in the given `guild` with the ones in the API. Arguments: - bot (discord.ext.commands.Bot): + bot (bot.bot.Bot): The bot instance that we're running with. guild (discord.Guild): diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index cd70e783a..2ece0095d 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -2,8 +2,9 @@ import logging import time from discord import Colour, Embed -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group +from bot.bot import Bot from bot.constants import Channels, Cooldowns, MODERATION_ROLES, Roles from bot.converters import TagContentConverter, TagNameConverter from bot.decorators import with_role diff --git a/bot/cogs/token_remover.py b/bot/cogs/token_remover.py index 5a0d20e57..7af7ed63a 100644 --- a/bot/cogs/token_remover.py +++ b/bot/cogs/token_remover.py @@ -6,9 +6,10 @@ import struct from datetime import datetime from discord import Colour, Message -from discord.ext.commands import Bot, Cog +from discord.ext.commands import Cog from discord.utils import snowflake_time +from bot.bot import Bot from bot.cogs.moderation import ModLog from bot.constants import Channels, Colours, Event, Icons diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py index 793fe4c1a..0ed996430 100644 --- a/bot/cogs/utils.py +++ b/bot/cogs/utils.py @@ -8,8 +8,9 @@ from typing import Tuple from dateutil import relativedelta from discord import Colour, Embed, Message, Role -from discord.ext.commands import Bot, Cog, Context, command +from discord.ext.commands import Cog, Context, command +from bot.bot import Bot from bot.constants import Channels, MODERATION_ROLES, Mention, STAFF_ROLES from bot.decorators import in_channel, with_role from bot.utils.time import humanize_delta diff --git a/bot/cogs/verification.py b/bot/cogs/verification.py index b5e8d4357..74eb0dbf8 100644 --- a/bot/cogs/verification.py +++ b/bot/cogs/verification.py @@ -3,8 +3,9 @@ from datetime import datetime from discord import Colour, Message, NotFound, Object from discord.ext import tasks -from discord.ext.commands import Bot, Cog, Context, command +from discord.ext.commands import Cog, Context, command +from bot.bot import Bot from bot.cogs.moderation import ModLog from bot.constants import ( Bot as BotConfig, diff --git a/bot/cogs/watchchannels/__init__.py b/bot/cogs/watchchannels/__init__.py index 86e1050fa..e18aea88a 100644 --- a/bot/cogs/watchchannels/__init__.py +++ b/bot/cogs/watchchannels/__init__.py @@ -1,7 +1,6 @@ import logging -from discord.ext.commands import Bot - +from bot.bot import Bot from .bigbrother import BigBrother from .talentpool import TalentPool diff --git a/bot/cogs/watchchannels/bigbrother.py b/bot/cogs/watchchannels/bigbrother.py index 49783bb09..306ed4c64 100644 --- a/bot/cogs/watchchannels/bigbrother.py +++ b/bot/cogs/watchchannels/bigbrother.py @@ -3,8 +3,9 @@ from collections import ChainMap from typing import Union from discord import User -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group +from bot.bot import Bot from bot.cogs.moderation.utils import post_infraction from bot.constants import Channels, MODERATION_ROLES, Webhooks from bot.decorators import with_role diff --git a/bot/cogs/watchchannels/talentpool.py b/bot/cogs/watchchannels/talentpool.py index 4ec42dcc1..cc8feeeee 100644 --- a/bot/cogs/watchchannels/talentpool.py +++ b/bot/cogs/watchchannels/talentpool.py @@ -4,9 +4,10 @@ from collections import ChainMap from typing import Union from discord import Color, Embed, Member, User -from discord.ext.commands import Bot, Cog, Context, group +from discord.ext.commands import Cog, Context, group from bot.api import ResponseCodeError +from bot.bot import Bot from bot.constants import Channels, Guild, MODERATION_ROLES, STAFF_ROLES, Webhooks from bot.decorators import with_role from bot.pagination import LinePaginator diff --git a/bot/cogs/watchchannels/watchchannel.py b/bot/cogs/watchchannels/watchchannel.py index 0bf75a924..bd0622554 100644 --- a/bot/cogs/watchchannels/watchchannel.py +++ b/bot/cogs/watchchannels/watchchannel.py @@ -10,9 +10,10 @@ from typing import Optional import dateutil.parser import discord from discord import Color, Embed, HTTPException, Message, Object, errors -from discord.ext.commands import BadArgument, Bot, Cog, Context +from discord.ext.commands import BadArgument, Cog, Context from bot.api import ResponseCodeError +from bot.bot import Bot from bot.cogs.moderation import ModLog from bot.constants import BigBrother as BigBrotherConfig, Guild as GuildConfig, Icons from bot.pagination import LinePaginator diff --git a/bot/cogs/wolfram.py b/bot/cogs/wolfram.py index ab0ed2472..c3c193cb9 100644 --- a/bot/cogs/wolfram.py +++ b/bot/cogs/wolfram.py @@ -7,8 +7,9 @@ import discord from dateutil.relativedelta import relativedelta from discord import Embed from discord.ext import commands -from discord.ext.commands import Bot, BucketType, Cog, Context, check, group +from discord.ext.commands import BucketType, Cog, Context, check, group +from bot.bot import Bot from bot.constants import Colours, STAFF_ROLES, Wolfram from bot.pagination import ImagePaginator from bot.utils.time import humanize_delta @@ -151,7 +152,7 @@ async def get_pod_pages(ctx: Context, bot: Bot, query: str) -> Optional[List[Tup class Wolfram(Cog): """Commands for interacting with the Wolfram|Alpha API.""" - def __init__(self, bot: commands.Bot): + def __init__(self, bot: Bot): self.bot = bot @group(name="wolfram", aliases=("wolf", "wa"), invoke_without_command=True) @@ -266,7 +267,7 @@ class Wolfram(Cog): await send_embed(ctx, message, color) -def setup(bot: commands.Bot) -> None: +def setup(bot: Bot) -> None: """Wolfram cog load.""" bot.add_cog(Wolfram(bot)) log.info("Cog loaded: Wolfram") diff --git a/bot/interpreter.py b/bot/interpreter.py index 76a3fc293..8b7268746 100644 --- a/bot/interpreter.py +++ b/bot/interpreter.py @@ -2,7 +2,9 @@ from code import InteractiveInterpreter from io import StringIO from typing import Any -from discord.ext.commands import Bot, Context +from discord.ext.commands import Context + +from bot.bot import Bot CODE_TEMPLATE = """ async def _func(): diff --git a/tests/helpers.py b/tests/helpers.py index b2daae92d..5df796c23 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -10,7 +10,9 @@ import unittest.mock from typing import Any, Iterable, Optional import discord -from discord.ext.commands import Bot, Context +from discord.ext.commands import Context + +from bot.bot import Bot for logger in logging.Logger.manager.loggerDict.values(): -- cgit v1.2.3 From 52924051e27d34c3f7e32c281fbe8ae0587a9766 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 7 Dec 2019 20:31:39 -0800 Subject: Override add_cog to log loading of cogs --- bot/bot.py | 5 +++++ bot/cogs/alias.py | 3 +-- bot/cogs/antimalware.py | 3 +-- bot/cogs/antispam.py | 3 +-- bot/cogs/bot.py | 3 +-- bot/cogs/clean.py | 3 +-- bot/cogs/defcon.py | 3 +-- bot/cogs/doc.py | 3 +-- bot/cogs/duck_pond.py | 3 +-- bot/cogs/error_handler.py | 3 +-- bot/cogs/eval.py | 3 +-- bot/cogs/extensions.py | 1 - bot/cogs/filtering.py | 3 +-- bot/cogs/free.py | 3 +-- bot/cogs/information.py | 3 +-- bot/cogs/jams.py | 3 +-- bot/cogs/logging.py | 3 +-- bot/cogs/moderation/__init__.py | 13 +------------ bot/cogs/off_topic_names.py | 3 +-- bot/cogs/reddit.py | 3 +-- bot/cogs/reminders.py | 3 +-- bot/cogs/security.py | 3 +-- bot/cogs/site.py | 3 +-- bot/cogs/snekbox.py | 3 +-- bot/cogs/sync/__init__.py | 7 +------ bot/cogs/tags.py | 3 +-- bot/cogs/token_remover.py | 3 +-- bot/cogs/utils.py | 3 +-- bot/cogs/verification.py | 3 +-- bot/cogs/watchchannels/__init__.py | 10 +--------- bot/cogs/wolfram.py | 3 +-- 31 files changed, 34 insertions(+), 80 deletions(-) diff --git a/bot/bot.py b/bot/bot.py index 05734ac1d..f39bfb50a 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -28,3 +28,8 @@ class Bot(commands.Bot): self.api_client = api.APIClient(loop=asyncio.get_event_loop()) log.addHandler(api.APILoggingHandler(self.api_client)) + + def add_cog(self, cog: commands.Cog) -> None: + """Adds a "cog" to the bot and logs the operation.""" + super().add_cog(cog) + log.info(f"Cog loaded: {cog.qualified_name}") diff --git a/bot/cogs/alias.py b/bot/cogs/alias.py index 4ee5a2aed..c1db38462 100644 --- a/bot/cogs/alias.py +++ b/bot/cogs/alias.py @@ -148,6 +148,5 @@ class Alias (Cog): def setup(bot: Bot) -> None: - """Alias cog load.""" + """Load the Alias cog.""" bot.add_cog(Alias(bot)) - log.info("Cog loaded: Alias") diff --git a/bot/cogs/antimalware.py b/bot/cogs/antimalware.py index 03c1e28a1..28e3e5d96 100644 --- a/bot/cogs/antimalware.py +++ b/bot/cogs/antimalware.py @@ -50,6 +50,5 @@ class AntiMalware(Cog): def setup(bot: Bot) -> None: - """Antimalware cog load.""" + """Load the AntiMalware cog.""" bot.add_cog(AntiMalware(bot)) - log.info("Cog loaded: AntiMalware") diff --git a/bot/cogs/antispam.py b/bot/cogs/antispam.py index 88912038a..f454061a6 100644 --- a/bot/cogs/antispam.py +++ b/bot/cogs/antispam.py @@ -277,7 +277,6 @@ def validate_config(rules: Mapping = AntiSpamConfig.rules) -> Dict[str, str]: def setup(bot: Bot) -> None: - """Antispam cog load.""" + """Validate the AntiSpam configs and load the AntiSpam cog.""" validation_errors = validate_config() bot.add_cog(AntiSpam(bot, validation_errors)) - log.info("Cog loaded: AntiSpam") diff --git a/bot/cogs/bot.py b/bot/cogs/bot.py index a2edb7576..b5642da82 100644 --- a/bot/cogs/bot.py +++ b/bot/cogs/bot.py @@ -378,6 +378,5 @@ class Bot(Cog): def setup(bot: Bot) -> None: - """Bot cog load.""" + """Load the Bot cog.""" bot.add_cog(Bot(bot)) - log.info("Cog loaded: Bot") diff --git a/bot/cogs/clean.py b/bot/cogs/clean.py index 3365d0934..c7168122d 100644 --- a/bot/cogs/clean.py +++ b/bot/cogs/clean.py @@ -212,6 +212,5 @@ class Clean(Cog): def setup(bot: Bot) -> None: - """Clean cog load.""" + """Load the Clean cog.""" bot.add_cog(Clean(bot)) - log.info("Cog loaded: Clean") diff --git a/bot/cogs/defcon.py b/bot/cogs/defcon.py index f062a7546..3e7350fcc 100644 --- a/bot/cogs/defcon.py +++ b/bot/cogs/defcon.py @@ -237,6 +237,5 @@ class Defcon(Cog): def setup(bot: Bot) -> None: - """DEFCON cog load.""" + """Load the Defcon cog.""" bot.add_cog(Defcon(bot)) - log.info("Cog loaded: Defcon") diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py index 7df159fd9..9506b195a 100644 --- a/bot/cogs/doc.py +++ b/bot/cogs/doc.py @@ -508,6 +508,5 @@ class Doc(commands.Cog): def setup(bot: Bot) -> None: - """Doc cog load.""" + """Load the Doc cog.""" bot.add_cog(Doc(bot)) - log.info("Cog loaded: Doc") diff --git a/bot/cogs/duck_pond.py b/bot/cogs/duck_pond.py index 879071d1b..345d2856c 100644 --- a/bot/cogs/duck_pond.py +++ b/bot/cogs/duck_pond.py @@ -178,6 +178,5 @@ class DuckPond(Cog): def setup(bot: Bot) -> None: - """Load the duck pond cog.""" + """Load the DuckPond cog.""" bot.add_cog(DuckPond(bot)) - log.info("Cog loaded: DuckPond") diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py index cf90e9f48..700f903a6 100644 --- a/bot/cogs/error_handler.py +++ b/bot/cogs/error_handler.py @@ -144,6 +144,5 @@ class ErrorHandler(Cog): def setup(bot: Bot) -> None: - """Error handler cog load.""" + """Load the ErrorHandler cog.""" bot.add_cog(ErrorHandler(bot)) - log.info("Cog loaded: Events") diff --git a/bot/cogs/eval.py b/bot/cogs/eval.py index 5daec3e39..9c729f28a 100644 --- a/bot/cogs/eval.py +++ b/bot/cogs/eval.py @@ -198,6 +198,5 @@ async def func(): # (None,) -> Any def setup(bot: Bot) -> None: - """Code eval cog load.""" + """Load the CodeEval cog.""" bot.add_cog(CodeEval(bot)) - log.info("Cog loaded: Eval") diff --git a/bot/cogs/extensions.py b/bot/cogs/extensions.py index 4d77d8205..f16e79fb7 100644 --- a/bot/cogs/extensions.py +++ b/bot/cogs/extensions.py @@ -234,4 +234,3 @@ class Extensions(commands.Cog): def setup(bot: Bot) -> None: """Load the Extensions cog.""" bot.add_cog(Extensions(bot)) - log.info("Cog loaded: Extensions") diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py index 2e54ccecb..74538542a 100644 --- a/bot/cogs/filtering.py +++ b/bot/cogs/filtering.py @@ -371,6 +371,5 @@ class Filtering(Cog): def setup(bot: Bot) -> None: - """Filtering cog load.""" + """Load the Filtering cog.""" bot.add_cog(Filtering(bot)) - log.info("Cog loaded: Filtering") diff --git a/bot/cogs/free.py b/bot/cogs/free.py index bbc9f063b..49cab6172 100644 --- a/bot/cogs/free.py +++ b/bot/cogs/free.py @@ -99,6 +99,5 @@ class Free(Cog): def setup(bot: Bot) -> None: - """Free cog load.""" + """Load the Free cog.""" bot.add_cog(Free()) - log.info("Cog loaded: Free") diff --git a/bot/cogs/information.py b/bot/cogs/information.py index 56bd37bec..1ede95ff4 100644 --- a/bot/cogs/information.py +++ b/bot/cogs/information.py @@ -392,6 +392,5 @@ class Information(Cog): def setup(bot: Bot) -> None: - """Information cog load.""" + """Load the Information cog.""" bot.add_cog(Information(bot)) - log.info("Cog loaded: Information") diff --git a/bot/cogs/jams.py b/bot/cogs/jams.py index 0c82e7962..985f28ce5 100644 --- a/bot/cogs/jams.py +++ b/bot/cogs/jams.py @@ -110,6 +110,5 @@ class CodeJams(commands.Cog): def setup(bot: Bot) -> None: - """Code Jams cog load.""" + """Load the CodeJams cog.""" bot.add_cog(CodeJams(bot)) - log.info("Cog loaded: CodeJams") diff --git a/bot/cogs/logging.py b/bot/cogs/logging.py index 44c771b42..d1b7dcab3 100644 --- a/bot/cogs/logging.py +++ b/bot/cogs/logging.py @@ -38,6 +38,5 @@ class Logging(Cog): def setup(bot: Bot) -> None: - """Logging cog load.""" + """Load the Logging cog.""" bot.add_cog(Logging(bot)) - log.info("Cog loaded: Logging") diff --git a/bot/cogs/moderation/__init__.py b/bot/cogs/moderation/__init__.py index 0cbdb3aa6..5243cb92d 100644 --- a/bot/cogs/moderation/__init__.py +++ b/bot/cogs/moderation/__init__.py @@ -1,24 +1,13 @@ -import logging - from bot.bot import Bot from .infractions import Infractions from .management import ModManagement from .modlog import ModLog from .superstarify import Superstarify -log = logging.getLogger(__name__) - def setup(bot: Bot) -> None: - """Load the moderation extension (Infractions, ModManagement, ModLog, & Superstarify cogs).""" + """Load the Infractions, ModManagement, ModLog, and Superstarify cogs.""" bot.add_cog(Infractions(bot)) - log.info("Cog loaded: Infractions") - bot.add_cog(ModLog(bot)) - log.info("Cog loaded: ModLog") - bot.add_cog(ModManagement(bot)) - log.info("Cog loaded: ModManagement") - bot.add_cog(Superstarify(bot)) - log.info("Cog loaded: Superstarify") diff --git a/bot/cogs/off_topic_names.py b/bot/cogs/off_topic_names.py index 18d9cfb01..bf777ea5a 100644 --- a/bot/cogs/off_topic_names.py +++ b/bot/cogs/off_topic_names.py @@ -185,6 +185,5 @@ class OffTopicNames(Cog): def setup(bot: Bot) -> None: - """Off topic names cog load.""" + """Load the OffTopicNames cog.""" bot.add_cog(OffTopicNames(bot)) - log.info("Cog loaded: OffTopicNames") diff --git a/bot/cogs/reddit.py b/bot/cogs/reddit.py index c76fcd937..bec316ae7 100644 --- a/bot/cogs/reddit.py +++ b/bot/cogs/reddit.py @@ -218,6 +218,5 @@ class Reddit(Cog): def setup(bot: Bot) -> None: - """Reddit cog load.""" + """Load the Reddit cog.""" bot.add_cog(Reddit(bot)) - log.info("Cog loaded: Reddit") diff --git a/bot/cogs/reminders.py b/bot/cogs/reminders.py index b805b24c5..45bf9a8f4 100644 --- a/bot/cogs/reminders.py +++ b/bot/cogs/reminders.py @@ -291,6 +291,5 @@ class Reminders(Scheduler, Cog): def setup(bot: Bot) -> None: - """Reminders cog load.""" + """Load the Reminders cog.""" bot.add_cog(Reminders(bot)) - log.info("Cog loaded: Reminders") diff --git a/bot/cogs/security.py b/bot/cogs/security.py index 45d0eb2f5..c680c5e27 100644 --- a/bot/cogs/security.py +++ b/bot/cogs/security.py @@ -27,6 +27,5 @@ class Security(Cog): def setup(bot: Bot) -> None: - """Security cog load.""" + """Load the Security cog.""" bot.add_cog(Security(bot)) - log.info("Cog loaded: Security") diff --git a/bot/cogs/site.py b/bot/cogs/site.py index 1d7bd03e4..2ea8c7a2e 100644 --- a/bot/cogs/site.py +++ b/bot/cogs/site.py @@ -139,6 +139,5 @@ class Site(Cog): def setup(bot: Bot) -> None: - """Site cog load.""" + """Load the Site cog.""" bot.add_cog(Site(bot)) - log.info("Cog loaded: Site") diff --git a/bot/cogs/snekbox.py b/bot/cogs/snekbox.py index 1ea61a8da..da33e27b2 100644 --- a/bot/cogs/snekbox.py +++ b/bot/cogs/snekbox.py @@ -228,6 +228,5 @@ class Snekbox(Cog): def setup(bot: Bot) -> None: - """Snekbox cog load.""" + """Load the Snekbox cog.""" bot.add_cog(Snekbox(bot)) - log.info("Cog loaded: Snekbox") diff --git a/bot/cogs/sync/__init__.py b/bot/cogs/sync/__init__.py index 0da81c60e..fe7df4e9b 100644 --- a/bot/cogs/sync/__init__.py +++ b/bot/cogs/sync/__init__.py @@ -1,12 +1,7 @@ -import logging - from bot.bot import Bot from .cog import Sync -log = logging.getLogger(__name__) - def setup(bot: Bot) -> None: - """Sync cog load.""" + """Load the Sync cog.""" bot.add_cog(Sync(bot)) - log.info("Cog loaded: Sync") diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index 2ece0095d..970301013 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -161,6 +161,5 @@ class Tags(Cog): def setup(bot: Bot) -> None: - """Tags cog load.""" + """Load the Tags cog.""" bot.add_cog(Tags(bot)) - log.info("Cog loaded: Tags") diff --git a/bot/cogs/token_remover.py b/bot/cogs/token_remover.py index 7af7ed63a..5d6618338 100644 --- a/bot/cogs/token_remover.py +++ b/bot/cogs/token_remover.py @@ -120,6 +120,5 @@ class TokenRemover(Cog): def setup(bot: Bot) -> None: - """Token Remover cog load.""" + """Load the TokenRemover cog.""" bot.add_cog(TokenRemover(bot)) - log.info("Cog loaded: TokenRemover") diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py index 0ed996430..47a59db66 100644 --- a/bot/cogs/utils.py +++ b/bot/cogs/utils.py @@ -177,6 +177,5 @@ class Utils(Cog): def setup(bot: Bot) -> None: - """Utils cog load.""" + """Load the Utils cog.""" bot.add_cog(Utils(bot)) - log.info("Cog loaded: Utils") diff --git a/bot/cogs/verification.py b/bot/cogs/verification.py index 74eb0dbf8..b32b9a29e 100644 --- a/bot/cogs/verification.py +++ b/bot/cogs/verification.py @@ -225,6 +225,5 @@ class Verification(Cog): def setup(bot: Bot) -> None: - """Verification cog load.""" + """Load the Verification cog.""" bot.add_cog(Verification(bot)) - log.info("Cog loaded: Verification") diff --git a/bot/cogs/watchchannels/__init__.py b/bot/cogs/watchchannels/__init__.py index e18aea88a..69d118df6 100644 --- a/bot/cogs/watchchannels/__init__.py +++ b/bot/cogs/watchchannels/__init__.py @@ -1,17 +1,9 @@ -import logging - from bot.bot import Bot from .bigbrother import BigBrother from .talentpool import TalentPool -log = logging.getLogger(__name__) - - def setup(bot: Bot) -> None: - """Monitoring cogs load.""" + """Load the BigBrother and TalentPool cogs.""" bot.add_cog(BigBrother(bot)) - log.info("Cog loaded: BigBrother") - bot.add_cog(TalentPool(bot)) - log.info("Cog loaded: TalentPool") diff --git a/bot/cogs/wolfram.py b/bot/cogs/wolfram.py index c3c193cb9..5d6b4630b 100644 --- a/bot/cogs/wolfram.py +++ b/bot/cogs/wolfram.py @@ -268,6 +268,5 @@ class Wolfram(Cog): def setup(bot: Bot) -> None: - """Wolfram cog load.""" + """Load the Wolfram cog.""" bot.add_cog(Wolfram(bot)) - log.info("Cog loaded: Wolfram") -- cgit v1.2.3 From a4a53f3b9d1cc9928ee03d4f0ecb8087a527e8ca Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sat, 7 Dec 2019 20:39:09 -0800 Subject: Fix name conflict with the Bot cog --- bot/cogs/bot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/cogs/bot.py b/bot/cogs/bot.py index b5642da82..e795e5960 100644 --- a/bot/cogs/bot.py +++ b/bot/cogs/bot.py @@ -17,7 +17,7 @@ log = logging.getLogger(__name__) RE_MARKDOWN = re.compile(r'([*_~`|>])') -class Bot(Cog): +class BotCog(Cog, name="Bot"): """Bot information commands.""" def __init__(self, bot: Bot): @@ -374,9 +374,9 @@ class Bot(Cog): bot_message = await channel.fetch_message(self.codeblock_message_ids[payload.message_id]) await bot_message.delete() del self.codeblock_message_ids[payload.message_id] - log.trace("User's incorrect code block has been fixed. Removing bot formatting message.") + log.trace("User's incorrect code block has been fixed. Removing bot formatting message.") def setup(bot: Bot) -> None: """Load the Bot cog.""" - bot.add_cog(Bot(bot)) + bot.add_cog(BotCog(bot)) -- cgit v1.2.3 From 56578525ac4e5c6d20392d1208b74623c8524bcd Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sun, 8 Dec 2019 01:40:19 -0800 Subject: Properly create and close aiohttp sessions aiohttp throws a warning when a session is created outside of a running async event loop. In aiohttp 4.0 this actually changes to an error instead of merely a warning. Since discord.py manages the event loop with client.run(), some of the "internal" coroutines of the client were overwritten in the bot subclass to be able to hook into when the bot starts and stops. Sessions of both the bot and the API client can now potentially be None if accessed before the sessions have been created. However, if called, the API client's methods will wait for a session to be ready. It will attempt to create a session as soon as the event loop starts (i.e. the bot is running). --- bot/api.py | 41 +++++++++++++++++++++++++++++++++++++++-- bot/bot.py | 34 ++++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/bot/api.py b/bot/api.py index 7f26e5305..56db99828 100644 --- a/bot/api.py +++ b/bot/api.py @@ -32,7 +32,7 @@ class ResponseCodeError(ValueError): class APIClient: """Django Site API wrapper.""" - def __init__(self, **kwargs): + def __init__(self, loop: asyncio.AbstractEventLoop, **kwargs): auth_headers = { 'Authorization': f"Token {Keys.site_api}" } @@ -42,12 +42,39 @@ class APIClient: else: kwargs['headers'] = auth_headers - self.session = aiohttp.ClientSession(**kwargs) + self.session: Optional[aiohttp.ClientSession] = None + self.loop = loop + + self._ready = asyncio.Event(loop=loop) + self._creation_task = None + self._session_args = kwargs + + self.recreate() @staticmethod def _url_for(endpoint: str) -> str: return f"{URLs.site_schema}{URLs.site_api}/{quote_url(endpoint)}" + async def _create_session(self) -> None: + """Create the aiohttp session and set the ready event.""" + self.session = aiohttp.ClientSession(**self._session_args) + self._ready.set() + + async def close(self) -> None: + """Close the aiohttp session and unset the ready event.""" + if not self._ready.is_set(): + return + + await self.session.close() + self._ready.clear() + + def recreate(self) -> None: + """Schedule the aiohttp session to be created if it's been closed.""" + if self.session is None or self.session.closed: + # Don't schedule a task if one is already in progress. + if self._creation_task is None or self._creation_task.done(): + self._creation_task = self.loop.create_task(self._create_session()) + async def maybe_raise_for_status(self, response: aiohttp.ClientResponse, should_raise: bool) -> None: """Raise ResponseCodeError for non-OK response if an exception should be raised.""" if should_raise and response.status >= 400: @@ -60,30 +87,40 @@ class APIClient: async def get(self, endpoint: str, *args, raise_for_status: bool = True, **kwargs) -> dict: """Site API GET.""" + await self._ready.wait() + async with self.session.get(self._url_for(endpoint), *args, **kwargs) as resp: await self.maybe_raise_for_status(resp, raise_for_status) return await resp.json() async def patch(self, endpoint: str, *args, raise_for_status: bool = True, **kwargs) -> dict: """Site API PATCH.""" + await self._ready.wait() + async with self.session.patch(self._url_for(endpoint), *args, **kwargs) as resp: await self.maybe_raise_for_status(resp, raise_for_status) return await resp.json() async def post(self, endpoint: str, *args, raise_for_status: bool = True, **kwargs) -> dict: """Site API POST.""" + await self._ready.wait() + async with self.session.post(self._url_for(endpoint), *args, **kwargs) as resp: await self.maybe_raise_for_status(resp, raise_for_status) return await resp.json() async def put(self, endpoint: str, *args, raise_for_status: bool = True, **kwargs) -> dict: """Site API PUT.""" + await self._ready.wait() + async with self.session.put(self._url_for(endpoint), *args, **kwargs) as resp: await self.maybe_raise_for_status(resp, raise_for_status) return await resp.json() async def delete(self, endpoint: str, *args, raise_for_status: bool = True, **kwargs) -> Optional[dict]: """Site API DELETE.""" + await self._ready.wait() + async with self.session.delete(self._url_for(endpoint), *args, **kwargs) as resp: if resp.status == 204: return None diff --git a/bot/bot.py b/bot/bot.py index f39bfb50a..4b3b991a3 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -1,6 +1,6 @@ -import asyncio import logging import socket +from typing import Optional import aiohttp from discord.ext import commands @@ -16,6 +16,30 @@ class Bot(commands.Bot): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.http_session: Optional[aiohttp.ClientSession] = None + self.api_client = api.APIClient(loop=self.loop) + + log.addHandler(api.APILoggingHandler(self.api_client)) + + def add_cog(self, cog: commands.Cog) -> None: + """Adds a "cog" to the bot and logs the operation.""" + super().add_cog(cog) + log.info(f"Cog loaded: {cog.qualified_name}") + + def clear(self) -> None: + """Clears the internal state of the bot and resets the API client.""" + super().clear() + self.api_client.recreate() + + async def close(self) -> None: + """Close the aiohttp session after closing the Discord connection.""" + await super().close() + + await self.http_session.close() + await self.api_client.close() + + async def start(self, *args, **kwargs) -> None: + """Open an aiohttp session before logging in and connecting to Discord.""" # Global aiohttp session for all cogs # - Uses asyncio for DNS resolution instead of threads, so we don't spam threads # - Uses AF_INET as its socket family to prevent https related problems both locally and in prod. @@ -26,10 +50,4 @@ class Bot(commands.Bot): ) ) - self.api_client = api.APIClient(loop=asyncio.get_event_loop()) - log.addHandler(api.APILoggingHandler(self.api_client)) - - def add_cog(self, cog: commands.Cog) -> None: - """Adds a "cog" to the bot and logs the operation.""" - super().add_cog(cog) - log.info(f"Cog loaded: {cog.qualified_name}") + await super().start(*args, **kwargs) -- cgit v1.2.3 From 23acfce3521cd420a2df6eb51f036a2a54140ef6 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sun, 8 Dec 2019 02:01:01 -0800 Subject: Fix test failures for setup log messages --- tests/bot/cogs/test_duck_pond.py | 12 ++---------- tests/bot/cogs/test_security.py | 11 +++-------- tests/bot/cogs/test_token_remover.py | 8 ++------ 3 files changed, 7 insertions(+), 24 deletions(-) diff --git a/tests/bot/cogs/test_duck_pond.py b/tests/bot/cogs/test_duck_pond.py index b801e86f1..d07b2bce1 100644 --- a/tests/bot/cogs/test_duck_pond.py +++ b/tests/bot/cogs/test_duck_pond.py @@ -578,15 +578,7 @@ class DuckPondSetupTests(unittest.TestCase): """Tests setup of the `DuckPond` cog.""" def test_setup(self): - """Setup of the cog should log a message at `INFO` level.""" + """Setup of the extension should call add_cog.""" bot = helpers.MockBot() - log = logging.getLogger('bot.cogs.duck_pond') - - with self.assertLogs(logger=log, level=logging.INFO) as log_watcher: - duck_pond.setup(bot) - - self.assertEqual(len(log_watcher.records), 1) - record = log_watcher.records[0] - self.assertEqual(record.levelno, logging.INFO) - + duck_pond.setup(bot) bot.add_cog.assert_called_once() diff --git a/tests/bot/cogs/test_security.py b/tests/bot/cogs/test_security.py index efa7a50b1..9d1a62f7e 100644 --- a/tests/bot/cogs/test_security.py +++ b/tests/bot/cogs/test_security.py @@ -1,4 +1,3 @@ -import logging import unittest from unittest.mock import MagicMock @@ -49,11 +48,7 @@ class SecurityCogLoadTests(unittest.TestCase): """Tests loading the `Security` cog.""" def test_security_cog_load(self): - """Cog loading logs a message at `INFO` level.""" + """Setup of the extension should call add_cog.""" bot = MagicMock() - with self.assertLogs(logger='bot.cogs.security', level=logging.INFO) as cm: - security.setup(bot) - bot.add_cog.assert_called_once() - - [line] = cm.output - self.assertIn("Cog loaded: Security", line) + security.setup(bot) + bot.add_cog.assert_called_once() diff --git a/tests/bot/cogs/test_token_remover.py b/tests/bot/cogs/test_token_remover.py index 3276cf5a5..a54b839d7 100644 --- a/tests/bot/cogs/test_token_remover.py +++ b/tests/bot/cogs/test_token_remover.py @@ -125,11 +125,7 @@ class TokenRemoverSetupTests(unittest.TestCase): """Tests setup of the `TokenRemover` cog.""" def test_setup(self): - """Setup of the cog should log a message at `INFO` level.""" + """Setup of the extension should call add_cog.""" bot = MockBot() - with self.assertLogs(logger='bot.cogs.token_remover', level=logging.INFO) as cm: - setup_cog(bot) - - [line] = cm.output + setup_cog(bot) bot.add_cog.assert_called_once() - self.assertIn("Cog loaded: TokenRemover", line) -- cgit v1.2.3 From dbd7220caed5a6ba759d0cf9efaa0c0c0e57f391 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Sun, 8 Dec 2019 23:40:17 -0800 Subject: Use the AsyncResolver for APIClient and discord.py sessions too Active thread counts are observed to be lower with it in use. --- bot/bot.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/bot/bot.py b/bot/bot.py index 4b3b991a3..8f808272f 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -14,10 +14,18 @@ class Bot(commands.Bot): """A subclass of `discord.ext.commands.Bot` with an aiohttp session and an API client.""" def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + # Use asyncio for DNS resolution instead of threads so threads aren't spammed. + # Use AF_INET as its socket family to prevent HTTPS related problems both locally + # and in production. + self.connector = aiohttp.TCPConnector( + resolver=aiohttp.AsyncResolver(), + family=socket.AF_INET, + ) + + super().__init__(*args, connector=self.connector, **kwargs) self.http_session: Optional[aiohttp.ClientSession] = None - self.api_client = api.APIClient(loop=self.loop) + self.api_client = api.APIClient(loop=self.loop, connector=self.connector) log.addHandler(api.APILoggingHandler(self.api_client)) @@ -40,14 +48,6 @@ class Bot(commands.Bot): async def start(self, *args, **kwargs) -> None: """Open an aiohttp session before logging in and connecting to Discord.""" - # Global aiohttp session for all cogs - # - Uses asyncio for DNS resolution instead of threads, so we don't spam threads - # - Uses AF_INET as its socket family to prevent https related problems both locally and in prod. - self.http_session = aiohttp.ClientSession( - connector=aiohttp.TCPConnector( - resolver=aiohttp.AsyncResolver(), - family=socket.AF_INET, - ) - ) + self.http_session = aiohttp.ClientSession(connector=self.connector) await super().start(*args, **kwargs) -- cgit v1.2.3 From 1b938af27cb9901acdb86579029dc4a7cbae0b7d Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Mon, 9 Dec 2019 23:18:32 -0800 Subject: Moderation: show HTTP status code in the log for deactivation failures --- bot/cogs/moderation/scheduler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/moderation/scheduler.py b/bot/cogs/moderation/scheduler.py index 3e0968121..703b09802 100644 --- a/bot/cogs/moderation/scheduler.py +++ b/bot/cogs/moderation/scheduler.py @@ -329,7 +329,7 @@ class InfractionScheduler(Scheduler): log_content = mod_role.mention except discord.HTTPException as e: log.exception(f"Failed to deactivate infraction #{id_} ({type_})") - log_text["Failure"] = f"HTTPException with code {e.code}." + log_text["Failure"] = f"HTTPException with status {e.status} and code {e.code}." log_content = mod_role.mention # Check if the user is currently being watched by Big Brother. -- cgit v1.2.3 From d0e14dca855179bd71c46747ecf63d4038045881 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Mon, 9 Dec 2019 23:34:30 -0800 Subject: Moderation: catch HTTPException when applying an infraction Only a warning is logged if it's a Forbidden error. Otherwise, the whole exception is logged. --- bot/cogs/moderation/scheduler.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bot/cogs/moderation/scheduler.py b/bot/cogs/moderation/scheduler.py index 703b09802..8e5b4691f 100644 --- a/bot/cogs/moderation/scheduler.py +++ b/bot/cogs/moderation/scheduler.py @@ -146,14 +146,18 @@ class InfractionScheduler(Scheduler): if expiry: # Schedule the expiration of the infraction. self.schedule_task(ctx.bot.loop, infraction["id"], infraction) - except discord.Forbidden: + except discord.HTTPException as e: # Accordingly display that applying the infraction failed. confirm_msg = f":x: failed to apply" expiry_msg = "" log_content = ctx.author.mention log_title = "failed to apply" - log.warning(f"Failed to apply {infr_type} infraction #{id_} to {user}.") + log_msg = f"Failed to apply {infr_type} infraction #{id_} to {user}" + if isinstance(e, discord.Forbidden): + log.warning(f"{log_msg}: bot lacks permissions.") + else: + log.exception(log_msg) # Send a confirmation message to the invoking context. log.trace(f"Sending infraction #{id_} confirmation message.") @@ -324,7 +328,7 @@ class InfractionScheduler(Scheduler): f"Attempted to deactivate an unsupported infraction #{id_} ({type_})!" ) except discord.Forbidden: - log.warning(f"Failed to deactivate infraction #{id_} ({type_}): bot lacks permissions") + log.warning(f"Failed to deactivate infraction #{id_} ({type_}): bot lacks permissions.") log_text["Failure"] = f"The bot lacks permissions to do this (role hierarchy?)" log_content = mod_role.mention except discord.HTTPException as e: -- cgit v1.2.3 From f0e993a3514c1ef7256c4b7593d4db94a6d34569 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Mon, 9 Dec 2019 23:41:27 -0800 Subject: Infractions: kick user from voice after muting (#644) --- bot/cogs/moderation/infractions.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bot/cogs/moderation/infractions.py b/bot/cogs/moderation/infractions.py index 2713a1b68..fe5150652 100644 --- a/bot/cogs/moderation/infractions.py +++ b/bot/cogs/moderation/infractions.py @@ -208,8 +208,13 @@ class Infractions(InfractionScheduler, commands.Cog): self.mod_log.ignore(Event.member_update, user.id) - action = user.add_roles(self._muted_role, reason=reason) - await self.apply_infraction(ctx, infraction, user, action) + async def action() -> None: + await user.add_roles(self._muted_role, reason=reason) + + log.trace(f"Attempting to kick {user} from voice because they've been muted.") + await user.move_to(None, reason=reason) + + await self.apply_infraction(ctx, infraction, user, action()) @respect_role_hierarchy() async def apply_kick(self, ctx: Context, user: Member, reason: str, **kwargs) -> None: -- cgit v1.2.3 From 9a3e83116e145b720fc47b0686b357fa6ae9e488 Mon Sep 17 00:00:00 2001 From: MarkKoz Date: Wed, 11 Dec 2019 02:05:18 -0800 Subject: ErrorHandler: fix #650 tag fallback not respecting checks --- bot/cogs/error_handler.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py index 49411814c..5fba9633b 100644 --- a/bot/cogs/error_handler.py +++ b/bot/cogs/error_handler.py @@ -75,6 +75,16 @@ class ErrorHandler(Cog): tags_get_command = self.bot.get_command("tags get") ctx.invoked_from_error_handler = True + log_msg = "Cancelling attempt to fall back to a tag due to failed checks." + try: + if not await tags_get_command.can_run(ctx): + log.debug(log_msg) + return + except CommandError as tag_error: + log.debug(log_msg) + await self.on_command_error(ctx, tag_error) + return + # Return to not raise the exception with contextlib.suppress(ResponseCodeError): await ctx.invoke(tags_get_command, tag_name=ctx.invoked_with) -- cgit v1.2.3