1
0
mirror of https://gitlab.com/ddorn/tfjm-discord-bot.git synced 2025-07-07 22:40:13 +02:00

Compare commits

...

26 Commits

Author SHA1 Message Date
07d7d9be7a <3 alias for hug 2020-05-16 18:09:42 +02:00
0fb3a3ca4a self tourist command 2020-05-16 12:53:29 +02:00
adcd719357 🐛 + fix pb printing + send poules to server 2020-05-16 12:50:35 +02:00
697f1caf91 ♻️ change TOKEN to DISCORD_TOKEN 2020-05-16 12:49:55 +02:00
53370fbcb6 add tfjm.org token + FAN_CLUBS ids 2020-05-16 12:49:28 +02:00
ef8144a115 Fan club command 2020-05-16 12:47:15 +02:00
fc08c60230 🐛 fix dice collisions 2020-05-16 12:46:14 +02:00
56cf3f2f55 show tirages for a team 2020-05-13 16:36:46 +02:00
3551068a3f poules à 5
+ 120s timeout for fracals
+ relative path for memes
2020-05-13 15:49:00 +02:00
f7db834f3c only one fractal at a time 2020-05-12 16:02:10 +02:00
860cbb563e 🐛 fix special characters in fractal seed + add FRACTAL_COOLDOWN 2020-05-12 12:12:46 +02:00
5a402eaf09 💬 More hug sentences 2020-05-12 11:27:40 +02:00
8cba9db097 fractals can be seeded 2020-05-11 23:26:11 +02:00
a632bdf088 send can get PrivateChanels too by id 2020-05-11 13:32:04 +02:00
d792a87be3 :spakles: ping cmd 2020-05-11 03:58:13 +02:00
5c522f07e3 jokes leaderboard 2020-05-11 03:37:11 +02:00
6815c33ea1 print direct messages 2020-05-10 17:30:45 +02:00
0f1bc903bf 🐛 + fix TimeoutError and hug more people 2020-05-10 16:00:57 +02:00
af1f2d1ae2 jokes with images 2020-05-10 12:43:21 +02:00
553d124b1a hug command 2020-05-10 11:57:50 +02:00
3dd50a75c3 🔧 fusion of Bordeaux and Nancy 2020-05-08 10:44:40 +02:00
a520abb90a 🐛 fix draw dump 2020-05-07 01:02:35 +02:00
a95c20c5b5 working new tirage 2020-05-06 19:04:36 +02:00
dfc8176084 🚧 new tirages working without save 2020-05-06 17:38:47 +02:00
cfdd55daa2 visible jokes + strip ! on help 2020-05-06 15:14:52 +02:00
62256a98e8 🚧 WIP: better and more flexible tirages 2020-05-06 02:27:09 +02:00
12 changed files with 1079 additions and 684 deletions

5
bot.py
View File

@ -1,5 +1,4 @@
from src import bot
from src.constants import TOKEN
from src import start
if __name__ == "__main__":
bot.run(TOKEN)
start()

View File

@ -1,12 +1,3 @@
```
- Quelle est votre principale qualité ?
- Je suis très rapide en calcul mental.
- 23 x 547 ?
- 56
- Mais c'est faux !
- Oui mais c'est rapide !
```
---
Why did the chicken cross the mobius strip?
|| To get to the same side. ||
---

View File

@ -1 +1 @@
from src.tfjm_discord_bot import bot
from src.tfjm_discord_bot import start

334
src/base_tirage.py Normal file
View File

