Code source de lgrez.features.gestion_actions

"""lg-rez / features / Gestion des actions

Liste, création, suppression, ouverture, fermeture d'actions

"""

import datetime

from discord.ext import commands
from sqlalchemy.sql.expression import and_, or_, not_

from lgrez.blocs import tools, bdd, bdd_tools
from lgrez.blocs.bdd import Actions, BaseActions, Joueurs, Taches
from lgrez.features import taches


[docs]async def get_actions(quoi, trigger, heure=None): """Renvoie la liste des actions répondant à un déclencheur donné Args: quoi (:class:`str`): Type d'opération en cours : - ``"open"`` : ouverture : ``Actions.decision_`` doit être None - ``"close"`` : fermeture : ``Actions.decision_`` ne doit pas être None - ``"remind"`` : rappel : ``Actions.decision_`` doit être "rien" trigger (:class:`str`): valeur de ``Actions.trigger_debut/fin`` à détecter heure (:class:`datetime.time`): si ``trigger == "temporel"``, ajoute la condition ``Actions.heure_debut/fin == heure`` """ if trigger == "temporel": if not heure: raise ValueError("Merci de préciser une heure......\n https://tenor.com/view/mr-bean-checking-time-waiting-gif-11570520") if quoi == "open": criteres = and_(Actions.trigger_debut == trigger, Actions.heure_debut == heure, Actions.decision_ == None) # Objets spéciaux SQLAlchemy : LAISSER le == ! elif quoi == "close": criteres = and_(Actions.trigger_fin == trigger, Actions.heure_fin == heure, Actions.decision_ != None) # Objets spéciaux SQLAlchemy : LAISSER le == ! elif quoi == "remind": criteres = and_(Actions.trigger_fin == trigger, Actions.heure_fin == heure, Actions.decision_ == "rien") else: if quoi == "open": criteres = and_(Actions.trigger_debut == trigger, Actions.decision_ == None) elif quoi == "close": criteres = and_(Actions.trigger_fin == trigger, Actions.decision_ != None) elif quoi == "remind": criteres = and_(Actions.trigger_fin == trigger, Actions.decision_ == "rien") return Actions.query.filter(criteres).all()
[docs]async def open_action(ctx, action, chan=None): """Ouvre une action Args: ctx (:class:`~discord.ext.commands.Context`): contexte quelconque (de ``!open``, ``!sync``) action (:class:`.bdd.Actions`): action à ouvrir chan (:class:`~discord.TextChannel`): salon ou informer le joueur concerné, par défaut son chan privé Opérations réalisées : - Vérification des conditions (cooldown, charges...) et reprogrammation si nécessaire ; - Gestion des tâches planifiées (planifie remind/close si applicable) ; - Information joueur dans ``chan``. """ joueur = Joueurs.query.get(action.player_id) assert joueur, f"!open_action : joueur de {action} introuvable" if not chan: # chan non défini ==> chan perso du joueur chan = ctx.guild.get_channel(joueur.chan_id_) assert chan, f"!open_action : chan privé de {joueur} introuvable" # Vérification cooldown if action.cooldown > 0: # Action en cooldown bdd_tools.modif(action, "cooldown", action.cooldown - 1) bdd.session.commit() await ctx.send(f"Action {action} : en cooldown, exit (reprogrammation si temporel).") if action.trigger_debut == "temporel": # Programmation action du lendemain ts = tools.next_occurence(action.heure_debut) taches.add_task(ctx.bot, ts, f"!open {action.id}", action=action.id) return # Vérification role_actif if not joueur.role_actif: # role_actif == False : on reprogramme la tâche au lendemain, tanpis await ctx.send(f"Action {action} : role_actif == False, exit (reprogrammation si temporel).") if action.trigger_debut == "temporel": ts = tools.next_occurence(action.heure_debut) taches.add_task(ctx.bot, ts, f"!open {action.id}", action=action.id) return # Vérification charges if action.charges == 0: # Plus de charges, mais action maintenue en base car refill / ... await ctx.send(f"Action {action} : plus de charges, exit (reprogrammation si temporel).") return # Action "automatiques" (passives : notaire...) : lance la procédure de clôture / résolution if action.trigger_fin == "auto": if action.trigger_debut == "temporel": await ctx.send(f"Action {action.action} pour {Joueurs.query.get(action.player_id).nom} pas vraiment automatique, {tools.mention_MJ(ctx)} VENEZ M'AIDER JE PANIQUE 😱 (comme je suis vraiment sympa je vous file son chan, {tools.private_chan(ctx.guild.get_member(Joueurs.query.get(action.player_id).discord_id)).mention})") else: await ctx.send(f"Action automatique, appel processus de clôture") await close_action(ctx, action, chan) return # Tous tests préliminaires n'ont pas return ==> Vraie action à lancer # Calcul heure de fin (si applicable) heure_fin = None if action.trigger_fin == "temporel": heure_fin = action.heure_fin ts = tools.next_occurence(heure_fin) elif action.trigger_fin == "delta": # Si delta, on calcule la vraie heure de fin (pas modifié en base) delta = action.heure_fin ts = datetime.datetime.now() + datetime.timedelta(hours=delta.hour, minutes=delta.minute, seconds=delta.second) heure_fin = ts.time() # Programmation remind / close if action.trigger_fin in ["temporel", "delta"]: taches.add_task(ctx.bot, ts - datetime.timedelta(minutes=30), f"!remind {action.id}", action=action.id) taches.add_task(ctx.bot, ts, f"!close {action.id}", action=action.id) elif action.trigger_fin == "perma": # Action permanente : fermer pour le WE ou rappel / réinitialisation chaque jour ts_matin = tools.next_occurence(datetime.time(hour=7)) ts_pause = tools.debut_pause() if ts_matin < ts_pause: taches.add_task(ctx.bot, ts_matin, f"!open {action.id}", action=action.id) # Réopen le lendamain else: taches.add_task(ctx.bot, ts_pause, f"!close {action.id}", action=action.id) # Sauf si pause d'ici là # Information du joueur if action.decision_ == "rien": # déjà ouverte message = await chan.send( f"""{tools.montre()} Rappel : tu peux utiliser quand tu le souhaites ton action {tools.code(action.action)} ! {tools.emoji(ctx, "action")} \n""" + (f"""Tu as jusqu'à {heure_fin} pour le faire. \n""" if heure_fin else "") + tools.ital(f"""Tape {tools.code('!action (ce que tu veux faire)')} ou utilise la réaction pour agir.""")) else: bdd_tools.modif(action, "decision_", "rien") message = await chan.send( f"""{tools.montre()} Tu peux maintenant utiliser ton action {tools.code(action.action)} ! {tools.emoji(ctx, "action")} \n""" + (f"""Tu as jusqu'à {heure_fin} pour le faire. \n""" if heure_fin else "") + tools.ital(f"""Tape {tools.code('!action (ce que tu veux faire)')} ou utilise la réaction pour agir.""")) await message.add_reaction(tools.emoji(ctx, "action")) bdd.session.commit()
[docs]async def close_action(ctx, action, chan=None): """Ferme une action Args: ctx (:class:`discord.ext.commands.Context`): contexte quelconque, (de ``!open``, ``!sync``)... action (:class:`.bdd.Actions`): action à clôturer chan (:class:`discord.TextChannel`): salon ou informer le joueur concerné, par défaut son chan privé Opérations réalisées : - Suppression si nécessaire ; - Gestion des tâches planifiées (planifie prochaine ouverture si applicable) ; - Information joueur dans <chan>. """ joueur = Joueurs.query.get(action.player_id) assert joueur, f"!open_action : joueur de {action} introuvable" if not chan: # chan non défini ==> chan perso du joueur chan = ctx.guild.get_channel(joueur.chan_id_) assert chan, f"!open_action : chan privé de {joueur} introuvable" deleted = False if action.decision_ != "rien" and not action.instant: # Résolution de l'action (pour l'instant juste charge -= 1 et suppression le cas échéant) if action.charges: bdd_tools.modif(action, "charges", action.charges - 1) pcs = " pour cette semaine" if "weekends" in action.refill else "" await chan.send(f"Il te reste {action.charges} charge(s){pcs}.") if action.charges == 0 and not action.refill: bdd.session.delete(action) deleted = True if not deleted: bdd_tools.modif(action, "decision_", None) # Si l'action a un cooldown, on le met ba = BaseActions.query.get(action.action) if ba and ba.base_cooldown > 0: bdd_tools.modif(action, "cooldown", ba.base_cooldown) # Programmation prochaine ouverture if action.trigger_debut == "temporel": ts = tools.next_occurence(action.heure_debut) taches.add_task(ctx.bot, ts, f"!open {action.id}", action=action.id) elif action.trigger_debut == "perma": # Action permanente : ouvrir après le WE ts = tools.fin_pause() taches.add_task(ctx.bot, ts, f"!open {action.id}", action=action.id) bdd.session.commit()
[docs]def add_action(ctx, action): """Enregistre et programme l'ouverture d'une action Args: ctx (:class:`~discord.ext.commands.Context`): contexte quelconque (de ``!open``, ``!sync``...) action (:class:`.bdd.Actions`): action à enregistrer """ bdd.session.add(action) bdd.session.commit() # Ajout tâche ouverture if action.trigger_debut == "temporel": # Temporel : on programme taches.add_task(ctx.bot, tools.next_occurence(action.heure_debut), f"!open {action.id}", action=action.id) if action.trigger_debut == "perma": # Perma : ON LANCE DIRECT taches.add_task(ctx.bot, datetime.datetime.now(), f"!open {action.id}", action=action.id)
[docs]def delete_action(ctx, action): """Supprime une action et annule les tâches en cours liées Args: ctx (:class:`~discord.ext.commands.Context`): contexte quelconque (de ``!open``, ``!sync``...) action (:class:`.bdd.Actions`): action à supprimer """ bdd.session.delete(action) bdd.session.commit() # Suppression tâches liées à l'action for tache in Taches.query.filter_by(action=action.id).all(): taches.cancel_task(ctx.bot, tache)