動的コマンドの実行、解析、およびストレージ。
Dyncommandsを使用すると、Python関数を動的にインポートおよび実行できます。再起動せずにIRCチャットボットまたはCLIアプリケーションにコマンドを追加するのに役立ちます。
文字列を解析するとき、コマンド名を引数から分離し、保存された関数をそれらの引数で実行します。パーサーが呼び出されるたびに、コマンドがアクセスできる独自のカスタムKwargsを渡すことができます。
すべてのコマンドモジュールは、実行を許可される前に、制限付きPythonを介してコンパイルされます。 CommandParser._unrestricted of 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'コマンドのメタデータは、 CommandParser.commands_pathディレクトリのcommands.jsonファイルに保存されます。これは、パーサーのすべてのデータがロードおよび保存される場所です。
すべてのcommands.jsonファイルは、jsonschema pythonパッケージを介してjsonスキーマで検証されます
| 鍵 | タイプ | 説明 | デフォルト | 必須 |
|---|---|---|---|---|
commandPrefix | 弦 | 文字列はこのプレフィックスから開始する必要があります。そうしないと、無視されます。空の文字列はすべてを受け入れます。 | n/a | はい |
commands | 配列[コマンド] | 保存されたコマンドモジュールのメタデータが含まれています。 | n/a | はい |
| 鍵 | タイプ | 説明 | デフォルト | 必須 |
|---|---|---|---|---|
name | 弦 | CommandParserへのコマンドを独自に識別します。 | n/a | はい |
usage | 弦 | 使用情報(ARGSの使用方法)。 | "" | いいえ |
description | 弦 | コマンドの説明。 | "" | いいえ |
permission | 整数 | CommandSourceがコマンドを実行するために必要な許可レベル。 | 0 | いいえ |
function | ブール、ヌル | ロードする関連するPythonモジュールがあるかどうか。 | ヌル | いいえ |
children | 配列[コマンド] | サブコマンド;これらは親の関数によって処理されます。 (それ自体に関連するモジュールはありません)。 | [] | いいえ |
overridable | ブール | CommandParserがこのオブジェクト内のデータをオーバーライドできるかどうか(手動で有効にする必要があります)。 | 真実 | いいえ |
disabled | ブール | Trueがまだロードコマンドをロードしますが、実行しようとするときに無効なエラーを上げます。 | 間違い | いいえ |
注:コマンドモジュールはfunction commands.jsonにリストされていない限りロードされません。
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とクワルグにアクセスできます。
「 self 」( Command )、実行されているコマンドのメタデータを収容します。
登録されたコマンドとコマンドデータのリストを保存する「 Parser 」( CommandParser )。
CommandSourceと解析のために送信された元のテキストを提供する ' Context '( CommandContext )。
CommandParser.parse(context: CommandContext, **kwargs)に渡されたカスタム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 、許可レベルの処理をネイティブにサポートするため、すべてのコマンド関数に同様のシステムを実装する必要はありません。
各コマンドにはメタデータ値のpermissionがあります(特別価値-1を除く)は、 CommandSourceから必要な最小許可レベルです。 -1 「無限の」要件を表し、許可システムがアクティブ中にCommandSourceを実行できません。
許可システムを無効にするには、 CommandParserの_ignore_permission属性をtrueに設定します。注:この属性は「_」で始まるため、コマンドの関数の内部から変更しようとすると、コンパイルが失敗し、例外が発生します。