|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
| 3 | +import asyncio |
3 | 4 | from typing import TYPE_CHECKING |
4 | 5 | from urllib.parse import urljoin |
5 | 6 |
|
@@ -35,6 +36,7 @@ def __init__(self, bot: AeBot): |
35 | 36 | self._client = bot.client |
36 | 37 | self._club_cache = {} |
37 | 38 | self._bot = bot |
| 39 | + self._background_tasks = set() |
38 | 40 |
|
39 | 41 | async def search_club( |
40 | 42 | self, current: str, *, only_existing: bool |
@@ -135,13 +137,54 @@ async def add_member(self, club: Club, member: Member): |
135 | 137 | async def remove_member( |
136 | 138 | self, club: Club, member: Member, *, make_former: bool = True |
137 | 139 | ): |
138 | | - role = utils.get(member.guild.roles, id=club.member_role_id) |
139 | | - former = utils.get(member.guild.roles, id=club.former_member_role_id) |
140 | | - await member.remove_roles(role, reason=f"{member.name} left club {club.name}") |
141 | | - if make_former: |
142 | | - await member.add_roles( |
143 | | - former, reason=f"{member.name} left club {club.name}" |
| 140 | + """Remove a member from the club. |
| 141 | +
|
| 142 | + Args: |
| 143 | + club: The club to remove the user from |
| 144 | + member: The member to remove |
| 145 | + make_former: |
| 146 | + if True, the member will receive |
| 147 | + a role indicating its former club membership |
| 148 | + """ |
| 149 | + await self.remove_members(club, [member], make_former=make_former) |
| 150 | + |
| 151 | + async def remove_members( |
| 152 | + self, |
| 153 | + club: Club, |
| 154 | + members: list[Member] | tuple[Member] | set[Member], |
| 155 | + *, |
| 156 | + make_former: bool = True, |
| 157 | + ): |
| 158 | + """Remove multiple members from a club. |
| 159 | +
|
| 160 | + Args: |
| 161 | + club: The club to remove the user from |
| 162 | + members: The members to remove |
| 163 | + make_former: |
| 164 | + if True, the member will receive |
| 165 | + a role indicating its former club membership |
| 166 | +
|
| 167 | + Warnings: |
| 168 | + This method sleeps for two seconds between each member |
| 169 | + (in order to avoid rate-limit), so it may be a bad idea |
| 170 | + to await it. |
| 171 | + Favour an execution inside a detached async Task. |
| 172 | + """ |
| 173 | + role_ids = [club.member_role_id, club.president_role_id, club.treasurer_role_id] |
| 174 | + roles = [self._bot.watched_guild.get_role(r) for r in role_ids] |
| 175 | + former = self._bot.watched_guild.get_role(club.former_member_role_id) |
| 176 | + for member in members: |
| 177 | + if len(members) > 1: |
| 178 | + # if there is more than one member, |
| 179 | + # sleep a little bit to avoid rate limit |
| 180 | + await asyncio.sleep(2) |
| 181 | + await member.remove_roles( |
| 182 | + *roles, reason=f"{member.name} left club {club.name}" |
144 | 183 | ) |
| 184 | + if make_former: |
| 185 | + await member.add_roles( |
| 186 | + former, reason=f"{member.name} left club {club.name}" |
| 187 | + ) |
145 | 188 |
|
146 | 189 | async def handover( |
147 | 190 | self, club: ClubSchema, new_pres: Member, new_treso: Member, guild: Guild |
@@ -176,25 +219,25 @@ async def stop_club(self, club: Club, guild: Guild): |
176 | 219 | role_pres = utils.get(guild.roles, id=club.president_role_id) |
177 | 220 | role_treso = utils.get(guild.roles, id=club.treasurer_role_id) |
178 | 221 | role_member = utils.get(guild.roles, id=club.member_role_id) |
179 | | - role_former = utils.get(guild.roles, id=club.former_member_role_id) |
180 | | - old_member = {*role_pres.members, *role_treso.members, *role_member.members} |
| 222 | + old_members = {*role_pres.members, *role_treso.members, *role_member.members} |
181 | 223 | category = utils.get(guild.categories, id=club.category_id) |
182 | 224 | await self.move_to_bottom(category) |
183 | 225 | await category.edit(name=club.name + " [inactif]") |
184 | | - await self.move_to_bottom(category) |
185 | | - |
186 | | - for e in old_member: |
187 | | - await e.remove_roles( |
188 | | - role_pres, |
189 | | - role_treso, |
190 | | - role_member, |
191 | | - reason=f"Arrêt du club : {club.name}", |
192 | | - ) |
193 | | - await e.add_roles(role_former, reason=f"Arrêt du club : {club.name}") |
| 226 | + # see https://docs.python.org/3/library/asyncio-task.html#creating-tasks |
| 227 | + task = asyncio.create_task( |
| 228 | + self.remove_members(club, old_members, make_former=True) |
| 229 | + ) |
| 230 | + self._background_tasks.add(task) |
| 231 | + task.add_done_callback(self._background_tasks.discard) |
194 | 232 |
|
195 | 233 | @staticmethod |
196 | 234 | async def move_to_bottom(category: CategoryChannel): |
197 | | - """Move this category after the last category belong to an active club.""" |
| 235 | + """Move this category after the last category belong to an active club. |
| 236 | +
|
| 237 | + Warnings: |
| 238 | + This method seems to have a high cost on discord's side. |
| 239 | + Using it a little bit too much is likely to end in rate-limit. |
| 240 | + """ |
198 | 241 | guild = category.guild |
199 | 242 | inactives = [c for c in guild.categories if c.name.endswith("[inactif]")] |
200 | 243 | if not inactives: |
|
0 commit comments