mirror of
				https://gitlab.com/ddorn/tfjm-discord-bot.git
				synced 2025-11-04 08:22:12 +01:00 
			
		
		
		
	🚧 new tirages working without save
This commit is contained in:
		@@ -1,5 +1,8 @@
 | 
			
		||||
import asyncio
 | 
			
		||||
import random
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
from functools import wraps
 | 
			
		||||
from pprint import pprint
 | 
			
		||||
 | 
			
		||||
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 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)}
 | 
			
		||||
 - Ordre au tirage: {self.tirage_order[round]}
 | 
			
		||||
 - Ordre de passage: {self.passage_order[round]}
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
#         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)}
 | 
			
		||||
#  - Ordre au tirage: {self.tirage_order[round]}
 | 
			
		||||
#  - Ordre de passage: {self.passage_order[round]}
 | 
			
		||||
# """
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Poule:
 | 
			
		||||
@@ -110,6 +115,9 @@ class BaseTirage:
 | 
			
		||||
        ]
 | 
			
		||||
        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):
 | 
			
		||||
        while True:
 | 
			
		||||
            event = await self.queue.get()
 | 
			
		||||
@@ -148,6 +156,7 @@ class BaseTirage:
 | 
			
		||||
                # TODO: avoid KeyError
 | 
			
		||||
                if dices[event.team] is None:
 | 
			
		||||
                    dices[event.team] = event.value
 | 
			
		||||
                    await self.info_dice(event.team, event.value)
 | 
			
		||||
                else:
 | 
			
		||||
                    await self.warn_twice(int)
 | 
			
		||||
 | 
			
		||||
@@ -173,6 +182,9 @@ class BaseTirage:
 | 
			
		||||
        return poules
 | 
			
		||||
 | 
			
		||||
    async def draw_poule(self, poule):
 | 
			
		||||
 | 
			
		||||
        await self.start_draw_poule(poule)
 | 
			
		||||
 | 
			
		||||
        # Trigrams in draw order
 | 
			
		||||
        trigrams = await self.draw_order(poule)
 | 
			
		||||
 | 
			
		||||
@@ -184,25 +196,27 @@ class BaseTirage:
 | 
			
		||||
            if team.accepted_problems[poule.rnd] is not None:
 | 
			
		||||
                # The team already accepted a problem
 | 
			
		||||
                current += 1
 | 
			
		||||
                current %= len(teams)
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            # Choose problem
 | 
			
		||||
            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
 | 
			
		||||
            # 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 = await self.next(bool, team.name)
 | 
			
		||||
            if accept:
 | 
			
		||||
                team.accepted_problems[poule.rnd] = event.value
 | 
			
		||||
                await self.info_accepted(team, event.value)
 | 
			
		||||
            if accept.value:
 | 
			
		||||
                team.accepted_problems[poule.rnd] = pevent.value
 | 
			
		||||
                await self.info_accepted(team, pevent.value)
 | 
			
		||||
            else:
 | 
			
		||||
                await self.info_rejected(team, event.value, rnd=poule.rnd)
 | 
			
		||||
                team.rejected[poule.rnd].add(event.value)
 | 
			
		||||
                await self.info_rejected(team, pevent.value, rnd=poule.rnd)
 | 
			
		||||
                team.rejected[poule.rnd].add(pevent.value)
 | 
			
		||||
 | 
			
		||||
            current += 1
 | 
			
		||||
            current %= len(teams)
 | 
			
		||||
 | 
			
		||||
        await self.annonce_poule(poule)
 | 
			
		||||
 | 
			
		||||
@@ -212,7 +226,7 @@ class BaseTirage:
 | 
			
		||||
        teams = self.poules[poule]
 | 
			
		||||
        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)
 | 
			
		||||
        return order
 | 
			
		||||
@@ -232,6 +246,9 @@ class BaseTirage:
 | 
			
		||||
    async def start_make_poule(self, 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):
 | 
			
		||||
        """Called when we start to draw the order."""
 | 
			
		||||
 | 
			
		||||
