동적 명령 실행, 구문 분석 및 스토리지.
Dyncommands를 사용하면 파이썬 기능을 동적으로 가져오고 실행할 수 있습니다. 다시 시작하지 않고 IRC 챗봇 또는 CLI 애플리케이션에 명령을 추가하는 데 유용합니다.
문자열을 구문 분석 할 때 명령 이름을 인수와 분리하고 그 인수로 저장된 함수를 실행합니다. 파서를 호출 할 때마다 명령이 액세스 할 수있는 자신의 커스텀 kwargs를 통과 할 수 있습니다.
모든 명령 모듈은 실행되기 전에 제한된 피를 통해 컴파일됩니다. CommandParser._unrestricted 설정하여 제한된 실행을 꺼질 수 있지만, 신뢰할 수없는 코드를 실행할 때 매우 권장되지 않습니다 .
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' 명령에 대한 메타 데이터는 CommandParser.commands_path 디렉토리의 commands.json 파일에 저장됩니다. 파서의 모든 데이터가로드 및 저장되는 곳입니다.
모든 commands.json 파일은 jsonschema python 패키지를 통해 JSON Schemas로 검증됩니다.
| 열쇠 | 유형 | 설명 | 기본 | 필수의 |
|---|---|---|---|---|
commandPrefix | 끈 | 문자열은이 접두사로 시작해야합니다. 그렇지 않으면 무시됩니다. 빈 문자열은 모든 것을 받아들입니다. | N/A | 예 |
commands | 배열 [ 명령 ] | 저장된 명령 모듈에 대한 메타 데이터가 포함되어 있습니다. | N/A | 예 |
| 열쇠 | 유형 | 설명 | 기본 | 필수의 |
|---|---|---|---|---|
name | 끈 | 명령을 고유하게 식별합니다. | N/A | 예 |
usage | 끈 | 사용 정보 (Args 사용 방법). | "" " | 아니요 |
description | 끈 | 명령에 대한 설명. | "" " | 아니요 |
permission | 정수 | CommandSource가 명령을 실행하는 데 필요한 권한 수준. | 0 | 아니요 |
function | 부울 , 널 | 로드 할 Python 모듈이 관련된지 여부. | 널 | 아니요 |
children | 배열 [ 명령 ] | 하위 명령; 이들은 부모의 기능에 의해 처리됩니다. (스스로 관련 모듈 없음). | [] | 아니요 |
overridable | 부울 | 명령 파서 가이 객체 내의 데이터를 무시할 수 있는지 여부 (수동으로 활성화되어야 함). | 진실 | 아니요 |
disabled | 부울 | True 가 여전히로드 된 경우 명령을로드하지만 실행을 시도 할 때 장애인을 올립니다. | 거짓 | 아니요 |
참고 : 명령 모듈은 commands.json 에 function 키를 true 로 설정하지 않으면로드되지 않습니다.
commands.json 컨텐츠 : {
"commandPrefix" : " ! " ,
"commands" : [
{
"name" : " test " ,
"usage" : " test [*args:any] " ,
"description" : " Test command. " ,
"permission" : 500 ,
"function" : true
},
{
"name" : " test2 " ,
"function" : false
}
]
} 동적으로로드 된 명령은 "zzz__"의 접두사가있는 파일 이름으로 표시됩니다. 명령 모듈 안에는 command 으로 정의 된 함수가 있습니다. 이 함수는 Command 의 함수 속성에 매핑되고 실행을 위해 메모리에 저장됩니다. 이 함수는 kwargs뿐만 아니라 구문 분석 된 모든 arg에 액세스 할 수 있습니다.
실행중인 명령에 대한 메타 데이터가있는 ' self '( Command ).
등록 된 명령 및 명령 데이터 목록을 저장하는 ' Parser '( CommandParser ).
' Context '( 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 로 설정하면 링크로 텍스트를 링크로 읽고 해당 링크에서 원시 텍스트 데이터를 얻을 수 있으므로 원격 설치를 허용하기 위해 온라인으로 명령 모듈을 저장할 수도 있습니다. 예 : Gist and Pastebin.
참고 : 명령을 추가 할 때 '이름'에 대한 메타 데이터를 채워야 합니다 . 이것은 주석 형태로 수행 할 수 있습니다.
이미 추가 된 명령을 제거하는 것은 비교적 쉽습니다. 제거하려는 명령의 이름으로 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 기본적으로 권한 수준 처리를 지원하므로 모든 명령 기능에서 유사한 시스템을 구현할 필요가 없습니다.
각 명령에는 메타 데이터 값 permission 있습니다 (특별 값 -1 을 제외하고)는 CommandSource 에서 요구하는 최소 권한 수준입니다. -1 "무한"요구 사항을 나타냅니다. 여기서 권한 시스템이 활성화되는 동안 CommandSource 실행할 수없는 경우가 없습니다.
권한 시스템을 비활성화하려면 CommandParser 의 _ignore_permission 속성을 true로 설정하십시오. 참고 : 이 속성은 "_"로 시작하므로 명령의 기능 내부에서 변경하려고하면 컴파일이 실패하고 예외가 발생합니다.