Code source de lgrez.features.annexe

"""lg-rez / features / Commandes annexes

Commandes diverses qu'on ne savait pas où ranger

"""

import random
import requests
import datetime

from discord.ext import commands
from akinator.async_aki import Akinator

from lgrez.blocs import tools
from lgrez.bdd import Joueur, Role, Camp


[docs]class Annexe(commands.Cog): """Commandes annexes aux usages divers""" next_roll = None @commands.command() async def roll(self, ctx, *, XdY): """Lance un ou plusieurs dés Args: XdY: dés à lancer + modifieurs, au format ``XdY + XdY + ... + Z - Z ...`` avec X le nombre de dés, Y le nombre de faces et Z les modifieurs (constants) ; OU roll spécial : ``joueur`` / ``vivant`` / ``mort`` / ``rôle`` / ``camp``. Examples: - ``!roll 1d6`` -> lance un dé à 6 faces - ``!roll 1d20 +3`` -> lance un dé à 20 faces, ajoute 3 au résultat - ``!roll 1d20 + 2d6 -8`` -> lance un dé 20 plus deux dés 6, enlève 8 au résultat - ``!roll vivant`` -> choisit un joueur vivant """ inp = XdY.lower() # Rolls spéciaux result = None if inp in ["joueur", "joueurs"]: result = random.choice(Joueur.query.all()).nom elif inp in ["vivant", "vivants"]: jr = random.choice(Joueur.query.filter(Joueur.est_vivant).all()) result = jr.nom elif inp in ["mort", "morts"]: jr = random.choice(Joueur.query.filter(Joueur.est_mort).all()) result = jr.nom elif inp in ["role", "rôle", "roles", "rôles"]: role = random.choice(Role.query.filter_by(actif=True).all()) result = role.nom_complet elif inp in ["camp", "camps"]: result = random.choice(Camp.query.filter_by(public=True).all()).nom elif inp in ["ludo", "ludopathe"]: result = random.choice(["Voyante", "Protecteur", "Notaire", "Popo de mort", "Chat-garou", "Espion"]) elif inp in ["taverne", "tavernier"]: result = random.choice(["Rôle choisi", "Vrai rôle", "Rôle random"]) if result: if self.next_roll is not None: result = self.next_roll self.next_roll = None await ctx.reply(result) return parts = inp.replace(" ", "").replace("-", "+-").split("+") # "1d6 + 5 - 2" -> ["1d6", "5", "-2"] sum = 0 rep = "" for part in parts: if not part: continue if "d" in part: # Lancer de dé nb, _, faces = part.partition("d") try: nb, faces = int(nb), int(faces) if faces < 1: raise ValueError except ValueError: raise commands.UserInputError( f"Pattern de dé non reconu : {part}" ) # Sécurité if abs(nb) > 1000 or faces > 1000000: await ctx.reply( "Suite à des abus (coucou Gabin), il est " "interdit de lancer plus de 1000 dés ou " "des dés à plus de 1 million de faces." ) return sig = -1 if nb < 0 else 1 sig_s = "-" if nb < 0 else "+" for _ in range(abs(nb)): val = random.randrange(faces) + 1 if self.next_roll is not None: try: val = int(self.next_roll) except ValueError: pass else: self.next_roll = None sum += sig * val rep += f" {sig_s} {val}{tools.sub_chiffre(faces, True)}₎" else: # Bonus / malus fixe try: val = int(part) except ValueError: raise commands.UserInputError( f"Pattern fixe non reconu : {part}" ) sum += val rep += f" {'-' if val < 0 else '+'} {abs(val)}" # Total sig = "- " if sum < 0 else "" rep += f" = {sig}{tools.emoji_chiffre(abs(sum), True)}" rep = rep[3:] if rep.startswith(" +") else rep await tools.send_blocs(ctx, rep) @commands.command() @tools.mjs_only async def nextroll(self, ctx, *, next=None): """✨ Shhhhhhhhhhhh. Ça sent la magouilleuh """ self.next_roll = next await ctx.message.add_reaction("🤫") @commands.command(aliases=["cf", "pf"]) async def coinflip(self, ctx): """Renvoie le résultat d'un tirage à Pile ou Face (aléatoire) Pile je gagne, face tu perds. """ await ctx.send(random.choice(["Pile", "Face"])) @commands.command(aliases=["pong"]) async def ping(self, ctx): """Envoie un ping au bot Pong Warning: Commande en bêta, non couverte par les tests unitaires. """ ts_rec = datetime.datetime.utcnow() delta_rec = ts_rec - ctx.message.created_at # Temps de réception = temps entre création message et sa réception pingpong = ctx.invoked_with.replace("i", "x").replace("I", "X") pingpong = pingpong.replace("o", "i").replace("O", "I") pingpong = pingpong.replace("x", "o").replace("X", "O") cont = ( f" Réception : {delta_rec.total_seconds()*1000:4.0f} ms\n" f" Latence : {ctx.bot.latency*1000:4.0f} ms\n" ) mess = await ctx.send( f"!{pingpong}\n" + tools.code_bloc(cont + " (...)") ) ts_ret = datetime.datetime.utcnow() delta_ret = ts_ret - mess.created_at # Retour information message réponse créé delta_env = ts_ret - ts_rec - delta_ret # Temps d'envoi = temps entre réception 1er message (traitement quasi # instantané) et création 2e, moins temps de retour d'information delta_tot = delta_rec + delta_ret + delta_env # Total = temps entre création message !pong et réception information # réponse envoyée await mess.edit(content=f"!{pingpong}\n" + tools.code_bloc( cont + f" Envoi : {delta_env.total_seconds()*1000:4.0f} ms\n" f" Retour : {delta_ret.total_seconds()*1000:4.0f} ms\n" f"——————————————————————\n" f" Total : {delta_tot.total_seconds()*1000:4.0f} ms" )) @commands.command() async def akinator(self, ctx): """J'ai glissé chef Implémentation directe de https://pypi.org/project/akinator.py Warning: Commande en bêta, non couverte par les tests unitaires. """ # Un jour mettre ça dans des embeds avec les https://fr.akinator.com/ # bundles/elokencesite/images/akitudes_670x1096/<akitude>.png croppées, # <akitude> in ["defi", "serein", "inspiration_legere", # "inspiration_forte", "confiant", "mobile", "leger_decouragement", # "vrai_decouragement", "deception", "triomphe"] await ctx.send( "Vous avez demandé à être mis en relation avec " + tools.ital("Akinator : Le Génie du web") + ".\nVeuillez patienter..." ) async with ctx.typing(): # Connexion aki = Akinator() question = await aki.start_game(language="fr") exit = False while not exit and aki.progression <= 80: mess = await ctx.send(f"({aki.step + 1}) {question}") reponse = await tools.wait_for_react_clic( mess, {"👍": "yes", "🤷": "idk", "👎": "no", "⏭️": "stop"} ) if reponse == "stop": exit = True else: async with ctx.typing(): question = await aki.answer(reponse) async with ctx.typing(): await aki.win() mess = await ctx.send( f"Tu penses à {tools.bold(aki.first_guess['name'])} " f"({tools.ital(aki.first_guess['description'])}) !\n" f"J'ai bon ?\n{aki.first_guess['absolute_picture_path']}" ) if await tools.yes_no(mess): await ctx.send( "Yay\nhttps://fr.akinator.com/bundles/elokencesite" "/images/akitudes_670x1096/triomphe.png" ) else: await ctx.send( "Oof\nhttps://fr.akinator.com/bundles/elokencesite" "/images/akitudes_670x1096/deception.png" ) @commands.command() async def xkcd(self, ctx, N): """J'ai aussi glissé chef, mais un peu moins Args: N: numéro du comic Warning: Commande en bêta, non couverte par les tests unitaires. """ async with ctx.typing(): r = requests.get(f"https://xkcd.com/{N}/info.0.json") if not r: await ctx.send("Paramètre incorrect ou service non accessible.") return url = r.json().get("img") if not url: await ctx.send("Paramètre incorrect ou service non accessible.") return await ctx.send(url)