@@ -253,6 +270,9 @@ class BaseTirage:
 | 
			
		||||
    async def info_finish(self):
 | 
			
		||||
        """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):
 | 
			
		||||
        """Called when a team draws a problem."""
 | 
			
		||||
 | 
			
		||||
@@ -262,3 +282,7 @@ class BaseTirage:
 | 
			
		||||
    async def info_rejected(self, team, pb, rnd):
 | 
			
		||||
        """Called when a team rejects a problem,
 | 
			
		||||
        before it is added to the rejected set."""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def setup(_):
 | 
			
		||||
    pass
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ from src.constants import *
 | 
			
		||||
from src.core import CustomBot
 | 
			
		||||
 | 
			
		||||
COGS_SHORTCUTS = {
 | 
			
		||||
    "bt": "src.base_tirage",
 | 
			
		||||
    "c": "src.constants",
 | 
			
		||||
    "d": "tirages",
 | 
			
		||||
    "e": "errors",
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,8 @@
 | 
			
		||||
 | 
			
		||||
import asyncio
 | 
			
		||||
import random
 | 
			
		||||
import sys
 | 
			
		||||
import traceback
 | 
			
		||||
from collections import defaultdict, namedtuple
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
from functools import wraps
 | 
			
		||||
@@ -15,7 +17,7 @@ from discord.ext import commands
 | 
			
		||||
from discord.ext.commands import group, Cog, Context
 | 
			
		||||
from discord.utils import get
 | 
			
		||||
 | 
			
		||||
from src.base_tirage import BaseTirage
 | 
			
		||||
from src.base_tirage import BaseTirage, Event
 | 
			
		||||
from src.constants import *
 | 
			
		||||
from src.core import CustomBot
 | 
			
		||||
from src.errors import TfjmError, UnwantedCommand
 | 
			
		||||
@@ -34,7 +36,7 @@ Record = namedtuple("Record", ["name", "pb", "penalite"])
 | 
			
		||||
 | 
			
		||||
def delete_and_pm(f):
 | 
			
		||||
    @wraps(f)
 | 
			
		||||
    def wrapper(self, *args, **kwargs):
 | 
			
		||||
    async def wrapper(self, *args, **kwargs):
 | 
			
		||||
        await self.ctx.message.delete()
 | 
			
		||||
        await self.ctx.author.send(
 | 
			
		||||
            "J'ai supprimé ton message:\n> "
 | 
			
		||||
@@ -47,6 +49,8 @@ def delete_and_pm(f):
 | 
			
		||||
        if msg:
 | 
			
		||||
            await self.ctx.author.send(f"Raison: {msg}")
 | 
			
		||||
 | 
			
		||||
    return wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def send_all(f):
 | 
			
		||||
    @wraps(f)
 | 
			
		||||
@@ -57,12 +61,33 @@ def send_all(f):
 | 
			
		||||
    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):
 | 
			
		||||
    def __init__(self, ctx, *teams, fmt):
 | 
			
		||||
        super(DiscordTirage, self).__init__(*teams, fmt=fmt)
 | 
			
		||||
        self.ctx = ctx
 | 
			
		||||
        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):
 | 
			
		||||
        """Get the strings needed for show the tirage in a list of Records"""
 | 
			
		||||
 | 
			
		||||
@@ -75,6 +100,36 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
            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
 | 
			
		||||
    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... "
 | 
			
		||||
            "et peut-être qu'il te plaira :) ",
 | 
			
		||||
            (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))
 | 
			
		||||
@@ -100,10 +157,12 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
            reason = "Je sais pas, le code ne devrait pas venir ici..."
 | 
			
		||||
        return reason
 | 
			
		||||
 | 
			
		||||
    @safe
 | 
			
		||||
    @delete_and_pm
 | 
			
		||||
    async def warn_wrong_team(self, expected, got):
 | 
			
		||||
        return "ce n'était pas à ton tour."
 | 
			
		||||
 | 
			
		||||
    @safe
 | 
			
		||||
    async def warn_colisions(self, collisions: List[str]):
 | 
			
		||||
        await self.ctx.send(
 | 
			
		||||
            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."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @safe
 | 
			
		||||
    @delete_and_pm
 | 
			
		||||
    async def warn_twice(self, typ: Type):
 | 
			
		||||
 | 
			
		||||
@@ -120,6 +180,7 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
        print("Weird, DiscordTirage.warn_twice was called with", typ)
 | 
			
		||||
        return "Je sais pas, le code ne devrait pas venir ici..."
 | 
			
		||||
 | 
			
		||||
    @safe
 | 
			
		||||
    @send_all
 | 
			
		||||
    async def start_make_poule(self, rnd):
 | 
			
		||||
        if rnd == 0:
 | 
			
		||||
@@ -135,50 +196,82 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
                f"afin de déterminer les poules du second tour."
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    async def start_draw_order(self, poule):
 | 
			
		||||
        print(poule)
 | 
			
		||||
    @safe
 | 
			
		||||
    @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):
 | 
			
		||||
        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):
 | 
			
		||||
        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
 | 
			
		||||
    async def annonce_draw_order(self, order):
 | 
			
		||||
        order_str = "\n ".join(f"{i}) {tri}" for i, tri in enumerate(order))
 | 
			
		||||
        yield "L'ordre de tirage des problèmes pour ce tour est donc: \n" + order_str
 | 
			
		||||
        order_str = "\n".join(f"{i+1}) {tri}" for i, tri in enumerate(order))
 | 
			
		||||
        yield f"L'ordre de tirage des problèmes pour ce tour est donc: ```{order_str}```"
 | 
			
		||||
 | 
			
		||||
    @safe
 | 
			
		||||
    async def annonce_poule(self, poule):
 | 
			
		||||
        teams = [self.teams[tri] for tri in self.poules[poule]]
 | 
			
		||||
 | 
			
		||||
        if len(teams) == 3:
 | 
			
		||||
            table = """```
 | 
			
		||||
            +-----+---------+---------+---------+
 | 
			
		||||
            |     | Phase 1 | Phase 2 | Phase 3 |
 | 
			
		||||
            |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |
 | 
			
		||||
            +-----+---------+---------+---------+
 | 
			
		||||
            | {0.name} |   Déf   |   Rap   |   Opp   |
 | 
			
		||||
            +-----+---------+---------+---------+
 | 
			
		||||
            | {1.name} |   Opp   |   Déf   |   Rap   |
 | 
			
		||||
            +-----+---------+---------+---------+
 | 
			
		||||
            | {2.name} |   Rap   |   Opp   |   Déf   |
 | 
			
		||||
            +-----+---------+---------+---------+
 | 
			
		||||
+-----+---------+---------+---------+
 | 
			
		||||
|     | Phase 1 | Phase 2 | Phase 3 |
 | 
			
		||||
|     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |
 | 
			
		||||
+-----+---------+---------+---------+
 | 
			
		||||
| {0.name} |   Déf   |   Rap   |   Opp   |
 | 
			
		||||
+-----+---------+---------+---------+
 | 
			
		||||
| {1.name} |   Opp   |   Déf   |   Rap   |
 | 
			
		||||
+-----+---------+---------+---------+
 | 
			
		||||
| {2.name} |   Rap   |   Opp   |   Déf   |
 | 
			
		||||
+-----+---------+---------+---------+
 | 
			
		||||
        ```"""
 | 
			
		||||
        else:
 | 
			
		||||
            table = """```
 | 
			
		||||
            +-----+---------+---------+---------+---------+
 | 
			
		||||
            |     | Phase 1 | Phase 2 | Phase 3 | Phase 4 |
 | 
			
		||||
            |     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |   Pb {3.pb}  |
 | 
			
		||||
            +-----+---------+---------+---------+---------+
 | 
			
		||||
            | {0.name} |   Déf   |         |   Rap   |   Opp   |
 | 
			
		||||
            +-----+---------+---------+---------+---------+
 | 
			
		||||
            | {1.name} |   Opp   |   Déf   |         |   Rap   |
 | 
			
		||||
            +-----+---------+---------+---------+---------+
 | 
			
		||||
            | {2.name} |   Rap   |   Opp   |   Déf   |         |
 | 
			
		||||
            +-----+---------+---------+---------+---------+
 | 
			
		||||
            | {3.name} |         |   Rap   |   Opp   |   Déf   |
 | 
			
		||||
            +-----+---------+---------+---------+---------+
 | 
			
		||||
+-----+---------+---------+---------+---------+
 | 
			
		||||
|     | Phase 1 | Phase 2 | Phase 3 | Phase 4 |
 | 
			
		||||
|     |   Pb {0.pb}  |   Pb {1.pb}  |   Pb {2.pb}  |   Pb {3.pb}  |
 | 
			
		||||
+-----+---------+---------+---------+---------+
 | 
			
		||||
| {0.name} |   Déf   |         |   Rap   |   Opp   |
 | 
			
		||||
+-----+---------+---------+---------+---------+
 | 
			
		||||
| {1.name} |   Opp   |   Déf   |         |   Rap   |
 | 
			
		||||
+-----+---------+---------+---------+---------+
 | 
			
		||||
| {2.name} |   Rap   |   Opp   |   Déf   |         |
 | 
			
		||||
+-----+---------+---------+---------+---------+
 | 
			
		||||
| {3.name} |         |   Rap   |   Opp   |   Déf   |
 | 
			
		||||
+-----+---------+---------+---------+---------+
 | 
			
		||||
        ```"""
 | 
			
		||||
 | 
			
		||||
        embed = discord.Embed(
 | 
			
		||||
@@ -205,6 +298,7 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
 | 
			
		||||
        await self.ctx.send(embed=embed)
 | 
			
		||||
 | 
			
		||||
    @safe
 | 
			
		||||
    @send_all
 | 
			
		||||
    async def info_start(self):
 | 
			
		||||
        yield (
 | 
			
		||||
@@ -225,6 +319,7 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
            "l'ordre de tirage pour le tour et les problèmes."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @safe
 | 
			
		||||
    @send_all
 | 
			
		||||
    async def info_finish(self):
 | 
			
		||||
        yield "Le tirage est fini, merci à tout le monde !"
 | 
			
		||||
@@ -235,9 +330,19 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
        # TODO: Save it
 | 
			
		||||
        # 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):
 | 
			
		||||
 | 
			
		||||
        yield (f"L'équipe {self.mention(team.name)} a tiré... **{pb}**")
 | 
			
		||||
 | 
			
		||||
        if pb in team.rejected[rnd]:
 | 
			
		||||
            await self.ctx.send(
 | 
			
		||||
            yield (
 | 
			
		||||
                f"Vous avez déjà refusé **{pb}**, "
 | 
			
		||||
                f"vous pouvez le refuser à nouveau (`!non`) et "
 | 
			
		||||
                f"tirer immédiatement un nouveau problème "
 | 
			
		||||
@@ -245,19 +350,20 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            if len(team.rejected[rnd]) >= MAX_REFUSE:
 | 
			
		||||
                await self.ctx.send(
 | 
			
		||||
                yield (
 | 
			
		||||
                    f"Vous pouvez accepter ou refuser **{pb}** "
 | 
			
		||||
                    f"mais si vous choisissez de le refuser, il y "
 | 
			
		||||
                    f"aura une pénalité de 0.5 sur le multiplicateur du "
 | 
			
		||||
                    f"défenseur."
 | 
			
		||||
                )
 | 
			
		||||
            else:
 | 
			
		||||
                await self.ctx.send(
 | 
			
		||||
                    f"Vous pouvez accepter (`!oui`) ou refuser (`!non`) **{pb}**. "
 | 
			
		||||
                yield (
 | 
			
		||||
                    f"Vous pouvez l'accepter (`!oui`) ou le refuser (`!non`). "
 | 
			
		||||
                    f"Il reste {MAX_REFUSE - len(team.rejected[rnd])} refus sans pénalité "
 | 
			
		||||
                    f"pour {team.mention}."
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
    @safe
 | 
			
		||||
    async def info_accepted(self, team, pb):
 | 
			
		||||
        await self.ctx.send(
 | 
			
		||||
            f"L'équipe {team.mention} a accepté "
 | 
			
		||||
@@ -265,6 +371,7 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
            f"ne peuvent plus l'accepter."
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @safe
 | 
			
		||||
    async def info_rejected(self, team, pb, rnd):
 | 
			
		||||
        msg = f"{team.mention} a refusé **{pb}** "
 | 
			
		||||
        if pb in team.rejected[rnd]:
 | 
			
		||||
@@ -274,152 +381,7 @@ class DiscordTirage(BaseTirage):
 | 
			
		||||
        await self.ctx.send(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Tirage(yaml.YAMLObject):
 | 
			
		||||
    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):
 | 
			
		||||
class TiragePhase:
 | 
			
		||||
    """The phase where captains accept or refuse random problems."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, tirage, round=0):
 | 
			
		||||
