From af26086019a0ce0f5c12e770c91ab49e6f93717e Mon Sep 17 00:00:00 2001 From: sunxyw Date: Mon, 2 Jan 2023 21:54:48 +0800 Subject: [PATCH 1/4] refactor command manual --- src/ZM/Event/Listener/WorkerEventListener.php | 2 +- .../CommandManual/CommandManualPlugin.php | 128 ++++++++++++++++++ .../CommandManual/StaticManualFactory.php | 74 ++++++++++ src/ZM/Plugin/CommandManualPlugin.php | 127 ----------------- 4 files changed, 203 insertions(+), 128 deletions(-) create mode 100644 src/ZM/Plugin/CommandManual/CommandManualPlugin.php create mode 100644 src/ZM/Plugin/CommandManual/StaticManualFactory.php delete mode 100644 src/ZM/Plugin/CommandManualPlugin.php diff --git a/src/ZM/Event/Listener/WorkerEventListener.php b/src/ZM/Event/Listener/WorkerEventListener.php index 42b0c809..6dc8d705 100644 --- a/src/ZM/Event/Listener/WorkerEventListener.php +++ b/src/ZM/Event/Listener/WorkerEventListener.php @@ -13,7 +13,7 @@ use ZM\Annotation\AnnotationParser; use ZM\Annotation\Framework\Init; use ZM\Exception\ZMKnownException; use ZM\Framework; -use ZM\Plugin\CommandManualPlugin; +use ZM\Plugin\CommandManual\CommandManualPlugin; use ZM\Plugin\OneBot12Adapter; use ZM\Plugin\PluginManager; use ZM\Process\ProcessStateManager; diff --git a/src/ZM/Plugin/CommandManual/CommandManualPlugin.php b/src/ZM/Plugin/CommandManual/CommandManualPlugin.php new file mode 100644 index 00000000..4328f3c7 --- /dev/null +++ b/src/ZM/Plugin/CommandManual/CommandManualPlugin.php @@ -0,0 +1,128 @@ + 'command', 'header' => false, 'indent' => false], + ['type' => 'description', 'header' => false, 'indent' => false], + ['type' => 'usage', 'header' => false, 'indent' => false], + ['type' => 'arguments', 'header' => '可用参数:', 'indent' => true], + ['type' => 'examples', 'header' => '使用示例:', 'indent' => true], + ]; + + /** + * 命令手册工厂,键为优先级,值为工厂 + * + * @var array + */ + private static array $manual_factories = [ + 10 => StaticManualFactory::class, + ]; + + /** + * 命令列表,键为命令名,值为命令实例 + * + * @var array + */ + private array $commands = []; + + /** + * 命令邻近注解,键为命令名,值为邻近注解数组 + * + * @var array + */ + private array $adjacent_annotations = []; + + public function __construct(AnnotationParser $parser) + { + parent::__construct(__DIR__); + + if (config('command_manual.template') !== null) { + $this->template = config('command_manual.template'); + } + + $parser->addSpecialParser(BotCommand::class, [$this, 'parseBotCommand']); + $parser->addSpecialParser(CommandHelp::class, fn () => false); + + $this->addBotCommand( + BotCommand::make('help', 'help', level: 10) + ->withArgument('command', '要查询的指令名', required: true) + ->on([$this, 'onHelp']) + ); + } + + /** + * 添加命令手册工厂 + * + * @param array|callable|string $factory 工厂 + * @param int $priority 优先级 + */ + public static function addManualFactory(array|callable|string $factory, int $priority = 20): void + { + self::$manual_factories[$priority] = $factory; + logger()->debug('命令手册工厂已添加 {factory} 优先级 {priority}', compact('factory', 'priority')); + } + + /** + * 解析 BotCommand 的参数和帮助 + * + * @param BotCommand $command 命令对象 + * @param null|array $adjacent_annotations 同一个方法的所有注解 + */ + public function parseBotCommand(BotCommand $command, ?array $adjacent_annotations = null): ?bool + { + $this->commands[$command->name] = $command; + $this->adjacent_annotations[$command->name] = $adjacent_annotations ?? []; + return true; + } + + /** + * 命令手册获取命令 + * + * @param BotContext $context 上下文 + */ + public function onHelp(BotContext $context): void + { + $command_name = $context->getParam('command'); + $command = $this->commands[$command_name] ?? null; + if ($command === null) { + $context->reply('命令不存在'); + return; + } + $adjacent_annotations = $this->adjacent_annotations[$command_name] ?? []; + + // 遍历工厂,直到找到一个返回非空的工厂 + foreach (self::$manual_factories as $factory) { + $manual = container()->call( + $factory, + [ + 'context' => $context, + 'command' => $command, + 'template' => $this->template, + 'adjacent_annotations' => $adjacent_annotations, + ] + ); + if ($manual !== null) { + $context->reply($manual); + return; + } + } + $context->reply("未找到指令 {$command} 的帮助"); + } +} diff --git a/src/ZM/Plugin/CommandManual/StaticManualFactory.php b/src/ZM/Plugin/CommandManual/StaticManualFactory.php new file mode 100644 index 00000000..4440c083 --- /dev/null +++ b/src/ZM/Plugin/CommandManual/StaticManualFactory.php @@ -0,0 +1,74 @@ +getSectionContent($command, $v['type'], $help); + $this->addSection($section, $content, $v); + } + return $section; + } + + private function getSectionContent(BotCommand $command, string $type, CommandHelp $help): string + { + switch ($type) { + case 'command': + return $command->name; + case 'description': + return $help->description; + case 'usage': + return $help->usage; + case 'arguments': + $ret = ''; + foreach ($command->getArguments() as $argument) { + /* @var CommandArgument $argument */ + $ret .= $argument->name . ' - ' . $argument->description . PHP_EOL; + } + return $ret; + case 'examples': + return $help->example; + default: + return ''; + } + } + + private function addSection(string &$section, string $content, array $options): void + { + if (!$content) { + return; + } + if ($options['header']) { + $section .= $options['header'] . PHP_EOL; + } + if ($options['indent']) { + $content = ' ' . str_replace(PHP_EOL, PHP_EOL . ' ', $content); + $content = rtrim($content); + } + $section .= $content . PHP_EOL; + } +} diff --git a/src/ZM/Plugin/CommandManualPlugin.php b/src/ZM/Plugin/CommandManualPlugin.php deleted file mode 100644 index 5646ad20..00000000 --- a/src/ZM/Plugin/CommandManualPlugin.php +++ /dev/null @@ -1,127 +0,0 @@ - 'command', 'header' => false, 'indent' => false], - ['type' => 'description', 'header' => false, 'indent' => false], - ['type' => 'usage', 'header' => false, 'indent' => false], - ['type' => 'arguments', 'header' => '可用参数:', 'indent' => true], - ['type' => 'examples', 'header' => '使用示例:', 'indent' => true], - ]; - - /** - * 命令(帮助)列表,键为命令名,值为命令帮助 - * - * @var array - */ - private array $command_list = []; - - public function __construct(AnnotationParser $parser) - { - parent::__construct(__DIR__); - - if (config('command_manual.template') !== null) { - $this->template = config('command_manual.template'); - } - - $parser->addSpecialParser(BotCommand::class, [$this, 'parseBotCommand']); - $parser->addSpecialParser(CommandHelp::class, fn () => false); - - $this->addBotCommand( - BotCommand::make('help', 'help') - ->withArgument('command', '要查询的指令名', required: true) - ->on([$this, 'onHelp']) - ); - logger()->info('CommandManualPlugin loaded.'); - } - - /** - * 解析 BotCommand 的参数和帮助 - * - * @param BotCommand $command 命令对象 - * @param null|array $same_method_annotations 同一个方法的所有注解 - */ - public function parseBotCommand(BotCommand $command, ?array $same_method_annotations = null): ?bool - { - if ($same_method_annotations) { - foreach ($same_method_annotations as $v) { - if ($v instanceof CommandHelp) { - $help = $v; - break; - } - } - } - $help = $help ?? new CommandHelp('', '', ''); - $section = ''; - foreach ($this->template as $v) { - $content = $this->getSectionContent($command, $v['type'], $help); - $this->addSection($section, $content, $v); - } - $this->command_list[$command->name] = $section; - return true; - } - - public function onHelp(BotContext $context): void - { - $command = $context->getParam('command'); - if (isset($this->command_list[$command])) { - $context->reply($this->command_list[$command]); - } else { - $context->reply('未找到指令 ' . $command); - } - } - - private function addSection(string &$section, string $content, array $options): void - { - if (!$content) { - return; - } - if ($options['header']) { - $section .= $options['header'] . PHP_EOL; - } - if ($options['indent']) { - $content = ' ' . str_replace(PHP_EOL, PHP_EOL . ' ', $content); - $content = rtrim($content); - } - $section .= $content . PHP_EOL; - } - - private function getSectionContent(BotCommand $command, string $type, CommandHelp $help): string - { - switch ($type) { - case 'command': - return $command->name; - case 'description': - return $help->description; - case 'usage': - return $help->usage; - case 'arguments': - $ret = ''; - foreach ($command->getArguments() as $argument) { - /* @var CommandArgument $argument */ - $ret .= $argument->name . ' - ' . $argument->description . PHP_EOL; - } - return $ret; - case 'examples': - return $help->example; - default: - return ''; - } - } -} From eb05cea4439f2eed16a1484bfc7a21d759125350 Mon Sep 17 00:00:00 2001 From: sunxyw Date: Mon, 2 Jan 2023 22:14:11 +0800 Subject: [PATCH 2/4] fix reversed priority --- src/ZM/Plugin/CommandManual/CommandManualPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZM/Plugin/CommandManual/CommandManualPlugin.php b/src/ZM/Plugin/CommandManual/CommandManualPlugin.php index 4328f3c7..8643f4c7 100644 --- a/src/ZM/Plugin/CommandManual/CommandManualPlugin.php +++ b/src/ZM/Plugin/CommandManual/CommandManualPlugin.php @@ -108,7 +108,7 @@ class CommandManualPlugin extends ZMPlugin $adjacent_annotations = $this->adjacent_annotations[$command_name] ?? []; // 遍历工厂,直到找到一个返回非空的工厂 - foreach (self::$manual_factories as $factory) { + foreach (array_reverse(self::$manual_factories) as $factory) { $manual = container()->call( $factory, [ From 2f94f5258df06921419ea935b9d5ab4c22d0f3b9 Mon Sep 17 00:00:00 2001 From: sunxyw Date: Mon, 2 Jan 2023 22:38:01 +0800 Subject: [PATCH 3/4] adjust StaticManualFactory method visibility --- src/ZM/Plugin/CommandManual/StaticManualFactory.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ZM/Plugin/CommandManual/StaticManualFactory.php b/src/ZM/Plugin/CommandManual/StaticManualFactory.php index 4440c083..7db0f5fd 100644 --- a/src/ZM/Plugin/CommandManual/StaticManualFactory.php +++ b/src/ZM/Plugin/CommandManual/StaticManualFactory.php @@ -34,7 +34,7 @@ class StaticManualFactory return $section; } - private function getSectionContent(BotCommand $command, string $type, CommandHelp $help): string + protected function getSectionContent(BotCommand $command, string $type, CommandHelp $help): string { switch ($type) { case 'command': @@ -57,7 +57,7 @@ class StaticManualFactory } } - private function addSection(string &$section, string $content, array $options): void + protected function addSection(string &$section, string $content, array $options): void { if (!$content) { return; From 8477bf153f1bf221cbea708f48e279fb3945ba05 Mon Sep 17 00:00:00 2001 From: sunxyw Date: Mon, 2 Jan 2023 23:06:58 +0800 Subject: [PATCH 4/4] fix CommandManualPlugin blocking annotations --- src/ZM/Plugin/CommandManual/CommandManualPlugin.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ZM/Plugin/CommandManual/CommandManualPlugin.php b/src/ZM/Plugin/CommandManual/CommandManualPlugin.php index 8643f4c7..6e7a89c5 100644 --- a/src/ZM/Plugin/CommandManual/CommandManualPlugin.php +++ b/src/ZM/Plugin/CommandManual/CommandManualPlugin.php @@ -58,7 +58,7 @@ class CommandManualPlugin extends ZMPlugin } $parser->addSpecialParser(BotCommand::class, [$this, 'parseBotCommand']); - $parser->addSpecialParser(CommandHelp::class, fn () => false); + $parser->addSpecialParser(CommandHelp::class, fn () => true); $this->addBotCommand( BotCommand::make('help', 'help', level: 10) @@ -89,7 +89,7 @@ class CommandManualPlugin extends ZMPlugin { $this->commands[$command->name] = $command; $this->adjacent_annotations[$command->name] = $adjacent_annotations ?? []; - return true; + return null; } /**