@ -0,0 +1,334 @@
import asyncio
import random
import sys
import traceback
from functools import wraps
from pathlib import Path
from pprint import pprint
from io import StringIO
from typing import Type, Union, Dict, List
import discord
import yaml
from src.constants import *
class Event(asyncio.Event):
def __init__(self, team: str, value: Union[bool, int, str]):
super(Event, self).__init__()
self.value = value
self.team = team
self.response = None
class Team:
yaml_tag = "Team"
def __init__(self, team_role):
self.name = team_role.name
self.mention = team_role.mention
self.accepted_problems = [None, None]
self.rejected = [set(), set()]
def __str__(self):
s = StringIO()
pprint(self.__dict__, stream=s)
s.seek(0)
return s.read()
__repr__ = __str__
def coeff(self, round):
if len(self.rejected[round]) <= MAX_REFUSE:
return 2
else:
return 2 - 0.5 * (len(self.rejected[round]) - MAX_REFUSE)
def details(self, round):
info = {
# "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 passage": self.passage_order[round],
}
width = max(map(len, info))
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]}
# """
class Poule(yaml.YAMLObject):
yaml_tag = "Poule"
def __init__(self, poule, rnd):
self.poule = poule
self.rnd = rnd
def __str__(self):
return f"{self.poule}{self.rnd + 1}"
class BaseTirage(yaml.YAMLObject):
yaml_tag = "Tirage"
def __init__(self, *teams: discord.Role, fmt=(3, 3)):
assert sum(fmt) == len(teams), "Different number of teams and format"
self.teams: Dict[str, Team] = {t.name: Team(t) for t in teams}
self.format = fmt
self.queue = asyncio.Queue()
self.poules: Dict[Poule, List[str]] = {}
"""A mapping between the poule and the list of teams in this poule."""
def availaible(self, pb, poule):
pbs = [
self.teams[team].accepted_problems[poule.rnd] for team in self.poules[poule]
]
if len(self.poules[poule]) < 5:
return pb not in pbs
else:
return pbs.count(pb) < 2
async def event(self, event: Event):
event.set()
await self.queue.put(event)
await event.wait()
return event.response
async def dice(self, trigram):
return await self.event(Event(trigram, random.randint(1, 100)))
async def rproblem(self, trigram):
team = self.teams[trigram]
rnd = 0 if team.accepted_problems[0] is None else 1
for poule, teams in self.poules.items():
if trigram in teams and poule.rnd == rnd:
break
else:
return await self.warn_wrong_team(None, trigram)
available = [
pb
for pb in PROBLEMS
if pb not in team.accepted_problems and self.availaible(pb, poule)
]
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()
if team is not None and event.team != team:
await self.warn_wrong_team(team, event.team)
elif not isinstance(event.value, typ):
await self.warn_unwanted(typ, event.value)
else:
event.clear()
return event
event.clear()
async def run(self):
await self.info_start()
self.poules = await self.make_poules()
for poule in self.poules:
await self.draw_poule(poule)
await self.info_finish()
async def get_dices(self, teams):
dices = {t: None for t in teams}
collisions = list(teams)
while collisions:
for t in collisions:
dices[t] = None
while None in dices.values():
event = await self.next(int)
if event.team not in dices:
await self.warn_wrong_team(None, event.team)
elif dices[event.team] is None:
dices[event.team] = event.value
await self.info_dice(event.team, event.value)
else:
await self.warn_twice(int)
collisions = [t for t in teams if list(dices.values()).count(dices[t]) > 1]
if collisions:
await self.warn_colisions(collisions)
return dices
async def make_poules(self):
poules = {}
for rnd in (0, 1):
await self.start_make_poule(rnd)
dices = await self.get_dices(self.teams)
sorted_teams = sorted(self.teams, key=lambda t: dices[t])
idx = 0
for i, qte in enumerate(self.format):
letter = chr(ord("A") + i)
poules[Poule(letter, rnd)] = sorted_teams[idx : idx + qte]
idx += qte
await self.annonce_poules(poules)
return poules
async def draw_poule(self, poule):
await self.start_draw_poule(poule)
# Trigrams in draw order
trigrams = await self.draw_order(poule)
# Teams in draw order
teams = [self.teams[tri] for tri in trigrams]
current = 0
while not all(team.accepted_problems[poule.rnd] for team in teams):
team = teams[current]
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)
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, pevent.value, poule.rnd)
# Accept it
accept = await self.next(bool, team.name)
if accept.value:
team.accepted_problems[poule.rnd] = pevent.value
await self.info_accepted(
team, pevent.value, self.availaible(pevent.value, poule)
)
else:
await self.info_rejected(team, pevent.value, rnd=poule.rnd)
team.rejected[poule.rnd].add(pevent.value)
current += 1
current %= len(teams)
if len(teams) == 5:
# We can determine the passage order only once problems are drawn.
order = [self.teams[tri] for tri in self.poules[poule]]
pbs = [team.accepted_problems[poule.rnd] for team in order]
doubles = []
i = 0
while i < len(order):
team = order[i]
if pbs.count(team.accepted_problems[poule.rnd]) == 2:
# We pop the two with the same pb and add them to the doubles
doubles.append(order.pop(i))
other = next(
filter(
lambda t: team.accepted_problems[poule.rnd]
== t.accepted_problems[poule.rnd],
order,
)
)
doubles.append(other)
order.remove(other)
else:
i += 1
# The conflicts
order = doubles + order
self.poules[poule] = order
await self.annonce_poule(poule)
async def draw_order(self, poule):
await self.start_draw_order(poule)
teams = self.poules[poule]
dices = await self.get_dices(teams)
order = sorted(teams, key=lambda t: dices[t], reverse=True)
await self.annonce_draw_order(order)
return order
async def warn_unwanted(self, wanted: Type, got: Type):
"""Called when a event of an unwanted type occurs."""
async def warn_wrong_team(self, expected, got):
"""Called when a team that should not play now put an event"""
async def warn_colisions(self, collisions: List[str]):
"""Called when there are collisions in a dice tirage."""
async def warn_twice(self, typ: Type):
"""Called when an event appears once again and not wanted."""
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."""
async def start_select_pb(self, team):
"""Called when a team needs to select a problem."""
async def annonce_poules(self, poules):
"""Called when all poules are defined."""
async def annonce_draw_order(self, order):
"""Called when the drawing order is defined."""
async def annonce_poule(self, poule):
"""Called when the problems and order for a poule is known."""
async def info_start(self):
"""Called at the start of the tirage."""
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."""
async def info_accepted(self, team, pb, still_available):
"""Called when a team accepts a problem."""
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

