2020-04-25 14:42:06 +00:00
|
|
|
#!/bin/python
|
|
|
|
import os
|
2020-04-25 23:50:06 +00:00
|
|
|
import random
|
2020-04-25 14:42:06 +00:00
|
|
|
import sys
|
2020-04-25 18:19:45 +00:00
|
|
|
import traceback
|
2020-04-25 18:26:56 +00:00
|
|
|
from collections import defaultdict
|
2020-04-25 18:41:43 +00:00
|
|
|
from operator import attrgetter
|
2020-04-25 18:19:45 +00:00
|
|
|
from time import sleep
|
2020-04-26 09:12:20 +00:00
|
|
|
from typing import Dict, Type
|
2020-04-25 14:42:06 +00:00
|
|
|
|
|
|
|
import discord
|
|
|
|
from discord.ext import commands
|
|
|
|
from discord.ext.commands import Context
|
2020-04-25 16:58:35 +00:00
|
|
|
from discord.utils import get
|
2020-04-25 14:42:06 +00:00
|
|
|
|
|
|
|
TOKEN = os.environ.get("TFJM_DISCORD_TOKEN")
|
|
|
|
|
|
|
|
if TOKEN is None:
|
|
|
|
print("No token for the bot were found.")
|
|
|
|
print("You need to set the TFJM_DISCORD_TOKEN variable in your environement")
|
|
|
|
print("Or just run:\n")
|
|
|
|
print(f' TFJM_DISCORD_TOKEN="your token here" python tfjm-discord-bot.py')
|
|
|
|
print()
|
|
|
|
quit(1)
|
|
|
|
|
|
|
|
GUILD = "690934836696973404"
|
2020-04-25 16:58:35 +00:00
|
|
|
ORGA_ROLE = "Orga"
|
2020-04-26 10:04:39 +00:00
|
|
|
CNO_ROLE = "CNO"
|
|
|
|
BENEVOLE_ROLE = "Bénévole"
|
2020-04-25 16:58:35 +00:00
|
|
|
CAPTAIN_ROLE = "Capitaine"
|
|
|
|
|
2020-04-25 21:19:29 +00:00
|
|
|
with open("problems") as f:
|
|
|
|
PROBLEMS = f.read().splitlines()
|
2020-04-25 22:58:06 +00:00
|
|
|
MAX_REFUSE = len(PROBLEMS) - 5
|
2020-04-25 18:19:45 +00:00
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
ROUND_NAMES = ["premier tour", "deuxième tour"]
|
|
|
|
|
2020-04-25 16:58:35 +00:00
|
|
|
|
|
|
|
class TfjmError(Exception):
|
|
|
|
def __init__(self, msg):
|
|
|
|
self.msg = msg
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return self.msg
|
|
|
|
|
|
|
|
|
|
|
|
class Team:
|
2020-04-25 18:19:45 +00:00
|
|
|
def __init__(self, ctx, name):
|
2020-04-25 16:58:35 +00:00
|
|
|
self.name = name
|
2020-04-25 18:19:45 +00:00
|
|
|
self.role = get(ctx.guild.roles, name=name)
|
2020-04-26 09:12:20 +00:00
|
|
|
self.tirage_order = [None, None]
|
|
|
|
self.passage_order = [None, None]
|
2020-04-25 16:58:35 +00:00
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
self.accepted_problems = [None, None]
|
2020-04-25 22:58:06 +00:00
|
|
|
self.drawn_problem = None # Waiting to be accepted or refused
|
2020-04-26 09:12:20 +00:00
|
|
|
self.rejected = [set(), set()]
|
2020-04-25 22:58:06 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def mention(self):
|
|
|
|
return self.role.mention
|
2020-04-25 21:19:29 +00:00
|
|
|
|
2020-04-25 16:58:35 +00:00
|
|
|
|
|
|
|
class Tirage:
|
2020-04-25 18:19:45 +00:00
|
|
|
def __init__(self, ctx, channel, teams):
|
2020-04-25 16:58:35 +00:00
|
|
|
assert len(teams) in (3, 4)
|
|
|
|
|
2020-04-25 18:19:45 +00:00
|
|
|
self.channel = channel
|
2020-04-25 18:32:14 +00:00
|
|
|
self.teams = [Team(ctx, team) for team in teams]
|
2020-04-26 09:12:20 +00:00
|
|
|
self.phase = TirageOrderPhase(self, round=0)
|
2020-04-25 18:19:45 +00:00
|
|
|
|
|
|
|
def team_for(self, author):
|
|
|
|
for team in self.teams:
|
2020-04-25 18:32:14 +00:00
|
|
|
if get(author.roles, name=team.name):
|
|
|
|
return team
|
2020-04-25 18:19:45 +00:00
|
|
|
|
|
|
|
# Should theoretically not happen
|
|
|
|
raise TfjmError(
|
|
|
|
"Tu n'es pas dans une des équipes qui font le tirage, "
|
|
|
|
"merci de ne pas intervenir."
|
|
|
|
)
|
|
|
|
|
|
|
|
async def dice(self, ctx, author, dice):
|
|
|
|
await self.phase.dice(ctx, author, dice)
|
|
|
|
await self.update_phase(ctx)
|
|
|
|
|
2020-04-25 22:58:06 +00:00
|
|
|
async def choose_problem(self, ctx, author, problem):
|
|
|
|
await self.phase.choose_problem(ctx, author, problem)
|
|
|
|
await self.update_phase(ctx)
|
|
|
|
|
|
|
|
async def accept(self, ctx, author, yes):
|
|
|
|
await self.phase.accept(ctx, author, yes)
|
|
|
|
await self.update_phase(ctx)
|
|
|
|
|
2020-04-25 18:19:45 +00:00
|
|
|
async def update_phase(self, ctx):
|
|
|
|
if self.phase.finished():
|
2020-04-26 09:12:20 +00:00
|
|
|
next_class = await self.phase.next(ctx)
|
2020-04-25 20:27:00 +00:00
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
if next_class is None:
|
2020-04-26 10:04:39 +00:00
|
|
|
await self.end(ctx)
|
2020-04-25 20:27:00 +00:00
|
|
|
else:
|
2020-04-26 09:12:20 +00:00
|
|
|
# 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)
|
2020-04-25 20:27:00 +00:00
|
|
|
await self.phase.start(ctx)
|
2020-04-25 18:19:45 +00:00
|
|
|
|
2020-04-26 10:04:39 +00:00
|
|
|
async def end(self, ctx):
|
|
|
|
await ctx.send("Le tirage est fini ! Bonne chance à tous pour la suite !")
|
|
|
|
del tirages[self.channel]
|
|
|
|
|
|
|
|
# Allow everyone to send messages again
|
|
|
|
send = discord.PermissionOverwrite(send_messages=None) # reset
|
|
|
|
await ctx.channel.edit(overwrites={ctx.guidl.default_role: send})
|
|
|
|
|
2020-04-25 18:19:45 +00:00
|
|
|
|
|
|
|
class Phase:
|
|
|
|
NEXT = None
|
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
def __init__(self, tirage, round=0):
|
|
|
|
"""
|
|
|
|
A Phase of the tirage.
|
|
|
|
|
|
|
|
:param tirage: Backreference to the tirage
|
|
|
|
:param round: round number, 0 for the first round and 1 for the second
|
|
|
|
"""
|
|
|
|
|
|
|
|
assert round in (0, 1)
|
|
|
|
self.round = round
|
2020-04-25 18:32:14 +00:00
|
|
|
self.tirage: Tirage = tirage
|
2020-04-25 18:19:45 +00:00
|
|
|
|
|
|
|
async def fais_pas_chier(self, ctx):
|
|
|
|
await ctx.send(
|
|
|
|
"Merci d'envoyer seulement les commandes nécessaires et suffisantes."
|
|
|
|
)
|
|
|
|
|
|
|
|
def team_for(self, author):
|
|
|
|
return self.tirage.team_for(author)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def teams(self):
|
|
|
|
return self.tirage.teams
|
|
|
|
|
2020-04-25 21:19:29 +00:00
|
|
|
@teams.setter
|
|
|
|
def teams(self, teams):
|
|
|
|
self.tirage.teams = teams
|
|
|
|
|
|
|
|
def captain_mention(self, ctx):
|
|
|
|
return get(ctx.guild.roles, name=CAPTAIN_ROLE).mention
|
|
|
|
|
2020-04-25 18:19:45 +00:00
|
|
|
async def dice(self, ctx: Context, author, dice):
|
|
|
|
await self.fais_pas_chier(ctx)
|
|
|
|
|
|
|
|
async def choose_problem(self, ctx: Context, author, problem):
|
|
|
|
await self.fais_pas_chier(ctx)
|
|
|
|
|
|
|
|
async def accept(self, ctx: Context, author, yes):
|
|
|
|
await self.fais_pas_chier(ctx)
|
|
|
|
|
|
|
|
def finished(self) -> bool:
|
|
|
|
return NotImplemented
|
|
|
|
|
2020-04-25 20:27:00 +00:00
|
|
|
async def start(self, ctx):
|
|
|
|
pass
|
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
async def next(self, ctx: Context) -> "Type[Phase]":
|
|
|
|
return self.NEXT
|
2020-04-25 18:19:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
class OrderPhase(Phase):
|
2020-04-26 09:12:20 +00:00
|
|
|
def __init__(self, tirage, round, name, order_name, reverse=False):
|
|
|
|
super().__init__(tirage, round)
|
2020-04-25 20:27:00 +00:00
|
|
|
self.name = name
|
2020-04-25 21:19:29 +00:00
|
|
|
self.reverse = reverse
|
2020-04-25 20:27:00 +00:00
|
|
|
self.order_name = order_name
|
|
|
|
|
|
|
|
def order_for(self, team):
|
2020-04-26 09:12:20 +00:00
|
|
|
return getattr(team, self.order_name)[self.round]
|
2020-04-25 20:27:00 +00:00
|
|
|
|
|
|
|
def set_order_for(self, team, order):
|
2020-04-26 09:12:20 +00:00
|
|
|
getattr(team, self.order_name)[self.round] = order
|
2020-04-25 20:27:00 +00:00
|
|
|
|
2020-04-25 18:19:45 +00:00
|
|
|
async def dice(self, ctx, author, dice):
|
|
|
|
team = self.team_for(author)
|
|
|
|
|
2020-04-25 20:27:00 +00:00
|
|
|
if self.order_for(team) is None:
|
|
|
|
self.set_order_for(team, dice)
|
2020-04-25 18:19:45 +00:00
|
|
|
print(f"Team {team.name} has rolled {dice}")
|
|
|
|
else:
|
|
|
|
await ctx.send(f"{author.mention}: merci de ne lancer qu'un dé.")
|
|
|
|
|
|
|
|
def finished(self) -> bool:
|
2020-04-25 20:27:00 +00:00
|
|
|
return all(self.order_for(team) is not None for team in self.teams)
|
2020-04-25 18:19:45 +00:00
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
async def next(self, ctx) -> "Type[Phase]":
|
2020-04-25 20:27:00 +00:00
|
|
|
orders = [self.order_for(team) for team in self.teams]
|
2020-04-25 18:19:45 +00:00
|
|
|
if len(set(orders)) == len(orders):
|
2020-04-25 18:26:56 +00:00
|
|
|
# All dice are different: good
|
2020-04-25 21:19:29 +00:00
|
|
|
self.teams.sort(key=self.order_for, reverse=self.reverse)
|
2020-04-25 18:41:43 +00:00
|
|
|
await ctx.send(
|
2020-04-26 09:12:20 +00:00
|
|
|
f"L'ordre {self.name} pour ce 9 est donc :\n"
|
2020-04-25 18:41:43 +00:00
|
|
|
" - "
|
|
|
|
+ "\n - ".join(
|
2020-04-25 20:27:00 +00:00
|
|
|
f"{team.role.mention} ({self.order_for(team)})"
|
|
|
|
for team in self.teams
|
2020-04-25 18:41:43 +00:00
|
|
|
)
|
|
|
|
)
|
2020-04-26 09:12:20 +00:00
|
|
|
return self.NEXT
|
2020-04-25 18:19:45 +00:00
|
|
|
else:
|
2020-04-25 18:26:56 +00:00
|
|
|
# Find dice that are the same
|
|
|
|
count = defaultdict(list)
|
2020-04-25 18:32:14 +00:00
|
|
|
for team in self.teams:
|
2020-04-25 20:27:00 +00:00
|
|
|
count[self.order_for(team)].append(team)
|
2020-04-25 18:26:56 +00:00
|
|
|
|
|
|
|
re_do = []
|
2020-04-25 20:27:00 +00:00
|
|
|
for teams in count.values():
|
2020-04-25 18:26:56 +00:00
|
|
|
if len(teams) > 1:
|
|
|
|
re_do.extend(teams)
|
|
|
|
|
|
|
|
teams_str = ", ".join(team.role.mention for team in re_do)
|
|
|
|
await ctx.send(
|
|
|
|
f"Les equipes {teams_str} ont fait le même résultat "
|
|
|
|
"et doivent relancer un dé. "
|
|
|
|
"Le nouveau lancer effacera l'ancien."
|
|
|
|
)
|
|
|
|
for team in re_do:
|
2020-04-25 20:27:00 +00:00
|
|
|
self.set_order_for(team, None)
|
2020-04-26 09:12:20 +00:00
|
|
|
# We need to do this phase again.
|
|
|
|
return self.__class__
|
|
|
|
|
|
|
|
|
|
|
|
class ShowPhase(Phase):
|
|
|
|
"""Phase where the bot resumes all that happened."""
|
|
|
|
|
|
|
|
NEXT = None
|
2020-04-25 16:58:35 +00:00
|
|
|
|
2020-04-25 14:42:06 +00:00
|
|
|
|
2020-04-25 21:19:29 +00:00
|
|
|
class TiragePhase(Phase):
|
|
|
|
"""The phase where captains accept or refuse random problems."""
|
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
NEXT = ShowPhase
|
|
|
|
|
|
|
|
def __init__(self, tirage, round=0):
|
|
|
|
"""
|
|
|
|
The main phase of the Tirage.
|
|
|
|
:param tirage: Backreference to the tirage
|
|
|
|
:param round: round number, 0 for the first round and 1 for the second
|
|
|
|
"""
|
|
|
|
|
|
|
|
super().__init__(tirage, round)
|
2020-04-25 22:58:06 +00:00
|
|
|
self.turn = 0
|
|
|
|
|
|
|
|
@property
|
|
|
|
def current_team(self):
|
|
|
|
return self.teams[self.turn]
|
|
|
|
|
|
|
|
def available(self, problem):
|
2020-04-26 09:12:20 +00:00
|
|
|
return all(team.accepted_problems[self.round] != problem for team in self.teams)
|
2020-04-25 22:58:06 +00:00
|
|
|
|
2020-04-25 21:19:29 +00:00
|
|
|
async def choose_problem(self, ctx: Context, author, problem):
|
2020-04-25 22:58:06 +00:00
|
|
|
team = self.current_team
|
|
|
|
if self.team_for(author) != team:
|
|
|
|
await ctx.send(
|
|
|
|
f"{author.mention}: c'est à {team.mention} "
|
|
|
|
f"de choisir un problème, merci d'attendre :)"
|
|
|
|
)
|
|
|
|
return
|
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
assert (
|
|
|
|
team.accepted_problems[self.round] is None
|
|
|
|
), "Choosing pb for a team that has a pb..."
|
2020-04-25 22:58:06 +00:00
|
|
|
|
|
|
|
if team.drawn_problem:
|
|
|
|
await ctx.send(
|
2020-04-25 23:50:06 +00:00
|
|
|
"Vous avez déjà tiré un problème, merci de l'accepter (`!yes`) "
|
2020-04-25 22:58:06 +00:00
|
|
|
"ou de le refuser (`!no)`."
|
|
|
|
)
|
|
|
|
elif not self.available(problem):
|
|
|
|
await ctx.send(
|
|
|
|
f"Malheureusement, **{problem}** à déjà été choisi, "
|
|
|
|
f"vous pouvez tirer un nouveau problème."
|
|
|
|
)
|
2020-04-26 09:12:20 +00:00
|
|
|
elif problem in team.accepted_problems:
|
|
|
|
await ctx.send(
|
|
|
|
f"{team.mention} à tiré **{problem}** mais "
|
|
|
|
f"l'a déjà présenté au premier tour. "
|
|
|
|
f"Vous pouvez directement piocher un autre problème (`!rp`)."
|
|
|
|
)
|
|
|
|
elif problem in team.rejected[self.round]:
|
2020-04-25 22:58:06 +00:00
|
|
|
team.drawn_problem = problem
|
|
|
|
await ctx.send(
|
|
|
|
f"Vous avez déjà refusé **{problem}**, "
|
|
|
|
f"vous pouvez le refuser à nouveau (`!refuse`) et "
|
|
|
|
f"tirer immédiatement un nouveau problème "
|
|
|
|
f"ou changer d'avis et l'accepter (`!accept`)."
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
team.drawn_problem = problem
|
2020-04-26 09:12:20 +00:00
|
|
|
if len(team.rejected[self.round]) >= MAX_REFUSE:
|
2020-04-25 22:58:06 +00:00
|
|
|
await ctx.send(
|
|
|
|
f"Vous pouvez accepter ou refuser **{problem}** "
|
|
|
|
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 ctx.send(
|
|
|
|
f"Vous pouvez accepter (`!oui`) ou refuser (`!non`) **{problem}**. "
|
2020-04-26 09:12:20 +00:00
|
|
|
f"Il reste {MAX_REFUSE - len(team.rejected[self.round])} refus sans pénalité "
|
2020-04-25 22:58:06 +00:00
|
|
|
f"pour {team.mention}."
|
|
|
|
)
|
2020-04-25 21:19:29 +00:00
|
|
|
|
|
|
|
async def accept(self, ctx: Context, author, yes):
|
2020-04-25 22:58:06 +00:00
|
|
|
team = self.current_team
|
|
|
|
|
|
|
|
if self.team_for(author) != team:
|
|
|
|
await ctx.send(
|
|
|
|
f"{author.mention}: c'est à {team.mention} "
|
|
|
|
f"de choisir un problème, merci d'attendre :)"
|
|
|
|
)
|
|
|
|
return
|
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
assert (
|
|
|
|
team.accepted_problems[self.round] is None
|
|
|
|
), "Choosing pb for a team that has a pb..."
|
2020-04-25 22:58:06 +00:00
|
|
|
|
|
|
|
if not team.drawn_problem:
|
|
|
|
if yes:
|
|
|
|
await ctx.send(
|
|
|
|
"Vous êtes bien optimistes pour vouloir accepter un problème "
|
|
|
|
"avant de l'avoir tiré !"
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
await ctx.send(
|
|
|
|
"Halte là ! Ce serait bien de tirer un problème d'abord... "
|
|
|
|
"et peut-être qu'il vous plaira :) "
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
if yes:
|
2020-04-26 09:12:20 +00:00
|
|
|
team.accepted_problems[self.round] = team.drawn_problem
|
2020-04-25 22:58:06 +00:00
|
|
|
await ctx.send(
|
|
|
|
f"L'équipe {team.mention} a accepté "
|
2020-04-26 09:12:20 +00:00
|
|
|
f"**{team.accepted_problems[self.round]}** ! Les autres équipes "
|
2020-04-25 22:58:06 +00:00
|
|
|
f"ne peuvent plus l'accepter."
|
|
|
|
)
|
|
|
|
else:
|
2020-04-26 09:12:20 +00:00
|
|
|
if team.drawn_problem in team.rejected[self.round]:
|
2020-04-25 22:58:06 +00:00
|
|
|
await ctx.send(
|
|
|
|
f"{team.mention} a refusé **{team.drawn_problem}** "
|
|
|
|
f"sans pénalité."
|
|
|
|
)
|
|
|
|
else:
|
2020-04-26 09:12:20 +00:00
|
|
|
team.rejected[self.round].add(team.drawn_problem)
|
2020-04-25 22:58:06 +00:00
|
|
|
await ctx.send(f"{team.mention} a refusé **{team.drawn_problem}**!")
|
|
|
|
|
|
|
|
team.drawn_problem = None
|
|
|
|
|
|
|
|
# Next turn
|
|
|
|
if self.finished():
|
|
|
|
self.turn = None
|
|
|
|
return
|
|
|
|
|
|
|
|
# Find next team that needs to draw.
|
|
|
|
i = (self.turn + 1) % len(self.teams)
|
2020-04-26 09:12:20 +00:00
|
|
|
while self.teams[i].accepted_problems[self.round]:
|
2020-04-25 22:58:06 +00:00
|
|
|
i = (i + 1) % len(self.teams)
|
|
|
|
self.turn = i
|
|
|
|
|
|
|
|
await ctx.send(
|
|
|
|
f"C'est au tour de {self.current_team.mention} de choisir un problème."
|
|
|
|
)
|
2020-04-25 21:19:29 +00:00
|
|
|
|
|
|
|
def finished(self) -> bool:
|
2020-04-26 09:12:20 +00:00
|
|
|
return all(team.accepted_problems[self.round] for team in self.teams)
|
2020-04-25 21:19:29 +00:00
|
|
|
|
|
|
|
async def start(self, ctx: Context):
|
|
|
|
# First sort teams according to the tirage_order
|
2020-04-26 09:12:20 +00:00
|
|
|
self.teams.sort(key=lambda team: team.tirage_order[self.round])
|
2020-04-25 21:19:29 +00:00
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
if self.round == 0:
|
2020-04-25 21:19:29 +00:00
|
|
|
sleep(0.5)
|
|
|
|
await ctx.send("Passons au tirage des problèmes !")
|
|
|
|
sleep(0.5)
|
|
|
|
await ctx.send(
|
2020-04-25 22:58:06 +00:00
|
|
|
f"Les {self.captain_mention(ctx)}s vont tirer des problèmes au "
|
2020-04-25 21:19:29 +00:00
|
|
|
f"hasard, avec `!random-problem` ou `!rp` pour ceux qui aiment "
|
|
|
|
f"les abbréviations."
|
|
|
|
)
|
|
|
|
sleep(0.5)
|
|
|
|
await ctx.send(
|
|
|
|
"Ils pouront ensuite accepter ou refuser les problèmes avec "
|
|
|
|
"`!accept` ou `!refuse`."
|
|
|
|
)
|
|
|
|
sleep(0.5)
|
|
|
|
await ctx.send(
|
2020-04-25 22:58:06 +00:00
|
|
|
f"Chaque équipe peut refuser jusqu'a {MAX_REFUSE} "
|
2020-04-25 21:19:29 +00:00
|
|
|
f"problèmes sans pénalité (voir §13 du règlement). "
|
|
|
|
f"Un problème déjà rejeté ne compte pas deux fois."
|
|
|
|
)
|
2020-04-25 22:58:06 +00:00
|
|
|
await ctx.send("Bonne chance à tous ! C'est parti...")
|
2020-04-25 21:19:29 +00:00
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
else:
|
|
|
|
# Second round
|
|
|
|
sleep(0.5)
|
2020-04-25 22:58:06 +00:00
|
|
|
await ctx.send(
|
2020-04-26 09:12:20 +00:00
|
|
|
"Il reste juste le tirage du deuxième tour. Les règles sont les mêmes qu'avant "
|
|
|
|
"à la seule différence qu'une équipe ne peut pas tirer le problème "
|
|
|
|
"sur lequel elle est passée au premier tour."
|
2020-04-25 22:58:06 +00:00
|
|
|
)
|
2020-04-25 21:19:29 +00:00
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
sleep(1.5)
|
|
|
|
await ctx.send(
|
|
|
|
f"{self.current_team.mention} à toi l'honneur! "
|
|
|
|
f"Lance `!random-problem` quand tu veux."
|
|
|
|
)
|
|
|
|
|
|
|
|
async def next(self, ctx: Context) -> "Type[Phase]":
|
|
|
|
if self.round == 0:
|
|
|
|
await ctx.send("Nous allons passer au deuxième tour")
|
|
|
|
self.round = 1
|
|
|
|
return TirageOrderPhase
|
|
|
|
return ShowPhase
|
|
|
|
|
2020-04-25 21:19:29 +00:00
|
|
|
|
2020-04-25 20:27:00 +00:00
|
|
|
class PassageOrderPhase(OrderPhase):
|
2020-04-25 21:19:29 +00:00
|
|
|
"""The phase to determine the chicken's order."""
|
|
|
|
|
|
|
|
NEXT = TiragePhase
|
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
def __init__(self, tirage, round=0):
|
|
|
|
super().__init__(tirage, round, "de passage", "passage_order", True)
|
2020-04-25 20:27:00 +00:00
|
|
|
|
|
|
|
async def start(self, ctx):
|
|
|
|
await ctx.send(
|
|
|
|
"Nous allons maintenant tirer l'ordre de passage durant le tour. "
|
|
|
|
"L'ordre du tour sera dans l'ordre décroissant des lancers, "
|
|
|
|
"c'est-à-dire que l'équipe qui tire le plus grand nombre "
|
|
|
|
"présentera en premier."
|
|
|
|
)
|
|
|
|
sleep(0.5)
|
|
|
|
|
|
|
|
captain = get(ctx.guild.roles, name=CAPTAIN_ROLE)
|
|
|
|
await ctx.send(
|
|
|
|
f"Les {captain.mention}s, vous pouvez lancer à nouveau un dé 100 (`!dice 100`)"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class TirageOrderPhase(OrderPhase):
|
2020-04-25 21:19:29 +00:00
|
|
|
"""Phase to determine the tirage's order."""
|
|
|
|
|
2020-04-25 20:27:00 +00:00
|
|
|
NEXT = PassageOrderPhase
|
|
|
|
|
2020-04-26 09:12:20 +00:00
|
|
|
def __init__(self, tirage, round=0):
|
|
|
|
super().__init__(tirage, round, "des tirages", "tirage_order", False)
|
2020-04-25 20:27:00 +00:00
|
|
|
|
|
|
|
async def start(self, ctx):
|
|
|
|
captain = get(ctx.guild.roles, name=CAPTAIN_ROLE)
|
|
|
|
|
|
|
|
sleep(0.5) # The bot is more human if it doesn't type at the speed of light
|
|
|
|
await ctx.send(
|
2020-04-26 09:12:20 +00:00
|
|
|
"Nous allons d'abord tirer au sort l'ordre de tirage des problèmes "
|
|
|
|
f"pour le {ROUND_NAMES[self.round]}, "
|
|
|
|
"puis l'ordre de passage lors de ce tour."
|
2020-04-25 20:27:00 +00:00
|
|
|
)
|
|
|
|
sleep(0.5)
|
|
|
|
await ctx.send(
|
|
|
|
f"Les {captain.mention}s, vous pouvez désormais lancer un dé 100 "
|
|
|
|
"comme ceci `!dice 100`. "
|
|
|
|
"L'ordre des tirages suivants sera l'ordre croissant des lancers. "
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2020-04-25 15:02:57 +00:00
|
|
|
bot = commands.Bot(
|
|
|
|
"!", help_command=commands.DefaultHelpCommand(no_category="Commandes")
|
|
|
|
)
|
2020-04-25 14:42:06 +00:00
|
|
|
|
2020-04-25 18:19:45 +00:00
|
|
|
tirages: Dict[int, Tirage] = {}
|
2020-04-25 16:58:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
@bot.command(
|
|
|
|
name="start-draw",
|
|
|
|
help="Commence un tirage avec 3 ou 4 équipes.",
|
|
|
|
usage="équipe1 équipe2 équipe3 (équipe4)",
|
|
|
|
)
|
|
|
|
@commands.has_role(ORGA_ROLE)
|
|
|
|
async def start_draw(ctx: Context, *teams):
|
2020-04-26 10:04:39 +00:00
|
|
|
channel: discord.TextChannel = ctx.channel
|
|
|
|
channel_id = channel.id
|
|
|
|
if channel_id in tirages:
|
2020-04-25 16:58:35 +00:00
|
|
|
raise TfjmError("Il y a déjà un tirage en cours sur cette Channel.")
|
|
|
|
|
|
|
|
if len(teams) not in (3, 4):
|
|
|
|
raise TfjmError("Il faut 3 ou 4 équipes pour un tirage.")
|
|
|
|
|
|
|
|
roles = [role.name for role in ctx.guild.roles]
|
|
|
|
for team in teams:
|
|
|
|
if team not in roles:
|
|
|
|
raise TfjmError("Le nom de l'équipe doit être exactement celui du rôle.")
|
|
|
|
|
2020-04-26 10:04:39 +00:00
|
|
|
# Here all data should be valid
|
|
|
|
|
|
|
|
# Prevent everyone from writing except Capitaines, Orga, CNO, Benevole
|
|
|
|
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(CAPTAIN_ROLE): send,
|
|
|
|
r(BENEVOLE_ROLE): send,
|
|
|
|
}
|
|
|
|
await channel.edit(overwrites=overwrites)
|
|
|
|
|
2020-04-25 16:58:35 +00:00
|
|
|
await ctx.send(
|
|
|
|
"Nous allons commencer le tirage du premier tour. "
|
|
|
|
"Seuls les capitaines de chaque équipe peuvent désormais écrire ici. "
|
|
|
|
"Pour plus de détails sur le déroulement du tirgae au sort, le règlement "
|
|
|
|
"est accessible sur https://tfjm.org/reglement."
|
|
|
|
)
|
|
|
|
|
2020-04-26 10:04:39 +00:00
|
|
|
tirages[channel_id] = Tirage(ctx, channel_id, teams)
|
|
|
|
await tirages[channel_id].phase.start(ctx)
|
|
|
|
|
|
|
|
|
|
|
|
@bot.command(
|
|
|
|
name="abort-draw", help="Annule le tirage en cours.",
|
|
|
|
)
|
|
|
|
@commands.has_role(ORGA_ROLE)
|
|
|
|
async def abort_draw_cmd(ctx):
|
|
|
|
channel_id = ctx.channel.id
|
|
|
|
if channel_id in tirages:
|
|
|
|
await tirages[channel_id].end(ctx)
|
|
|
|
await ctx.send("Le tirage est annulé.")
|
|
|
|
del tirages[channel_id]
|
2020-04-25 18:19:45 +00:00
|
|
|
|
2020-04-25 14:42:06 +00:00
|
|
|
|
2020-04-25 21:19:29 +00:00
|
|
|
@bot.command(name="draw-skip")
|
|
|
|
async def draw_skip(ctx, *teams):
|
|
|
|
channel = ctx.channel.id
|
|
|
|
tirages[channel] = tirage = Tirage(ctx, channel, teams)
|
|
|
|
|
|
|
|
tirage.phase = TiragePhase(tirage)
|
|
|
|
for i, team in enumerate(tirage.teams):
|
2020-04-26 09:12:20 +00:00
|
|
|
team.tirage_order = [i, None]
|
|
|
|
team.passage_order = [i, None]
|
2020-04-25 21:19:29 +00:00
|
|
|
|
|
|
|
await ctx.send(f"Skipping to {tirage.phase.__class__.__name__}.")
|
|
|
|
await tirage.phase.start(ctx)
|
|
|
|
|
|
|
|
|
2020-04-25 14:42:06 +00:00
|
|
|
@bot.event
|
|
|
|
async def on_ready():
|
|
|
|
print(f"{bot.user} has connected to Discord!")
|
|
|
|
|
|
|
|
|
|
|
|
@bot.command(
|
|
|
|
name="dice",
|
|
|
|
help="Lance un dé à `n` faces. ",
|
|
|
|
aliases=["de", "dé", "roll"],
|
|
|
|
usage="n",
|
|
|
|
)
|
|
|
|
async def dice(ctx: Context, n: int):
|
|
|
|
if n < 1:
|
2020-04-25 16:58:35 +00:00
|
|
|
raise TfjmError(f"Je ne peux pas lancer un dé à {n} faces, désolé.")
|
2020-04-25 14:42:06 +00:00
|
|
|
|
|
|
|
dice = random.randint(1, n)
|
|
|
|
await ctx.send(f"Le dé à {n} faces s'est arrêté sur... **{dice}**")
|
|
|
|
|
2020-04-25 18:19:45 +00:00
|
|
|
# Here we seed the result to Tirage if needed
|
|
|
|
channel = ctx.channel.id
|
|
|
|
if n == 100 and channel in tirages:
|
|
|
|
# If it is a captain
|
|
|
|
author: discord.Member = ctx.author
|
|
|
|
if get(author.roles, name=CAPTAIN_ROLE) is not None:
|
|
|
|
await tirages[channel].dice(ctx, author, dice)
|
|
|
|
|
2020-04-25 14:42:06 +00:00
|
|
|
|
|
|
|
@bot.command(
|
|
|
|
name="choose",
|
|
|
|
help="Choisit une option parmi tous les arguments.",
|
|
|
|
usage="choix1 choix2...",
|
|
|
|
aliases=["choice", "choix", "ch"],
|
|
|
|
)
|
|
|
|
async def choose(ctx: Context, *args):
|
|
|
|
choice = random.choice(args)
|
|
|
|
await ctx.send(f"J'ai choisi... **{choice}**")
|
|
|
|
|
|
|
|
|
2020-04-25 15:02:57 +00:00
|
|
|
@bot.command(
|
|
|
|
name="random-problem",
|
|
|
|
help="Choisit un problème parmi ceux de cette année.",
|
|
|
|
aliases=["rp", "problème-aléatoire", "probleme-aleatoire", "pa"],
|
|
|
|
)
|
|
|
|
async def random_problem(ctx: Context):
|
|
|
|
problems = open("problems").readlines()
|
|
|
|
problems = [p.strip() for p in problems]
|
|
|
|
problem = random.choice(problems)
|
|
|
|
await ctx.send(f"Le problème tiré est... **{problem}**")
|
|
|
|
|
2020-04-25 22:58:06 +00:00
|
|
|
channel = ctx.channel.id
|
|
|
|
if channel in tirages:
|
|
|
|
# If it is a captain
|
|
|
|
author: discord.Member = ctx.author
|
|
|
|
if get(author.roles, name=CAPTAIN_ROLE) is not None:
|
|
|
|
await tirages[channel].choose_problem(ctx, ctx.author, problem)
|
|
|
|
|
|
|
|
|
|
|
|
@bot.command(
|
|
|
|
name="accept",
|
|
|
|
help="Accepte le problème qui vient d'être tiré. \n Ne fonctionne que lors d'un tirage.",
|
|
|
|
aliases=["oui", "yes", "o", "accepte", "ouiiiiiii"],
|
|
|
|
)
|
|
|
|
async def accept_cmd(ctx):
|
|
|
|
channel = ctx.channel.id
|
|
|
|
if channel in tirages:
|
|
|
|
# If it is a captain
|
|
|
|
author: discord.Member = ctx.author
|
|
|
|
if get(author.roles, name=CAPTAIN_ROLE) is not None:
|
|
|
|
await tirages[channel].accept(ctx, ctx.author, True)
|
|
|
|
|
|
|
|
|
|
|
|
@bot.command(
|
|
|
|
name="refuse",
|
|
|
|
help="Refuse le problème qui vient d'être tiré. \n Ne fonctionne que lors d'un tirage.",
|
|
|
|
aliases=["non", "no", "n"],
|
|
|
|
)
|
|
|
|
async def refuse_cmd(ctx):
|
|
|
|
channel = ctx.channel.id
|
|
|
|
if channel in tirages:
|
|
|
|
# If it is a captain
|
|
|
|
author: discord.Member = ctx.author
|
|
|
|
if get(author.roles, name=CAPTAIN_ROLE) is not None:
|
|
|
|
await tirages[channel].accept(ctx, ctx.author, False)
|
|
|
|
|
2020-04-25 15:02:57 +00:00
|
|
|
|
2020-04-25 14:42:06 +00:00
|
|
|
@bot.event
|
2020-04-25 16:58:35 +00:00
|
|
|
async def on_command_error(ctx: Context, error, *args, **kwargs):
|
|
|
|
if isinstance(error, commands.CommandInvokeError):
|
2020-04-25 18:19:45 +00:00
|
|
|
msg = str(error.original) or str(error)
|
|
|
|
traceback.print_tb(error.original.__traceback__, file=sys.stderr)
|
2020-04-25 16:58:35 +00:00
|
|
|
else:
|
|
|
|
msg = str(error)
|
|
|
|
|
2020-04-25 18:19:45 +00:00
|
|
|
print(repr(error), dir(error), file=sys.stderr)
|
2020-04-25 16:58:35 +00:00
|
|
|
await ctx.send(msg)
|
2020-04-25 14:42:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
bot.run(TOKEN)
|