Code source de lgrez.blocs.one_command
"""lg-rez / blocs / Limitation à une commande
Système maison pour ne pas pouvoir utiliser plus d'une commande
à la fois
"""
import asyncio
from discord.ext import commands
from lgrez import config
#: list[~discord.ext.command.Command]: Commands exempted from
#: one_command limitation, registered by :func:`.do_not_limit`
exempted = []
[docs]class AlreadyInCommand(commands.CheckFailure):
"""Salon déjà occupé par une commande non finissable.
Exception levée lorsqu'un membre veut lancer une commande dans un
salon où une commande est déjà en cours d'exécution, et que cette
commande n'a pas pu être arrêtée (ou pas assez rapidement) par
un message ``"stop"``.
Dérive de :exc:`discord.ext.commands.CheckFailure`.
"""
pass
[docs]async def not_in_command(ctx):
"""Check : assure qu'une commande n'est pas en cours dans ce salon.
Fonction à utiliser comme check global, pour toutes les commandes
(enregistrer avec :meth:`~discord.ext.commands.Bot.add_check`)
Args:
ctx (discord.ext.commands.Context): contexte d'invocation
de la commande.
Returns:
``True``
Raises:
:exc:`AlreadyInCommand`.
"""
if ctx.command in exempted:
return True # Commandes exemptées
if ctx.channel.id not in config.bot.in_command:
return True # Channel libre
# On envoie (discrètement) l'ordre d'arrêter la commande précédente
await ctx.send(config.stop_keywords[0], delete_after=0)
# On attend qu'il soit pris en compte
await asyncio.sleep(1)
if ctx.channel.id in config.bot.in_command: # Si ça n'a pas suffit
raise AlreadyInCommand() # on raise l'erreur
return True
# @bot.before_invoke
[docs]async def add_to_in_command(ctx):
"""Ajoute le channel à la liste des channels dans une commande.
Fonction à appeller avant chaque appel de fonction
(enregistrer avec :meth:`~discord.ext.commands.Bot.before_invoke`)
Elle est appellée seulement si les checks sont OK, donc pas si le
salon est déjà dans :attr:`config.bot.in_command <.LGBot.in_command>`.
Args:
ctx (discord.ext.commands.Context): contexte d'invocation de
la commande.
"""
if ctx.command not in exempted and not ctx.message.webhook_id:
config.bot.in_command.append(ctx.channel.id)
# @bot.after_invoke
[docs]async def remove_from_in_command(ctx):
"""Retire le channel de la liste des channels dans une commande.
Fonction à appeller après chaque appel de fonction.
(enregistrer avec :meth:`~discord.ext.commands.Bot.after_invoke`)
Elle attend 0.1 secondes avant d'enlever le joueur afin d'éviter
que le bot réagisse « nativement » (IA) à un message déjà traité
par un :func:`.tools.wait_for_message` ayant mené à la fin de la
commande.
Args:
ctx (discord.ext.commands.Context): contexte d'invocation de
la commande.
"""
await asyncio.sleep(0.1) # On attend un peu
if (ctx.channel.id in config.bot.in_command
and ctx.command not in exempted):
config.bot.in_command.remove(ctx.channel.id)
class _Bypasser():
def __init__(self, ctx):
self.ctx = ctx
def __enter__(self):
config.bot.in_command.remove(self.ctx.channel.id)
return self
def __exit__(self, exc_type, exc, tb):
config.bot.in_command.append(self.ctx.channel.id)
[docs]def bypass(ctx):
"""Context manager: bypass one-command limitation.
Args:
ctx (discord.ext.commands.Context): running command context.
Use in a command callback to launch a second one without problems::
with one_command.bypass(ctx):
await config.bot.process_commands(some_message)
"""
return _Bypasser(ctx)
[docs]def do_not_limit(command):
"""Decorator for commands not concerned by one_command limitation.
Register the command in :attr:`.one_command.exempted`.
Args:
command (discord.ext.commands.Command): the command to register.
Returns:
:class:`discord.ext.commands.Command`
"""
if not isinstance(command, commands.Command):
raise TypeError("one_command.not_limited is a decorator for Commands")
exempted.append(command)
return command