@@ -654,6 +616,15 @@ class TirageCog(Cog, name="Tirages"):
 | 
			
		||||
            dice = random.randint(1, n)
 | 
			
		||||
            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(
 | 
			
		||||
        name="random-problem",
 | 
			
		||||
        aliases=["rp", "problème-aléatoire", "probleme-aleatoire", "pa"],
 | 
			
		||||
@@ -663,7 +634,7 @@ class TirageCog(Cog, name="Tirages"):
 | 
			
		||||
 | 
			
		||||
        channel = ctx.channel.id
 | 
			
		||||
        if channel in self.tirages:
 | 
			
		||||
            await self.tirages[channel].choose_problem(ctx)
 | 
			
		||||
            await self.tirages[channel].rproblem(ctx)
 | 
			
		||||
        else:
 | 
			
		||||
            problem = random.choice(PROBLEMS)
 | 
			
		||||
            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)",
 | 
			
		||||
    )
 | 
			
		||||
    @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.
 | 
			
		||||
 | 
			
		||||
@@ -730,28 +701,25 @@ class TirageCog(Cog, name="Tirages"):
 | 
			
		||||
                "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(
 | 
			
		||||
                "Il faut 3 ou 4 équipes pour un tirage. "
 | 
			
		||||
                "Exemple: `!draw start @AAA @BBB @CCC`"
 | 
			
		||||
                "Le premier argument doit être le format du tournoi, "
 | 
			
		||||
                "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
 | 
			
		||||
 | 
			
		||||
        # Prevent everyone from writing except Capitaines, Orga, CNO, Benevole
 | 
			
		||||
        if False:
 | 
			
		||||
            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] = DiscordTirage(ctx, *teams, fmt=fmt)
 | 
			
		||||
        await self.tirages[channel_id].run()
 | 
			
		||||
 | 
			
		||||
        self.tirages[channel_id] = Tirage(ctx, channel_id, teams)
 | 
			
		||||
        await self.tirages[channel_id].phase.start(ctx)
 | 
			
		||||
        if self.tirages[channel_id]:
 | 
			
		||||
            # Check if aborted in an other way
 | 
			
		||||
            del self.tirages[channel_id]
 | 
			
		||||
 | 
			
		||||
    @draw_group.command(name="abort")
 | 
			
		||||
    @commands.has_any_role(*Role.ORGAS)
 | 
			
		||||