View File

@ -2,7 +2,7 @@ import asyncio
from pprint import pprint
import discord
from discord import TextChannel, PermissionOverwrite, Message
from discord import TextChannel, PermissionOverwrite, Message, ChannelType
from discord.ext.commands import (
command,
has_role,
@ -16,8 +16,10 @@ from ptpython.repl import embed
from src.constants import *
from src.core import CustomBot
from src.utils import fg
COGS_SHORTCUTS = {
"bt": "src.base_tirage",
"c": "src.constants",
"d": "tirages",
"e": "errors",
@ -53,7 +55,7 @@ class DevCog(Cog, name="Dev tools"):
def send(msg, channel=None):
if isinstance(channel, int):
channel = get(ctx.guild.channels, id=channel)
channel = self.bot.get_channel(channel)
channel = channel or ctx.channel
asyncio.create_task(channel.send(msg))
@ -202,6 +204,15 @@ class DevCog(Cog, name="Dev tools"):
await channel.delete_messages(to_delete)
await ctx.message.delete()
@Cog.listener()
async def on_message(self, msg: Message):
ch: TextChannel = msg.channel
if ch.type == ChannelType.private:
m = f"""{fg(msg.author.name)}: {msg.content}
MSG_ID: {fg(msg.id, 0x03A678)}
CHA_ID: {fg(msg.channel.id, 0x03A678)}"""
print(m)
def setup(bot: CustomBot):
bot.add_cog(DevCog(bot))

View File

@ -1,16 +1,18 @@
import asyncio
import datetime
import io
import itertools
import random
import urllib
from dataclasses import dataclass, field
from operator import attrgetter
from time import time
from typing import List, Set
from typing import List, Set, Union
import aiohttp
import discord
import yaml
from discord import Guild
from discord import Guild, Member
from discord.ext import commands
from discord.ext.commands import (
Cog,
@ -20,11 +22,16 @@ from discord.ext.commands import (
CommandError,
Group,
group,
MemberConverter,
BadArgument,
RoleConverter,
)
from discord.utils import get
from src.constants import *
from src.constants import Emoji
from src.core import CustomBot
from src.errors import TfjmError
from src.utils import has_role, start_time, send_and_bin
@ -37,6 +44,7 @@ class Joke(yaml.YAMLObject):
joker: int
likes: Set[int] = field(default_factory=set)
dislikes: Set[int] = field(default_factory=set)
file: str = None
class MiscCog(Cog, name="Divers"):
@ -44,6 +52,7 @@ class MiscCog(Cog, name="Divers"):
self.bot = bot
self.show_hidden = False
self.verify_checks = True
self.computing = False
@command(
name="choose",
@ -94,15 +103,128 @@ class MiscCog(Cog, name="Divers"):
@command(hidden=True)
async def fractal(self, ctx: Context):
await ctx.message.add_reaction(Emoji.CHECK)
seed = random.randint(0, 1_000_000_000)
if self.computing:
return await ctx.send("Il y a déjà une fractale en cours de calcul...")
try:
self.computing = True
await ctx.message.add_reaction(Emoji.CHECK)
msg: discord.Message = ctx.message
seed = msg.content[len("!fractal ") :]
seed = seed or str(random.randint(0, 1_000_000_000))
async with aiohttp.ClientSession() as session:
async with session.get(FRACTAL_URL.format(seed=seed)) as resp:
async with session.get(
FRACTAL_URL.format(seed=urllib.parse.quote(seed)), timeout=120
) as resp:
if resp.status != 200:
return await ctx.send("Could not download file...")
return await ctx.send(
"Il y a un problème pour calculer/télécharger l'image..."
)
data = io.BytesIO(await resp.read())
await ctx.send(file=discord.File(data, "cool_image.png"))
await ctx.send(
f"Seed: {seed}", file=discord.File(data, f"{seed}.png")
)
finally:
self.computing = False
@command(hidden=True, aliases=["bang", "pan"])
async def pew(self, ctx):
await ctx.send("Tu t'es raté ! Kwaaack :duck:")
@command(aliases=["<3"])
async def hug(self, ctx, who="everyone"):
"""Fait un câlin à quelqu'un."""
if who != "everyone":
try:
who = await RoleConverter().convert(ctx, who)
except BadArgument:
try:
who = await MemberConverter().convert(ctx, who)
except BadArgument:
return await ctx.send(f'Il n\'y a pas de "{who}". :man_shrugging:')
else:
who = ctx.guild.default_role
who: Union[discord.Role, Member]
bonuses = [
"C'est trop meuuuugnon !",
"Ça remonte le moral ! :D",
":hugging:",
":smiling_face_with_3_hearts:",
"Oh wiiii",
"Iel se sent désormais prêt à travailler à fond sur les solutions de AQT",
f"{who.mention} en redemande un !",
"Le·a pauvre, iel est tout·e rouge !",
"Hihi, il gratte ton pull en laine ! :sheep:",
]
if (
isinstance(who, discord.Member)
and has_role(who, Role.JURY)
and has_role(ctx.author, Role.PARTICIPANT)
):
bonuses += ["Il s'agit surement là d'une tentative de corruption !"]
if who == ctx.author:
msg = f"{who.mention} se fait un auto-calin !"
bonuses += [
"Mais c'est un peu ridicule...",
"Mais iel a les bras trop courts ! :cactus:",
"Il en faut peu pour être heureux :wink:",
]
elif who == ctx.guild.default_role:
msg = f"{ctx.author.mention} fait un câlin a touuuut le monde !"
bonuses += [
"Ça fait beaucoup de gens pour un câlin !",
"Plus on est, plus on est calins !",
"C'est pas très COVID-19 tout ça !",
"Tout le monde est heureux maintenant !",
]
elif who == self.bot.user:
bonuses += ["Je trouve ça très bienveillant <3"]
else:
msg = f"{ctx.author.mention} fait un gros câlin à {who.mention} !"
bonuses += [
f"Mais {who.mention} n'apprécie pas...",
"Et ils s'en vont chasser des canards ensemble :wink:",
"Oh ! Iel sent bon...",
f"{who.mention} a serré tellment fort qu'iel vous a coupé en deux :scream:",
f"{who.mention} propose à {ctx.author.mention} de se revoir autour d'une :pizza: !",
"Les drones du commissaire Winston passent par là et vous ordonnent d'arrêter.",
"Après ce beau moment de tendresse, ils décident d'aller discuter en créant des puzzles.",
f"{who.mention} se réfugie dans l'entrepôt d'Animath et bloque l'entrée avec un meuble.",
]
bonus = random.choice(bonuses)
await ctx.send(f"{msg} {bonus}")
@command(aliases=["pong"])
async def ping(self, ctx):
"""Affiche la latence avec le bot."""
ping = time()
msg: discord.Message = await ctx.send("Pong !")
pong = time()
await msg.edit(content=f"Pong ! Ça a pris {int(1000 * (pong - ping))}ms")
@command(name="fan", aliases=["join", "adhere"], hidden=True)
async def fan_club_cmd(self, ctx: Context, who: Member):
"""Permet de rejoindre le fan-club d'Ananas ou Citron Vert."""
role_id = FAN_CLUBS.get(who.id, None)
role = get(ctx.guild.roles, id=role_id)
if role is not None:
await ctx.author.add_roles(role)
await ctx.send(f"Bienvenue au {role.mention} !! :tada:")
else:
await ctx.send(
f"{who.mention} n'a pas encore de fan club. Peut-être qu'un jour "
f"iel sera un membre influent du CNO ?"
)
# ---------------- Jokes ---------------- #
@ -119,35 +241,58 @@ class MiscCog(Cog, name="Divers"):
with open(File.JOKES_V2, "w") as f:
yaml.safe_dump_all(jokes, f)
@group(name="joke", hidden=True, invoke_without_command=True)
async def joke(self, ctx):
await ctx.message.delete()
@group(name="joke", invoke_without_command=True)
async def joke(self, ctx: Context, id: int = None):
m: discord.Message = ctx.message
await m.delete()
jokes = self.load_jokes()
if id is not None:
joke_id = id
jokes = sorted(
jokes, key=lambda j: len(j.likes) - len(j.dislikes), reverse=True
)
else:
joke_id = random.randrange(len(jokes))
joke = jokes[joke_id]
message: discord.Message = await ctx.send(joke.joke)
try:
joke = jokes[joke_id]
except IndexError:
raise TfjmError("Il n'y a pas de blague avec cet ID.")
if joke.file:
file = discord.File(File.MEMES / joke.file)
else:
file = None
message: discord.Message = await ctx.send(joke.joke, file=file)
await message.add_reaction(Emoji.PLUS_1)
await message.add_reaction(Emoji.MINUS_1)
await self.wait_for_joke_reactions(joke_id, message)
@joke.command(name="new", hidden=True)
@joke.command(name="new")
@send_and_bin
async def new_joke(self, ctx: Context):
"""Ajoute une blague pour le concours de blague."""
jokes = self.load_jokes()
joke_id = len(jokes)
author: discord.Member = ctx.author
message: discord.Message = ctx.message
start = "!joke new "
msg = message.content[len(start) :]
msg = message.content[len("!joke new ") :]
joke = Joke(msg, ctx.author.id, set())
jokes = self.load_jokes()
if message.attachments:
file: discord.Attachment = message.attachments[0]
joke.file = str(f"{joke_id}-{file.filename}")
await file.save(File.MEMES / joke.file)
jokes.append(joke)
self.save_jokes(jokes)
joke_id = len(jokes) - 1
await message.add_reaction(Emoji.PLUS_1)
await message.add_reaction(Emoji.MINUS_1)
@ -163,9 +308,13 @@ class MiscCog(Cog, name="Divers"):
start = time()
end = start + 24 * 60 * 60
while time() < end:
try:
reaction, user = await self.bot.wait_for(
"reaction_add", check=check, timeout=end - time()
)
except asyncio.TimeoutError:
return
if user.id == BOT:
continue
@ -178,6 +327,28 @@ class MiscCog(Cog, name="Divers"):
self.save_jokes(jokes)
@joke.command(name="top", hidden=True)
async def best_jokes(self, ctx: Context):
"""Affiche le palmares des blagues."""
jokes = self.load_jokes()
s = sorted(jokes, key=lambda j: len(j.likes) - len(j.dislikes), reverse=True)
embed = discord.Embed(title="Palmares des blagues.")
for i, joke in enumerate(s[:10]):
who = get(ctx.guild.members, id=joke.joker)
text = joke.joke
if joke.file:
text += " - image non inclue - "
embed.add_field(
name=f"{i} - {who.display_name} - {len(joke.likes)}", value=text
)
await ctx.send(embed=embed)
# ----------------- Help ---------------- #
@command(name="help", aliases=["h"])
@ -231,7 +402,7 @@ class MiscCog(Cog, name="Divers"):
return await ctx.send(embed=embed)
async def send_command_help(self, ctx, args):
name = " ".join(args)
name = " ".join(args).strip("!")
comm: Command = self.bot.get_command(name)
if comm is None:
return await ctx.send(

View File

@ -9,7 +9,7 @@ from discord.utils import get, find
from src.constants import *
from src.core import CustomBot
from src.utils import has_role, send_and_bin
from src.utils import has_role, send_and_bin, french_join
Team = namedtuple("Team", ["name", "trigram", "tournoi", "secret", "status"])
@ -90,33 +90,30 @@ class TeamsCog(Cog, name="Teams"):
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):
async def touriste_cmd(self, ctx: Context, tournoi):
"""
(orga) Accepte quelqu'un comme touriste pour une certaine poule.
Permet de voir et ecouter dans les salons d'un certain tournoi.
Exemple:
`!tourist A Diego` - Ajoute Diego comme touriste dans la Poule A
`!tourist Bordeaux-Nancy` - Donne les droits de spectateurs pour Bordeaux-Nancy
"""
poule = f"Poule {poule}"
tournoi = find(lambda r: r.name.startswith("Orga"), ctx.author.roles)
tournoi_name = tournoi.name.partition(" ")[2]
if ctx.author.top_role != ctx.guild.default_role:
return f"{ctx.author.mention} tu as déjà un role, devenir spéctateur t'enlèverait des droits."
if tournoi not in TOURNOIS:
return f"{tournoi} n'est pas un nom de tournoi. Possibilités: {french_join(TOURNOIS)}"
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}"
tournoi_role = get(guild.roles, name=tournoi)
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}"
await ctx.author.add_roles(
touriste_role, tournoi_role, reason="Demande via le bot."
)
return f"{ctx.author.mention} à été ajouté comme spectateur dans à {tournoi}."
@group(name="team", invoke_without_command=True)
async def team(self, ctx):
@ -279,7 +276,9 @@ class TeamsCog(Cog, name="Teams"):
channel_name,
overwrites={
guild.default_role: discord.PermissionOverwrite(read_messages=False),
team_role: discord.PermissionOverwrite(read_messages=True),
team_role: discord.PermissionOverwrite(
read_messages=True, manage_channels=True
),
},
category=team_channel_category,
reason=f"{ctx.author.name} à demandé une channel pour son équipe.",

File diff suppressed because it is too large Load Diff

View File

@ -3,25 +3,31 @@ from pathlib import Path
from time import time
__all__ = [
"TOKEN",
"DISCORD_TOKEN",
"TFJM_TOKEN",
"Role",
"PROBLEMS",
"MAX_REFUSE",
"ROUND_NAMES",
"TEAMS_CHANNEL_CATEGORY",
"DIEGO",
"ANANAS",
"FAN_CLUBS",
"BOT",
"TOURNOIS",
"EMBED_COLOR",
"FRACTAL_URL",
"FRACTAL_COOLDOWN",
"File",
"Emoji",
]
TOKEN = os.environ.get("TFJM_DISCORD_TOKEN")
DISCORD_TOKEN = os.environ.get("TFJM_DISCORD_TOKEN")
TFJM_TOKEN = os.environ.get("TFJM_ORG_TOKEN")
if TOKEN is None:
if DISCORD_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:")
@ -32,10 +38,16 @@ if TOKEN is None:
GUILD = "690934836696973404"
DIEGO = 430566197868625920 # Mon id
ANANAS = 619132180408303616
FAN_CLUBS = {
DIEGO: 706586020841259078,
ANANAS: 706586027535368223,
}
BOT = 703305132300959754
TEAMS_CHANNEL_CATEGORY = "Channels d'équipes"
TEAMS_CHANNEL_CATEGORY = "Channels d'équipes 2"
EMBED_COLOR = 0xFFA500
FRACTAL_URL = "https://thefractal.space/img/{seed}.png?size=1500"
FRACTAL_URL = "https://thefractal.space/img/{seed}.png?size=1000"
FRACTAL_COOLDOWN = 30 # seconds
ROUND_NAMES = ["premier tour", "deuxième tour"]
TOURNOIS = [
@ -44,8 +56,7 @@ TOURNOIS = [
"Paris-Saclay",
"Paris-Avignon-Est",
"Tours",
"Bordeaux",
"Nancy",
"Bordeaux-Nancy",
"Rennes",
]
@ -55,6 +66,7 @@ class Role:
DEV = "dev"
ORGA = "Orga"
ORGAS = tuple(f"Orga {t}" for t in TOURNOIS)
JURY = tuple(f"Jury {t}" for t in TOURNOIS)
BENEVOLE = "Bénévole"
CAPTAIN = "Capitaine"
PARTICIPANT = "Participant"
@ -77,6 +89,7 @@ class File:
TEAMS = TOP_LEVEL / "data" / "teams"
JOKES = TOP_LEVEL / "data" / "jokes"
JOKES_V2 = TOP_LEVEL / "data" / "jokesv2"
MEMES = TOP_LEVEL / "data" / "memes"
with open(File.TOP_LEVEL / "data" / "problems") as f:

View File

@ -2,7 +2,7 @@ import asyncio
import sys
from importlib import reload
from discord import User, Message, Reaction, NotFound
from discord import User, Message, Reaction, NotFound, Forbidden
from discord.ext.commands import Bot
__all__ = ["CustomBot"]
@ -60,8 +60,12 @@ class CustomBot(Bot):
reaction, u = await bot.wait_for(
"reaction_add", check=check, timeout=timeout
)
the_msg = get(msgs, id=reaction.message.id)
the_msg: Message = get(msgs, id=reaction.message.id)
try:
await the_msg.delete()
except NotFound:
pass # message was deleted
msgs.remove(the_msg)
except asyncio.TimeoutError:
pass
@ -69,6 +73,6 @@ class CustomBot(Bot):
for m in msgs:
try:
await m.clear_reaction(Emoji.BIN)
except NotFound:
# Message or reaction deleted
except (NotFound, Forbidden):
# Message or reaction deleted / in dm channel
pass

View File

@ -5,7 +5,8 @@ from src.core import CustomBot
# We allow "! " to catch people that put a space in their commands.
# It must be in first otherwise "!" always match first and the space is not recognised
bot = CustomBot(("! ", "!"))
from src.utils import fg
# Global variable to hold the tirages.
# We *want* it to be global so we can reload the tirages cog without
@ -13,11 +14,13 @@ bot = CustomBot(("! ", "!"))
tirages = {}
def start():
bot = CustomBot(("! ", "!"))
@bot.event
async def on_ready():
print(f"{bot.user} has connected to Discord!")
bot.remove_command("help")
bot.load_extension("src.cogs.dev")
bot.load_extension("src.cogs.errors")
@ -26,6 +29,8 @@ bot.load_extension("src.cogs.teams")
bot.load_extension("src.cogs.tirages")
bot.load_extension("src.utils")
bot.run(DISCORD_TOKEN)
if __name__ == "__main__":
bot.run(TOKEN)
start()

View File

@ -4,6 +4,19 @@ import psutil
from discord.ext.commands import Bot
def fg(text, color: int = 0xFFA500):
r = color >> 16
g = color >> 8 & 0xFF
b = color & 0xFF
return f"\033[38;2;{r};{g};{b}m{text}\033[m"
def french_join(l):
l = list(l)
start = ", ".join(l[:-1])
return f"{start} et {l[-1]}"
def has_role(member, role: str):
"""Return whether the member has a role with this name."""