diff --git a/cogs/admin.py b/cogs/admin.py index 176cc74..2cb671b 100644 --- a/cogs/admin.py +++ b/cogs/admin.py @@ -4,6 +4,8 @@ from discord.ext import commands from discord import app_commands +from discord.app_commands.errors import CommandInvokeError + from typing import Literal from src.queries.puzzle import ( @@ -13,9 +15,13 @@ create_puzzle, delete_puzzle, ) +from src.queries.player import get_player, remove_player +from src.queries.team import get_team, get_team_members, remove_team from src.config import config +from src.context.team import remove_member_from_team + EXEC_ID = "Executives" @@ -172,6 +178,41 @@ async def set_hint_channel(self, interaction: discord.Interaction): f"Hints will now be redirected to <#{channel.id}>" ) + @app_commands.command( + name="remove_member", description="Forcibly removes a member from a team." + ) + @commands.has_role(EXEC_ID) + async def remove_member( + self, interaction: discord.Interaction, member: discord.Member + ): + await interaction.response.defer(ephemeral=True) + status = await remove_member_from_team(interaction.guild, member) + + if status is None: + await interaction.followup.send( + f"{member.display_name} is not part of a team.", ephemeral=True + ) + return + + elif status == "removed": + await interaction.followup.send( + f"{member.display_name} has been successfully kicked from the team.", + ephemeral=True, + ) + return + + elif status == "deleted": + try: + await interaction.followup.send( + f"{member.display_name} has been successfully kicked from the team. " + + "Since the team is empty, the corresponding roles and channels will be deleted.", + ephemeral=True, + ) + except CommandInvokeError: + return + + return + async def setup(bot: commands.Bot): await bot.add_cog(Admin(bot)) diff --git a/cogs/team.py b/cogs/team.py index edca420..e87692d 100644 --- a/cogs/team.py +++ b/cogs/team.py @@ -2,9 +2,13 @@ from discord.ext import commands from discord import app_commands +from discord.app_commands.errors import CommandInvokeError + import src.queries.team as team_query import src.queries.player as player_query +from src.context.team import remove_member_from_team + BOT_ID = 1208986388226121849 MAX_TEAM_SIZE = 6 EXEC_ID = "Executives" @@ -79,57 +83,32 @@ async def create_team(self, interaction: discord.Interaction, team_name: str): @app_commands.command(name="leave", description="Leave your current team.") async def leave_team(self, interaction: discord.Interaction): - # remove team role from user - user = interaction.user - guild = interaction.guild - await interaction.response.defer(ephemeral=True) - # get the team name from the user - discord_id = user.id - player = await player_query.get_player(discord_id) + user = interaction.user - if not player: + status = await remove_member_from_team(interaction.guild, user) + + if status is None: await interaction.followup.send( "You are not part of a team.", ephemeral=True ) return - team_name = player.team_name - - team = await team_query.get_team(team_name) - role = guild.get_role(team.team_role_id) - - await user.remove_roles(role) - - # delete player - await player_query.remove_player(discord_id) - - # check amount of people still in team - # if none, delete team and respective channels - # also delete the role - team_members = await team_query.get_team_members(team_name) - if team_members: + elif status == "removed": await interaction.followup.send("You have left the team.", ephemeral=True) return - await interaction.followup.send( - "You have left the team. Since there are no members left in the team, the channels will be deleted.", - ephemeral=True, - ) - - # if here, then there are no members remaining in the teams - text_channel = guild.get_channel(team.text_channel_id) - voice_channel = guild.get_channel(team.voice_channel_id) - category_channel = guild.get_channel(team.category_channel_id) - - await text_channel.delete() - await voice_channel.delete() - await category_channel.delete() - await role.delete() + elif status == "deleted": + try: + await interaction.followup.send( + "You have left the team. Since there are no members left, the channels will be deleted.", + ephemeral=True, + ) + except CommandInvokeError: + return - # also delete the team - await team_query.remove_team(team_name) + return @app_commands.command( name="invite", description="Invite another member into your team!" diff --git a/src/context/team.py b/src/context/team.py new file mode 100644 index 0000000..52c25bb --- /dev/null +++ b/src/context/team.py @@ -0,0 +1,75 @@ +import discord + +from typing import List + +from src.queries.player import get_player, remove_player +from src.queries.team import get_team, get_team_members, remove_team + +from src.models.player import Player + + +async def get_team_channels(guild: discord.Guild, team_name: str): + team = await get_team(team_name) + + voice_channel = guild.get_channel(team.voice_channel_id) + text_channel = guild.get_channel(team.text_channel_id) + category_channel = guild.get_channel(team.category_channel_id) + + return [voice_channel, text_channel, category_channel] + + +async def delete_roles_and_channels( + roles: List[discord.Role], + channels: List[ + discord.TextChannel | discord.VoiceChannel | discord.CategoryChannel + ], +): + for role in roles: + await role.delete() + + for channel in channels: + await channel.delete() + + +async def remove_role_and_player( + guild: discord.Guild, player: Player, member: discord.Member +): + if not player: + return None + + team = await get_team(player.team_name) + role = guild.get_role(team.team_role_id) + + await member.remove_roles(role) + await remove_player(member.id) + + return role + + +# kicked: a True/False value that lets the function know whether the member +# is leaving the team on their own or is being kicked from the team by +# an admin +async def remove_member_from_team(guild: discord.Guild, member: discord.Member): + player = await get_player(member.id) + + role = await remove_role_and_player(guild, player, member) + + if not role: + return None + + team_name = player.team_name + + # delete team if no more members + team_members = await get_team_members(team_name) + + if team_members: + return "removed" + + channels = await get_team_channels(guild, team_name) + + # delete roles and channels + await delete_roles_and_channels([role], channels) + + await remove_team(team_name) + + return "deleted"