Динамическое выполнение команды, анализ и хранение.
DynCommands позволяет динамически импортировать и запускать функции Python. Полезно для добавления команд в IRC Chatbots или CLI -приложения без перезапуска.
При анализе строки она отделяет имя команды от аргументов и выполняет хранимую функцию с этими аргументами. Каждый раз, когда называется анализатор, вы можете передавать свои собственные собственные Kwargs, к которым будет иметь доступ к команде.
Все командные модули скомпилируются через ограниченную синтинон, прежде чем будет разрешено работать. Вы можете отключить ограниченное выполнение, установив CommandParser._unrestricted true , хотя это сильно обескуражено при запуске нерешенного кода.
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' Метаданные для команд хранятся в файле commands.json в каталоге CommandParser.commands_path . Именно здесь все данные для анализатора загружаются и хранятся.
All commands.json файлы проверены с помощью схем JSON через пакет Python Jsonschema Python
| ключ | тип | описание | по умолчанию | необходимый |
|---|---|---|---|---|
commandPrefix | нить | Строки должны начинаться с этого префикса, в противном случае он игнорируется. Пустая строка принимает все. | N/a | Да |
commands | массив [ Команда ] | Содержит метаданные для хранимых командных модулей. | N/a | Да |
| ключ | тип | описание | по умолчанию | необходимый |
|---|---|---|---|---|
name | нить | Уникально идентифицирует команду CommandParser. | N/a | Да |
usage | нить | Информация об использовании (как использовать ARG). | "" | Нет |
description | нить | Описание команды. | "" | Нет |
permission | целое число | Уровень разрешения, который требует команды для запуска команды. | 0 | Нет |
function | логический , нулевый | Существует ли связанный модуль Python для загрузки. | нулевой | Нет |
children | массив [ Команда ] | Субъекты; Они обрабатываются функцией родителя. (Нет связанных модулей для себя). | [] | Нет |
overridable | логический | Может ли CommandParser переопределять любые данные внутри этого объекта (должны быть включены вручную). | истинный | Нет |
disabled | логический | Если True по -прежнему загрузите команду, но поднимите отключенный мощность при попытке выполнить. | ЛОЖЬ | Нет |
Примечание. Модули команд не загружаются , если они не указаны в commands.json function
commands.json Содержание: {
"commandPrefix" : " ! " ,
"commands" : [
{
"name" : " test " ,
"usage" : " test [*args:any] " ,
"description" : " Test command. " ,
"permission" : 500 ,
"function" : true
},
{
"name" : " test2 " ,
"function" : false
}
]
} Динамически загруженные команды обозначены именем файла с префиксом «ZZZ__». Внутри командного модуля есть функция, определенная как command . Эта функция будет сопоставлена с атрибутом функции Command и сохранена в памяти для выполнения. Функция имеет доступ к любым анализам ARG, а также Kwargs:
« Self » ( Command ), в котором расположены метаданные для выполнения команды.
« Parser » ( CommandParser ), в котором хранится список зарегистрированных команд и данных команд.
« Контекст » ( CommandContext ), который поставляет CommandSource и исходный текст, отправленный для анализа.
CommandParser.parse(context: CommandContext, **kwargs) . Поскольку команды не могут импортировать свои собственные модули, некоторые включены в глобальные значения ( math , random и string ). Другими атрибутами, включенными в глобальный объем, являются: getitem ( operator.getitem ) и ImproperUsageError ( dyncommands.exceptions.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 ) В любое время вы можете вызвать CommandParser.reload() чтобы перезагрузить все модули команд и метаданные из дискового хранилища.
../
│
├───[commands_path]/
│ ├─── commands.json
│ ├─── zzz__[command1].py
│ ├─── zzz__[command2].py
│ └─── zzz__[command3].py
│
Чтобы добавить команды, вы можете либо вручную ввести данные в файл commands.json , либо использовать метод CommandParser.add_command(text: str, link: bool = False, **kwargs) . Самый простой способ использовать этот метод - это прочитать модуль команд в качестве текста и передать его в первый аргумент. Вы также можете сохранить модули командных команд, чтобы обеспечить удаленную установку, поскольку настройка параметра ссылки на True будет читать текст в виде ссылки и получить необработанные текстовые данные по этой ссылке. Пример: Гист и Пастебин.
Примечание. При добавлении команды метаданные для «Имя» должны быть заполнены. Это можно сделать в виде комментариев.
Удаление уже добавленной команды относительно просто. Просто вызовите CommandParser.remove_command(name: str) с именем команды, которую вы хотите удалить, и он удалит как метаданные, так и командный модуль с диска.
Если вы не хотите удалять команду при удалении, лучшей альтернативой является отключение ее с помощью 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 ) DynCommand CommandParser Nangy поддерживает обработку уровня разрешений, поэтому вам не нужно реализовать аналогичную систему в каждой командной функции.
Каждая команда имеет permission на значение метаданных (за исключением специального значения -1 ) является минимальным уровнем разрешения, необходимым от CommandSource . -1 представляет собой «бесконечное» требование, где ни одна CommandSource не сможет выполнить его, пока система разрешений активна.
Чтобы отключить систему разрешений, установите атрибут CommandParser S _ignore_permission на TRUE. Примечание. Поскольку этот атрибут начинается с «_», попытка изменить его изнутри функции команды приведет к неудачной компиляции и исключению.