tfjm-discord-bot/src/cogs/teams.py

351 lines
13 KiB
Python
Raw Normal View History

2020-04-27 12:46:04 +00:00
from collections import namedtuple
from typing import List, Tuple
2020-04-27 12:46:04 +00:00
import discord
2020-05-02 14:19:45 +00:00
from discord import Member, VoiceChannel, PermissionOverwrite
2020-04-27 12:46:04 +00:00
from discord.ext import commands
2020-04-30 19:22:17 +00:00
from discord.ext.commands import Cog, group, Context
2020-04-27 12:46:04 +00:00
from discord.utils import get, find
from src.constants import *
2020-04-30 15:26:33 +00:00
from src.core import CustomBot
2020-04-30 18:11:07 +00:00
from src.utils import has_role, send_and_bin
2020-04-27 12:46:04 +00:00
Team = namedtuple("Team", ["name", "trigram", "tournoi", "secret", "status"])
class TeamsCog(Cog, name="Teams"):
2020-04-30 15:26:33 +00:00
def __init__(self, bot: CustomBot):
2020-04-29 11:42:49 +00:00
self.bot = bot
2020-04-27 12:46:04 +00:00
self.teams = self.load_teams()
def load_teams(self):
2020-04-29 23:04:54 +00:00
with open(File.TEAMS) as f:
2020-04-27 12:46:04 +00:00
# first line is header
lines = f.read().splitlines()[1:]
teams = [
Team(*[field.strip('"') for field in line.split(";")]) for line in lines
]
return teams
def teams_for(self, member) -> List[Tuple[Team, discord.Role]]:
"""Return a list of pairs (role, team) corresponding to the teams of the member"""
teams = []
for role in member.roles:
team = get(self.teams, trigram=role.name)
if team:
teams.append((team, role))
return teams
2020-05-02 14:19:45 +00:00
@commands.command(name="poule")
@commands.has_role(Role.CNO)
@send_and_bin
async def setup_poule(
self,
ctx: Context,
category: discord.CategoryChannel,
poule: str,
*teams: discord.Role,
):
"""(cno) Setup les permissions pour un salon vocal de poule"""
assert poule in "AB"
see = PermissionOverwrite(connect=True, speak=False)
speak = PermissionOverwrite(connect=True, speak=True)
guild: discord.Guild = ctx.guild
name = "Poule" + ("" if poule is None else " " + poule)
channel: VoiceChannel = get(
guild.voice_channels, category__name=category.name, name=name
)
role: discord.Role
for role in channel.overwrites:
# remove all permissions
await channel.set_permissions(role, overwrite=None)
orga = get(guild.roles, name=f"Orga {category.name}")
jury = get(guild.roles, name=f"Jury {category.name}")
await channel.set_permissions(
guild.default_role, connect=False, view_channel=False
)
await channel.set_permissions(
jury, view_channel=True, connect=True, mute_members=True
)
await channel.set_permissions(
orga, view_channel=True, connect=True, mute_members=True
)
for team in teams:
await channel.set_permissions(team, view_channel=True, connect=True)
# tourist_name = f"{category.name} {poule}"
# tourist = get(guild.roles, name=tourist_name)
# if tourist is None:
# tourist = await guild.create_role(name=tourist_name)
#
# await channel.set_permissions(tourist, connect=True, speak=False)
# return str(channel.changed_roles)
return "C'est fait !"
@commands.command(name="tourist")
@commands.has_any_role(*Role.ORGAS)
@send_and_bin
async def touriste_cmd(self, ctx: Context, poule, member: Member):
"""
(orga) Accepte quelqu'un comme touriste pour une certaine poule.
Exemple:
`!tourist A Diego` - Ajoute Diego comme touriste dans la Poule A
"""
poule = f"Poule {poule}"
tournoi = find(lambda r: r.name.startswith("Orga"), ctx.author.roles)
tournoi_name = tournoi.name.partition(" ")[2]
guild: discord.Guild = ctx.guild
poule_channel: VoiceChannel = get(
guild.voice_channels, name=poule, category__name=tournoi_name
)
if poule_channel is None:
return f"La poule '{poule}' n'existe pas à {tournoi_name}"
touriste_role = get(guild.roles, name=Role.TOURIST)
region = get(guild.roles, name=tournoi_name)
await member.add_roles(touriste_role, region)
await poule_channel.set_permissions(member, view_channel=True, connect=True)
return f"{member.mention} à été ajouté comme spectateur dans la {poule} de {tournoi_name}"
2020-04-29 11:42:49 +00:00
@group(name="team", invoke_without_command=True)
2020-04-27 12:46:04 +00:00
async def team(self, ctx):
"""Groupe de commandes pour la gestion des équipes."""
2020-04-29 11:42:49 +00:00
await ctx.invoke(self.bot.get_command("help"), "team")
2020-04-27 12:46:04 +00:00
@team.command(name="create")
async def create_team(self, ctx: Context, trigram, team_secret):
"""
2020-04-30 18:11:07 +00:00
Permet aux capitaines de créer leur equipe.
Pour utiliser cette commande, il faut ton trigram et ton code
d'équipe. Tu peux ensuite écrire `!team create TRIGRAM SECRET`
en gradant le point d'éclamation et en remplaçant `TRIGRAM` et
`SECRET` par les bonnes valeurs. Le message que tu envoie sera
immédiatement supprimé pour pas que d'autres voient ton secret,
donc ne t'inquiète pas si il disparait.
Exemple:
`!team create FOX abq23j`
"""
2020-04-27 12:46:04 +00:00
await ctx.message.delete()
team: Team = get(self.teams, trigram=trigram)
role: discord.Role = get(ctx.guild.roles, name=trigram)
captain_role = get(ctx.guild.roles, name=Role.CAPTAIN)
if team is None:
2020-04-30 18:11:07 +00:00
msg = (
2020-04-27 12:46:04 +00:00
f"{ctx.author.mention}: le trigram `{trigram}` "
f"n'est pas valide. Es-tu sûr d'avoir le bon ?"
)
elif role is not None:
# Team exists
captain = find(lambda m: captain_role in m.roles, role.members)
2020-04-30 18:11:07 +00:00
msg = (
2020-04-27 12:46:04 +00:00
f"{ctx.author.mention}: l'équipe {trigram} "
f"existe déjà. Tu peux demander a ton capitaine "
f"{captain.mention} de t'ajouter à l'équipe avec "
f"`!team add {ctx.author.name}`"
2020-04-27 12:46:04 +00:00
)
elif team_secret != team.secret:
2020-04-30 18:11:07 +00:00
msg = (
2020-04-27 12:46:04 +00:00
f"{ctx.author.mention}: ton secret n'est pas valide, "
f"Tu peux le trouver sur https://inscription.tfjm.org/mon-equipe."
)
else:
# Team creation !
2020-04-27 13:07:21 +00:00
guild: discord.Guild = ctx.guild
team_role = await guild.create_role(
name=trigram,
color=discord.Colour.from_rgb(255, 255, 255),
reason="Creation of a new team",
)
tournoi = get(guild.roles, name=team.tournoi)
participant = get(guild.roles, name=Role.PARTICIPANT)
2020-04-27 13:07:21 +00:00
await ctx.author.add_roles(
team_role,
captain_role,
tournoi,
participant,
reason="Creation of team " + trigram,
2020-04-27 13:07:21 +00:00
)
2020-04-27 12:46:04 +00:00
2020-04-30 18:11:07 +00:00
msg = (
2020-04-27 13:07:21 +00:00
f"L'équipe {team_role.mention} a été créée et son capitaine "
f"est {ctx.author.mention}"
)
2020-04-27 12:46:04 +00:00
2020-05-04 14:16:06 +00:00
diego = get(ctx.guild.members, id=DIEGO)
await ctx.author.send(
2020-05-04 14:16:06 +00:00
"Salut Capitaine !\n"
"On va être amené à faire de nombreuses choses ensemble "
"ces prochains jours, donc n'hésite pas à abuser de `!help`. "
"Tu peux l'utiliser ici mais malheureusement tu ne pourra pas voir "
"les commandes qui sont réservés aux capitaines. \n"
"Une commande que tu peux avoir envie d'utiliser c'est "
"`!team channel un-super-nom` pour créer une channel réservée à "
2020-05-04 14:16:06 +00:00
"ton équipe. `!team voice un-super-nom` permet "
"aussi de créer un salon vocal :wink: \n\n"
"Si tu as des suggestions pour que le bot permette à chacun d'avoir "
f"une meilleure expérience ici, envoie un petit message à {diego.mention} ;)"
)
2020-04-30 18:11:07 +00:00
msg = await ctx.send(msg)
await self.bot.wait_for_bin(ctx.author, msg)
@team.command(name="add")
@commands.has_role(Role.CAPTAIN)
2020-04-30 18:11:07 +00:00
@send_and_bin
async def team_add(self, ctx, member: discord.Member):
"""
(cap) Ajoute un membre a ton équipe.
Commande réservée aux capitaines pour ajouter un
membre dans leur équipe. Cela permet juste de donner
les bons roles pour que les bonnes *channels* soient
accessibles.
Exemple:
`!team add @Jean-Mich-Much`
"""
author_teams = self.teams_for(ctx.author)
member_teams = self.teams_for(member)
if member_teams:
2020-04-30 18:11:07 +00:00
return (
f"{member.mention} est déjà dans une équipe "
f"et ne peut pas être dans deux à la fois."
)
elif len(author_teams) > 1:
2020-04-30 18:11:07 +00:00
return (
f"Tu est dans plusieurs équipes, je ne sais "
f"pas où l'ajouter. Il faut demander à un organisateur "
f"de le faire."
)
else:
the_team = author_teams[0]
tournoi = get(ctx.guild.roles, name=the_team[0].tournoi)
participant = get(ctx.guild.roles, name=Role.PARTICIPANT)
await member.add_roles(
the_team[1],
tournoi,
participant,
reason=f"{ctx.author.name} l'a ajouté à son équipe",
)
2020-04-30 18:11:07 +00:00
return f"{member.mention} a été ajouté dans l'équipe {the_team[1].mention}"
2020-04-28 18:08:57 +00:00
@team.command(name="channel")
@commands.has_role(Role.CAPTAIN)
2020-04-30 18:11:07 +00:00
@send_and_bin
2020-04-28 18:08:57 +00:00
async def team_channel(self, ctx, *channel_name):
"""
(cap) Crée une channel privée pour l'équipe
Crée un endroit de discussion privé seulement pour l'équipe
personne d'autre n'y aura accès.
Exemple:
`!team channel un-nom-sympa`
"""
2020-04-28 18:08:57 +00:00
if not channel_name:
2020-04-30 18:11:07 +00:00
return (
2020-05-04 14:16:06 +00:00
"Tu dois mettre un nom de salon, par exemple "
2020-04-28 18:08:57 +00:00
"`!team channel un-super-nom`"
)
channel_name = " ".join(channel_name)
guild: discord.Guild = ctx.guild
team_role = self.teams_for(ctx.author)[0][1]
team_channel_category = get(guild.categories, name=TEAMS_CHANNEL_CATEGORY)
2020-04-30 18:11:07 +00:00
channel = await guild.create_text_channel(
channel_name,
overwrites={
guild.default_role: discord.PermissionOverwrite(read_messages=False),
team_role: discord.PermissionOverwrite(read_messages=True),
},
category=team_channel_category,
reason=f"{ctx.author.name} à demandé une channel pour son équipe.",
)
2020-04-30 18:11:07 +00:00
return f"{ctx.author.mention}: Le salon d'équipe {channel.mention} à été créé."
2020-04-28 19:03:35 +00:00
@team.command(name="voice", usage="Nom du salon")
@commands.has_role(Role.CAPTAIN)
2020-04-30 18:11:07 +00:00
@send_and_bin
2020-04-28 21:08:34 +00:00
async def team_voice(self, ctx, *channel_name):
2020-04-28 19:03:35 +00:00
"""
(cap) Crée une channel vocale privée pour l'équipe
Crée un endroit de discussion privé seulement pour l'équipe
personne d'autre n'y aura accès.
Exemple:
`!team voice un-nom-sympa`
"""
if not channel_name:
2020-04-30 18:11:07 +00:00
return (
2020-05-04 14:16:06 +00:00
"Tu dois mettre un nom de salon, par exemple "
2020-04-29 15:16:01 +00:00
"`!team voice un-super-nom`"
2020-04-28 19:03:35 +00:00
)
channel_name = " ".join(channel_name)
guild: discord.Guild = ctx.guild
team_role = self.teams_for(ctx.author)[0][1]
team_channel_category = get(guild.categories, name=TEAMS_CHANNEL_CATEGORY)
2020-04-30 18:11:07 +00:00
channel = await guild.create_voice_channel(
2020-04-28 19:03:35 +00:00
channel_name,
overwrites={
guild.default_role: discord.PermissionOverwrite(read_messages=False),
team_role: discord.PermissionOverwrite(read_messages=True),
},
category=team_channel_category,
2020-04-29 15:16:01 +00:00
reason=f"{ctx.author.name} à demandé un salon vocale pour son équipe.",
2020-04-28 19:03:35 +00:00
)
2020-04-30 18:11:07 +00:00
return f"{ctx.author.mention}: La salon vocal '{channel.mention}' à été créé."
2020-04-29 16:43:07 +00:00
@team.command(name="list")
@commands.has_role(Role.CNO)
async def list_cmd(self, ctx):
"""(cno) Affiche les équipes de chaque tournoi présentes sur le discord."""
embed = discord.Embed(title="Liste des équipes", color=EMBED_COLOR)
captains = [m for m in ctx.guild.members if has_role(m, Role.CAPTAIN)]
tournois = {
tournoi: [c for c in captains if has_role(c, tournoi)]
for tournoi in TOURNOIS
}
for tournoi, caps in tournois.items():
# we assume captains have exactly one team.
txt = "\n".join(self.teams_for(c)[0][0].trigram for c in caps)
txt = txt or "Il n'y a pas encore d'équipes sur le discord."
embed.add_field(name=tournoi, value=txt)
await ctx.send(embed=embed)
2020-04-27 12:46:04 +00:00
2020-04-30 18:11:07 +00:00
def setup(bot: CustomBot):
2020-04-29 11:42:49 +00:00
bot.add_cog(TeamsCog(bot))