Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions src/commands/club.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,11 @@ async def remove_club_member(
club: Transform[ClubSchema, ClubTransformer],
member: Member,
):
await interaction.response.defer(thinking=True)
db_club = Club.get_or_none(Club.sith_id == club.id)
await interaction.response.defer(thinking=True, ephemeral=True)
db_club: Club = Club.get_or_none(Club.sith_id == club.id)
if not db_club:
await interaction.followup.send(f"Le club : {club.name} n'existe pas")
return
role_membre = utils.get(member.guild.roles, id=db_club.member_role_id)
if (
not interaction.user.guild_permissions.manage_roles
and not interaction.user.get_role(db_club.president_role_id)
Expand All @@ -87,9 +86,19 @@ async def remove_club_member(
"Seul le président du club et les admins peuvent retirer un membre"
)
return
if role_membre not in member.roles:
member_role = interaction.guild.get_role(db_club.member_role_id)
board_roles = [db_club.president_role_id, db_club.treasurer_role_id]
if member_role not in member.roles:
await interaction.followup.send("Cet utilisateur n'est pas dans le club")
return
if any(member.get_role(r) for r in board_roles):
await interaction.followup.send(
"Cette commande ne peut pas être utilisée pour retirer "
"le président ou le trésorier dun club.\n\n"
"Utilisez plutôt `/club passation` ou `/club arret`."
)
return

await self.club_service.remove_member(db_club, member)
await interaction.followup.send(
f"{member.name} a été retiré du club :thumbs_up:"
Expand All @@ -107,9 +116,9 @@ async def add_club_member(
club: Transform[ClubSchema, ClubTransformer],
member: Member,
):
await interaction.response.defer(thinking=True)
await interaction.response.defer(thinking=True, ephemeral=True)
db_club = Club.get_or_none(Club.sith_id == club.id)
role_membre = member.guild.get_role(db_club.member_role_id)
role_membre = interaction.guild.get_role(db_club.member_role_id)
if not db_club:
await interaction.followup.send(f"Le club : {club.name} n'existe pas")
return
Expand Down Expand Up @@ -139,7 +148,7 @@ async def add_club_member(
async def create_club(
self, interaction: Interaction, club: Transform[ClubSchema, ClubTransformer]
):
await interaction.response.defer(thinking=True)
await interaction.response.defer(thinking=True, ephemeral=True)
if Club.filter(Club.sith_id == club.id).exists():
await interaction.followup.send(f"Le club : {club.name} existe déjà...")
else:
Expand Down Expand Up @@ -176,7 +185,7 @@ async def handover(
new_president: Member,
new_treasurer: Member,
):
await interaction.response.defer(thinking=True)
await interaction.response.defer(thinking=True, ephemeral=True)
db_club = Club.get_or_none(Club.sith_id == club.id)
guild = interaction.guild

Expand Down Expand Up @@ -213,7 +222,7 @@ async def handover(
async def stop_club(
self, interaction: Interaction, club: Transform[ClubSchema, ClubTransformer]
):
await interaction.response.defer(thinking=True)
await interaction.response.defer(thinking=True, ephemeral=True)
db_club = Club.get_or_none(Club.sith_id == club.id)
await self.club_service.stop_club(db_club, interaction.guild)
annonce = await self.club_service.get_channel(
Expand Down
2 changes: 1 addition & 1 deletion src/commands/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
class MiscCog(commands.Cog):
@app_commands.command(name="ping", description="Ping le bot")
async def ping(self, interaction: Interaction[AeBot]):
await interaction.response.send_message("pong")
await interaction.response.send_message("pong", ephemeral=True)
81 changes: 62 additions & 19 deletions src/services/club.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import asyncio
from typing import TYPE_CHECKING
from urllib.parse import urljoin

Expand Down Expand Up @@ -35,6 +36,7 @@ def __init__(self, bot: AeBot):
self._client = bot.client
self._club_cache = {}
self._bot = bot
self._background_tasks = set()

async def search_club(
self, current: str, *, only_existing: bool
Expand Down Expand Up @@ -135,13 +137,54 @@ async def add_member(self, club: Club, member: Member):
async def remove_member(
self, club: Club, member: Member, *, make_former: bool = True
):
role = utils.get(member.guild.roles, id=club.member_role_id)
former = utils.get(member.guild.roles, id=club.former_member_role_id)
await member.remove_roles(role, reason=f"{member.name} left club {club.name}")
if make_former:
await member.add_roles(
former, reason=f"{member.name} left club {club.name}"
"""Remove a member from the club.

Args:
club: The club to remove the user from
member: The member to remove
make_former:
if True, the member will receive
a role indicating its former club membership
"""
await self.remove_members(club, [member], make_former=make_former)

async def remove_members(
self,
club: Club,
members: list[Member] | tuple[Member] | set[Member],
*,
make_former: bool = True,
):
"""Remove multiple members from a club.

Args:
club: The club to remove the user from
members: The members to remove
make_former:
if True, the member will receive
a role indicating its former club membership

Warnings:
This method sleeps for two seconds between each member
(in order to avoid rate-limit), so it may be a bad idea
to await it.
Favour an execution inside a detached async Task.
"""
role_ids = [club.member_role_id, club.president_role_id, club.treasurer_role_id]
roles = [self._bot.watched_guild.get_role(r) for r in role_ids]
former = self._bot.watched_guild.get_role(club.former_member_role_id)
for member in members:
if len(members) > 1:
# if there is more than one member,
# sleep a little bit to avoid rate limit
await asyncio.sleep(2)
await member.remove_roles(
*roles, reason=f"{member.name} left club {club.name}"
)
if make_former:
await member.add_roles(
former, reason=f"{member.name} left club {club.name}"
)

async def handover(
self, club: ClubSchema, new_pres: Member, new_treso: Member, guild: Guild
Expand Down Expand Up @@ -176,25 +219,25 @@ async def stop_club(self, club: Club, guild: Guild):
role_pres = utils.get(guild.roles, id=club.president_role_id)
role_treso = utils.get(guild.roles, id=club.treasurer_role_id)
role_member = utils.get(guild.roles, id=club.member_role_id)
role_former = utils.get(guild.roles, id=club.former_member_role_id)
old_member = {*role_pres.members, *role_treso.members, *role_member.members}
old_members = {*role_pres.members, *role_treso.members, *role_member.members}
category = utils.get(guild.categories, id=club.category_id)
await self.move_to_bottom(category)
await category.edit(name=club.name + " [inactif]")
await self.move_to_bottom(category)

for e in old_member:
await e.remove_roles(
role_pres,
role_treso,
role_member,
reason=f"Arrêt du club : {club.name}",
)
await e.add_roles(role_former, reason=f"Arrêt du club : {club.name}")
# see https://docs.python.org/3/library/asyncio-task.html#creating-tasks
task = asyncio.create_task(
self.remove_members(club, old_members, make_former=True)
)
self._background_tasks.add(task)
task.add_done_callback(self._background_tasks.discard)

@staticmethod
async def move_to_bottom(category: CategoryChannel):
"""Move this category after the last category belong to an active club."""
"""Move this category after the last category belong to an active club.

Warnings:
This method seems to have a high cost on discord's side.
Using it a little bit too much is likely to end in rate-limit.
"""
guild = category.guild
inactives = [c for c in guild.categories if c.name.endswith("[inactif]")]
if not inactives:
Expand Down