Skip to content

Commit

Permalink
Merge branch 'main' into 165-move-configuration-to-tex-bot-deployment…
Browse files Browse the repository at this point in the history
…yaml
  • Loading branch information
CarrotManMatt committed Jun 14, 2024
2 parents 30be863 + 6026888 commit 7d26822
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 49 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ The meaning of each error code is given here:
* `E1024` - Your [Discord guild](https://discord.com/developers/docs/resources/guild) does not contain a [role](https://discord.com/developers/docs/topics/permissions#role-object) with the name "**@Archivist**".
(This [role](https://discord.com/developers/docs/topics/permissions#role-object) is required for the `/archive` [command](https://discord.com/developers/docs/interactions/application-commands))

* `E1025` - Your [Discord guild](https://discord.com/developers/docs/resources/guild) does not contain a [role](https://discord.com/developers/docs/topics/permissions#role-object) with the name "**@Applicant**". (This [role](https://discord.com/developers/docs/topics/permissions#role-object) is required for the `/make-applicant` [command](https://discord.com/developers/docs/interactions/application-commands) and respective user and message commands)

* `E1031` - Your [Discord guild](https://discord.com/developers/docs/resources/guild) does not contain a [text channel](https://docs.pycord.dev/en/stable/api/models.html#discord.TextChannel) with the name "#**roles**".
(This [text channel](https://docs.pycord.dev/en/stable/api/models.html#discord.TextChannel) is required for the `/writeroles` [command](https://discord.com/developers/docs/interactions/application-commands))

Expand Down
5 changes: 5 additions & 0 deletions cogs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"DeleteAllCommandsCog",
"EditMessageCommandCog",
"EnsureMembersInductedCommandCog",
"MakeApplicantSlashCommandCog",
"MakeApplicantContextCommandsCog",
"InductSlashCommandCog",
"InductSendMessageCog",
"InductContextCommandsCog",
Expand Down Expand Up @@ -49,6 +51,7 @@
InductSlashCommandCog,
)
from cogs.kill import KillCommandCog
from cogs.make_applicant import MakeApplicantContextCommandsCog, MakeApplicantSlashCommandCog
from cogs.make_member import MakeMemberCommandCog
from cogs.ping import PingCommandCog
from cogs.remind_me import ClearRemindersBacklogTaskCog, RemindMeCommandCog
Expand Down Expand Up @@ -80,6 +83,8 @@ def setup(bot: TeXBot) -> None:
InductSendMessageCog,
InductContextCommandsCog,
KillCommandCog,
MakeApplicantSlashCommandCog,
MakeApplicantContextCommandsCog,
MakeMemberCommandCog,
PingCommandCog,
ClearRemindersBacklogTaskCog,
Expand Down
8 changes: 4 additions & 4 deletions cogs/induct.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from config import messages, settings
from db.core.models import IntroductionReminderOptOutMember
from exceptions import (
ApplicantRoleDoesNotExistError,
CommitteeRoleDoesNotExistError,
GuestRoleDoesNotExistError,
GuildDoesNotExistError,
Expand Down Expand Up @@ -247,10 +248,9 @@ async def _perform_induction(self, ctx: TeXBotApplicationContext, induction_memb
reason=f"{ctx.user} used TeX Bot slash-command: \"/induct\"",
)

applicant_role: discord.Role | None = discord.utils.get(
main_guild.roles,
name="Applicant",
)
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(
Expand Down
10 changes: 5 additions & 5 deletions cogs/kill.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ class ConfirmKillView(View):
)
async def confirm_shutdown_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501
"""When the shutdown button is pressed, delete the message."""
logger.debug("Confirm button pressed. %s", interaction)
logger.debug("\"Confirm\" button pressed. %s", interaction)

@discord.ui.button( # type: ignore[misc]
label="CANCEL",
style=discord.ButtonStyle.green,
style=discord.ButtonStyle.grey,
custom_id="shutdown_cancel",
)
async def cancel_shutdown_button_callback(self, _: discord.Button, interaction: discord.Interaction) -> None: # noqa: E501
"""When the cancel button is pressed, delete the message."""
logger.debug("Cancel button pressed. %s", interaction)
logger.debug("\"Cancel\" button pressed. %s", interaction)


class KillCommandCog(TeXBotBaseCog):
Expand Down Expand Up @@ -89,18 +89,18 @@ async def kill(self, ctx: TeXBotApplicationContext) -> None:
),
)

await confirmation_message.edit(view=None)

if button_interaction.data["custom_id"] == "shutdown_confirm": # type: ignore[index, typeddict-item]
await confirmation_message.edit(
content="My battery is low and it's getting dark...",
view=None,
)
await self.bot.perform_kill_and_close(initiated_by_user=ctx.interaction.user)
return

if button_interaction.data["custom_id"] == "shutdown_cancel": # type: ignore[index, typeddict-item]
await confirmation_message.edit(
content="Shutdown has been cancelled.",
view=None,
)
logger.info("Manual shutdown cancelled by %s.", ctx.interaction.user)
return
Expand Down
202 changes: 202 additions & 0 deletions cogs/make_applicant.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
"""Contains cog classes for making a user into an applicant."""

from collections.abc import Sequence

__all__: Sequence[str] = (
"BaseMakeApplicantCog",
"MakeApplicantSlashCommandCog",
"MakeApplicantContextCommandsCog",
)


import logging
from logging import Logger
from typing import Final

import discord

from exceptions.does_not_exist import ApplicantRoleDoesNotExistError, GuildDoesNotExistError
from utils import CommandChecks, TeXBotApplicationContext, TeXBotBaseCog

logger: Logger = logging.getLogger("TeX-Bot")


class BaseMakeApplicantCog(TeXBotBaseCog):
"""
Base making-applicant cog container class.
Defines the methods for making users into group-applicants, that are called by
child cog container classes.
"""

async def _perform_make_applicant(self, ctx: TeXBotApplicationContext, applicant_member: discord.Member) -> None: # noqa: E501
"""Perform the actual process of making the user into a group-applicant."""
main_guild: discord.Guild = ctx.bot.main_guild
applicant_role: discord.Role = await ctx.bot.applicant_role
guest_role: discord.Role = await ctx.bot.guest_role

intro_channel: discord.TextChannel | None = discord.utils.get(
main_guild.text_channels,
name="introductions",
)

if applicant_member.bot:
await self.command_send_error(ctx, message="Cannot make a bot user an applicant!")
return

initial_response: discord.Interaction | discord.WebhookMessage = await ctx.respond(
":hourglass: Attempting to make user an applicant... :hourglass:",
ephemeral=True,
)

AUDIT_MESSAGE: Final[str] = f"{ctx.user} used TeX Bot Command \"Make User Applicant\""

await applicant_member.add_roles(applicant_role, reason=AUDIT_MESSAGE)

logger.debug("Applicant role given to user %s", applicant_member)

if guest_role in applicant_member.roles:
await applicant_member.remove_roles(guest_role, reason=AUDIT_MESSAGE)
logger.debug("Removed Guest role from user %s", applicant_member)


tex_emoji: discord.Emoji | None = self.bot.get_emoji(743218410409820213)
if not tex_emoji:
tex_emoji = discord.utils.get(main_guild.emojis, name="TeX")

if intro_channel:
recent_message: discord.Message
for recent_message in await intro_channel.history(limit=30).flatten():
if recent_message.author.id == applicant_member.id:
forbidden_error: discord.Forbidden
try:
if tex_emoji:
await recent_message.add_reaction(tex_emoji)
await recent_message.add_reaction("👋")
except discord.Forbidden as forbidden_error:
if "90001" not in str(forbidden_error):
raise forbidden_error from forbidden_error

logger.info(
"Failed to add reactions because the user, %s, "
"has blocked the bot.",
recent_message.author,
)
break

await initial_response.edit(content=":white_check_mark: User is now an applicant.")


class MakeApplicantSlashCommandCog(BaseMakeApplicantCog):
"""Cog class that defines the "/make_applicant" slash-command."""

@staticmethod
async def autocomplete_get_members(ctx: TeXBotApplicationContext) -> set[discord.OptionChoice]: # noqa: E501
"""
Autocomplete callable that generates the set of available selectable members.
This list of selectable members is used in any of the "make_applicant" slash-command
options that have a member input-type.
"""
try:
guild: discord.Guild = ctx.bot.main_guild
applicant_role: discord.Role = await ctx.bot.applicant_role
except (GuildDoesNotExistError, ApplicantRoleDoesNotExistError):
return set()

members: set[discord.Member] = {
member
for member
in guild.members
if not member.bot and applicant_role not in member.roles
}

if not ctx.value or ctx.value.startswith("@"):
return {
discord.OptionChoice(name=f"@{member.name}", value=str(member.id))
for member
in members
}

return {
discord.OptionChoice(name=member.name, value=str(member.id))
for member
in members
}


@discord.slash_command( # type: ignore[no-untyped-call, misc]
name="make-applicant",
description=(
"Gives the user @Applicant role and removes the @Guest role if present."
),
)
@discord.option( # type: ignore[no-untyped-call, misc]
name="user",
description="The user to make an Applicant",
input_type=str,
autocomplete=discord.utils.basic_autocomplete(autocomplete_get_members), # type: ignore[arg-type]
required=True,
parameter_name="str_applicant_member_id",
)
@CommandChecks.check_interaction_user_has_committee_role
@CommandChecks.check_interaction_user_in_main_guild
async def make_applicant(self, ctx: TeXBotApplicationContext, str_applicant_member_id: str) -> None: # noqa: E501
"""
Definition & callback response of the "make_applicant" command.
The "make_applicant" command gives the specified user the "Applicant" role and
removes the "Guest" role if they have it.
"""
member_id_not_integer_error: ValueError
try:
applicant_member: discord.Member = await self.bot.get_member_from_str_id(
str_applicant_member_id,
)
except ValueError as member_id_not_integer_error:
await self.command_send_error(ctx, message=member_id_not_integer_error.args[0])
return

await self._perform_make_applicant(ctx, applicant_member)


class MakeApplicantContextCommandsCog(BaseMakeApplicantCog):
"""Cog class that defines the "/make_applicant" context commands."""

@discord.user_command(name="Make Applicant") #type: ignore[no-untyped-call, misc]
@CommandChecks.check_interaction_user_has_committee_role
@CommandChecks.check_interaction_user_in_main_guild
async def user_make_applicant(self, ctx: TeXBotApplicationContext, member: discord.Member) -> None: # noqa: E501
"""
Definition and callback response of the "make_applicant" user-context-command.
The "make_applicant" user-context-command executes the same process as
the "make_applicant" slash-command, and thus gives the specified user the
"Applicant" role and removes the "Guest" role if they have it.
"""
await self._perform_make_applicant(ctx, member)

@discord.message_command(name="Make Message Author Applicant") # type: ignore[no-untyped-call, misc]
@CommandChecks.check_interaction_user_has_committee_role
@CommandChecks.check_interaction_user_in_main_guild
async def message_make_applicant(self, ctx: TeXBotApplicationContext, message: discord.Message) -> None: # noqa: E501
"""
Definition of the "message_make_applicant" message-context-command.
The "make_applicant" message-context-command executes the same process as
the "make_applicant" slash-command, and thus gives the specified user the
"Applicant" role and removes the "Guest" role if they have it.
"""
try:
member: discord.Member = await self.bot.get_member_from_str_id(
str(message.author.id),
)
except ValueError:
await ctx.respond((
":information_source: No changes made. User cannot be made into an applicant "
"because they have left the server :information_source:"
),
ephemeral=True,
)

await self._perform_make_applicant(ctx, member)
16 changes: 10 additions & 6 deletions cogs/make_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@

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

logger: Final[Logger] = logging.getLogger("TeX-Bot")
Expand Down Expand Up @@ -94,7 +98,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:
async def make_member(self, ctx: TeXBotApplicationContext, group_member_id: str) -> None: # noqa: PLR0915
"""
Definition & callback response of the "make_member" command.
Expand Down Expand Up @@ -255,10 +259,10 @@ async def make_member(self, ctx: TeXBotApplicationContext, group_member_id: str)
reason="TeX Bot slash-command: \"/makemember\"",
)

applicant_role: discord.Role | None = discord.utils.get(
self.bot.main_guild.roles,
name="Applicant",
)
applicant_role: discord.Role | None = None
with contextlib.suppress(ApplicantRoleDoesNotExistError):
applicant_role = await ctx.bot.applicant_role

if applicant_role and applicant_role in interaction_member.roles:
await interaction_member.remove_roles(
applicant_role,
Expand Down
Loading

0 comments on commit 7d26822

Please sign in to comment.