@@ -768,32 +736,32 @@ class TirageCog(Cog, name="Tirages"):
 | 
			
		||||
        if channel_id in self.tirages:
 | 
			
		||||
            print(self.tirages, channel_id)
 | 
			
		||||
            print(self.tirages[channel_id])
 | 
			
		||||
 | 
			
		||||
            await self.tirages[channel_id].end(ctx)
 | 
			
		||||
            del self.tirages[channel_id]
 | 
			
		||||
            await ctx.send("Le tirage est annulé.")
 | 
			
		||||
        else:
 | 
			
		||||
            await ctx.send("Il n'y a pas de tirage en cours.")
 | 
			
		||||
 | 
			
		||||
    @draw_group.command(name="skip", aliases=["s"])
 | 
			
		||||
    @commands.has_role(Role.DEV)
 | 
			
		||||
    async def draw_skip(self, ctx, *teams: discord.Role):
 | 
			
		||||
        """(dev) Passe certaines phases du tirage."""
 | 
			
		||||
        channel = ctx.channel.id
 | 
			
		||||
        self.tirages[channel] = tirage = Tirage(ctx, channel, teams)
 | 
			
		||||
    #
 | 
			
		||||
    # @draw_group.command(name="skip", aliases=["s"])
 | 
			
		||||
    # @commands.has_role(Role.DEV)
 | 
			
		||||
    # async def draw_skip(self, ctx, *teams: discord.Role):
 | 
			
		||||
    #     """(dev) Passe certaines phases du tirage."""
 | 
			
		||||
    #     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)
 | 
			
		||||
        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]:
 | 
			
		||||
    def get_tirages(self) -> Dict[int, BaseTirage]:
 | 
			
		||||
        if not File.TIRAGES.exists():
 | 
			
		||||
            return {}
 | 
			
		||||
 | 
			
		||||
@@ -812,6 +780,7 @@ class TirageCog(Cog, name="Tirages"):
 | 
			
		||||
            `!draw show 42` - Affiche le tirage n°42
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        return
 | 
			
		||||
        tirages = self.get_tirages()
 | 
			
		||||
 | 
			
		||||
        if not tirages:
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ from discord.ext.commands import Bot
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def french_join(l):
 | 
			
		||||
    l = list(l)
 | 
			
		||||
    start = ", ".join(l[:-1])
 | 
			
		||||
    return f"{start} et {l[-1]}"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user