aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS2
-rw-r--r--bot/converters.py14
-rw-r--r--bot/exts/backend/sync/_cog.py16
-rw-r--r--bot/exts/backend/sync/_syncers.py13
-rw-r--r--bot/exts/info/doc/_cog.py2
-rw-r--r--bot/exts/moderation/clean.py5
-rw-r--r--bot/exts/moderation/defcon.py33
-rw-r--r--bot/exts/moderation/incidents.py2
-rw-r--r--bot/exts/moderation/modpings.py2
-rw-r--r--bot/exts/moderation/slowmode.py21
-rw-r--r--bot/exts/utils/extensions.py25
-rw-r--r--bot/resources/tags/or-gotcha.md1
-rw-r--r--bot/utils/extensions.py34
-rw-r--r--poetry.lock10
-rw-r--r--pyproject.toml4
-rw-r--r--tests/helpers.py2
16 files changed, 86 insertions, 100 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index ea69f7677..0bc2bb793 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -18,10 +18,8 @@ bot/exts/recruitment/** @wookie184
bot/rules/** @mbaruh
# Utils
-bot/utils/extensions.py @MarkKoz
bot/utils/function.py @MarkKoz
bot/utils/lock.py @MarkKoz
-bot/utils/scheduling.py @MarkKoz
# Tests
tests/_autospec.py @MarkKoz
diff --git a/bot/converters.py b/bot/converters.py
index a3f4630a0..910ed9e39 100644
--- a/bot/converters.py
+++ b/bot/converters.py
@@ -9,19 +9,19 @@ import dateutil.parser
import discord
from aiohttp import ClientConnectorError
from botcore.site_api import ResponseCodeError
+from botcore.utils import unqualify
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
-from bot import exts
+from bot import exts, instance as bot_instance
from bot.constants import URLs
from bot.errors import InvalidInfraction
from bot.exts.info.doc import _inventory_parser
from bot.exts.info.tags import TagIdentifier
from bot.log import get_logger
from bot.utils import time
-from bot.utils.extensions import EXTENSIONS, unqualify
if t.TYPE_CHECKING:
from bot.exts.info.source import SourceType
@@ -150,13 +150,13 @@ class Extension(Converter):
argument = argument.lower()
- if argument in EXTENSIONS:
+ if argument in bot_instance.all_extensions:
return argument
- elif (qualified_arg := f"{exts.__name__}.{argument}") in EXTENSIONS:
+ elif (qualified_arg := f"{exts.__name__}.{argument}") in bot_instance.all_extensions:
return qualified_arg
matches = []
- for ext in EXTENSIONS:
+ for ext in bot_instance.all_extensions:
if argument == unqualify(ext):
matches.append(ext)
@@ -382,8 +382,8 @@ class Age(DurationDelta):
class OffTopicName(Converter):
"""A converter that ensures an added off-topic name is valid."""
- ALLOWED_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!?'`-<>"
- TRANSLATED_CHARACTERS = "๐– ๐–ก๐–ข๐–ฃ๐–ค๐–ฅ๐–ฆ๐–ง๐–จ๐–ฉ๐–ช๐–ซ๐–ฌ๐–ญ๐–ฎ๐–ฏ๐–ฐ๐–ฑ๐–ฒ๐–ณ๐–ด๐–ต๐–ถ๐–ท๐–ธ๐–นวƒ๏ผŸโ€™โ€™-๏ผœ๏ผž"
+ ALLOWED_CHARACTERS = r"ABCDEFGHIJKLMNOPQRSTUVWXYZ!?'`-<>\/"
+ TRANSLATED_CHARACTERS = "๐– ๐–ก๐–ข๐–ฃ๐–ค๐–ฅ๐–ฆ๐–ง๐–จ๐–ฉ๐–ช๐–ซ๐–ฌ๐–ญ๐–ฎ๐–ฏ๐–ฐ๐–ฑ๐–ฒ๐–ณ๐–ด๐–ต๐–ถ๐–ท๐–ธ๐–นวƒ๏ผŸโ€™โ€™-๏ผœ๏ผžโงนโงธ"
@classmethod
def translate_name(cls, name: str, *, from_unicode: bool = True) -> str:
diff --git a/bot/exts/backend/sync/_cog.py b/bot/exts/backend/sync/_cog.py
index a5bf82397..85266340b 100644
--- a/bot/exts/backend/sync/_cog.py
+++ b/bot/exts/backend/sync/_cog.py
@@ -1,9 +1,11 @@
+import asyncio
+import datetime
from typing import Any, Dict
from botcore.site_api import ResponseCodeError
from discord import Member, Role, User
from discord.ext import commands
-from discord.ext.commands import Cog, Context
+from discord.ext.commands import Cog, Context, errors
from bot import constants
from bot.bot import Bot
@@ -27,6 +29,18 @@ class Sync(Cog):
if guild is None:
return
+ log.info("Waiting for guild to be chunked to start syncers.")
+ end = datetime.datetime.now() + datetime.timedelta(minutes=30)
+ while not guild.chunked:
+ await asyncio.sleep(10)
+ if datetime.datetime.now() > end:
+ # More than 30 minutes have passed while trying, abort
+ raise errors.ExtensionFailed(
+ self.__class__.__name__,
+ RuntimeError("The guild was not chunked in time, not loading sync cog.")
+ )
+
+ log.info("Starting syncers.")
for syncer in (_syncers.RoleSyncer, _syncers.UserSyncer):
await syncer.sync(guild)
diff --git a/bot/exts/backend/sync/_syncers.py b/bot/exts/backend/sync/_syncers.py
index e1c4541ef..799137cb9 100644
--- a/bot/exts/backend/sync/_syncers.py
+++ b/bot/exts/backend/sync/_syncers.py
@@ -2,6 +2,7 @@ import abc
import typing as t
from collections import namedtuple
+import discord.errors
from botcore.site_api import ResponseCodeError
from discord import Guild
from discord.ext.commands import Context
@@ -9,7 +10,6 @@ from more_itertools import chunked
import bot
from bot.log import get_logger
-from bot.utils.members import get_or_fetch_member
log = get_logger(__name__)
@@ -157,7 +157,16 @@ class UserSyncer(Syncer):
if db_user[db_field] != guild_value:
updated_fields[db_field] = guild_value
- if guild_user := await get_or_fetch_member(guild, db_user["id"]):
+ guild_user = guild.get_member(db_user["id"])
+ if not guild_user and db_user["in_guild"]:
+ # The member was in the guild during the last sync.
+ # We try to fetch them to verify cache integrity.
+ try:
+ guild_user = await guild.fetch_member(db_user["id"])
+ except discord.errors.NotFound:
+ guild_user = None
+
+ if guild_user:
seen_guild_users.add(guild_user.id)
maybe_update("name", guild_user.name)
diff --git a/bot/exts/info/doc/_cog.py b/bot/exts/info/doc/_cog.py
index 079bfc942..dece44063 100644
--- a/bot/exts/info/doc/_cog.py
+++ b/bot/exts/info/doc/_cog.py
@@ -431,7 +431,7 @@ class DocCog(commands.Cog):
async def refresh_command(self, ctx: commands.Context) -> None:
"""Refresh inventories and show the difference."""
old_inventories = set(self.base_urls)
- with ctx.typing():
+ async with ctx.typing():
await self.refresh_inventories()
new_inventories = set(self.base_urls)
diff --git a/bot/exts/moderation/clean.py b/bot/exts/moderation/clean.py
index 32e485996..0f14f515e 100644
--- a/bot/exts/moderation/clean.py
+++ b/bot/exts/moderation/clean.py
@@ -441,7 +441,10 @@ class Clean(Cog):
f"A log of the deleted messages can be found here {log_url}."
)
if log_url and is_mod_channel(ctx.channel):
- await ctx.reply(success_message)
+ try:
+ await ctx.reply(success_message)
+ except errors.NotFound:
+ await ctx.send(success_message)
elif log_url:
if mods := self.bot.get_channel(Channels.mods):
await mods.send(f"{ctx.author.mention} {success_message}")
diff --git a/bot/exts/moderation/defcon.py b/bot/exts/moderation/defcon.py
index d092af6e9..1df79149d 100644
--- a/bot/exts/moderation/defcon.py
+++ b/bot/exts/moderation/defcon.py
@@ -71,10 +71,11 @@ class Defcon(Cog):
scheduling.create_task(self._sync_settings(), event_loop=self.bot.loop)
- @property
- def mod_log(self) -> ModLog:
+ async def get_mod_log(self) -> ModLog:
"""Get currently loaded ModLog cog instance."""
- return self.bot.get_cog("ModLog")
+ while not (cog := self.bot.get_cog("ModLog")):
+ await asyncio.sleep(1)
+ return cog
@defcon_settings.atomic_transaction
async def _sync_settings(self) -> None:
@@ -82,20 +83,6 @@ class Defcon(Cog):
log.trace("Waiting for the guild to become available before syncing.")
await self.bot.wait_until_guild_available()
- # Load order isn't guaranteed, attempt to check mod log load status 3 times before erroring.
- for _ in range(3):
- if self.mod_log:
- break
- else:
- await asyncio.sleep(5)
- else:
- log.exception("Modlog cog not loaded, aborting sync.")
- await self.channel.send(
- f"<@&{Roles.moderators}> <@&{Roles.devops}> **WARNING**: Unable to get DEFCON settings!"
- f"\n\nmod log cog could not be found after 3 tries."
- )
- return
-
self.channel = await self.bot.fetch_channel(Channels.defcon)
log.trace("Syncing settings.")
@@ -118,7 +105,7 @@ class Defcon(Cog):
self._update_notifier()
log.info(f"DEFCON synchronized: {time.humanize_delta(self.threshold) if self.threshold else '-'}")
- self._update_channel_topic()
+ await self._update_channel_topic()
@Cog.listener()
async def on_member_join(self, member: Member) -> None:
@@ -150,7 +137,7 @@ class Defcon(Cog):
if not message_sent:
message = f"{message}\n\nUnable to send rejection message via DM; they probably have DMs disabled."
- await self.mod_log.send_log_message(
+ await (await self.get_mod_log()).send_log_message(
Icons.defcon_denied, Colours.soft_red, "Entry denied",
message, member.display_avatar.url
)
@@ -226,12 +213,12 @@ class Defcon(Cog):
await role.edit(reason="DEFCON unshutdown", permissions=permissions)
await ctx.send(f"{Action.SERVER_OPEN.value.emoji} Server reopened.")
- def _update_channel_topic(self) -> None:
+ async def _update_channel_topic(self) -> None:
"""Update the #defcon channel topic with the current DEFCON status."""
threshold = time.humanize_delta(self.threshold) if self.threshold else '-'
new_topic = f"{BASE_CHANNEL_TOPIC}\n(Threshold: {threshold})"
- self.mod_log.ignore(Event.guild_channel_update, Channels.defcon)
+ (await self.get_mod_log()).ignore(Event.guild_channel_update, Channels.defcon)
scheduling.create_task(self.channel.edit(topic=new_topic))
@defcon_settings.atomic_transaction
@@ -290,7 +277,7 @@ class Defcon(Cog):
await channel.send(message)
await self._send_defcon_log(action, author)
- self._update_channel_topic()
+ await self._update_channel_topic()
self._log_threshold_stat(threshold)
@@ -318,7 +305,7 @@ class Defcon(Cog):
)
status_msg = f"DEFCON {action.name.lower()}"
- await self.mod_log.send_log_message(info.icon, info.color, status_msg, log_msg)
+ await (await self.get_mod_log()).send_log_message(info.icon, info.color, status_msg, log_msg)
def _update_notifier(self) -> None:
"""Start or stop the notifier according to the DEFCON status."""
diff --git a/bot/exts/moderation/incidents.py b/bot/exts/moderation/incidents.py
index b65f9262f..155b123ca 100644
--- a/bot/exts/moderation/incidents.py
+++ b/bot/exts/moderation/incidents.py
@@ -404,7 +404,7 @@ class Incidents(Cog):
def check(payload: discord.RawReactionActionEvent) -> bool:
return payload.message_id == incident.id
- coroutine = self.bot.wait_for(event="raw_message_delete", check=check, timeout=timeout)
+ coroutine = self.bot.wait_for("raw_message_delete", check=check, timeout=timeout)
return scheduling.create_task(coroutine, event_loop=self.bot.loop)
async def process_event(self, reaction: str, incident: discord.Message, member: discord.Member) -> None:
diff --git a/bot/exts/moderation/modpings.py b/bot/exts/moderation/modpings.py
index 4d02c4530..e82099c88 100644
--- a/bot/exts/moderation/modpings.py
+++ b/bot/exts/moderation/modpings.py
@@ -42,7 +42,7 @@ class ModPings(Cog):
async def cog_load(self) -> None:
"""Schedule both when to reapply role and all mod ping schedules."""
- await self.reschedule_modpings_schedule()
+ # await self.reschedule_modpings_schedule()
await self.reschedule_roles()
async def reschedule_roles(self) -> None:
diff --git a/bot/exts/moderation/slowmode.py b/bot/exts/moderation/slowmode.py
index 1be568a56..c43ae8b0c 100644
--- a/bot/exts/moderation/slowmode.py
+++ b/bot/exts/moderation/slowmode.py
@@ -1,7 +1,7 @@
-from typing import Optional
+from typing import Literal, Optional, Union
from dateutil.relativedelta import relativedelta
-from discord import TextChannel
+from discord import TextChannel, Thread
from discord.ext.commands import Cog, Context, group, has_any_role
from bot.bot import Bot
@@ -20,6 +20,8 @@ COMMONLY_SLOWMODED_CHANNELS = {
Channels.off_topic_0: "ot0",
}
+MessageHolder = Optional[Union[TextChannel, Thread]]
+
class Slowmode(Cog):
"""Commands for getting and setting slowmode delays of text channels."""
@@ -33,7 +35,7 @@ class Slowmode(Cog):
await ctx.send_help(ctx.command)
@slowmode_group.command(name='get', aliases=['g'])
- async def get_slowmode(self, ctx: Context, channel: Optional[TextChannel]) -> None:
+ async def get_slowmode(self, ctx: Context, channel: MessageHolder) -> None:
"""Get the slowmode delay for a text channel."""
# Use the channel this command was invoked in if one was not given
if channel is None:
@@ -44,7 +46,12 @@ class Slowmode(Cog):
await ctx.send(f'The slowmode delay for {channel.mention} is {humanized_delay}.')
@slowmode_group.command(name='set', aliases=['s'])
- async def set_slowmode(self, ctx: Context, channel: Optional[TextChannel], delay: DurationDelta) -> None:
+ async def set_slowmode(
+ self,
+ ctx: Context,
+ channel: MessageHolder,
+ delay: Union[DurationDelta, Literal["0s", "0seconds"]],
+ ) -> None:
"""Set the slowmode delay for a text channel."""
# Use the channel this command was invoked in if one was not given
if channel is None:
@@ -52,8 +59,10 @@ class Slowmode(Cog):
# Convert `dateutil.relativedelta.relativedelta` to `datetime.timedelta`
# Must do this to get the delta in a particular unit of time
- slowmode_delay = time.relativedelta_to_timedelta(delay).total_seconds()
+ if isinstance(delay, str):
+ delay = relativedelta(seconds=0)
+ slowmode_delay = time.relativedelta_to_timedelta(delay).total_seconds()
humanized_delay = time.humanize_delta(delay)
# Ensure the delay is within discord's limits
@@ -80,7 +89,7 @@ class Slowmode(Cog):
)
@slowmode_group.command(name='reset', aliases=['r'])
- async def reset_slowmode(self, ctx: Context, channel: Optional[TextChannel]) -> None:
+ async def reset_slowmode(self, ctx: Context, channel: MessageHolder) -> None:
"""Reset the slowmode delay for a text channel to 0 seconds."""
await self.set_slowmode(ctx, channel, relativedelta(seconds=0))
diff --git a/bot/exts/utils/extensions.py b/bot/exts/utils/extensions.py
index 95ce94c2c..0f5fc0de4 100644
--- a/bot/exts/utils/extensions.py
+++ b/bot/exts/utils/extensions.py
@@ -12,7 +12,6 @@ from bot.constants import Emojis, MODERATION_ROLES, Roles, URLs
from bot.converters import Extension
from bot.log import get_logger
from bot.pagination import LinePaginator
-from bot.utils.extensions import EXTENSIONS
log = get_logger(__name__)
@@ -53,9 +52,9 @@ class Extensions(commands.Cog):
return
if "*" in extensions or "**" in extensions:
- extensions = set(EXTENSIONS) - set(self.bot.extensions.keys())
+ extensions = set(self.bot.all_extensions) - set(self.bot.extensions.keys())
- msg = self.batch_manage(Action.LOAD, *extensions)
+ msg = await self.batch_manage(Action.LOAD, *extensions)
await ctx.send(msg)
@extensions_group.command(name="unload", aliases=("ul",))
@@ -77,7 +76,7 @@ class Extensions(commands.Cog):
if "*" in extensions or "**" in extensions:
extensions = set(self.bot.extensions.keys()) - UNLOAD_BLACKLIST
- msg = self.batch_manage(Action.UNLOAD, *extensions)
+ msg = await self.batch_manage(Action.UNLOAD, *extensions)
await ctx.send(msg)
@@ -96,12 +95,12 @@ class Extensions(commands.Cog):
return
if "**" in extensions:
- extensions = EXTENSIONS
+ extensions = self.bot.all_extensions
elif "*" in extensions:
extensions = set(self.bot.extensions.keys()) | set(extensions)
extensions.remove("*")
- msg = self.batch_manage(Action.RELOAD, *extensions)
+ msg = await self.batch_manage(Action.RELOAD, *extensions)
await ctx.send(msg)
@@ -136,7 +135,7 @@ class Extensions(commands.Cog):
"""Return a mapping of extension names and statuses to their categories."""
categories = {}
- for ext in EXTENSIONS:
+ for ext in self.bot.all_extensions:
if ext in self.bot.extensions:
status = Emojis.status_online
else:
@@ -152,21 +151,21 @@ class Extensions(commands.Cog):
return categories
- def batch_manage(self, action: Action, *extensions: str) -> str:
+ async def batch_manage(self, action: Action, *extensions: str) -> str:
"""
Apply an action to multiple extensions and return a message with the results.
If only one extension is given, it is deferred to `manage()`.
"""
if len(extensions) == 1:
- msg, _ = self.manage(action, extensions[0])
+ msg, _ = await self.manage(action, extensions[0])
return msg
verb = action.name.lower()
failures = {}
for extension in extensions:
- _, error = self.manage(action, extension)
+ _, error = await self.manage(action, extension)
if error:
failures[extension] = error
@@ -181,17 +180,17 @@ class Extensions(commands.Cog):
return msg
- def manage(self, action: Action, ext: str) -> t.Tuple[str, t.Optional[str]]:
+ async def manage(self, action: Action, ext: str) -> t.Tuple[str, t.Optional[str]]:
"""Apply an action to an extension and return the status message and any error message."""
verb = action.name.lower()
error_msg = None
try:
- action.value(self.bot, ext)
+ await action.value(self.bot, ext)
except (commands.ExtensionAlreadyLoaded, commands.ExtensionNotLoaded):
if action is Action.RELOAD:
# When reloading, just load the extension if it was not loaded.
- return self.manage(Action.LOAD, ext)
+ return await self.manage(Action.LOAD, ext)
msg = f":x: Extension `{ext}` is already {verb}ed."
log.debug(msg[4:])
diff --git a/bot/resources/tags/or-gotcha.md b/bot/resources/tags/or-gotcha.md
index d75a73d78..25ade8620 100644
--- a/bot/resources/tags/or-gotcha.md
+++ b/bot/resources/tags/or-gotcha.md
@@ -1,5 +1,6 @@
When checking if something is equal to one thing or another, you might think that this is possible:
```py
+# Incorrect...
if favorite_fruit == 'grapefruit' or 'lemon':
print("That's a weird favorite fruit to have.")
```
diff --git a/bot/utils/extensions.py b/bot/utils/extensions.py
deleted file mode 100644
index 50350ea8d..000000000
--- a/bot/utils/extensions.py
+++ /dev/null
@@ -1,34 +0,0 @@
-import importlib
-import inspect
-import pkgutil
-from typing import Iterator, NoReturn
-
-from bot import exts
-
-
-def unqualify(name: str) -> str:
- """Return an unqualified name given a qualified module/package `name`."""
- return name.rsplit(".", maxsplit=1)[-1]
-
-
-def walk_extensions() -> Iterator[str]:
- """Yield extension names from the bot.exts subpackage."""
-
- def on_error(name: str) -> NoReturn:
- raise ImportError(name=name) # pragma: no cover
-
- for module in pkgutil.walk_packages(exts.__path__, f"{exts.__name__}.", onerror=on_error):
- if unqualify(module.name).startswith("_"):
- # Ignore module/package names starting with an underscore.
- continue
-
- if module.ispkg:
- imported = importlib.import_module(module.name)
- if not inspect.isfunction(getattr(imported, "setup", None)):
- # If it lacks a setup function, it's not an extension.
- continue
-
- yield module.name
-
-
-EXTENSIONS = frozenset(walk_extensions())
diff --git a/poetry.lock b/poetry.lock
index e90274559..8281394e6 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -125,7 +125,7 @@ lxml = ["lxml"]
[[package]]
name = "bot-core"
-version = "6.0.0"
+version = "6.4.0"
description = "Bot-Core provides the core functionality and utilities for the bots of the Python Discord community."
category = "main"
optional = false
@@ -133,7 +133,7 @@ python-versions = "3.9.*"
[package.dependencies]
async-rediscache = {version = "0.2.0", extras = ["fakeredis"], optional = true, markers = "extra == \"async-rediscache\""}
-"discord.py" = {url = "https://github.com/Rapptz/discord.py/archive/987235d5649e7c2b1a927637bab6547244ecb2cf.zip"}
+"discord.py" = {url = "https://github.com/Rapptz/discord.py/archive/5a06fa5f3e28d2b7191722e1a84c541560008aea.zip"}
statsd = "3.3.0"
[package.extras]
@@ -141,7 +141,7 @@ async-rediscache = ["async-rediscache[fakeredis] (==0.2.0)"]
[package.source]
type = "url"
-url = "https://github.com/python-discord/bot-core/archive/refs/tags/v6.0.0.zip"
+url = "https://github.com/python-discord/bot-core/archive/refs/tags/v6.4.0.zip"
[[package]]
name = "certifi"
version = "2021.10.8"
@@ -263,7 +263,7 @@ voice = ["PyNaCl (>=1.3.0,<1.6)"]
[package.source]
type = "url"
-url = "https://github.com/Rapptz/discord.py/archive/987235d5649e7c2b1a927637bab6547244ecb2cf.zip"
+url = "https://github.com/Rapptz/discord.py/archive/5a06fa5f3e28d2b7191722e1a84c541560008aea.zip"
[[package]]
name = "distlib"
@@ -1150,7 +1150,7 @@ multidict = ">=4.0"
[metadata]
lock-version = "1.1"
python-versions = "3.9.*"
-content-hash = "3a9451cdbafd9880f794fe5ea4ece75663a778708707e7b5fb5e9aaffc2bbbc8"
+content-hash = "a07f619c75f8133982984eb506ad350144829f10c704421f09b3dbe72cd037d8"
[metadata.files]
aiodns = [
diff --git a/pyproject.toml b/pyproject.toml
index 2a4415419..402d05f70 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,9 +8,9 @@ license = "MIT"
[tool.poetry.dependencies]
python = "3.9.*"
-"discord.py" = {url = "https://github.com/Rapptz/discord.py/archive/987235d5649e7c2b1a927637bab6547244ecb2cf.zip"}
+"discord.py" = {url = "https://github.com/Rapptz/discord.py/archive/5a06fa5f3e28d2b7191722e1a84c541560008aea.zip"}
# See https://bot-core.pythondiscord.com/ for docs.
-bot-core = {url = "https://github.com/python-discord/bot-core/archive/refs/tags/v6.0.0.zip", extras = ["async-rediscache"]}
+bot-core = {url = "https://github.com/python-discord/bot-core/archive/refs/tags/v6.4.0.zip", extras = ["async-rediscache"]}
aiodns = "3.0.0"
aiohttp = "3.8.1"
diff --git a/tests/helpers.py b/tests/helpers.py
index a6e4bdd66..5f3111616 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -171,7 +171,7 @@ class MockGuild(CustomMockMixin, unittest.mock.Mock, HashableMixin):
spec_set = guild_instance
def __init__(self, roles: Optional[Iterable[MockRole]] = None, **kwargs) -> None:
- default_kwargs = {'id': next(self.discord_id), 'members': []}
+ default_kwargs = {'id': next(self.discord_id), 'members': [], "chunked": True}
super().__init__(**collections.ChainMap(kwargs, default_kwargs))
self.roles = [MockRole(name="@everyone", position=1, id=0)]