aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Chris Lovering <[email protected]>2022-03-19 16:34:01 +0000
committerGravatar Chris Lovering <[email protected]>2022-04-18 17:44:24 +0100
commit43b6fee9eba12a6836530029a642cba6e7e505f0 (patch)
tree680e5d6c42d4b81504359722f7e9b3f8b6b82d73
parentBump d.py and bot-core (diff)
Use bot-core scheduling and member util functions
-rw-r--r--bot/__init__.py17
-rw-r--r--bot/async_stats.py3
-rw-r--r--bot/converters.py2
-rw-r--r--bot/decorators.py3
-rw-r--r--bot/exts/backend/logging.py2
-rw-r--r--bot/exts/filters/antispam.py3
-rw-r--r--bot/exts/filters/filtering.py4
-rw-r--r--bot/exts/filters/token_remover.py5
-rw-r--r--bot/exts/fun/off_topic_names.py2
-rw-r--r--bot/exts/help_channels/_cog.py3
-rw-r--r--bot/exts/info/codeblock/_cog.py3
-rw-r--r--bot/exts/info/doc/_batch_parser.py2
-rw-r--r--bot/exts/info/doc/_cog.py4
-rw-r--r--bot/exts/info/subscribe.py2
-rw-r--r--bot/exts/moderation/defcon.py5
-rw-r--r--bot/exts/moderation/incidents.py2
-rw-r--r--bot/exts/moderation/infraction/_scheduler.py3
-rw-r--r--bot/exts/moderation/metabase.py5
-rw-r--r--bot/exts/moderation/modpings.py5
-rw-r--r--bot/exts/moderation/silence.py4
-rw-r--r--bot/exts/moderation/stream.py3
-rw-r--r--bot/exts/moderation/watchchannels/_watchchannel.py3
-rw-r--r--bot/exts/recruitment/talentpool/_cog.py3
-rw-r--r--bot/exts/recruitment/talentpool/_review.py2
-rw-r--r--bot/exts/utils/reminders.py5
-rw-r--r--bot/exts/utils/snekbox.py5
-rw-r--r--bot/monkey_patches.py76
-rw-r--r--bot/utils/messages.py2
-rw-r--r--bot/utils/scheduling.py194
-rw-r--r--tests/bot/exts/backend/sync/test_cog.py2
-rw-r--r--tests/bot/exts/filters/test_filtering.py2
31 files changed, 53 insertions, 323 deletions
diff --git a/bot/__init__.py b/bot/__init__.py
index 17d99105a..c652897be 100644
--- a/bot/__init__.py
+++ b/bot/__init__.py
@@ -1,11 +1,10 @@
import asyncio
import os
-from functools import partial, partialmethod
from typing import TYPE_CHECKING
-from discord.ext import commands
+from botcore.utils import apply_monkey_patches
-from bot import log, monkey_patches
+from bot import log
if TYPE_CHECKING:
from bot.bot import Bot
@@ -16,16 +15,6 @@ log.setup()
if os.name == "nt":
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
-monkey_patches.patch_typing()
-
-# This patches any convertors that use PartialMessage, but not the PartialMessageConverter itself
-# as library objects are made by this mapping.
-# https://github.com/Rapptz/discord.py/blob/1a4e73d59932cdbe7bf2c281f25e32529fc7ae1f/discord/ext/commands/converter.py#L984-L1004
-commands.converter.PartialMessageConverter = monkey_patches.FixedPartialMessageConverter
-
-# Monkey-patch discord.py decorators to use the Command subclass which supports root aliases.
-# Must be patched before any cogs are added.
-commands.command = partial(commands.command, cls=monkey_patches.Command)
-commands.GroupMixin.command = partialmethod(commands.GroupMixin.command, cls=monkey_patches.Command)
+apply_monkey_patches()
instance: "Bot" = None # Global Bot instance.
diff --git a/bot/async_stats.py b/bot/async_stats.py
index 2af832e5b..0303de7a1 100644
--- a/bot/async_stats.py
+++ b/bot/async_stats.py
@@ -1,10 +1,9 @@
import asyncio
import socket
+from botcore.utils import scheduling
from statsd.client.base import StatsClientBase
-from bot.utils import scheduling
-
class AsyncStatsClient(StatsClientBase):
"""An async transport method for statsd communication."""
diff --git a/bot/converters.py b/bot/converters.py
index 3522a32aa..e819e4713 100644
--- a/bot/converters.py
+++ b/bot/converters.py
@@ -8,7 +8,7 @@ from ssl import CertificateError
import dateutil.parser
import discord
from aiohttp import ClientConnectorError
-from botcore.regex import DISCORD_INVITE
+from botcore.utils.regex import DISCORD_INVITE
from dateutil.relativedelta import relativedelta
from discord.ext.commands import BadArgument, Bot, Context, Converter, IDConverter, MemberConverter, UserConverter
from discord.utils import escape_markdown, snowflake_time
diff --git a/bot/decorators.py b/bot/decorators.py
index 8971898b3..466770c3a 100644
--- a/bot/decorators.py
+++ b/bot/decorators.py
@@ -5,13 +5,14 @@ import typing as t
from contextlib import suppress
import arrow
+from botcore.utils import scheduling
from discord import Member, NotFound
from discord.ext import commands
from discord.ext.commands import Cog, Context
from bot.constants import Channels, DEBUG_MODE, RedirectOutput
from bot.log import get_logger
-from bot.utils import function, scheduling
+from bot.utils import function
from bot.utils.checks import ContextCheckFailure, in_whitelist_check
from bot.utils.function import command_wraps
diff --git a/bot/exts/backend/logging.py b/bot/exts/backend/logging.py
index 2d03cd580..469331ae5 100644
--- a/bot/exts/backend/logging.py
+++ b/bot/exts/backend/logging.py
@@ -1,10 +1,10 @@
+from botcore.utils import scheduling
from discord import Embed
from discord.ext.commands import Cog
from bot.bot import Bot
from bot.constants import Channels, DEBUG_MODE
from bot.log import get_logger
-from bot.utils import scheduling
log = get_logger(__name__)
diff --git a/bot/exts/filters/antispam.py b/bot/exts/filters/antispam.py
index bcd845a43..d9e23b25e 100644
--- a/bot/exts/filters/antispam.py
+++ b/bot/exts/filters/antispam.py
@@ -8,6 +8,7 @@ from operator import attrgetter, itemgetter
from typing import Dict, Iterable, List, Set
import arrow
+from botcore.utils import scheduling
from discord import Colour, Member, Message, NotFound, Object, TextChannel
from discord.ext.commands import Cog
@@ -20,7 +21,7 @@ from bot.converters import Duration
from bot.exts.events.code_jams._channels import CATEGORY_NAME as JAM_CATEGORY_NAME
from bot.exts.moderation.modlog import ModLog
from bot.log import get_logger
-from bot.utils import lock, scheduling
+from bot.utils import lock
from bot.utils.message_cache import MessageCache
from bot.utils.messages import format_user, send_attachments
diff --git a/bot/exts/filters/filtering.py b/bot/exts/filters/filtering.py
index b9f2a0e51..32efcc307 100644
--- a/bot/exts/filters/filtering.py
+++ b/bot/exts/filters/filtering.py
@@ -9,7 +9,8 @@ import dateutil.parser
import regex
import tldextract
from async_rediscache import RedisCache
-from botcore.regex import DISCORD_INVITE
+from botcore.utils import scheduling
+from botcore.utils.regex import DISCORD_INVITE
from dateutil.relativedelta import relativedelta
from discord import ChannelType, Colour, Embed, Forbidden, HTTPException, Member, Message, NotFound, TextChannel
from discord.ext.commands import Cog
@@ -21,7 +22,6 @@ from bot.constants import Channels, Colours, Filter, Guild, Icons, URLs
from bot.exts.events.code_jams._channels import CATEGORY_NAME as JAM_CATEGORY_NAME
from bot.exts.moderation.modlog import ModLog
from bot.log import get_logger
-from bot.utils import scheduling
from bot.utils.messages import format_user
log = get_logger(__name__)
diff --git a/bot/exts/filters/token_remover.py b/bot/exts/filters/token_remover.py
index 520283ba3..436e6dc19 100644
--- a/bot/exts/filters/token_remover.py
+++ b/bot/exts/filters/token_remover.py
@@ -1,5 +1,4 @@
import base64
-import binascii
import re
import typing as t
@@ -182,7 +181,7 @@ class TokenRemover(Cog):
# that means it's not a valid user id.
return None
return int(string)
- except (binascii.Error, ValueError):
+ except ValueError:
return None
@staticmethod
@@ -198,7 +197,7 @@ class TokenRemover(Cog):
try:
decoded_bytes = base64.urlsafe_b64decode(b64_content)
timestamp = int.from_bytes(decoded_bytes, byteorder="big")
- except (binascii.Error, ValueError) as e:
+ except ValueError as e:
log.debug(f"Failed to decode token timestamp '{b64_content}': {e}")
return False
diff --git a/bot/exts/fun/off_topic_names.py b/bot/exts/fun/off_topic_names.py
index 7df1d172d..33f43f2a8 100644
--- a/bot/exts/fun/off_topic_names.py
+++ b/bot/exts/fun/off_topic_names.py
@@ -2,6 +2,7 @@ import difflib
from datetime import timedelta
import arrow
+from botcore.utils import scheduling
from discord import Colour, Embed
from discord.ext.commands import Cog, Context, group, has_any_role
from discord.utils import sleep_until
@@ -12,7 +13,6 @@ from bot.constants import Channels, MODERATION_ROLES
from bot.converters import OffTopicName
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils import scheduling
CHANNELS = (Channels.off_topic_0, Channels.off_topic_1, Channels.off_topic_2)
log = get_logger(__name__)
diff --git a/bot/exts/help_channels/_cog.py b/bot/exts/help_channels/_cog.py
index a93acffb6..fc80c968c 100644
--- a/bot/exts/help_channels/_cog.py
+++ b/bot/exts/help_channels/_cog.py
@@ -7,6 +7,7 @@ from operator import attrgetter
import arrow
import discord
import discord.abc
+from botcore.utils import members, scheduling
from discord.ext import commands
from bot import constants
@@ -14,7 +15,7 @@ from bot.bot import Bot
from bot.constants import Channels, RedirectOutput
from bot.exts.help_channels import _caches, _channel, _message, _name, _stats
from bot.log import get_logger
-from bot.utils import channel as channel_utils, lock, members, scheduling
+from bot.utils import channel as channel_utils, lock
log = get_logger(__name__)
diff --git a/bot/exts/info/codeblock/_cog.py b/bot/exts/info/codeblock/_cog.py
index a859d8cef..9027105d9 100644
--- a/bot/exts/info/codeblock/_cog.py
+++ b/bot/exts/info/codeblock/_cog.py
@@ -2,6 +2,7 @@ import time
from typing import Optional
import discord
+from botcore.utils import scheduling
from discord import Message, RawMessageUpdateEvent
from discord.ext.commands import Cog
@@ -11,7 +12,7 @@ from bot.exts.filters.token_remover import TokenRemover
from bot.exts.filters.webhook_remover import WEBHOOK_URL_RE
from bot.exts.info.codeblock._instructions import get_instructions
from bot.log import get_logger
-from bot.utils import has_lines, scheduling
+from bot.utils import has_lines
from bot.utils.channel import is_help_channel
from bot.utils.messages import wait_for_deletion
diff --git a/bot/exts/info/doc/_batch_parser.py b/bot/exts/info/doc/_batch_parser.py
index c27f28eac..41a15fb6e 100644
--- a/bot/exts/info/doc/_batch_parser.py
+++ b/bot/exts/info/doc/_batch_parser.py
@@ -8,12 +8,12 @@ from operator import attrgetter
from typing import Deque, Dict, List, NamedTuple, Optional, Union
import discord
+from botcore.utils import scheduling
from bs4 import BeautifulSoup
import bot
from bot.constants import Channels
from bot.log import get_logger
-from bot.utils import scheduling
from . import _cog, doc_cache
from ._parsing import get_symbol_markdown
diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py
index 4dc5276d9..3789fdbe3 100644
--- a/bot/exts/info/doc/_cog.py
+++ b/bot/exts/info/doc/_cog.py
@@ -10,6 +10,8 @@ from typing import Dict, NamedTuple, Optional, Tuple, Union
import aiohttp
import discord
+from botcore.utils import scheduling
+from botcore.utils.scheduling import Scheduler
from discord.ext import commands
from bot.api import ResponseCodeError
@@ -18,10 +20,8 @@ from bot.constants import MODERATION_ROLES, RedirectOutput
from bot.converters import Inventory, PackageName, ValidURL, allowed_strings
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils import scheduling
from bot.utils.lock import SharedEvent, lock
from bot.utils.messages import send_denial, wait_for_deletion
-from bot.utils.scheduling import Scheduler
from . import NAMESPACE, PRIORITY_PACKAGES, _batch_parser, doc_cache
from ._inventory_parser import InvalidHeaderError, InventoryDict, fetch_inventory
diff --git a/bot/exts/info/subscribe.py b/bot/exts/info/subscribe.py
index eff0c13b8..ed134ff78 100644
--- a/bot/exts/info/subscribe.py
+++ b/bot/exts/info/subscribe.py
@@ -5,6 +5,7 @@ from dataclasses import dataclass
import arrow
import discord
+from botcore.utils import members, scheduling
from discord.ext import commands
from discord.interactions import Interaction
@@ -12,7 +13,6 @@ from bot import constants
from bot.bot import Bot
from bot.decorators import redirect_output
from bot.log import get_logger
-from bot.utils import members, scheduling
@dataclass(frozen=True)
diff --git a/bot/exts/moderation/defcon.py b/bot/exts/moderation/defcon.py
index 178be734d..a8640cb1b 100644
--- a/bot/exts/moderation/defcon.py
+++ b/bot/exts/moderation/defcon.py
@@ -7,6 +7,8 @@ from typing import Optional, Union
import arrow
from aioredis import RedisError
from async_rediscache import RedisCache
+from botcore.utils import scheduling
+from botcore.utils.scheduling import Scheduler
from dateutil.relativedelta import relativedelta
from discord import Colour, Embed, Forbidden, Member, TextChannel, User
from discord.ext import tasks
@@ -17,9 +19,8 @@ from bot.constants import Channels, Colours, Emojis, Event, Icons, MODERATION_RO
from bot.converters import DurationDelta, Expiry
from bot.exts.moderation.modlog import ModLog
from bot.log import get_logger
-from bot.utils import scheduling, time
+from bot.utils import time
from bot.utils.messages import format_user
-from bot.utils.scheduling import Scheduler
log = get_logger(__name__)
diff --git a/bot/exts/moderation/incidents.py b/bot/exts/moderation/incidents.py
index b579416a6..d34c1c7fa 100644
--- a/bot/exts/moderation/incidents.py
+++ b/bot/exts/moderation/incidents.py
@@ -6,12 +6,12 @@ from typing import Optional
import discord
from async_rediscache import RedisCache
+from botcore.utils import scheduling
from discord.ext.commands import Cog, Context, MessageConverter, MessageNotFound
from bot.bot import Bot
from bot.constants import Channels, Colours, Emojis, Guild, Roles, Webhooks
from bot.log import get_logger
-from bot.utils import scheduling
from bot.utils.messages import format_user, sub_clyde
log = get_logger(__name__)
diff --git a/bot/exts/moderation/infraction/_scheduler.py b/bot/exts/moderation/infraction/_scheduler.py
index 2fc54856f..9f5800e2a 100644
--- a/bot/exts/moderation/infraction/_scheduler.py
+++ b/bot/exts/moderation/infraction/_scheduler.py
@@ -6,6 +6,7 @@ from gettext import ngettext
import arrow
import dateutil.parser
import discord
+from botcore.utils import scheduling
from discord.ext.commands import Context
from bot import constants
@@ -16,7 +17,7 @@ from bot.converters import MemberOrUser
from bot.exts.moderation.infraction import _utils
from bot.exts.moderation.modlog import ModLog
from bot.log import get_logger
-from bot.utils import messages, scheduling, time
+from bot.utils import messages, time
from bot.utils.channel import is_mod_channel
log = get_logger(__name__)
diff --git a/bot/exts/moderation/metabase.py b/bot/exts/moderation/metabase.py
index ce9c220b3..d68726faf 100644
--- a/bot/exts/moderation/metabase.py
+++ b/bot/exts/moderation/metabase.py
@@ -8,15 +8,16 @@ import arrow
from aiohttp.client_exceptions import ClientResponseError
from arrow import Arrow
from async_rediscache import RedisCache
+from botcore.utils import scheduling
+from botcore.utils.scheduling import Scheduler
from discord.ext.commands import Cog, Context, group, has_any_role
from bot.bot import Bot
from bot.constants import Metabase as MetabaseConfig, Roles
from bot.converters import allowed_strings
from bot.log import get_logger
-from bot.utils import scheduling, send_to_paste_service
+from bot.utils import send_to_paste_service
from bot.utils.channel import is_mod_channel
-from bot.utils.scheduling import Scheduler
log = get_logger(__name__)
diff --git a/bot/exts/moderation/modpings.py b/bot/exts/moderation/modpings.py
index b5cd29b12..cb1e4fd05 100644
--- a/bot/exts/moderation/modpings.py
+++ b/bot/exts/moderation/modpings.py
@@ -3,6 +3,8 @@ import datetime
import arrow
from async_rediscache import RedisCache
+from botcore.utils import scheduling
+from botcore.utils.scheduling import Scheduler
from dateutil.parser import isoparse, parse as dateutil_parse
from discord import Embed, Member
from discord.ext.commands import Cog, Context, group, has_any_role
@@ -11,8 +13,7 @@ from bot.bot import Bot
from bot.constants import Colours, Emojis, Guild, Icons, MODERATION_ROLES, Roles
from bot.converters import Expiry
from bot.log import get_logger
-from bot.utils import scheduling, time
-from bot.utils.scheduling import Scheduler
+from bot.utils import time
log = get_logger(__name__)
diff --git a/bot/exts/moderation/silence.py b/bot/exts/moderation/silence.py
index 511520252..307729181 100644
--- a/bot/exts/moderation/silence.py
+++ b/bot/exts/moderation/silence.py
@@ -5,6 +5,8 @@ from datetime import datetime, timedelta, timezone
from typing import Optional, OrderedDict, Union
from async_rediscache import RedisCache
+from botcore.utils import scheduling
+from botcore.utils.scheduling import Scheduler
from discord import Guild, PermissionOverwrite, TextChannel, Thread, VoiceChannel
from discord.ext import commands, tasks
from discord.ext.commands import Context
@@ -14,9 +16,7 @@ from bot import constants
from bot.bot import Bot
from bot.converters import HushDurationConverter
from bot.log import get_logger
-from bot.utils import scheduling
from bot.utils.lock import LockedResourceError, lock, lock_arg
-from bot.utils.scheduling import Scheduler
log = get_logger(__name__)
diff --git a/bot/exts/moderation/stream.py b/bot/exts/moderation/stream.py
index 985cc6eb1..17d24eb89 100644
--- a/bot/exts/moderation/stream.py
+++ b/bot/exts/moderation/stream.py
@@ -5,6 +5,7 @@ import arrow
import discord
from arrow import Arrow
from async_rediscache import RedisCache
+from botcore.utils import scheduling
from discord.ext import commands
from bot.bot import Bot
@@ -14,7 +15,7 @@ from bot.constants import (
from bot.converters import Expiry
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils import scheduling, time
+from bot.utils import time
from bot.utils.members import get_or_fetch_member
log = get_logger(__name__)
diff --git a/bot/exts/moderation/watchchannels/_watchchannel.py b/bot/exts/moderation/watchchannels/_watchchannel.py
index ee9b6ba45..bae7ecd02 100644
--- a/bot/exts/moderation/watchchannels/_watchchannel.py
+++ b/bot/exts/moderation/watchchannels/_watchchannel.py
@@ -7,6 +7,7 @@ from dataclasses import dataclass
from typing import Any, Dict, Optional
import discord
+from botcore.utils import scheduling
from discord import Color, DMChannel, Embed, HTTPException, Message, errors
from discord.ext.commands import Cog, Context
@@ -18,7 +19,7 @@ from bot.exts.filters.webhook_remover import WEBHOOK_URL_RE
from bot.exts.moderation.modlog import ModLog
from bot.log import CustomLogger, get_logger
from bot.pagination import LinePaginator
-from bot.utils import CogABCMeta, messages, scheduling, time
+from bot.utils import CogABCMeta, messages, time
from bot.utils.members import get_or_fetch_member
log = get_logger(__name__)
diff --git a/bot/exts/recruitment/talentpool/_cog.py b/bot/exts/recruitment/talentpool/_cog.py
index 0554bf37a..0d51af2ca 100644
--- a/bot/exts/recruitment/talentpool/_cog.py
+++ b/bot/exts/recruitment/talentpool/_cog.py
@@ -5,6 +5,7 @@ from typing import Optional, Union
import discord
from async_rediscache import RedisCache
+from botcore.utils import scheduling
from discord import Color, Embed, Member, PartialMessage, RawReactionActionEvent, User
from discord.ext.commands import BadArgument, Cog, Context, group, has_any_role
@@ -15,7 +16,7 @@ from bot.converters import MemberOrUser, UnambiguousMemberOrUser
from bot.exts.recruitment.talentpool._review import Reviewer
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils import scheduling, time
+from bot.utils import time
from bot.utils.members import get_or_fetch_member
AUTOREVIEW_ENABLED_KEY = "autoreview_enabled"
diff --git a/bot/exts/recruitment/talentpool/_review.py b/bot/exts/recruitment/talentpool/_review.py
index b4d177622..214d85851 100644
--- a/bot/exts/recruitment/talentpool/_review.py
+++ b/bot/exts/recruitment/talentpool/_review.py
@@ -9,6 +9,7 @@ from datetime import datetime, timedelta
from typing import List, Optional, Union
import arrow
+from botcore.utils.scheduling import Scheduler
from dateutil.parser import isoparse
from discord import Embed, Emoji, Member, Message, NoMoreItems, NotFound, PartialMessage, TextChannel
from discord.ext.commands import Context
@@ -20,7 +21,6 @@ from bot.log import get_logger
from bot.utils import time
from bot.utils.members import get_or_fetch_member
from bot.utils.messages import count_unique_users_reaction, pin_no_system_message
-from bot.utils.scheduling import Scheduler
if typing.TYPE_CHECKING:
from bot.exts.recruitment.talentpool._cog import TalentPool
diff --git a/bot/exts/utils/reminders.py b/bot/exts/utils/reminders.py
index ad82d49c9..62603697c 100644
--- a/bot/exts/utils/reminders.py
+++ b/bot/exts/utils/reminders.py
@@ -5,6 +5,8 @@ from datetime import datetime, timezone
from operator import itemgetter
import discord
+from botcore.utils import scheduling
+from botcore.utils.scheduling import Scheduler
from dateutil.parser import isoparse
from discord.ext.commands import Cog, Context, Greedy, group
@@ -13,12 +15,11 @@ from bot.constants import Guild, Icons, MODERATION_ROLES, POSITIVE_REPLIES, Role
from bot.converters import Duration, UnambiguousUser
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils import scheduling, time
+from bot.utils import time
from bot.utils.checks import has_any_role_check, has_no_roles_check
from bot.utils.lock import lock_arg
from bot.utils.members import get_or_fetch_member
from bot.utils.messages import send_denial
-from bot.utils.scheduling import Scheduler
log = get_logger(__name__)
diff --git a/bot/exts/utils/snekbox.py b/bot/exts/utils/snekbox.py
index 3c1009d2a..2b073ed72 100644
--- a/bot/exts/utils/snekbox.py
+++ b/bot/exts/utils/snekbox.py
@@ -7,7 +7,8 @@ from signal import Signals
from textwrap import dedent
from typing import Optional, Tuple
-from botcore.regex import FORMATTED_CODE_REGEX, RAW_CODE_REGEX
+from botcore.utils import scheduling
+from botcore.utils.regex import FORMATTED_CODE_REGEX, RAW_CODE_REGEX
from discord import AllowedMentions, HTTPException, Message, NotFound, Reaction, User
from discord.ext.commands import Cog, Command, Context, Converter, command, guild_only
@@ -15,7 +16,7 @@ from bot.bot import Bot
from bot.constants import Categories, Channels, Roles, URLs
from bot.decorators import redirect_output
from bot.log import get_logger
-from bot.utils import scheduling, send_to_paste_service
+from bot.utils import send_to_paste_service
from bot.utils.messages import wait_for_deletion
log = get_logger(__name__)
diff --git a/bot/monkey_patches.py b/bot/monkey_patches.py
deleted file mode 100644
index 4840fa454..000000000
--- a/bot/monkey_patches.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import re
-from datetime import timedelta
-
-import arrow
-from discord import Forbidden, http
-from discord.ext import commands
-
-from bot.log import get_logger
-
-log = get_logger(__name__)
-MESSAGE_ID_RE = re.compile(r'(?P<message_id>[0-9]{15,20})$')
-
-
-class Command(commands.Command):
- """
- A `discord.ext.commands.Command` subclass which supports root aliases.
-
- A `root_aliases` keyword argument is added, which is a sequence of alias names that will act as
- top-level commands rather than being aliases of the command's group. It's stored as an attribute
- also named `root_aliases`.
- """
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.root_aliases = kwargs.get("root_aliases", [])
-
- if not isinstance(self.root_aliases, (list, tuple)):
- raise TypeError("Root aliases of a command must be a list or a tuple of strings.")
-
-
-def patch_typing() -> None:
- """
- Sometimes discord turns off typing events by throwing 403's.
-
- Handle those issues by patching the trigger_typing method so it ignores 403's in general.
- """
- log.debug("Patching send_typing, which should fix things breaking when discord disables typing events. Stay safe!")
-
- original = http.HTTPClient.send_typing
- last_403 = None
-
- async def honeybadger_type(self, channel_id: int) -> None: # noqa: ANN001
- nonlocal last_403
- if last_403 and (arrow.utcnow() - last_403) < timedelta(minutes=5):
- log.warning("Not sending typing event, we got a 403 less than 5 minutes ago.")
- return
- try:
- await original(self, channel_id)
- except Forbidden:
- last_403 = arrow.utcnow()
- log.warning("Got a 403 from typing event!")
- pass
-
- http.HTTPClient.send_typing = honeybadger_type
-
-
-class FixedPartialMessageConverter(commands.PartialMessageConverter):
- """
- Make the Message converter infer channelID from the given context if only a messageID is given.
-
- Discord.py's Message converter is supposed to infer channelID based
- on ctx.channel if only a messageID is given. A refactor commit, linked below,
- a few weeks before d.py's archival broke this defined behaviour of the converter.
- Currently, if only a messageID is given to the converter, it will only find that message
- if it's in the bot's cache.
-
- https://github.com/Rapptz/discord.py/commit/1a4e73d59932cdbe7bf2c281f25e32529fc7ae1f
- """
-
- @staticmethod
- def _get_id_matches(ctx: commands.Context, argument: str) -> tuple[int, int, int]:
- """Inserts ctx.channel.id before calling super method if argument is just a messageID."""
- match = MESSAGE_ID_RE.match(argument)
- if match:
- argument = f"{ctx.channel.id}-{match.group('message_id')}"
- return commands.PartialMessageConverter._get_id_matches(ctx, argument)
diff --git a/bot/utils/messages.py b/bot/utils/messages.py
index e55c07062..a5ed84351 100644
--- a/bot/utils/messages.py
+++ b/bot/utils/messages.py
@@ -6,12 +6,12 @@ from io import BytesIO
from typing import Callable, List, Optional, Sequence, Union
import discord
+from botcore.utils import scheduling
from discord.ext.commands import Context
import bot
from bot.constants import Emojis, MODERATION_ROLES, NEGATIVE_REPLIES
from bot.log import get_logger
-from bot.utils import scheduling
log = get_logger(__name__)
diff --git a/bot/utils/scheduling.py b/bot/utils/scheduling.py
deleted file mode 100644
index 23acacf74..000000000
--- a/bot/utils/scheduling.py
+++ /dev/null
@@ -1,194 +0,0 @@
-import asyncio
-import contextlib
-import inspect
-import typing as t
-from datetime import datetime
-from functools import partial
-
-from arrow import Arrow
-
-from bot.log import get_logger
-
-
-class Scheduler:
- """
- Schedule the execution of coroutines and keep track of them.
-
- When instantiating a Scheduler, a name must be provided. This name is used to distinguish the
- instance's log messages from other instances. Using the name of the class or module containing
- the instance is suggested.
-
- Coroutines can be scheduled immediately with `schedule` or in the future with `schedule_at`
- or `schedule_later`. A unique ID is required to be given in order to keep track of the
- resulting Tasks. Any scheduled task can be cancelled prematurely using `cancel` by providing
- the same ID used to schedule it. The `in` operator is supported for checking if a task with a
- given ID is currently scheduled.
-
- Any exception raised in a scheduled task is logged when the task is done.
- """
-
- def __init__(self, name: str):
- self.name = name
-
- self._log = get_logger(f"{__name__}.{name}")
- self._scheduled_tasks: t.Dict[t.Hashable, asyncio.Task] = {}
-
- def __contains__(self, task_id: t.Hashable) -> bool:
- """Return True if a task with the given `task_id` is currently scheduled."""
- return task_id in self._scheduled_tasks
-
- def schedule(self, task_id: t.Hashable, coroutine: t.Coroutine) -> None:
- """
- Schedule the execution of a `coroutine`.
-
- If a task with `task_id` already exists, close `coroutine` instead of scheduling it. This
- prevents unawaited coroutine warnings. Don't pass a coroutine that'll be re-used elsewhere.
- """
- self._log.trace(f"Scheduling task #{task_id}...")
-
- msg = f"Cannot schedule an already started coroutine for #{task_id}"
- assert inspect.getcoroutinestate(coroutine) == "CORO_CREATED", msg
-
- if task_id in self._scheduled_tasks:
- self._log.debug(f"Did not schedule task #{task_id}; task was already scheduled.")
- coroutine.close()
- return
-
- task = asyncio.create_task(coroutine, name=f"{self.name}_{task_id}")
- task.add_done_callback(partial(self._task_done_callback, task_id))
-
- self._scheduled_tasks[task_id] = task
- self._log.debug(f"Scheduled task #{task_id} {id(task)}.")
-
- def schedule_at(self, time: t.Union[datetime, Arrow], task_id: t.Hashable, coroutine: t.Coroutine) -> None:
- """
- Schedule `coroutine` to be executed at the given `time`.
-
- If `time` is timezone aware, then use that timezone to calculate now() when subtracting.
- If `time` is naïve, then use UTC.
-
- If `time` is in the past, schedule `coroutine` immediately.
-
- If a task with `task_id` already exists, close `coroutine` instead of scheduling it. This
- prevents unawaited coroutine warnings. Don't pass a coroutine that'll be re-used elsewhere.
- """
- now_datetime = datetime.now(time.tzinfo) if time.tzinfo else datetime.utcnow()
- delay = (time - now_datetime).total_seconds()
- if delay > 0:
- coroutine = self._await_later(delay, task_id, coroutine)
-
- self.schedule(task_id, coroutine)
-
- def schedule_later(self, delay: t.Union[int, float], task_id: t.Hashable, coroutine: t.Coroutine) -> None:
- """
- Schedule `coroutine` to be executed after the given `delay` number of seconds.
-
- If a task with `task_id` already exists, close `coroutine` instead of scheduling it. This
- prevents unawaited coroutine warnings. Don't pass a coroutine that'll be re-used elsewhere.
- """
- self.schedule(task_id, self._await_later(delay, task_id, coroutine))
-
- def cancel(self, task_id: t.Hashable) -> None:
- """Unschedule the task identified by `task_id`. Log a warning if the task doesn't exist."""
- self._log.trace(f"Cancelling task #{task_id}...")
-
- try:
- task = self._scheduled_tasks.pop(task_id)
- except KeyError:
- self._log.warning(f"Failed to unschedule {task_id} (no task found).")
- else:
- task.cancel()
-
- self._log.debug(f"Unscheduled task #{task_id} {id(task)}.")
-
- def cancel_all(self) -> None:
- """Unschedule all known tasks."""
- self._log.debug("Unscheduling all tasks")
-
- for task_id in self._scheduled_tasks.copy():
- self.cancel(task_id)
-
- async def _await_later(self, delay: t.Union[int, float], task_id: t.Hashable, coroutine: t.Coroutine) -> None:
- """Await `coroutine` after the given `delay` number of seconds."""
- try:
- self._log.trace(f"Waiting {delay} seconds before awaiting coroutine for #{task_id}.")
- await asyncio.sleep(delay)
-
- # Use asyncio.shield to prevent the coroutine from cancelling itself.
- self._log.trace(f"Done waiting for #{task_id}; now awaiting the coroutine.")
- await asyncio.shield(coroutine)
- finally:
- # Close it to prevent unawaited coroutine warnings,
- # which would happen if the task was cancelled during the sleep.
- # Only close it if it's not been awaited yet. This check is important because the
- # coroutine may cancel this task, which would also trigger the finally block.
- state = inspect.getcoroutinestate(coroutine)
- if state == "CORO_CREATED":
- self._log.debug(f"Explicitly closing the coroutine for #{task_id}.")
- coroutine.close()
- else:
- self._log.debug(f"Finally block reached for #{task_id}; {state=}")
-
- def _task_done_callback(self, task_id: t.Hashable, done_task: asyncio.Task) -> None:
- """
- Delete the task and raise its exception if one exists.
-
- If `done_task` and the task associated with `task_id` are different, then the latter
- will not be deleted. In this case, a new task was likely rescheduled with the same ID.
- """
- self._log.trace(f"Performing done callback for task #{task_id} {id(done_task)}.")
-
- scheduled_task = self._scheduled_tasks.get(task_id)
-
- if scheduled_task and done_task is scheduled_task:
- # A task for the ID exists and is the same as the done task.
- # Since this is the done callback, the task is already done so no need to cancel it.
- self._log.trace(f"Deleting task #{task_id} {id(done_task)}.")
- del self._scheduled_tasks[task_id]
- elif scheduled_task:
- # A new task was likely rescheduled with the same ID.
- self._log.debug(
- f"The scheduled task #{task_id} {id(scheduled_task)} "
- f"and the done task {id(done_task)} differ."
- )
- elif not done_task.cancelled():
- self._log.warning(
- f"Task #{task_id} not found while handling task {id(done_task)}! "
- f"A task somehow got unscheduled improperly (i.e. deleted but not cancelled)."
- )
-
- with contextlib.suppress(asyncio.CancelledError):
- exception = done_task.exception()
- # Log the exception if one exists.
- if exception:
- self._log.error(f"Error in task #{task_id} {id(done_task)}!", exc_info=exception)
-
-
-def create_task(
- coro: t.Awaitable,
- *,
- suppressed_exceptions: tuple[t.Type[Exception]] = (),
- event_loop: t.Optional[asyncio.AbstractEventLoop] = None,
- **kwargs,
-) -> asyncio.Task:
- """
- Wrapper for creating asyncio `Task`s which logs exceptions raised in the task.
-
- If the loop kwarg is provided, the task is created from that event loop, otherwise the running loop is used.
- """
- if event_loop is not None:
- task = event_loop.create_task(coro, **kwargs)
- else:
- task = asyncio.create_task(coro, **kwargs)
- task.add_done_callback(partial(_log_task_exception, suppressed_exceptions=suppressed_exceptions))
- return task
-
-
-def _log_task_exception(task: asyncio.Task, *, suppressed_exceptions: t.Tuple[t.Type[Exception]]) -> None:
- """Retrieve and log the exception raised in `task` if one exists."""
- with contextlib.suppress(asyncio.CancelledError):
- exception = task.exception()
- # Log the exception if one exists.
- if exception and not isinstance(exception, suppressed_exceptions):
- log = get_logger(__name__)
- log.error(f"Error in task {task.get_name()} {id(task)}!", exc_info=exception)
diff --git a/tests/bot/exts/backend/sync/test_cog.py b/tests/bot/exts/backend/sync/test_cog.py
index fdd0ab74a..7dff38f96 100644
--- a/tests/bot/exts/backend/sync/test_cog.py
+++ b/tests/bot/exts/backend/sync/test_cog.py
@@ -60,7 +60,7 @@ class SyncCogTestCase(unittest.IsolatedAsyncioTestCase):
class SyncCogTests(SyncCogTestCase):
"""Tests for the Sync cog."""
- @mock.patch("bot.utils.scheduling.create_task")
+ @mock.patch("botcore.utils.scheduling.create_task")
@mock.patch.object(Sync, "sync_guild", new_callable=mock.MagicMock)
def test_sync_cog_init(self, sync_guild, create_task):
"""Should instantiate syncers and run a sync for the guild."""
diff --git a/tests/bot/exts/filters/test_filtering.py b/tests/bot/exts/filters/test_filtering.py
index 8ae59c1f1..bd26532f1 100644
--- a/tests/bot/exts/filters/test_filtering.py
+++ b/tests/bot/exts/filters/test_filtering.py
@@ -11,7 +11,7 @@ class FilteringCogTests(unittest.IsolatedAsyncioTestCase):
def setUp(self):
"""Instantiate the bot and cog."""
self.bot = MockBot()
- with patch("bot.utils.scheduling.create_task", new=lambda task, **_: task.close()):
+ with patch("botcore.utils.scheduling.create_task", new=lambda task, **_: task.close()):
self.cog = filtering.Filtering(self.bot)
@autospec(filtering.Filtering, "_get_filterlist_items", pass_mocks=False, return_value=["TOKEN"])