mirror of
				https://gitlab.com/ddorn/tfjm-discord-bot.git
				synced 2025-10-31 00:59:51 +01:00 
			
		
		
		
	🚧 new tirages working without save
This commit is contained in:
		| @@ -1,5 +1,8 @@ | |||||||
| import asyncio | import asyncio | ||||||
| import random | import random | ||||||
|  | import sys | ||||||
|  | import traceback | ||||||
|  | from functools import wraps | ||||||
| from pprint import pprint | from pprint import pprint | ||||||
|  |  | ||||||
| from io import StringIO | from io import StringIO | ||||||
| @@ -57,12 +60,14 @@ class Team: | |||||||
|  |  | ||||||
|         return "\n".join(f"`{n.rjust(width)}`: {v}" for n, v in info.items()) |         return "\n".join(f"`{n.rjust(width)}`: {v}" for n, v in info.items()) | ||||||
|  |  | ||||||
|         return f""" - Accepté: {self.accepted_problems[round]} |  | ||||||
|  - Refusés: {", ".join(p[0] for p in self.rejected[round]) if self.rejected[round] else "aucun"} | # | ||||||
|  - Coefficient: {self.coeff(round)} | #         return f""" - Accepté: {self.accepted_problems[round]} | ||||||
|  - Ordre au tirage: {self.tirage_order[round]} | #  - Refusés: {", ".join(p[0] for p in self.rejected[round]) if self.rejected[round] else "aucun"} | ||||||
|  - Ordre de passage: {self.passage_order[round]} | #  - Coefficient: {self.coeff(round)} | ||||||
| """ | #  - Ordre au tirage: {self.tirage_order[round]} | ||||||
|  | #  - Ordre de passage: {self.passage_order[round]} | ||||||
|  | # """ | ||||||
|  |  | ||||||
|  |  | ||||||
| class Poule: | class Poule: | ||||||
| @@ -110,6 +115,9 @@ class BaseTirage: | |||||||
|         ] |         ] | ||||||
|         return await self.event(Event(trigram, random.choice(available))) |         return await self.event(Event(trigram, random.choice(available))) | ||||||
|  |  | ||||||
|  |     async def accept(self, trigram, yes: bool): | ||||||
|  |         return await self.event(Event(trigram, yes)) | ||||||
|  |  | ||||||
|     async def next(self, typ, team=None): |     async def next(self, typ, team=None): | ||||||
|         while True: |         while True: | ||||||
|             event = await self.queue.get() |             event = await self.queue.get() | ||||||
| @@ -148,6 +156,7 @@ class BaseTirage: | |||||||
|                 # TODO: avoid KeyError |                 # TODO: avoid KeyError | ||||||
|                 if dices[event.team] is None: |                 if dices[event.team] is None: | ||||||
|                     dices[event.team] = event.value |                     dices[event.team] = event.value | ||||||
|  |                     await self.info_dice(event.team, event.value) | ||||||
|                 else: |                 else: | ||||||
|                     await self.warn_twice(int) |                     await self.warn_twice(int) | ||||||
|  |  | ||||||
| @@ -173,6 +182,9 @@ class BaseTirage: | |||||||
|         return poules |         return poules | ||||||
|  |  | ||||||
|     async def draw_poule(self, poule): |     async def draw_poule(self, poule): | ||||||
|  |  | ||||||
|  |         await self.start_draw_poule(poule) | ||||||
|  |  | ||||||
|         # Trigrams in draw order |         # Trigrams in draw order | ||||||
|         trigrams = await self.draw_order(poule) |         trigrams = await self.draw_order(poule) | ||||||
|  |  | ||||||
| @@ -184,25 +196,27 @@ class BaseTirage: | |||||||
|             if team.accepted_problems[poule.rnd] is not None: |             if team.accepted_problems[poule.rnd] is not None: | ||||||
|                 # The team already accepted a problem |                 # The team already accepted a problem | ||||||
|                 current += 1 |                 current += 1 | ||||||
|  |                 current %= len(teams) | ||||||
|                 continue |                 continue | ||||||
|  |  | ||||||
|             # Choose problem |             # Choose problem | ||||||
|             await self.start_select_pb(team) |             await self.start_select_pb(team) | ||||||
|             event = await self.next(str, team.name) |             pevent = await self.next(str, team.name) | ||||||
|             # TODO: Add check for already selected / taken by someone else |             # TODO: Add check for already selected / taken by someone else | ||||||
|             # This is not a bug for now, since it cannot happen yet |             # This is not a bug for now, since it cannot happen yet | ||||||
|             await self.info_draw_pb(team, event.value, rnd) |             await self.info_draw_pb(team, pevent.value, poule.rnd) | ||||||
|  |  | ||||||
|             # Accept it |             # Accept it | ||||||
|             accept = await self.next(bool, team.name) |             accept = await self.next(bool, team.name) | ||||||
|             if accept: |             if accept.value: | ||||||
|                 team.accepted_problems[poule.rnd] = event.value |                 team.accepted_problems[poule.rnd] = pevent.value | ||||||
|                 await self.info_accepted(team, event.value) |                 await self.info_accepted(team, pevent.value) | ||||||
|             else: |             else: | ||||||
|                 await self.info_rejected(team, event.value, rnd=poule.rnd) |                 await self.info_rejected(team, pevent.value, rnd=poule.rnd) | ||||||
|                 team.rejected[poule.rnd].add(event.value) |                 team.rejected[poule.rnd].add(pevent.value) | ||||||
|  |  | ||||||
|             current += 1 |             current += 1 | ||||||
|  |             current %= len(teams) | ||||||
|  |  | ||||||
|         await self.annonce_poule(poule) |         await self.annonce_poule(poule) | ||||||
|  |  | ||||||
| @@ -212,7 +226,7 @@ class BaseTirage: | |||||||
|         teams = self.poules[poule] |         teams = self.poules[poule] | ||||||
|         dices = await self.get_dices(teams) |         dices = await self.get_dices(teams) | ||||||
|  |  | ||||||
|         order = sorted(self.teams, key=lambda t: dices[t], reverse=True) |         order = sorted(teams, key=lambda t: dices[t], reverse=True) | ||||||
|  |  | ||||||
|         await self.annonce_draw_order(order) |         await self.annonce_draw_order(order) | ||||||
|         return order |         return order | ||||||
| @@ -232,6 +246,9 @@ class BaseTirage: | |||||||
|     async def start_make_poule(self, rnd): |     async def start_make_poule(self, rnd): | ||||||
|         """Called when it starts drawing the poules for round `rnd`""" |         """Called when it starts drawing the poules for round `rnd`""" | ||||||
|  |  | ||||||
|  |     async def start_draw_poule(self, poule): | ||||||
|  |         """Called when we start a poule.""" | ||||||
|  |  | ||||||
|     async def start_draw_order(self, poule): |     async def start_draw_order(self, poule): | ||||||
|         """Called when we start to draw the order.""" |         """Called when we start to draw the order.""" | ||||||
|  |  | ||||||
| @@ -253,6 +270,9 @@ class BaseTirage: | |||||||
|     async def info_finish(self): |     async def info_finish(self): | ||||||
|         """Called when the tirage has ended.""" |         """Called when the tirage has ended.""" | ||||||
|  |  | ||||||
|  |     async def info_dice(self, team, dice): | ||||||
|  |         """Called on a dice roll.""" | ||||||
|  |  | ||||||
|     async def info_draw_pb(self, team, pb, rnd): |     async def info_draw_pb(self, team, pb, rnd): | ||||||
|         """Called when a team draws a problem.""" |         """Called when a team draws a problem.""" | ||||||
|  |  | ||||||
| @@ -262,3 +282,7 @@ class BaseTirage: | |||||||
|     async def info_rejected(self, team, pb, rnd): |     async def info_rejected(self, team, pb, rnd): | ||||||
|         """Called when a team rejects a problem, |         """Called when a team rejects a problem, | ||||||
|         before it is added to the rejected set.""" |         before it is added to the rejected set.""" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def setup(_): | ||||||
|  |     pass | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ from src.constants import * | |||||||
| from src.core import CustomBot | from src.core import CustomBot | ||||||
|  |  | ||||||
| COGS_SHORTCUTS = { | COGS_SHORTCUTS = { | ||||||
|  |     "bt": "src.base_tirage", | ||||||
|     "c": "src.constants", |     "c": "src.constants", | ||||||
|     "d": "tirages", |     "d": "tirages", | ||||||
|     "e": "errors", |     "e": "errors", | ||||||
|   | |||||||
| @@ -2,6 +2,8 @@ | |||||||
|  |  | ||||||
| import asyncio | import asyncio | ||||||
| import random | import random | ||||||
|  | import sys | ||||||
|  | import traceback | ||||||
| from collections import defaultdict, namedtuple | from collections import defaultdict, namedtuple | ||||||
| from dataclasses import dataclass | from dataclasses import dataclass | ||||||
| from functools import wraps | from functools import wraps | ||||||
| @@ -15,7 +17,7 @@ from discord.ext import commands | |||||||
| from discord.ext.commands import group, Cog, Context | from discord.ext.commands import group, Cog, Context | ||||||
| from discord.utils import get | from discord.utils import get | ||||||
|  |  | ||||||
| from src.base_tirage import BaseTirage | from src.base_tirage import BaseTirage, Event | ||||||
| from src.constants import * | from src.constants import * | ||||||
| from src.core import CustomBot | from src.core import CustomBot | ||||||
| from src.errors import TfjmError, UnwantedCommand | from src.errors import TfjmError, UnwantedCommand | ||||||
| @@ -34,7 +36,7 @@ Record = namedtuple("Record", ["name", "pb", "penalite"]) | |||||||
|  |  | ||||||
| def delete_and_pm(f): | def delete_and_pm(f): | ||||||
|     @wraps(f) |     @wraps(f) | ||||||
|     def wrapper(self, *args, **kwargs): |     async def wrapper(self, *args, **kwargs): | ||||||
|         await self.ctx.message.delete() |         await self.ctx.message.delete() | ||||||
|         await self.ctx.author.send( |         await self.ctx.author.send( | ||||||
|             "J'ai supprimé ton message:\n> " |             "J'ai supprimé ton message:\n> " | ||||||
| @@ -47,6 +49,8 @@ def delete_and_pm(f): | |||||||
|         if msg: |         if msg: | ||||||
|             await self.ctx.author.send(f"Raison: {msg}") |             await self.ctx.author.send(f"Raison: {msg}") | ||||||
|  |  | ||||||
|  |     return wrapper | ||||||
|  |  | ||||||
|  |  | ||||||
| def send_all(f): | def send_all(f): | ||||||
|     @wraps(f) |     @wraps(f) | ||||||
| @@ -57,12 +61,33 @@ def send_all(f): | |||||||
|     return wrapper |     return wrapper | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def safe(f): | ||||||
|  |     @wraps(f) | ||||||
|  |     async def wrapper(*args, **kwargs): | ||||||
|  |         try: | ||||||
|  |             return await f(*args, **kwargs) | ||||||
|  |         except Exception as e: | ||||||
|  |             traceback.print_tb(e.__traceback__, file=sys.stderr) | ||||||
|  |             print(e) | ||||||
|  |  | ||||||
|  |     return wrapper | ||||||
|  |  | ||||||
|  |  | ||||||
| class DiscordTirage(BaseTirage): | class DiscordTirage(BaseTirage): | ||||||
|     def __init__(self, ctx, *teams, fmt): |     def __init__(self, ctx, *teams, fmt): | ||||||
|         super(DiscordTirage, self).__init__(*teams, fmt=fmt) |         super(DiscordTirage, self).__init__(*teams, fmt=fmt) | ||||||
|         self.ctx = ctx |         self.ctx = ctx | ||||||
|         self.captain_mention = get(ctx.guild.roles, name=Role.CAPTAIN).mention |         self.captain_mention = get(ctx.guild.roles, name=Role.CAPTAIN).mention | ||||||
|  |  | ||||||
|  |     def team_for(self, author): | ||||||
|  |         for team in self.teams: | ||||||
|  |             if get(author.roles, name=team): | ||||||
|  |                 return team | ||||||
|  |         return None | ||||||
|  |  | ||||||
|  |     def mention(self, trigram): | ||||||
|  |         return get(self.ctx.guild.roles, name=trigram).mention | ||||||
|  |  | ||||||
|     def records(self, teams, rnd): |     def records(self, teams, rnd): | ||||||
|         """Get the strings needed for show the tirage in a list of Records""" |         """Get the strings needed for show the tirage in a list of Records""" | ||||||
|  |  | ||||||
| @@ -75,6 +100,36 @@ class DiscordTirage(BaseTirage): | |||||||
|             for team in teams |             for team in teams | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |     async def dice(self, ctx, n): | ||||||
|  |         self.ctx = ctx | ||||||
|  |         trigram = self.team_for(ctx.author) | ||||||
|  |  | ||||||
|  |         if trigram is None: | ||||||
|  |             await self.warn_wrong_team(None, None) | ||||||
|  |         elif n == 100: | ||||||
|  |             await super().dice(trigram) | ||||||
|  |         else: | ||||||
|  |             await self.warn_unwanted(int, int) | ||||||
|  |  | ||||||
|  |     async def rproblem(self, ctx): | ||||||
|  |         self.ctx = ctx | ||||||
|  |         trigram = self.team_for(ctx.author) | ||||||
|  |  | ||||||
|  |         if trigram is None: | ||||||
|  |             await self.warn_wrong_team(None, None) | ||||||
|  |         else: | ||||||
|  |             await super().rproblem(trigram) | ||||||
|  |  | ||||||
|  |     async def accept(self, ctx, yes): | ||||||
|  |         self.ctx = ctx | ||||||
|  |         trigram = self.team_for(ctx.author) | ||||||
|  |  | ||||||
|  |         if trigram is None: | ||||||
|  |             await self.warn_wrong_team(None, None) | ||||||
|  |         else: | ||||||
|  |             await super().accept(trigram, yes) | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     @delete_and_pm |     @delete_and_pm | ||||||
|     async def warn_unwanted(self, wanted: Type, got: Type): |     async def warn_unwanted(self, wanted: Type, got: Type): | ||||||
|  |  | ||||||
| @@ -91,6 +146,8 @@ class DiscordTirage(BaseTirage): | |||||||
|             else "Halte là ! Ce serait bien de tirer un problème d'abord... " |             else "Halte là ! Ce serait bien de tirer un problème d'abord... " | ||||||
|             "et peut-être qu'il te plaira :) ", |             "et peut-être qu'il te plaira :) ", | ||||||
|             (bool, int): "Il tirer un dé avec `!dice 100` d'abord.", |             (bool, int): "Il tirer un dé avec `!dice 100` d'abord.", | ||||||
|  |             (int, int): "Il faut lancer un dé à 100 faces.", | ||||||
|  |             (str, str): f"'{got}' n'est pas un problème valide.", | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         reason = texts.get((type(got), wanted)) |         reason = texts.get((type(got), wanted)) | ||||||
| @@ -100,10 +157,12 @@ class DiscordTirage(BaseTirage): | |||||||
|             reason = "Je sais pas, le code ne devrait pas venir ici..." |             reason = "Je sais pas, le code ne devrait pas venir ici..." | ||||||
|         return reason |         return reason | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     @delete_and_pm |     @delete_and_pm | ||||||
|     async def warn_wrong_team(self, expected, got): |     async def warn_wrong_team(self, expected, got): | ||||||
|         return "ce n'était pas à ton tour." |         return "ce n'était pas à ton tour." | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     async def warn_colisions(self, collisions: List[str]): |     async def warn_colisions(self, collisions: List[str]): | ||||||
|         await self.ctx.send( |         await self.ctx.send( | ||||||
|             f"Les equipes {french_join(collisions)} ont fait le même résultat " |             f"Les equipes {french_join(collisions)} ont fait le même résultat " | ||||||
| @@ -111,6 +170,7 @@ class DiscordTirage(BaseTirage): | |||||||
|             "Le nouveau lancer effacera l'ancien." |             "Le nouveau lancer effacera l'ancien." | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     @delete_and_pm |     @delete_and_pm | ||||||
|     async def warn_twice(self, typ: Type): |     async def warn_twice(self, typ: Type): | ||||||
|  |  | ||||||
| @@ -120,6 +180,7 @@ class DiscordTirage(BaseTirage): | |||||||
|         print("Weird, DiscordTirage.warn_twice was called with", typ) |         print("Weird, DiscordTirage.warn_twice was called with", typ) | ||||||
|         return "Je sais pas, le code ne devrait pas venir ici..." |         return "Je sais pas, le code ne devrait pas venir ici..." | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     @send_all |     @send_all | ||||||
|     async def start_make_poule(self, rnd): |     async def start_make_poule(self, rnd): | ||||||
|         if rnd == 0: |         if rnd == 0: | ||||||
| @@ -135,50 +196,82 @@ class DiscordTirage(BaseTirage): | |||||||
|                 f"afin de déterminer les poules du second tour." |                 f"afin de déterminer les poules du second tour." | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|     async def start_draw_order(self, poule): |     @safe | ||||||
|         print(poule) |     @send_all | ||||||
|  |     async def start_draw_poule(self, poule): | ||||||
|  |         yield ( | ||||||
|  |             f"Nous allons commencer le tirage pour la poule **{poule}** entre les " | ||||||
|  |             f"équipes {french_join('**%s**' %p for p in self.poules[poule])}. Les autres équipes peuvent " | ||||||
|  |             f"quitter le salon si elles le souhaitent et revenir quand elles seront mentionnées." | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|  |     @send_all | ||||||
|  |     async def start_draw_order(self, poule): | ||||||
|  |         mentions = [self.mention(tri) for tri in self.poules[poule]] | ||||||
|  |         yield ( | ||||||
|  |             f"Les capitaines de {french_join(mentions)}, vous pouvez à nouveau lancer un dé 100, " | ||||||
|  |             f"qui déterminera l'ordre de tirage des problèmes. Le plus grand lancer tirera en premier " | ||||||
|  |             f"les problèmes." | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     async def start_select_pb(self, team): |     async def start_select_pb(self, team): | ||||||
|         await self.ctx.send(f"C'est au tour de {team.mention} de choisir un problème.") |         await self.ctx.send(f"C'est au tour de {team.mention} de choisir un problème.") | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|  |     @send_all | ||||||
|     async def annonce_poules(self, poules): |     async def annonce_poules(self, poules): | ||||||
|         print(poules) |         first = "\n".join( | ||||||
|  |             f"{p}: {french_join(t)}" for p, t in poules.items() if p.rnd == 0 | ||||||
|  |         ) | ||||||
|  |         second = "\n".join( | ||||||
|  |             f"{p}: {french_join(t)}" for p, t in poules.items() if p.rnd == 1 | ||||||
|  |         ) | ||||||
|  |         yield ( | ||||||
|  |             f"Les poules sont donc, pour le premier tour :" | ||||||
|  |             f"```{first}```\n" | ||||||
|  |             f"Et pour le second tour :" | ||||||
|  |             f"```{second}```" | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     @send_all |     @send_all | ||||||
|     async def annonce_draw_order(self, order): |     async def annonce_draw_order(self, order): | ||||||
|         order_str = "\n ".join(f"{i}) {tri}" for i, tri in enumerate(order)) |         order_str = "\n".join(f"{i+1}) {tri}" for i, tri in enumerate(order)) | ||||||
|         yield "L'ordre de tirage des problèmes pour ce tour est donc: \n" + order_str |         yield f"L'ordre de tirage des problèmes pour ce tour est donc: ```{order_str}```" | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     async def annonce_poule(self, poule): |     async def annonce_poule(self, poule): | ||||||
|         teams = [self.teams[tri] for tri in self.poules[poule]] |         teams = [self.teams[tri] for tri in self.poules[poule]] | ||||||
|  |  | ||||||
|         if len(teams) == 3: |         if len(teams) == 3: | ||||||
|             table = """``` |             table = """``` | ||||||
|             +-----+---------+---------+---------+ | +-----+---------+---------+---------+ | ||||||
|             |     | Phase 1 | Phase 2 | Phase 3 | | |     | Phase 1 | Phase 2 | Phase 3 | | ||||||
|             |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  | | |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  | | ||||||
|             +-----+---------+---------+---------+ | +-----+---------+---------+---------+ | ||||||
|             | {0.name} |   Déf   |   Rap   |   Opp   | | | {0.name} |   Déf   |   Rap   |   Opp   | | ||||||
|             +-----+---------+---------+---------+ | +-----+---------+---------+---------+ | ||||||
|             | {1.name} |   Opp   |   Déf   |   Rap   | | | {1.name} |   Opp   |   Déf   |   Rap   | | ||||||
|             +-----+---------+---------+---------+ | +-----+---------+---------+---------+ | ||||||
|             | {2.name} |   Rap   |   Opp   |   Déf   | | | {2.name} |   Rap   |   Opp   |   Déf   | | ||||||
|             +-----+---------+---------+---------+ | +-----+---------+---------+---------+ | ||||||
|         ```""" |         ```""" | ||||||
|         else: |         else: | ||||||
|             table = """``` |             table = """``` | ||||||
|             +-----+---------+---------+---------+---------+ | +-----+---------+---------+---------+---------+ | ||||||
|             |     | Phase 1 | Phase 2 | Phase 3 | Phase 4 | | |     | Phase 1 | Phase 2 | Phase 3 | Phase 4 | | ||||||
|             |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |   Pb {3.pb}  | | |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |   Pb {3.pb}  | | ||||||
|             +-----+---------+---------+---------+---------+ | +-----+---------+---------+---------+---------+ | ||||||
|             | {0.name} |   Déf   |         |   Rap   |   Opp   | | | {0.name} |   Déf   |         |   Rap   |   Opp   | | ||||||
|             +-----+---------+---------+---------+---------+ | +-----+---------+---------+---------+---------+ | ||||||
|             | {1.name} |   Opp   |   Déf   |         |   Rap   | | | {1.name} |   Opp   |   Déf   |         |   Rap   | | ||||||
|             +-----+---------+---------+---------+---------+ | +-----+---------+---------+---------+---------+ | ||||||
|             | {2.name} |   Rap   |   Opp   |   Déf   |         | | | {2.name} |   Rap   |   Opp   |   Déf   |         | | ||||||
|             +-----+---------+---------+---------+---------+ | +-----+---------+---------+---------+---------+ | ||||||
|             | {3.name} |         |   Rap   |   Opp   |   Déf   | | | {3.name} |         |   Rap   |   Opp   |   Déf   | | ||||||
|             +-----+---------+---------+---------+---------+ | +-----+---------+---------+---------+---------+ | ||||||
|         ```""" |         ```""" | ||||||
|  |  | ||||||
|         embed = discord.Embed( |         embed = discord.Embed( | ||||||
| @@ -205,6 +298,7 @@ class DiscordTirage(BaseTirage): | |||||||
|  |  | ||||||
|         await self.ctx.send(embed=embed) |         await self.ctx.send(embed=embed) | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     @send_all |     @send_all | ||||||
|     async def info_start(self): |     async def info_start(self): | ||||||
|         yield ( |         yield ( | ||||||
| @@ -225,6 +319,7 @@ class DiscordTirage(BaseTirage): | |||||||
|             "l'ordre de tirage pour le tour et les problèmes." |             "l'ordre de tirage pour le tour et les problèmes." | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     @send_all |     @send_all | ||||||
|     async def info_finish(self): |     async def info_finish(self): | ||||||
|         yield "Le tirage est fini, merci à tout le monde !" |         yield "Le tirage est fini, merci à tout le monde !" | ||||||
| @@ -235,9 +330,19 @@ class DiscordTirage(BaseTirage): | |||||||
|         # TODO: Save it |         # TODO: Save it | ||||||
|         # TODO: make them available with the api |         # TODO: make them available with the api | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|  |     @send_all | ||||||
|  |     async def info_dice(self, team, dice): | ||||||
|  |         yield f"L'équipe {team} a lancé un... {dice} :game_die:" | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|  |     @send_all | ||||||
|     async def info_draw_pb(self, team, pb, rnd): |     async def info_draw_pb(self, team, pb, rnd): | ||||||
|  |  | ||||||
|  |         yield (f"L'équipe {self.mention(team.name)} a tiré... **{pb}**") | ||||||
|  |  | ||||||
|         if pb in team.rejected[rnd]: |         if pb in team.rejected[rnd]: | ||||||
|             await self.ctx.send( |             yield ( | ||||||
|                 f"Vous avez déjà refusé **{pb}**, " |                 f"Vous avez déjà refusé **{pb}**, " | ||||||
|                 f"vous pouvez le refuser à nouveau (`!non`) et " |                 f"vous pouvez le refuser à nouveau (`!non`) et " | ||||||
|                 f"tirer immédiatement un nouveau problème " |                 f"tirer immédiatement un nouveau problème " | ||||||
| @@ -245,19 +350,20 @@ class DiscordTirage(BaseTirage): | |||||||
|             ) |             ) | ||||||
|         else: |         else: | ||||||
|             if len(team.rejected[rnd]) >= MAX_REFUSE: |             if len(team.rejected[rnd]) >= MAX_REFUSE: | ||||||
|                 await self.ctx.send( |                 yield ( | ||||||
|                     f"Vous pouvez accepter ou refuser **{pb}** " |                     f"Vous pouvez accepter ou refuser **{pb}** " | ||||||
|                     f"mais si vous choisissez de le refuser, il y " |                     f"mais si vous choisissez de le refuser, il y " | ||||||
|                     f"aura une pénalité de 0.5 sur le multiplicateur du " |                     f"aura une pénalité de 0.5 sur le multiplicateur du " | ||||||
|                     f"défenseur." |                     f"défenseur." | ||||||
|                 ) |                 ) | ||||||
|             else: |             else: | ||||||
|                 await self.ctx.send( |                 yield ( | ||||||
|                     f"Vous pouvez accepter (`!oui`) ou refuser (`!non`) **{pb}**. " |                     f"Vous pouvez l'accepter (`!oui`) ou le refuser (`!non`). " | ||||||
|                     f"Il reste {MAX_REFUSE - len(team.rejected[rnd])} refus sans pénalité " |                     f"Il reste {MAX_REFUSE - len(team.rejected[rnd])} refus sans pénalité " | ||||||
|                     f"pour {team.mention}." |                     f"pour {team.mention}." | ||||||
|                 ) |                 ) | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     async def info_accepted(self, team, pb): |     async def info_accepted(self, team, pb): | ||||||
|         await self.ctx.send( |         await self.ctx.send( | ||||||
|             f"L'équipe {team.mention} a accepté " |             f"L'équipe {team.mention} a accepté " | ||||||
| @@ -265,6 +371,7 @@ class DiscordTirage(BaseTirage): | |||||||
|             f"ne peuvent plus l'accepter." |             f"ne peuvent plus l'accepter." | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|  |     @safe | ||||||
|     async def info_rejected(self, team, pb, rnd): |     async def info_rejected(self, team, pb, rnd): | ||||||
|         msg = f"{team.mention} a refusé **{pb}** " |         msg = f"{team.mention} a refusé **{pb}** " | ||||||
|         if pb in team.rejected[rnd]: |         if pb in team.rejected[rnd]: | ||||||
| @@ -274,152 +381,7 @@ class DiscordTirage(BaseTirage): | |||||||
|         await self.ctx.send(msg) |         await self.ctx.send(msg) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Tirage(yaml.YAMLObject): | class TiragePhase: | ||||||
|     yaml_tag = "Tirage" |  | ||||||
|  |  | ||||||
|     def __init__(self, ctx, channel, teams): |  | ||||||
|         assert len(teams) in (3, 4) |  | ||||||
|  |  | ||||||
|         self.channel: int = channel |  | ||||||
|         self.teams = [Team(team) for team in teams] |  | ||||||
|         self.phase = TirageOrderPhase(self, round=0) |  | ||||||
|  |  | ||||||
|     async def update_phase(self, ctx): |  | ||||||
|         if self.phase.finished(): |  | ||||||
|             next_class = await self.phase.next(ctx) |  | ||||||
|  |  | ||||||
|             if next_class is None: |  | ||||||
|                 await self.end(ctx) |  | ||||||
|             else: |  | ||||||
|                 # Continue on the same round. |  | ||||||
|                 # If a Phase wants to change the round |  | ||||||
|                 # it needs to change its own round. |  | ||||||
|                 self.phase = next_class(self, self.phase.round) |  | ||||||
|                 await self.phase.start(ctx) |  | ||||||
|  |  | ||||||
|     async def end(self, ctx): |  | ||||||
|         self.phase = None |  | ||||||
|         if False: |  | ||||||
|             # Allow everyone to send messages again |  | ||||||
|             send = discord.PermissionOverwrite()  # reset |  | ||||||
|             await ctx.channel.edit(overwrites={ctx.guild.default_role: send}) |  | ||||||
|  |  | ||||||
|         tl = {} |  | ||||||
|         if File.TIRAGES.exists(): |  | ||||||
|             with open(File.TIRAGES) as f: |  | ||||||
|                 tl = yaml.load(f) |  | ||||||
|         else: |  | ||||||
|             File.TIRAGES.touch() |  | ||||||
|  |  | ||||||
|         key = max(0, *tl.keys()) + 1 |  | ||||||
|         tl[key] = self |  | ||||||
|         with open(File.TIRAGES, "w") as f: |  | ||||||
|             yaml.dump(tl, f) |  | ||||||
|  |  | ||||||
|         await ctx.send( |  | ||||||
|             f"A tout moment, ce rapport peut " f"être envoyé avec `!draw show {key}`" |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         from src.tfjm_discord_bot import tirages |  | ||||||
|  |  | ||||||
|         if self.channel in tirages: |  | ||||||
|             del tirages[self.channel] |  | ||||||
|  |  | ||||||
|     async def show_tex(self, ctx): |  | ||||||
|         if len(self.teams) == 3: |  | ||||||
|             table = r""" |  | ||||||
| \begin{{table}}[] |  | ||||||
| \begin{{tabular}}{{|c|c|c|c|}} |  | ||||||
| \hline |  | ||||||
|           & Phase 1 - {0.pb} & Phase 2 - {1.pb} & Phase {2.pb} \\\\ \hline |  | ||||||
|  {0.name} & Déf & Rap & Opp \\ \hline |  | ||||||
|  {1.name} & Opp & Déf & Rap \\ \hline |  | ||||||
|  {2.name} & Rap & Opp & Déf \\ \hline |  | ||||||
| \end{{tabular}} |  | ||||||
| \end{{table}} |  | ||||||
| """ |  | ||||||
|         else: |  | ||||||
|             table = r""" |  | ||||||
|             \begin{{table}}[] |  | ||||||
|             \begin{{tabular}}{{|c|c|c|c|c|}} |  | ||||||
|             \hline |  | ||||||
|                       & Phase 1 - {0.pb} & Phase 2 - {1.pb} & Phase 3 - {2.pb} & Phase 4 - {3.pb} \\\\ \hline |  | ||||||
|              {0.name} & Déf &     & Rap & Opp \\ \hline |  | ||||||
|              {1.name} & Opp & Déf &     & Rap \\ \hline |  | ||||||
|              {2.name} & Rap & Opp & Déf &     \\ \hline |  | ||||||
|              {3.name} &     & Rap & Opp & Déf \\ \hline |  | ||||||
|             \end{{tabular}} |  | ||||||
|             \end{{table}} |  | ||||||
|             """ |  | ||||||
|         msg = ",tex " |  | ||||||
|         for i in (0, 1): |  | ||||||
|             msg += rf"\section{{ {ROUND_NAMES[i].capitalize()} }}" |  | ||||||
|             msg += table.format(*self.records(i)) |  | ||||||
|         await ctx.send(msg) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Phase: |  | ||||||
|     ... |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class OrderPhase(Phase): |  | ||||||
|     def __init__(self, tirage, round, name, order_name, reverse=False): |  | ||||||
|         super().__init__(tirage, round) |  | ||||||
|         self.name = name |  | ||||||
|         self.reverse = reverse |  | ||||||
|         self.order_name = order_name |  | ||||||
|  |  | ||||||
|     def order_for(self, team): |  | ||||||
|         return getattr(team, self.order_name)[self.round] |  | ||||||
|  |  | ||||||
|     def set_order_for(self, team, order): |  | ||||||
|         getattr(team, self.order_name)[self.round] = order |  | ||||||
|  |  | ||||||
|     async def dice(self, ctx, author, dice): |  | ||||||
|         team = self.team_for(author) |  | ||||||
|  |  | ||||||
|         if self.order_for(team) is None: |  | ||||||
|             self.set_order_for(team, dice) |  | ||||||
|             await ctx.send(f"L'équipe {team.mention} a obtenu... **{dice}**") |  | ||||||
|         else: |  | ||||||
|             raise UnwantedCommand("tu as déjà lancé un dé !") |  | ||||||
|  |  | ||||||
|     def finished(self) -> bool: |  | ||||||
|         return all(self.order_for(team) is not None for team in self.teams) |  | ||||||
|  |  | ||||||
|     async def next(self, ctx) -> "Type[Phase]": |  | ||||||
|         orders = [self.order_for(team) for team in self.teams] |  | ||||||
|         if len(set(orders)) == len(orders): |  | ||||||
|             # All dice are different: good |  | ||||||
|             self.teams.sort(key=self.order_for, reverse=self.reverse) |  | ||||||
|             await ctx.send( |  | ||||||
|                 f"L'ordre {self.name} pour ce tour est donc :\n" |  | ||||||
|                 " - " |  | ||||||
|                 + "\n - ".join( |  | ||||||
|                     f"{team.mention} ({self.order_for(team)})" for team in self.teams |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
|             return self.NEXT |  | ||||||
|         else: |  | ||||||
|             # Find dice that are the same |  | ||||||
|             count = defaultdict(list) |  | ||||||
|             for team in self.teams: |  | ||||||
|                 count[self.order_for(team)].append(team) |  | ||||||
|  |  | ||||||
|             re_do = [] |  | ||||||
|             for teams in count.values(): |  | ||||||
|                 if len(teams) > 1: |  | ||||||
|                     re_do.extend(teams) |  | ||||||
|  |  | ||||||
|             teams_str = ", ".join(team.mention for team in re_do) |  | ||||||
|  |  | ||||||
|             for team in re_do: |  | ||||||
|                 self.set_order_for(team, None) |  | ||||||
|             # We need to do this phase again. |  | ||||||
|             return self.__class__ |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TiragePhase(Phase): |  | ||||||
|     """The phase where captains accept or refuse random problems.""" |     """The phase where captains accept or refuse random problems.""" | ||||||
|  |  | ||||||
|     def __init__(self, tirage, round=0): |     def __init__(self, tirage, round=0): | ||||||
| @@ -654,6 +616,15 @@ class TirageCog(Cog, name="Tirages"): | |||||||
|             dice = random.randint(1, n) |             dice = random.randint(1, n) | ||||||
|             return f"{ctx.author.mention} : {Emoji.DICE} {dice}" |             return f"{ctx.author.mention} : {Emoji.DICE} {dice}" | ||||||
|  |  | ||||||
|  |     @commands.command(name="dice-all", aliases=["da"], hidden=True) | ||||||
|  |     @commands.has_role(Role.DEV) | ||||||
|  |     async def dice_all_cmd(self, ctx, *teams): | ||||||
|  |         channel = ctx.channel.id | ||||||
|  |         if channel in self.tirages: | ||||||
|  |             for t in teams: | ||||||
|  |                 d = random.randint(1, 100) | ||||||
|  |                 await self.tirages[channel].event(Event(t, d)) | ||||||
|  |  | ||||||
|     @commands.command( |     @commands.command( | ||||||
|         name="random-problem", |         name="random-problem", | ||||||
|         aliases=["rp", "problème-aléatoire", "probleme-aleatoire", "pa"], |         aliases=["rp", "problème-aléatoire", "probleme-aleatoire", "pa"], | ||||||
| @@ -663,7 +634,7 @@ class TirageCog(Cog, name="Tirages"): | |||||||
|  |  | ||||||
|         channel = ctx.channel.id |         channel = ctx.channel.id | ||||||
|         if channel in self.tirages: |         if channel in self.tirages: | ||||||
|             await self.tirages[channel].choose_problem(ctx) |             await self.tirages[channel].rproblem(ctx) | ||||||
|         else: |         else: | ||||||
|             problem = random.choice(PROBLEMS) |             problem = random.choice(PROBLEMS) | ||||||
|             await ctx.send(f"Le problème tiré est... **{problem}**") |             await ctx.send(f"Le problème tiré est... **{problem}**") | ||||||
| @@ -712,7 +683,7 @@ class TirageCog(Cog, name="Tirages"): | |||||||
|         name="start", usage="équipe1 équipe2 équipe3 (équipe4)", |         name="start", usage="équipe1 équipe2 équipe3 (équipe4)", | ||||||
|     ) |     ) | ||||||
|     @commands.has_any_role(*Role.ORGAS) |     @commands.has_any_role(*Role.ORGAS) | ||||||
|     async def start(self, ctx: Context, *teams: discord.Role): |     async def start(self, ctx: Context, fmt, *teams: discord.Role): | ||||||
|         """ |         """ | ||||||
|         (orga) Commence un tirage avec 3 ou 4 équipes. |         (orga) Commence un tirage avec 3 ou 4 équipes. | ||||||
|  |  | ||||||
| @@ -730,28 +701,25 @@ class TirageCog(Cog, name="Tirages"): | |||||||
|                 "il est possible d'en commencer un autre sur une autre channel." |                 "il est possible d'en commencer un autre sur une autre channel." | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|         if len(teams) not in (3, 4): |         try: | ||||||
|  |             fmt = list(map(int, fmt.split("+"))) | ||||||
|  |         except ValueError: | ||||||
|             raise TfjmError( |             raise TfjmError( | ||||||
|                 "Il faut 3 ou 4 équipes pour un tirage. " |                 "Le premier argument doit être le format du tournoi, " | ||||||
|                 "Exemple: `!draw start @AAA @BBB @CCC`" |                 "par exemple `3+3` pour deux poules à trois équipes" | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|  |         if not set(fmt).issubset({3, 4}): | ||||||
|  |             raise TfjmError("Seuls les poules à 3 ou 4 équipes sont suportées.") | ||||||
|  |  | ||||||
|         # Here all data should be valid |         # Here all data should be valid | ||||||
|  |  | ||||||
|         # Prevent everyone from writing except Capitaines, Orga, CNO, Benevole |         self.tirages[channel_id] = DiscordTirage(ctx, *teams, fmt=fmt) | ||||||
|         if False: |         await self.tirages[channel_id].run() | ||||||
|             read = discord.PermissionOverwrite(send_messages=False) |  | ||||||
|             send = discord.PermissionOverwrite(send_messages=True) |  | ||||||
|             r = lambda role_name: get(ctx.guild.roles, name=role_name) |  | ||||||
|             overwrites = { |  | ||||||
|                 ctx.guild.default_role: read, |  | ||||||
|                 r(Role.CAPTAIN): send, |  | ||||||
|                 r(Role.BENEVOLE): send, |  | ||||||
|             } |  | ||||||
|             await channel.edit(overwrites=overwrites) |  | ||||||
|  |  | ||||||
|         self.tirages[channel_id] = Tirage(ctx, channel_id, teams) |         if self.tirages[channel_id]: | ||||||
|         await self.tirages[channel_id].phase.start(ctx) |             # Check if aborted in an other way | ||||||
|  |             del self.tirages[channel_id] | ||||||
|  |  | ||||||
|     @draw_group.command(name="abort") |     @draw_group.command(name="abort") | ||||||
|     @commands.has_any_role(*Role.ORGAS) |     @commands.has_any_role(*Role.ORGAS) | ||||||
| @@ -768,32 +736,32 @@ class TirageCog(Cog, name="Tirages"): | |||||||
|         if channel_id in self.tirages: |         if channel_id in self.tirages: | ||||||
|             print(self.tirages, channel_id) |             print(self.tirages, channel_id) | ||||||
|             print(self.tirages[channel_id]) |             print(self.tirages[channel_id]) | ||||||
|  |             del self.tirages[channel_id] | ||||||
|             await self.tirages[channel_id].end(ctx) |  | ||||||
|             await ctx.send("Le tirage est annulé.") |             await ctx.send("Le tirage est annulé.") | ||||||
|         else: |         else: | ||||||
|             await ctx.send("Il n'y a pas de tirage en cours.") |             await ctx.send("Il n'y a pas de tirage en cours.") | ||||||
|  |  | ||||||
|     @draw_group.command(name="skip", aliases=["s"]) |     # | ||||||
|     @commands.has_role(Role.DEV) |     # @draw_group.command(name="skip", aliases=["s"]) | ||||||
|     async def draw_skip(self, ctx, *teams: discord.Role): |     # @commands.has_role(Role.DEV) | ||||||
|         """(dev) Passe certaines phases du tirage.""" |     # async def draw_skip(self, ctx, *teams: discord.Role): | ||||||
|         channel = ctx.channel.id |     #     """(dev) Passe certaines phases du tirage.""" | ||||||
|         self.tirages[channel] = tirage = Tirage(ctx, channel, teams) |     #     channel = ctx.channel.id | ||||||
|  |     #     self.tirages[channel] = tirage = Tirage(ctx, channel, teams) | ||||||
|  |     # | ||||||
|  |     #     tirage.phase = TiragePhase(tirage, round=1) | ||||||
|  |     #     for i, team in enumerate(tirage.teams): | ||||||
|  |     #         team.tirage_order = [i + 1, i + 1] | ||||||
|  |     #         team.passage_order = [i + 1, i + 1] | ||||||
|  |     #         team.accepted_problems = [PROBLEMS[i], PROBLEMS[-i - 1]] | ||||||
|  |     #     tirage.teams[0].rejected = [{PROBLEMS[3]}, set(PROBLEMS[4:8])] | ||||||
|  |     #     tirage.teams[1].rejected = [{PROBLEMS[7]}, set()] | ||||||
|  |     # | ||||||
|  |     #     await ctx.send(f"Skipping to {tirage.phase.__class__.__name__}.") | ||||||
|  |     #     await tirage.phase.start(ctx) | ||||||
|  |     #     await tirage.update_phase(ctx) | ||||||
|  |  | ||||||
|         tirage.phase = TiragePhase(tirage, round=1) |     def get_tirages(self) -> Dict[int, BaseTirage]: | ||||||
|         for i, team in enumerate(tirage.teams): |  | ||||||
|             team.tirage_order = [i + 1, i + 1] |  | ||||||
|             team.passage_order = [i + 1, i + 1] |  | ||||||
|             team.accepted_problems = [PROBLEMS[i], PROBLEMS[-i - 1]] |  | ||||||
|         tirage.teams[0].rejected = [{PROBLEMS[3]}, set(PROBLEMS[4:8])] |  | ||||||
|         tirage.teams[1].rejected = [{PROBLEMS[7]}, set()] |  | ||||||
|  |  | ||||||
|         await ctx.send(f"Skipping to {tirage.phase.__class__.__name__}.") |  | ||||||
|         await tirage.phase.start(ctx) |  | ||||||
|         await tirage.update_phase(ctx) |  | ||||||
|  |  | ||||||
|     def get_tirages(self) -> Dict[int, Tirage]: |  | ||||||
|         if not File.TIRAGES.exists(): |         if not File.TIRAGES.exists(): | ||||||
|             return {} |             return {} | ||||||
|  |  | ||||||
| @@ -812,6 +780,7 @@ class TirageCog(Cog, name="Tirages"): | |||||||
|             `!draw show 42` - Affiche le tirage n°42 |             `!draw show 42` - Affiche le tirage n°42 | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|  |         return | ||||||
|         tirages = self.get_tirages() |         tirages = self.get_tirages() | ||||||
|  |  | ||||||
|         if not tirages: |         if not tirages: | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ from discord.ext.commands import Bot | |||||||
|  |  | ||||||
|  |  | ||||||
| def french_join(l): | def french_join(l): | ||||||
|  |     l = list(l) | ||||||
|     start = ", ".join(l[:-1]) |     start = ", ".join(l[:-1]) | ||||||
|     return f"{start} et {l[-1]}" |     return f"{start} et {l[-1]}" | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user