Skip to content
Merged
2 changes: 1 addition & 1 deletion cogs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
"StartupCog",
"StatsCommandsCog",
"StrikeCommandCog",
"StrikeUserCommandCog",
"StrikeContextCommandsCog",
"WriteRolesCommandCog",
"setup",
)
Expand Down
15 changes: 5 additions & 10 deletions cogs/command_error.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
"""Contains cog classes for any command_error interactions."""

import contextlib
import logging
from typing import TYPE_CHECKING

import discord
from discord import Forbidden
from discord.ext.commands.errors import CheckAnyFailure

from exceptions import (
CommitteeRoleDoesNotExistError,
GuildDoesNotExistError,
)
from exceptions import GuildDoesNotExistError
from exceptions.base import BaseErrorWithErrorCode
from utils import CommandChecks, TeXBotBaseCog

Expand Down Expand Up @@ -60,11 +56,10 @@ async def on_application_command_error(
)

elif CommandChecks.is_interaction_user_has_committee_role_failure(error.checks[0]): # type: ignore[arg-type]
# noinspection PyUnusedLocal
committee_role_mention: str = "@Committee"
with contextlib.suppress(CommitteeRoleDoesNotExistError):
committee_role_mention = (await self.bot.committee_role).mention
message = f"Only {committee_role_mention} members can run this command."
message = (
f"Only {await self.bot.get_mention_string(self.bot.committee_role)} "
"members can run this command."
)

