Exécution de commande dynamique, analyse et stockage.
Dyncommands vous permet d'importer et d'exécuter dynamiquement les fonctions Python. Utile pour ajouter des commandes aux chatbots IRC ou aux applications CLI sans redémarrage.
Lors de l'analyse d'une chaîne, il sépare le nom de commande des arguments et exécute la fonction stockée avec ces arguments. Chaque fois que l'analyseur est appelé, vous pouvez transmettre vos propres kwargs personnalisés auxquels la commande aura accès.
Tous les modules de commande sont compilés via RestRitedPython avant d'être autorisés à s'exécuter. Vous pouvez désactiver l'exécution restreinte en définissant CommandParser._unrestricted à TRUE , bien que cela soit très découragé lors de l'exécution du code non fiable.
from pathlib import Path
from dyncommands import CommandParser , CommandContext , CommandSource
output : str = ''
def callback ( text , * args ):
global output
output = text
path = Path ( 'path/to/directory' ) # Must be a directory with a `commands.json` file in it
parser = CommandParser ( path ) # Create the parser, which initializes using data located in the path directory
source = CommandSource ( callback ) # Create a source, which is used to talk back to the caller
input_ = 'command-that-returns-wow arg1 arg2' # this command would call zzz__command-that-returns-wow.py with arg1 and arg2
parser . parse ( CommandContext ( input_ , source )) # Parse the new context and run the command and callback (If no errors occur)
assert output == 'wow' Les métadonnées des commandes sont stockées dans le fichier commands.json du répertoire CommandParser.commands_path . C'est là que toutes les données de l'analyseur sont chargées et stockées.
Tous les fichiers commands.json sont validés avec des schémas JSON via le package JSonschema Python
| clé | taper | description | défaut | requis |
|---|---|---|---|---|
commandPrefix | chaîne | Les cordes doivent commencer par ce préfixe, sinon il est ignoré. La chaîne vide accepte tout. | N / A | Oui |
commands | array [ Command ] | Contient des métadonnées pour les modules de commande stockés. | N / A | Oui |
| clé | taper | description | défaut | requis |
|---|---|---|---|---|
name | chaîne | Identifie de manière unique la commande au CommandParser. | N / A | Oui |
usage | chaîne | Informations d'utilisation (comment utiliser les args). | "" | Non |
description | chaîne | Description de la commande. | "" | Non |
permission | entier | Le niveau d'autorisation que le commandement nécessite d'exécuter la commande. | 0 | Non |
function | booléen , nul | S'il existe un module Python associé à charger. | nul | Non |
children | array [ Command ] | Sous-communs; Ceux-ci sont gérés par la fonction du parent. (Pas de modules associés pour eux-mêmes). | [] | Non |
overridable | booléen | Si le CommandPaSer peut remplacer les données à l'intérieur de cet objet (doit être activé manuellement). | vrai | Non |
disabled | booléen | Si True Chargez toujours la commande, mais augmentez un DisabledError lors de la tentative d'exécution. | FAUX | Non |
Remarque: Les modules des commandes ne sont chargés que s'ils sont répertoriés dans commands.json avec la clé function définie sur true .
commands.json Contenu: {
"commandPrefix" : " ! " ,
"commands" : [
{
"name" : " test " ,
"usage" : " test [*args:any] " ,
"description" : " Test command. " ,
"permission" : 500 ,
"function" : true
},
{
"name" : " test2 " ,
"function" : false
}
]
} Les commandes chargées dynamiquement sont désignées par nom de fichier avec un préfixe de "ZZZ__". À l'intérieur d'un module de commande, il y a une fonction définie comme command . Cette fonction sera mappée à l'attribut de fonction d' Command et stockée en mémoire pour l'exécution. La fonction a accès à tous les args analysés, ainsi qu'aux kwargs:
« Self » ( Command ), qui abrite les métadonnées de la commande exécutée.
« Parser » ( CommandParser ), qui stocke la liste des commandes et des données de commande enregistrées.
« Context » ( CommandContext ), qui fournit la CommandSource et le texte d'origine envoyé pour l'analyse.
CommandParser.parse(context: CommandContext, **kwargs) . Étant donné que les commandes ne peuvent pas importer leurs propres modules, certains sont inclus dans les globaux ( math , random et string ). Les autres attributs inclus dans la portée globale sont les suivants: getitem ( operator.getItem ), et ImproperUsageError ( dyncommands.exception.improperUsageError ).
def command ( * args , ** kwargs ):
self , context = kwargs . pop ( 'self' ), kwargs . pop ( 'context' )
source = context . source
if len ( args ) == 2 :
amount , sides = abs ( int ( getitem ( args , 0 ))), abs ( int ( getitem ( args , 1 )))
if amount > 0 and sides > 0 :
dice_rolls = [ f" { ( str ( i + 1 ) + ':' ) if amount > 1 else '' } { str ( random . randint ( 1 , sides )) } / { sides } " for i in range ( amount )]
source . send_feedback ( f"/me U0001f3b2 { source . display_name } rolled { 'a die' if amount == 1 else str ( amount ) + ' dice' } with { sides } side { '' if sides == 1 else 's' } : { ', ' . join ( dice_rolls ) } U0001f3b2 " )
else :
raise ImproperUsageError ( self , context )
else :
raise ImproperUsageError ( self , context ) À tout moment, vous pouvez appeler CommandParser.reload() pour recharger tous les modules de commande et métadonnées à partir du stockage du disque.
../
│
├───[commands_path]/
│ ├─── commands.json
│ ├─── zzz__[command1].py
│ ├─── zzz__[command2].py
│ └─── zzz__[command3].py
│
Pour ajouter des commandes, vous pouvez soit saisir manuellement les données dans un fichier commands.json , soit utiliser la méthode CommandParser.add_command(text: str, link: bool = False, **kwargs) . La façon la plus simple d'utiliser cette méthode consiste à lire le module de commande comme texte et à passer cela au premier argument. Vous pouvez également stocker des modules de commande en ligne pour permettre une installation à distance, car la définition du paramètre de lien vers True lira le texte en tant que lien, et obtiendra les données de texte brutes à partir de ce lien. Ex: Gist et Pastebin.
Remarque: Lors de l'ajout d'une commande, les métadonnées du «nom» doivent être remplies. Cela peut être fait sous forme de commentaires.
La suppression d'une commande déjà ajoutée est relativement facile. Appelez simplement CommandParser.remove_command(name: str) avec le nom de la commande que vous souhaitez supprimer, et il supprimera à la fois les métadonnées et le module de commande du disque.
Si vous ne souhaitez pas supprimer la commande lors de la suppression, une meilleure alternative consiste à le désactiver avec CommandParser.set_disabled(name: str, value: bool) .
# Name: points
# Usage: points [get (username:string) | set (username:string amount:integer)]
# Description: Get your current points
# Permission: 0
# Children: [{'name': 'get', 'usage': 'get (username:string)', 'permission':0}, {'name': 'set', 'usage': 'set (username:string amount:integer)', 'permission':500}]
def command ( * args , ** kwargs ):
... parser = CommandParser ( './' )
with open ( 'some_metadata.json' ) as _file :
get_ = { 'name' : 'get' , 'usage' : 'get (username:string)' , 'permission' : 0 }
set_ = { 'name' : 'set' , 'usage' : 'set (username:string amount:integer)' , 'permission' : 500 }
children = [ get_ , set_ ]
parser . add_command ( _file . read (), name = 'my-command' , description = 'Command with child commands.' , children = children ) parser = CommandParser ( './' )
with open ( 'some_metadata.json' ) as _file :
metadata = json . load ( _file )
parser . add_command ( 'https://gist.github.com/random/892hdh2fh389x0wcmksio7m' , link = True , ** metadata ) Le Dyncommand CommandParser prend en charge Nativement le traitement du niveau d'autorisation, vous n'avez donc pas à implémenter un système similaire dans chaque fonction de commande.
Chaque commande a l' permission de valeur de métadonnées (à l'exception de la valeur spéciale -1 ) est le niveau d'autorisation minimum requis de la CommandSource . -1 représente une exigence "infinie", où aucune CommandSource ne pourra l'exécuter alors que le système d'autorisation est actif.
Pour désactiver le système d'autorisation, définissez l'attribut _ignore_permission de CommandParser à true. Remarque: Étant donné que cet attribut commence par un "_", la tentative de la modification de la fonction d'une commande entraînera une compilation échouée et une exception.