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 lgrez import config
from lgrez.blocs import tools
from lgrez.bdd import Action, BaseAction, Tache, Utilisation, ActionTrigger


[docs]def add_action(joueur, base, cooldown=0, charges=None, active=True): """Enregistre une action et programme son ouverture le cas échéant. Si une action existe déjà pour ce joueur et cette base, la modifie ; sinon, en crée une nouvelle. Args: joueur, base, cooldown, charges, active: Paramètre de l'action """ action = Action.query.filter_by(joueur=joueur, base=base).first() if action: action.cooldown = cooldown action.charges = charges action.active = active action.update() else: action = Action(joueur=joueur, base=base, cooldown=cooldown, charges=charges, active=active) action.add() # Ajout tâche ouverture if action.base.trigger_debut == ActionTrigger.temporel: # Temporel : on programme Tache(timestamp=tools.next_occurence(action.base.heure_debut), commande=f"!open {action.id}", action=action).add() elif action.base.trigger_debut == ActionTrigger.perma: # Perma : ON LANCE DIRECT (sera repoussé si jeu fermé) Tache(timestamp=datetime.datetime.now(), commande=f"!open {action.id}", action=action).add()
[docs]def delete_action(action): """Archive une action et annule les tâches en cours liées. Depuis la version 2.1, l'action n'est plus supprimée mais est passée à :attr:`~.bdd.Action.active` = ``False``. Args: action (.bdd.Action): l'action à supprimer """ # Suppression tâches liées à l'action if action.taches: Tache.delete(*action.taches) if action.is_open: action.utilisation_ouverte.close() action.active = False action.update()
# action.delete()
[docs]async def open_action(action): """Ouvre l'action. Args: action (.bdd.Action): l'action à ouvir 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 du joueur. """ joueur = action.joueur chan = joueur.private_chan # Vérification base if not action.base: await tools.log(f"{action} : pas de base, exit") return # Vérification active if not action.active: await tools.log(f"{action} : inactive, exit (pas de reprogrammation)") return # Vérification cooldown if action.cooldown > 0: # Action en cooldown action.cooldown = action.cooldown - 1 config.session.commit() await tools.log( f"{action} : en cooldown, exit " "(reprogrammation si temporel)." ) if action.base.trigger_debut == ActionTrigger.temporel: # Programmation action du lendemain ts = tools.next_occurence(action.base.heure_debut) Tache(timestamp=ts, commande=f"!open {action.id}", action=action).add() return # Vérification role_actif if not joueur.role_actif: # role_actif == False : on reprogramme la tâche au lendemain tanpis await tools.log( f"{action} : role_actif == False, exit " "(reprogrammation si temporel)." ) if action.base.trigger_debut == ActionTrigger.temporel: ts = tools.next_occurence(action.base.heure_debut) Tache(timestamp=ts, commande=f"!open {action.id}", action=action).add() return # Vérification charges if action.charges == 0: # Plus de charges, mais action maintenue en base car refill / ... await tools.log( f"{action} : plus de charges, exit " "(reprogrammation si temporel)." ) return # Action "automatiques" (passives : notaire...) : # lance la procédure de clôture / résolution if action.base.trigger_fin == ActionTrigger.auto: if action.base.trigger_debut == ActionTrigger.temporel: await tools.log( f"Action {action.base.slug} pour {joueur.nom} pas vraiment " f"automatique, {config.Role.mj.mention} VENEZ M'AIDER " "JE PANIQUE 😱 (comme je suis vraiment sympa je vous " f"file son chan, {chan.mention})" ) else: await tools.log( f"{action} : automatique, appel processus de clôture" ) await close_action(action) return # Tous tests préliminaires n'ont pas return ==> Vraie action à lancer # Calcul heure de fin (si applicable) heure_fin = None if action.base.trigger_fin == ActionTrigger.temporel: heure_fin = action.base.heure_fin ts = tools.next_occurence(heure_fin) elif action.base.trigger_fin == ActionTrigger.delta: # Si delta, on calcule la vraie heure de fin (pas modifié en base) delta = action.base.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.base.trigger_fin in [ActionTrigger.temporel, ActionTrigger.delta]: Tache(timestamp=ts - datetime.timedelta(minutes=30), commande=f"!remind {action.id}", action=action).add() Tache(timestamp=ts, commande=f"!close {action.id}", action=action).add() elif action.base.trigger_fin == ActionTrigger.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: # Réopen le lendamain Tache(timestamp=ts_matin, commande=f"!open {action.id}", action=action).add() else: # Sauf si pause d'ici là Tache(timestamp=ts_pause, commande=f"!close {action.id}", action=action).add() # Information du joueur if action.is_open: # déjà ouverte message = await chan.send( f"{tools.montre()} Rappel : tu peux utiliser quand tu le " f"souhaites ton action {tools.code(action.base.slug)} ! " f" {config.Emoji.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: # on ouvre ! util = Utilisation(action=action) util.add() util.open() message = await chan.send( f"{tools.montre()} Tu peux maintenant utiliser ton action " f"{tools.code(action.base.slug)} ! {config.Emoji.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(config.Emoji.action) config.session.commit()
[docs]async def close_action(action): """Ferme l'action. Args: action (.bdd.Action): l'action à enregistrer Opérations réalisées : - Archivage si nécessaire ; - Gestion des tâches planifiées (planifie prochaine ouverture si applicable) ; - Information du joueur (si charge-- seulement). """ joueur = action.joueur chan = joueur.private_chan # Vérification base if not action.base: await tools.log(f"{action} : pas de base, exit") return # Vérification active if not action.active: await tools.log(f"{action} : inactive, exit") return # Vérification ouverte if not action.is_open: await tools.log(f"{action} : pas ouverte, exit") return deleted = False if not action.is_waiting: # décision prise # Résolution de l'action # (pour l'instant juste charge -= 1 et suppression le cas échéant) if action.charges: action.charges = action.charges - 1 pcs = (" pour cette semaine" if "weekends" in action.base.refill else "") await chan.send(f"Il te reste {action.charges} charge(s){pcs}.") if action.charges == 0 and not action.base.refill: delete_action(action) deleted = True if not deleted: # Si l'action a été faite et a un cooldown, on le met if (not action.is_waiting) and (action.base.base_cooldown > 0): action.cooldown = action.base.base_cooldown action.utilisation_ouverte.close() # Programmation prochaine ouverture if action.base.trigger_debut == ActionTrigger.temporel: ts = tools.next_occurence(action.base.heure_debut) Tache(timestamp=ts, commande=f"!open {action.id}", action=action).add() elif action.base.trigger_debut == ActionTrigger.perma: # Action permanente : ouvrir après le WE ts = tools.fin_pause() Tache(timestamp=ts, commande=f"!open {action.id}", action=action).add() config.session.commit()
[docs]def get_actions(quoi, trigger, heure=None): """Renvoie les actions répondant à un déclencheur donné. Args: quoi (str): Type d'opération en cours : - ``"open"`` : ouverture, :attr:`Action.is_open` doit être faux; - ``"close"`` : fermeture, :attr:`Action.is_open` doit être vrai; - ``"remind"`` : rappel, :attr:`Action.is_waiting` doit être vrai. trigger (bdd.ActionTrigger): valeur de ``Action.trigger_debut/fin`` à détecter. heure (datetime.time): si ``trigger == "temporel"``, ajoute la condition ``Action.heure_debut/fin == heure``. Returns: Sequence[.bdd.Action]: La liste des actions correspondantes. """ criteres = Action.active.is_(True) if quoi == "open": criteres &= Action.base.has(trigger_debut=trigger) & ~Action.is_open elif quoi == "close": criteres &= Action.base.has(trigger_fin=trigger) & Action.is_open elif quoi == "remind": criteres &= Action.base.has(trigger_fin=trigger) & Action.is_waiting else: raise commands.UserInputError(f"bad value for quoi: '{quoi}'") if trigger == ActionTrigger.temporel: if not heure: raise commands.UserInputError("merci de préciser une heure") if quoi == "open": criteres &= Action.base.has(heure_debut=heure) else: # close / remind criteres &= Action.base.has(heure_fin=heure) return Action.query.filter(criteres).all()