await self.command_send_error(
ctx,
Expand Down
1 change: 0 additions & 1 deletion cogs/delete_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ async def _delete_all(
ctx: "TeXBotApplicationContext", delete_model: type["AsyncBaseModel"]
) -> None:
"""Perform the actual deletion process of all instances of the given model class."""
# noinspection PyProtectedMember
await delete_model._default_manager.all().adelete()

delete_model_instances_name_plural: str = (
Expand Down
2 changes: 0 additions & 2 deletions cogs/edit_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@


class EditMessageCommandCog(TeXBotBaseCog):
# noinspection SpellCheckingInspection
"""Cog class that defines the "/edit-message" command and its call-back method."""

@staticmethod
Expand All @@ -50,7 +49,6 @@ async def autocomplete_get_text_channels(

return await TeXBotBaseCog.autocomplete_get_text_channels(ctx)

# noinspection SpellCheckingInspection
@discord.slash_command( # type: ignore[no-untyped-call, misc]
name="edit-message",
description="Edits a message sent by TeX-Bot to the value supplied.",
Expand Down
114 changes: 53 additions & 61 deletions cogs/induct.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
GuestRoleDoesNotExistError,
GuildDoesNotExistError,
MemberRoleDoesNotExistError,
RolesChannelDoesNotExistError,
RulesChannelDoesNotExistError,
)
from utils import (
CommandChecks,
Expand Down Expand Up @@ -77,59 +75,60 @@ async def on_member_update(self, before: discord.Member, after: discord.Member)
await IntroductionReminderOptOutMember.objects.aget(discord_id=before.id)
).adelete()

async for message in after.history():
reminder_message: discord.Message
async for reminder_message in after.history():
MESSAGE_IS_INTRODUCTION_REMINDER: bool = bool(
("joined the " in message.content)
and (" Discord guild but have not yet introduced" in message.content)
and message.author.bot
("joined the " in reminder_message.content)
and (" Discord guild but have not yet introduced" in reminder_message.content)
and reminder_message.author.bot
)
if MESSAGE_IS_INTRODUCTION_REMINDER:
await message.delete(
await reminder_message.delete(
reason="Delete introduction reminders after member is inducted.",
)

# noinspection PyUnusedLocal
rules_channel_mention: str = "**`#welcome`**"
with contextlib.suppress(RulesChannelDoesNotExistError):
rules_channel_mention = (await self.bot.rules_channel).mention

# noinspection PyUnusedLocal
roles_channel_mention: str = "**`#roles`**"
with contextlib.suppress(RolesChannelDoesNotExistError):
roles_channel_mention = (await self.bot.roles_channel).mention

user_type: Literal["guest", "member"] = "guest"
with contextlib.suppress(MemberRoleDoesNotExistError):
if await self.bot.member_role in after.roles:
user_type = "member"

user_type: Literal["guest", "member"]
try:
await after.send(
user_type = "member" if await self.bot.member_role in after.roles else "guest"
except MemberRoleDoesNotExistError:
user_type = "guest"

messages_to_send: list[str] = [
(
f"**Congrats on joining the {self.bot.group_short_name} Discord server "
f"as a {user_type}!** "
"You now have access to communicate in all the public channels.\n\n"
"Some things to do to get started:\n"
f"1. Check out our rules in {rules_channel_mention}\n"
f"2. Head to {roles_channel_mention} and click on the icons to get "
"optional roles like pronouns and year groups\n"
f"1. Check out our rules in {
await self.bot.get_mention_string(self.bot.rules_channel)
}\n"
f"2. Head to {
await self.bot.get_mention_string(self.bot.roles_channel)
} and click on the icons to get optional roles like pronouns and year groups\n"
"3. Change your nickname to whatever you wish others to refer to you as "
"(You can do this by right-clicking your name in the members-list "
'to the right & selecting "Edit Server Profile").',
'to the right & selecting "Edit Server Profile").'
),
]

if user_type != "member":
messages_to_send.append(
f"You can also get yourself an annual membership "
f"to {self.bot.group_full_name} for only £5! "
f"Just head to {settings['PURCHASE_MEMBERSHIP_URL']}. "
"You'll get awesome perks like a free T-shirt:shirt:, "
"access to member only events:calendar_spiral: and a cool green name on "
f"the {self.bot.group_short_name} Discord server:green_square:! "
f"Checkout all the perks at {settings['MEMBERSHIP_PERKS_URL']}",
)
if user_type != "member":
await after.send(
f"You can also get yourself an annual membership "
f"to {self.bot.group_full_name} for only £5! "
f"Just head to {settings['PURCHASE_MEMBERSHIP_URL']}. "
"You'll get awesome perks like a free T-shirt:shirt:, "
"access to member only events:calendar_spiral: and a cool green name on "
f"the {self.bot.group_short_name} Discord server:green_square:! "
f"Checkout all the perks at {settings['MEMBERSHIP_PERKS_URL']}",
)

try:
message_to_send: str
for message_to_send in messages_to_send:
await after.send(message_to_send)
except discord.Forbidden:
logger.info(
"Failed to open DM channel to user %s so no welcome message was sent.",
after,
"Failed to open DM channel to user %s so no welcome message was sent.", after
)


Expand Down Expand Up @@ -227,34 +226,28 @@ async def _perform_induction(
return

if not silent:
general_channel: discord.TextChannel = await self.bot.general_channel

# noinspection PyUnusedLocal
roles_channel_mention: str = "**`#roles`**"
with contextlib.suppress(RolesChannelDoesNotExistError):
roles_channel_mention = (await self.bot.roles_channel).mention

await general_channel.send(
await (await self.bot.general_channel).send(
f"{await self.get_random_welcome_message(induction_member)} :tada:\n"
f"Remember to grab your roles in {roles_channel_mention} "
"and say hello to everyone here! :wave:",
f"Remember to grab your roles in {
await self.bot.get_mention_string(self.bot.roles_channel)
} and say hello to everyone here! :wave:",
)

await induction_member.add_roles(
guest_role,
reason=INDUCT_AUDIT_MESSAGE,
)

# noinspection PyUnusedLocal
applicant_role: discord.Role | None = None
with contextlib.suppress(ApplicantRoleDoesNotExistError):
applicant_role = await ctx.bot.applicant_role

if applicant_role and applicant_role in induction_member.roles:
await induction_member.remove_roles(
applicant_role,
reason=INDUCT_AUDIT_MESSAGE,
)
try:
applicant_role: discord.Role = await ctx.bot.applicant_role
except ApplicantRoleDoesNotExistError:
pass
else:
if applicant_role in induction_member.roles:
await induction_member.remove_roles(
applicant_role,
reason=INDUCT_AUDIT_MESSAGE,
)

tex_emoji: discord.Emoji | None = self.bot.get_emoji(743218410409820213)
if not tex_emoji:
Expand Down Expand Up @@ -365,7 +358,7 @@ async def induct( # type: ignore[misc]


class InductContextCommandsCog(BaseInductCog):
"""Cog class that defines the context-menu induction commands & their call-back methods."""
"""Cog class to define the context-menu induction commands and their call-back methods."""

@discord.user_command(name="Induct User") # type: ignore[no-untyped-call, misc]
@CommandChecks.check_interaction_user_has_committee_role
Expand Down Expand Up @@ -434,7 +427,6 @@ async def non_silent_message_induct( # type: ignore[misc]
class EnsureMembersInductedCommandCog(TeXBotBaseCog):
"""Cog class that defines the "/ensure-members-inducted" command and call-back method."""

# noinspection SpellCheckingInspection
@discord.slash_command( # type: ignore[no-untyped-call, misc]
name="ensure-members-inducted",
description="Ensures all users with the @Member role also have the @Guest role.",
Expand Down
7 changes: 4 additions & 3 deletions cogs/kill.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Contains cog classes for any killing interactions."""

import contextlib
import logging
from typing import TYPE_CHECKING

Expand Down Expand Up @@ -64,9 +63,11 @@ async def kill(self, ctx: "TeXBotApplicationContext") -> None: # type: ignore[m
The "kill" command shuts down TeX-Bot,
but only after the user has confirmed that this is the action they wish to take.
"""
committee_role: discord.Role | None = None
with contextlib.suppress(CommitteeRoleDoesNotExistError):
committee_role: discord.Role | None
try:
committee_role = await self.bot.committee_role
except CommitteeRoleDoesNotExistError:
committee_role = None

response: discord.Message | discord.Interaction = await ctx.respond(
content=(
Expand Down
36 changes: 11 additions & 25 deletions cogs/make_member.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""Contains cog classes for any make_member interactions."""

import contextlib
import logging
import re
from typing import TYPE_CHECKING
Expand All @@ -13,11 +12,7 @@

from config import settings
from db.core.models import GroupMadeMember
from exceptions import (
ApplicantRoleDoesNotExistError,
CommitteeRoleDoesNotExistError,
GuestRoleDoesNotExistError,
)
from exceptions import ApplicantRoleDoesNotExistError, GuestRoleDoesNotExistError
from utils import CommandChecks, TeXBotBaseCog

if TYPE_CHECKING:
Expand Down Expand Up @@ -78,10 +73,8 @@


class MakeMemberCommandCog(TeXBotBaseCog):
# noinspection SpellCheckingInspection
"""Cog class that defines the "/makemember" command and its call-back method."""

# noinspection SpellCheckingInspection
@discord.slash_command( # type: ignore[no-untyped-call, misc]
name="makemember",
description=(
Expand Down Expand Up @@ -118,7 +111,7 @@ class MakeMemberCommandCog(TeXBotBaseCog):
parameter_name="group_member_id",
)
@CommandChecks.check_interaction_user_in_main_guild
async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: str) -> None: # type: ignore[misc] # noqa: PLR0915
async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: str) -> None: # type: ignore[misc]
"""
Definition & callback response of the "make_member" command.

Expand Down Expand Up @@ -152,24 +145,17 @@ async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: st
)
return

GROUP_MEMBER_ID_IS_ALREADY_USED: Final[
bool
] = await GroupMadeMember.objects.filter(
if await GroupMadeMember.objects.filter(
hashed_group_member_id=GroupMadeMember.hash_group_member_id(
group_member_id, self.bot.group_member_id_type
)
).aexists()
if GROUP_MEMBER_ID_IS_ALREADY_USED:
# noinspection PyUnusedLocal
committee_mention: str = "committee"
with contextlib.suppress(CommitteeRoleDoesNotExistError):
committee_mention = (await self.bot.committee_role).mention

).aexists():
await ctx.followup.send(
content=(
":information_source: No changes made. This student ID has already "
f"been used. Please contact a {committee_mention} member if this is "
"an error. :information_source:"
f"been used. Please contact a {
await self.bot.get_mention_string(self.bot.committee_role)
} member if this is an error. :information_source:"
),
ephemeral=True,
)
Expand Down Expand Up @@ -278,11 +264,11 @@ async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: st
guest_role,
reason='TeX Bot slash-command: "/makemember"',
)

# noinspection PyUnusedLocal
applicant_role: discord.Role | None = None
with contextlib.suppress(ApplicantRoleDoesNotExistError):
applicant_role: discord.Role | None
try:
applicant_role = await ctx.bot.applicant_role
except ApplicantRoleDoesNotExistError:
applicant_role = None

if applicant_role and applicant_role in interaction_member.roles:
await interaction_member.remove_roles(
Expand Down
4 changes: 1 addition & 3 deletions cogs/remind_me.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ async def remind_me( # type: ignore[misc]

The "remind_me" command responds with the given message after the specified time.
"""
# noinspection PyTypeChecker
parsed_time: tuple[time.struct_time, int] = parsedatetime.Calendar().parseDT(
delay,
tzinfo=timezone.get_current_timezone(),
Expand Down Expand Up @@ -289,7 +288,7 @@ def __init__(self, bot: "TeXBot") -> None:
@override
def cog_unload(self) -> None:
"""
Unload hook that ends all running tasks whenever the tasks cog is unloaded.
Unload-hook that ends all running tasks whenever the tasks cog is unloaded.

This may be run dynamically or when the bot closes.
"""
Expand Down Expand Up @@ -333,7 +332,6 @@ async def clear_reminders_backlog(self) -> None:
await reminder.adelete()
continue

# noinspection PyUnresolvedReferences
channel: discord.PartialMessageable = self.bot.get_partial_messageable(
reminder.channel_id,
type=reminder.channel_type,
Expand Down
Loading