From 9f18b0def8281644bf612317bb255f728df566b0 Mon Sep 17 00:00:00 2001 From: Matty Widdop <18513864+MattyTheHacker@users.noreply.github.com> Date: Mon, 18 May 2026 22:51:55 +0100 Subject: [PATCH 1/8] Bump py-cord to 2.8 --- pyproject.toml | 2 +- uv.lock | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0888b0ac..7909a005 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ main = [ "matplotlib>=3.10", "mplcyberpunk>=0.7", "parsedatetime>=2.6", - "py-cord>=2.6,<2.7", + "py-cord>=2.8", "python-dotenv>=1.0", "python-logging-discord-handler>=0.1", "typed_classproperties>=1.2", diff --git a/uv.lock b/uv.lock index 1b968b48..d37f8983 100644 --- a/uv.lock +++ b/uv.lock @@ -818,14 +818,15 @@ wheels = [ [[package]] name = "py-cord" -version = "2.6.1" +version = "2.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, + { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/27/c7/c539d69d5cfa1ea5891d596212f73d619e40c7fc9f02ae906f4147993b94/py_cord-2.6.1.tar.gz", hash = "sha256:36064f225f2c7bbddfe542d5ed581f2a5744f618e039093cf7cd2659a58bc79b", size = 965087, upload-time = "2024-09-15T19:36:39.245Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/73/02ad1b5ced7685e2868c25d574edc6b4eb154a214cf4caf36270480fc65c/py_cord-2.8.0.tar.gz", hash = "sha256:57a7efefa80e7f6ef890fbfc054d686782c347c9e4d86fa7c91f7ae5285b353f", size = 1173848, upload-time = "2026-05-18T13:02:09.092Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/90/2690ded84e34b15ca2619932a358c1b7dc6d28fe845dfbd01929fc33c9da/py_cord-2.6.1-py3-none-any.whl", hash = "sha256:e3d3b528c5e37b0e0825f5b884cbb9267860976c1e4878e28b55da8fd3af834b", size = 1089154, upload-time = "2024-09-15T19:36:35.34Z" }, + { url = "https://files.pythonhosted.org/packages/b1/52/cd690a9103b3deadc5c047d3823b750a9afd61c35e57373bbd9e5dbd765f/py_cord-2.8.0-py3-none-any.whl", hash = "sha256:24f9180b4df432ddef732e388a905aa7d9cdf8f2f26318f18a193bccb2d7f642", size = 1264552, upload-time = "2026-05-18T13:02:06.959Z" }, ] [[package]] @@ -1129,6 +1130,7 @@ type-check = [ ] [package.metadata] + [package.metadata.requires-dev] dev = [ { name = "django-stubs", extras = ["compatible-mypy"], specifier = ">=5.1" }, @@ -1154,7 +1156,7 @@ main = [ { name = "matplotlib", specifier = ">=3.10" }, { name = "mplcyberpunk", specifier = ">=0.7" }, { name = "parsedatetime", specifier = ">=2.6" }, - { name = "py-cord", specifier = ">=2.6,<2.7" }, + { name = "py-cord", specifier = ">=2.8" }, { name = "python-dotenv", specifier = ">=1.0" }, { name = "python-logging-discord-handler", specifier = ">=0.1" }, { name = "typed-classproperties", specifier = ">=1.2" }, From 8d31037bc6a100953cbc15082df709fbd3bd47a8 Mon Sep 17 00:00:00 2001 From: Matty Widdop <18513864+MattyTheHacker@users.noreply.github.com> Date: Mon, 18 May 2026 23:06:14 +0100 Subject: [PATCH 2/8] Fix mypy errors --- cogs/induct.py | 2 +- cogs/make_applicant.py | 2 +- cogs/remind_me.py | 10 ++++++++-- cogs/stats/__init__.py | 26 +++++++++++++++++++++++++- cogs/strike.py | 30 ++++++++++++++++-------------- stubs/discord/__init__.pyi | 1 - 6 files changed, 51 insertions(+), 20 deletions(-) diff --git a/cogs/induct.py b/cogs/induct.py index e718fd7d..66a157d4 100644 --- a/cogs/induct.py +++ b/cogs/induct.py @@ -252,7 +252,7 @@ async def _perform_induction( applicant_role, reason=INDUCT_AUDIT_MESSAGE ) - tex_emoji: discord.Emoji | None = self.bot.get_emoji(743218410409820213) + tex_emoji: discord.GuildEmoji | discord.AppEmoji | None = self.bot.get_emoji(743218410409820213) if not tex_emoji: tex_emoji = discord.utils.get(main_guild.emojis, name="TeX") diff --git a/cogs/make_applicant.py b/cogs/make_applicant.py index 27e4e168..167bc239 100644 --- a/cogs/make_applicant.py +++ b/cogs/make_applicant.py @@ -76,7 +76,7 @@ async def _perform_make_applicant( await applicant_member.add_roles(applicant_role, reason=AUDIT_MESSAGE) logger.debug("Applicant role given to user %s", applicant_member) - tex_emoji: discord.Emoji | None = self.bot.get_emoji(743218410409820213) + tex_emoji: discord.GuildEmoji | discord.AppEmoji | None = self.bot.get_emoji(743218410409820213) if not tex_emoji: tex_emoji = discord.utils.get(main_guild.emojis, name="TeX") diff --git a/cogs/remind_me.py b/cogs/remind_me.py index bd3457a2..f6ddfa55 100644 --- a/cogs/remind_me.py +++ b/cogs/remind_me.py @@ -213,6 +213,11 @@ async def remind_me( parsed_time: tuple[time.struct_time, int] = parsedatetime.Calendar().parseDT( delay, tzinfo=timezone.get_current_timezone() ) + if not ctx.channel: + await self.command_send_error( + ctx, message="This command can only be used in channels." + ) + return if parsed_time[1] == 0: await self.command_send_error( @@ -305,8 +310,9 @@ async def clear_reminders_backlog(self) -> None: discord.utils.utcnow() - reminder.send_datetime ) if time_since_reminder_needed_to_be_sent > datetime.timedelta(minutes=15): - user: discord.User | None = await self.bot.get_or_fetch_user( - int(reminder.discord_member.discord_id) + user: discord.User | None = await self.bot.get_or_fetch( + object_type=discord.User, + object_id=int(reminder.discord_member.discord_id) ) if not user: diff --git a/cogs/stats/__init__.py b/cogs/stats/__init__.py index 8b331c22..9202ecb9 100644 --- a/cogs/stats/__init__.py +++ b/cogs/stats/__init__.py @@ -86,7 +86,13 @@ async def channel_stats( # NOTE: Shortcut accessors are placed at the top of the function so that the exceptions they raise are displayed before any further errors may be sent main_guild: discord.Guild = self.bot.main_guild - channel_id: int = ctx.channel_id + if isinstance(ctx.channel, (None | discord.CategoryChannel | discord.ForumChannel)): + await self.command_send_error( + ctx, message="This command can only be used in text channels." + ) + return + + channel_id: int | None = ctx.channel_id if str_channel_id: if not re.fullmatch(r"\A\d{17,20}\Z", str_channel_id): @@ -157,6 +163,12 @@ async def server_stats(self, ctx: "TeXBotApplicationContext") -> None: main_guild: discord.Guild = self.bot.main_guild guest_role: discord.Role = await self.bot.guest_role + if isinstance(ctx.channel, (None | discord.CategoryChannel | discord.ForumChannel)): + await self.command_send_error( + ctx, message="This command can only be used in text channels." + ) + return + await ctx.defer(ephemeral=True) message_counts: Mapping[str, Mapping[str, int]] = await get_server_message_counts( @@ -237,6 +249,12 @@ async def user_stats(self, ctx: "TeXBotApplicationContext") -> None: interaction_member: discord.Member = await self.bot.get_main_guild_member(ctx.user) guest_role: discord.Role = await self.bot.guest_role + if isinstance(ctx.channel, (None | discord.CategoryChannel | discord.ForumChannel)): + await self.command_send_error( + ctx, message="This command can only be used in text channels." + ) + return + if guest_role not in interaction_member.roles: await self.command_send_error( ctx, @@ -314,6 +332,12 @@ async def left_member_stats(self, ctx: "TeXBotApplicationContext") -> None: # NOTE: Shortcut accessors are placed at the top of the function so that the exceptions they raise are displayed before any further errors may be sent main_guild: discord.Guild = self.bot.main_guild + if isinstance(ctx.channel, (None | discord.CategoryChannel | discord.ForumChannel)): + await self.command_send_error( + ctx, message="This command can only be used in text channels." + ) + return + await ctx.defer(ephemeral=True) left_member_counts: dict[str, int] = { diff --git a/cogs/strike.py b/cogs/strike.py index 0f3778f7..97280cf5 100644 --- a/cogs/strike.py +++ b/cogs/strike.py @@ -94,7 +94,7 @@ class ConfirmStrikeMemberView(View): @discord.ui.button( label="Yes", style=discord.ButtonStyle.red, custom_id="yes_strike_member" ) - async def yes_strike_member_button_callback( # type: ignore[misc] + async def yes_strike_member_button_callback( self, _: discord.Button, interaction: discord.Interaction ) -> None: """ @@ -113,7 +113,7 @@ async def yes_strike_member_button_callback( # type: ignore[misc] @discord.ui.button( label="No", style=discord.ButtonStyle.grey, custom_id="no_strike_member" ) - async def no_strike_member_button_callback( # type: ignore[misc] + async def no_strike_member_button_callback( self, _: discord.Button, interaction: discord.Interaction ) -> None: """ @@ -136,7 +136,7 @@ class ConfirmManualModerationView(View): @discord.ui.button( label="Yes", style=discord.ButtonStyle.red, custom_id="yes_manual_moderation_action" ) - async def yes_manual_moderation_action_button_callback( # type: ignore[misc] + async def yes_manual_moderation_action_button_callback( self, _: discord.Button, interaction: discord.Interaction ) -> None: """ @@ -156,7 +156,7 @@ async def yes_manual_moderation_action_button_callback( # type: ignore[misc] @discord.ui.button( label="No", style=discord.ButtonStyle.grey, custom_id="no_manual_moderation_action" ) - async def no_manual_moderation_action_button_callback( # type: ignore[misc] + async def no_manual_moderation_action_button_callback( self, _: discord.Button, interaction: discord.Interaction ) -> None: """ @@ -180,7 +180,7 @@ class ConfirmStrikesOutOfSyncWithBanView(View): @discord.ui.button( label="Yes", style=discord.ButtonStyle.red, custom_id="yes_out_of_sync_ban_member" ) - async def yes_out_of_sync_ban_member_button_callback( # type: ignore[misc] + async def yes_out_of_sync_ban_member_button_callback( self, _: discord.Button, interaction: discord.Interaction ) -> None: """ @@ -200,7 +200,7 @@ async def yes_out_of_sync_ban_member_button_callback( # type: ignore[misc] @discord.ui.button( label="No", style=discord.ButtonStyle.grey, custom_id="no_out_of_sync_ban_member" ) - async def no_out_of_sync_ban_member_button_callback( # type: ignore[misc] + async def no_out_of_sync_ban_member_button_callback( self, _: discord.Button, interaction: discord.Interaction ) -> None: """ @@ -264,7 +264,7 @@ async def _send_strike_user_message( async def _confirm_perform_moderation_action( self, message_sender_component: "MessageSavingSenderComponent", - interaction_user: discord.User, + interaction_user: discord.User | discord.Member, strike_user: discord.Member, confirm_strike_message: str, actual_strike_amount: int, @@ -315,7 +315,7 @@ async def _confirm_perform_moderation_action( async def _confirm_increase_strike( self, message_sender_component: "MessageSavingSenderComponent", - interaction_user: discord.User, + interaction_user: discord.User | discord.Member, strike_user: discord.User | discord.Member, member_strikes: DiscordMemberStrikes, button_callback_channel: discord.TextChannel | discord.DMChannel, @@ -401,6 +401,12 @@ async def _command_perform_strike( Also calls the process of performing the appropriate moderation action, given the new number of strikes that the member has. """ + if not isinstance(ctx.channel, (discord.TextChannel, discord.DMChannel)): + await self.command_send_error( + ctx, message="This command can only be used in text channels or DMs." + ) + return + if strike_member.bot: await self.command_send_error( ctx, @@ -504,8 +510,7 @@ async def _confirm_manual_add_strike( # noqa: PLR0915 async for _audit_log_entry in main_guild.audit_logs( after=discord.utils.utcnow() - datetime.timedelta(minutes=1), action=action ) - if _audit_log_entry.target.id - == strike_user.id # NOTE: IDs are checked here rather than the objects themselves as the audit log provides an unusual object type in some cases. + if _audit_log_entry.target and _audit_log_entry.target.id == strike_user.id # NOTE: IDs are checked here rather than the objects themselves as the audit log provides an unusual object type in some cases. ) except (StopIteration, StopAsyncIteration): logger.debug("Printing 5 most recent audit logs:") @@ -750,10 +755,7 @@ async def on_member_update(self, before: discord.Member, after: discord.Member) audit_log_entry: discord.AuditLogEntry async for audit_log_entry in main_guild.audit_logs(limit=5): - FOUND_CORRECT_AUDIT_LOG_ENTRY: bool = audit_log_entry.target.id == after.id and ( - audit_log_entry.action - == discord.AuditLogAction.auto_moderation_user_communication_disabled - ) + FOUND_CORRECT_AUDIT_LOG_ENTRY: bool = ((audit_log_entry.target is not None) and (audit_log_entry.target.id == after.id) and (audit_log_entry.action == discord.AuditLogAction.auto_moderation_user_communication_disabled)) if FOUND_CORRECT_AUDIT_LOG_ENTRY: await self._confirm_manual_add_strike( strike_user=after, action=audit_log_entry.action diff --git a/stubs/discord/__init__.pyi b/stubs/discord/__init__.pyi index 83fa70d4..f1a003ed 100644 --- a/stubs/discord/__init__.pyi +++ b/stubs/discord/__init__.pyi @@ -62,7 +62,6 @@ from .team import * from .template import * from .threads import * from .user import * -from .voice_client import * from .webhook import * from .welcome_screen import * from .widget import * From 95c3a40d48c302215b06f32d3f4532e9005b0e3a Mon Sep 17 00:00:00 2001 From: Matty Widdop <18513864+MattyTheHacker@users.noreply.github.com> Date: Mon, 18 May 2026 23:06:30 +0100 Subject: [PATCH 3/8] Ruff format --- cogs/induct.py | 4 +++- cogs/make_applicant.py | 4 +++- cogs/remind_me.py | 3 +-- cogs/strike.py | 13 +++++++++++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cogs/induct.py b/cogs/induct.py index 66a157d4..2c7af6b9 100644 --- a/cogs/induct.py +++ b/cogs/induct.py @@ -252,7 +252,9 @@ async def _perform_induction( applicant_role, reason=INDUCT_AUDIT_MESSAGE ) - tex_emoji: discord.GuildEmoji | discord.AppEmoji | None = self.bot.get_emoji(743218410409820213) + tex_emoji: discord.GuildEmoji | discord.AppEmoji | None = self.bot.get_emoji( + 743218410409820213 + ) if not tex_emoji: tex_emoji = discord.utils.get(main_guild.emojis, name="TeX") diff --git a/cogs/make_applicant.py b/cogs/make_applicant.py index 167bc239..7b322bbb 100644 --- a/cogs/make_applicant.py +++ b/cogs/make_applicant.py @@ -76,7 +76,9 @@ async def _perform_make_applicant( await applicant_member.add_roles(applicant_role, reason=AUDIT_MESSAGE) logger.debug("Applicant role given to user %s", applicant_member) - tex_emoji: discord.GuildEmoji | discord.AppEmoji | None = self.bot.get_emoji(743218410409820213) + tex_emoji: discord.GuildEmoji | discord.AppEmoji | None = self.bot.get_emoji( + 743218410409820213 + ) if not tex_emoji: tex_emoji = discord.utils.get(main_guild.emojis, name="TeX") diff --git a/cogs/remind_me.py b/cogs/remind_me.py index f6ddfa55..ce99d0e1 100644 --- a/cogs/remind_me.py +++ b/cogs/remind_me.py @@ -311,8 +311,7 @@ async def clear_reminders_backlog(self) -> None: ) if time_since_reminder_needed_to_be_sent > datetime.timedelta(minutes=15): user: discord.User | None = await self.bot.get_or_fetch( - object_type=discord.User, - object_id=int(reminder.discord_member.discord_id) + object_type=discord.User, object_id=int(reminder.discord_member.discord_id) ) if not user: diff --git a/cogs/strike.py b/cogs/strike.py index 97280cf5..1f384692 100644 --- a/cogs/strike.py +++ b/cogs/strike.py @@ -510,7 +510,9 @@ async def _confirm_manual_add_strike( # noqa: PLR0915 async for _audit_log_entry in main_guild.audit_logs( after=discord.utils.utcnow() - datetime.timedelta(minutes=1), action=action ) - if _audit_log_entry.target and _audit_log_entry.target.id == strike_user.id # NOTE: IDs are checked here rather than the objects themselves as the audit log provides an unusual object type in some cases. + if _audit_log_entry.target + and _audit_log_entry.target.id + == strike_user.id # NOTE: IDs are checked here rather than the objects themselves as the audit log provides an unusual object type in some cases. ) except (StopIteration, StopAsyncIteration): logger.debug("Printing 5 most recent audit logs:") @@ -755,7 +757,14 @@ async def on_member_update(self, before: discord.Member, after: discord.Member) audit_log_entry: discord.AuditLogEntry async for audit_log_entry in main_guild.audit_logs(limit=5): - FOUND_CORRECT_AUDIT_LOG_ENTRY: bool = ((audit_log_entry.target is not None) and (audit_log_entry.target.id == after.id) and (audit_log_entry.action == discord.AuditLogAction.auto_moderation_user_communication_disabled)) + FOUND_CORRECT_AUDIT_LOG_ENTRY: bool = ( + (audit_log_entry.target is not None) + and (audit_log_entry.target.id == after.id) + and ( + audit_log_entry.action + == discord.AuditLogAction.auto_moderation_user_communication_disabled + ) + ) if FOUND_CORRECT_AUDIT_LOG_ENTRY: await self._confirm_manual_add_strike( strike_user=after, action=audit_log_entry.action From c67838679c55f72693ac191b41a9225a3d38c9fa Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 18 May 2026 22:08:48 +0000 Subject: [PATCH 4/8] [autofix.ci] apply automated fixes --- uv.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/uv.lock b/uv.lock index d37f8983..9d86a5c8 100644 --- a/uv.lock +++ b/uv.lock @@ -1130,7 +1130,6 @@ type-check = [ ] [package.metadata] - [package.metadata.requires-dev] dev = [ { name = "django-stubs", extras = ["compatible-mypy"], specifier = ">=5.1" }, From 2f139e5bf0f4db9afb6480a4fabe9947b31049fe Mon Sep 17 00:00:00 2001 From: Matty Widdop <18513864+MattyTheHacker@users.noreply.github.com> Date: Mon, 18 May 2026 23:17:53 +0100 Subject: [PATCH 5/8] Fix more mypy errors --- cogs/kill.py | 4 ++-- cogs/send_introduction_reminders.py | 2 +- utils/message_sender_components.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cogs/kill.py b/cogs/kill.py index 2ca4e3b3..ff242c78 100644 --- a/cogs/kill.py +++ b/cogs/kill.py @@ -28,7 +28,7 @@ class ConfirmKillView(View): @discord.ui.button( label="SHUTDOWN", style=discord.ButtonStyle.red, custom_id="shutdown_confirm" ) - async def confirm_shutdown_button_callback( # type: ignore[misc] + async def confirm_shutdown_button_callback( self, _: discord.Button, interaction: discord.Interaction ) -> None: """When the shutdown button is pressed, delete the message.""" @@ -37,7 +37,7 @@ async def confirm_shutdown_button_callback( # type: ignore[misc] @discord.ui.button( label="CANCEL", style=discord.ButtonStyle.grey, custom_id="shutdown_cancel" ) - async def cancel_shutdown_button_callback( # type: ignore[misc] + async def cancel_shutdown_button_callback( self, _: discord.Button, interaction: discord.Interaction ) -> None: """When the cancel button is pressed, delete the message.""" diff --git a/cogs/send_introduction_reminders.py b/cogs/send_introduction_reminders.py index 67480680..9b89b1cb 100644 --- a/cogs/send_introduction_reminders.py +++ b/cogs/send_introduction_reminders.py @@ -233,7 +233,7 @@ async def send_error( style=discord.ButtonStyle.red, emoji=discord.PartialEmoji.from_str(emoji.emojize(":no_good:", language="alias")), ) - async def opt_out_introduction_reminders_button_callback( # type: ignore[misc] + async def opt_out_introduction_reminders_button_callback( self, button: discord.Button, interaction: discord.Interaction ) -> None: """ diff --git a/utils/message_sender_components.py b/utils/message_sender_components.py index 99de05c1..dd2e2ec5 100644 --- a/utils/message_sender_components.py +++ b/utils/message_sender_components.py @@ -83,7 +83,7 @@ async def delete(self) -> None: await self.sent_message.delete() else: - await self.sent_message.delete_original_message() + await self.sent_message.delete_original_response() class ChannelMessageSender(MessageSavingSenderComponent): From 3dd8cae0e64b03955599b1d83706c574420a67c7 Mon Sep 17 00:00:00 2001 From: Matty Widdop <18513864+MattyTheHacker@users.noreply.github.com> Date: Mon, 18 May 2026 23:29:42 +0100 Subject: [PATCH 6/8] Add ignores --- cogs/command_error.py | 5 +++-- cogs/kill.py | 4 ++-- cogs/send_introduction_reminders.py | 2 +- cogs/strike.py | 12 ++++++------ utils/tex_bot_base_cog.py | 5 +++-- utils/tex_bot_contexts.py | 2 +- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/cogs/command_error.py b/cogs/command_error.py index fcac8ee1..8d6c0819 100644 --- a/cogs/command_error.py +++ b/cogs/command_error.py @@ -75,10 +75,11 @@ async def on_application_command_error( command_name: str = ( ctx.command.callback.__name__ if ( - hasattr(ctx.command, "callback") + ctx.command + and hasattr(ctx.command, "callback") and not ctx.command.callback.__name__.startswith("_") ) - else ctx.command.qualified_name + else (ctx.command.qualified_name if ctx.command else "unknown") ) logger.critical( " ".join( diff --git a/cogs/kill.py b/cogs/kill.py index ff242c78..7eb149f3 100644 --- a/cogs/kill.py +++ b/cogs/kill.py @@ -25,7 +25,7 @@ class ConfirmKillView(View): """A discord.View containing two buttons to confirm shutting down TeX-Bot.""" - @discord.ui.button( + @discord.ui.button( # type: ignore[arg-type] label="SHUTDOWN", style=discord.ButtonStyle.red, custom_id="shutdown_confirm" ) async def confirm_shutdown_button_callback( @@ -34,7 +34,7 @@ async def confirm_shutdown_button_callback( """When the shutdown button is pressed, delete the message.""" logger.debug('"Confirm" button pressed. %s', interaction) - @discord.ui.button( + @discord.ui.button( # type: ignore[arg-type] label="CANCEL", style=discord.ButtonStyle.grey, custom_id="shutdown_cancel" ) async def cancel_shutdown_button_callback( diff --git a/cogs/send_introduction_reminders.py b/cogs/send_introduction_reminders.py index 9b89b1cb..b3dbe079 100644 --- a/cogs/send_introduction_reminders.py +++ b/cogs/send_introduction_reminders.py @@ -227,7 +227,7 @@ async def send_error( logging_message=logging_message, ) - @ui.button( + @ui.button( # type: ignore[arg-type] label="Opt-out of introduction reminders", custom_id="opt_out_introduction_reminders_button", style=discord.ButtonStyle.red, diff --git a/cogs/strike.py b/cogs/strike.py index 1f384692..5961983e 100644 --- a/cogs/strike.py +++ b/cogs/strike.py @@ -91,7 +91,7 @@ async def perform_moderation_action( class ConfirmStrikeMemberView(View): """A discord.View containing two buttons to confirm giving the member a strike.""" - @discord.ui.button( + @discord.ui.button( # type: ignore[arg-type] label="Yes", style=discord.ButtonStyle.red, custom_id="yes_strike_member" ) async def yes_strike_member_button_callback( @@ -110,7 +110,7 @@ async def yes_strike_member_button_callback( view=None ) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error - @discord.ui.button( + @discord.ui.button( # type: ignore[arg-type] label="No", style=discord.ButtonStyle.grey, custom_id="no_strike_member" ) async def no_strike_member_button_callback( @@ -133,7 +133,7 @@ async def no_strike_member_button_callback( class ConfirmManualModerationView(View): """A discord.View to confirm manually applying a moderation action.""" - @discord.ui.button( + @discord.ui.button( # type: ignore[arg-type] label="Yes", style=discord.ButtonStyle.red, custom_id="yes_manual_moderation_action" ) async def yes_manual_moderation_action_button_callback( @@ -153,7 +153,7 @@ async def yes_manual_moderation_action_button_callback( view=None ) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error - @discord.ui.button( + @discord.ui.button( # type: ignore[arg-type] label="No", style=discord.ButtonStyle.grey, custom_id="no_manual_moderation_action" ) async def no_manual_moderation_action_button_callback( @@ -177,7 +177,7 @@ async def no_manual_moderation_action_button_callback( class ConfirmStrikesOutOfSyncWithBanView(View): """A discord.View containing two buttons to confirm banning a member with > 3 strikes.""" - @discord.ui.button( + @discord.ui.button( # type: ignore[arg-type] label="Yes", style=discord.ButtonStyle.red, custom_id="yes_out_of_sync_ban_member" ) async def yes_out_of_sync_ban_member_button_callback( @@ -197,7 +197,7 @@ async def yes_out_of_sync_ban_member_button_callback( view=None ) # NOTE: Despite removing the view within the normal command processing loop, the view also needs to be removed here to prevent an Unknown Webhook error - @discord.ui.button( + @discord.ui.button( # type: ignore[arg-type] label="No", style=discord.ButtonStyle.grey, custom_id="no_out_of_sync_ban_member" ) async def no_out_of_sync_ban_member_button_callback( diff --git a/utils/tex_bot_base_cog.py b/utils/tex_bot_base_cog.py index e18c06e9..16d2a71d 100644 --- a/utils/tex_bot_base_cog.py +++ b/utils/tex_bot_base_cog.py @@ -84,10 +84,11 @@ async def command_send_error( COMMAND_NAME: Final[str] = ( ctx.command.callback.__name__ if ( - hasattr(ctx.command, "callback") + ctx.command + and hasattr(ctx.command, "callback") and not ctx.command.callback.__name__.startswith("_") ) - else ctx.command.qualified_name + else (ctx.command.qualified_name if ctx.command else "unknown") ) await self.send_error( diff --git a/utils/tex_bot_contexts.py b/utils/tex_bot_contexts.py index 52a88c50..a0788161 100644 --- a/utils/tex_bot_contexts.py +++ b/utils/tex_bot_contexts.py @@ -43,4 +43,4 @@ class TeXBotApplicationContext(discord.ApplicationContext): bot: "TeXBot" # type: ignore[mutable-override] - respond: "Callable[..., Awaitable[Interaction | WebhookMessage]]" # type: ignore[explicit-any] + respond: "Callable[..., Awaitable[Interaction | WebhookMessage]]" # type: ignore[assignment,explicit-any] From 98808d2493919d4149168e4ecf085465bfc88eb2 Mon Sep 17 00:00:00 2001 From: Matty Widdop <18513864+MattyTheHacker@users.noreply.github.com> Date: Mon, 18 May 2026 23:38:52 +0100 Subject: [PATCH 7/8] Fix type union errors --- cogs/stats/__init__.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cogs/stats/__init__.py b/cogs/stats/__init__.py index 9202ecb9..c8a608b8 100644 --- a/cogs/stats/__init__.py +++ b/cogs/stats/__init__.py @@ -86,7 +86,9 @@ async def channel_stats( # NOTE: Shortcut accessors are placed at the top of the function so that the exceptions they raise are displayed before any further errors may be sent main_guild: discord.Guild = self.bot.main_guild - if isinstance(ctx.channel, (None | discord.CategoryChannel | discord.ForumChannel)): + if not ctx.channel or isinstance( + ctx.channel, (discord.CategoryChannel, discord.ForumChannel) + ): await self.command_send_error( ctx, message="This command can only be used in text channels." ) @@ -163,7 +165,9 @@ async def server_stats(self, ctx: "TeXBotApplicationContext") -> None: main_guild: discord.Guild = self.bot.main_guild guest_role: discord.Role = await self.bot.guest_role - if isinstance(ctx.channel, (None | discord.CategoryChannel | discord.ForumChannel)): + if not ctx.channel or isinstance( + ctx.channel, (discord.CategoryChannel, discord.ForumChannel) + ): await self.command_send_error( ctx, message="This command can only be used in text channels." ) @@ -249,7 +253,9 @@ async def user_stats(self, ctx: "TeXBotApplicationContext") -> None: interaction_member: discord.Member = await self.bot.get_main_guild_member(ctx.user) guest_role: discord.Role = await self.bot.guest_role - if isinstance(ctx.channel, (None | discord.CategoryChannel | discord.ForumChannel)): + if not ctx.channel or isinstance( + ctx.channel, (discord.CategoryChannel, discord.ForumChannel) + ): await self.command_send_error( ctx, message="This command can only be used in text channels." ) @@ -332,7 +338,9 @@ async def left_member_stats(self, ctx: "TeXBotApplicationContext") -> None: # NOTE: Shortcut accessors are placed at the top of the function so that the exceptions they raise are displayed before any further errors may be sent main_guild: discord.Guild = self.bot.main_guild - if isinstance(ctx.channel, (None | discord.CategoryChannel | discord.ForumChannel)): + if not ctx.channel or isinstance( + ctx.channel, (discord.CategoryChannel, discord.ForumChannel) + ): await self.command_send_error( ctx, message="This command can only be used in text channels." ) From 1f56d87754099b4ea8ad544f7d9ca0a0241cc3d0 Mon Sep 17 00:00:00 2001 From: Matty Widdop <18513864+MattyTheHacker@users.noreply.github.com> Date: Mon, 18 May 2026 23:40:58 +0100 Subject: [PATCH 8/8] Fix flake8 error --- utils/tex_bot_contexts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/tex_bot_contexts.py b/utils/tex_bot_contexts.py index a0788161..0701bccf 100644 --- a/utils/tex_bot_contexts.py +++ b/utils/tex_bot_contexts.py @@ -43,4 +43,4 @@ class TeXBotApplicationContext(discord.ApplicationContext): bot: "TeXBot" # type: ignore[mutable-override] - respond: "Callable[..., Awaitable[Interaction | WebhookMessage]]" # type: ignore[assignment,explicit-any] + respond: "Callable[..., Awaitable[Interaction | WebhookMessage]]" # type: ignore[assignment, explicit-any]