From dfcb8a455087c77436f9790497a401d70c31c87d Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Fri, 30 Dec 2022 16:13:25 +0800 Subject: [PATCH 1/3] separate bot action method to BotActionTrait --- .gitignore | 2 +- src/ZM/Context/BotContext.php | 57 ++--------------- src/ZM/Context/Trait/BotActionTrait.php | 81 +++++++++++++++++++++++++ src/ZM/Utils/ZMUtil.php | 4 +- src/ZM/ZMApplication.php | 5 +- 5 files changed, 94 insertions(+), 55 deletions(-) create mode 100644 src/ZM/Context/Trait/BotActionTrait.php diff --git a/.gitignore b/.gitignore index 43dcb5ba..a712e595 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ /site/ /plugins/ /doxy/ - +/walle/ # 框架审计文件 audit.log diff --git a/src/ZM/Context/BotContext.php b/src/ZM/Context/BotContext.php index 023460b2..eb08a7f6 100644 --- a/src/ZM/Context/BotContext.php +++ b/src/ZM/Context/BotContext.php @@ -4,22 +4,21 @@ declare(strict_types=1); namespace ZM\Context; -use Choir\Http\HttpFactory; use OneBot\Driver\Event\Http\HttpRequestEvent; use OneBot\Driver\Event\WebSocket\WebSocketMessageEvent; -use OneBot\Util\Utils; use OneBot\V12\Object\Action; use OneBot\V12\Object\MessageSegment; use OneBot\V12\Object\OneBotEvent; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; -use ZM\Annotation\AnnotationHandler; -use ZM\Annotation\OneBot\BotAction; +use ZM\Context\Trait\BotActionTrait; use ZM\Exception\OneBot12Exception; use ZM\Utils\MessageUtil; class BotContext implements ContextInterface { + use BotActionTrait; + private static array $echo_id_list = []; private array $self; @@ -28,9 +27,10 @@ class BotContext implements ContextInterface private bool $replied = false; - public function __construct(string $bot_id, string $platform) + public function __construct(string $bot_id, string $platform, null|WebSocketMessageEvent|HttpRequestEvent $event = null) { $this->self = ['user_id' => $bot_id, 'platform' => $platform]; + $this->base_event = $event; } public function getEvent(): OneBotEvent @@ -82,21 +82,9 @@ class BotContext implements ContextInterface */ public function getBot(string $bot_id, string $platform = ''): BotContext { - // TODO: 完善多机器人支持 return $this; } - /** - * @throws \Throwable - */ - public function sendMessage(\Stringable|array|MessageSegment|string $message, string $detail_type, array $params = []) - { - $message = MessageUtil::convertToArr($message); - $params['message'] = $message; - $params['detail_type'] = $detail_type; - return $this->sendAction(Utils::camelToSeparator(__FUNCTION__), $params, $this->self); - } - /** * 设置该消息下解析出来的参数列表 * @@ -134,39 +122,4 @@ class BotContext implements ContextInterface { return self::$echo_id_list[$echo] ?? null; } - - /** - * @throws \Throwable - */ - private function sendAction(string $action, array $params = [], ?array $self = null) - { - // 声明 Action 对象 - $a = new Action($action, $params, ob_uuidgen(), $self); - self::$echo_id_list[$a->echo] = $a; - // 调用事件在回复之前的回调 - $handler = new AnnotationHandler(BotAction::class); - container()->set(Action::class, $a); - $handler->setRuleCallback(fn (BotAction $act) => $act->action === '' || $act->action === $action && !$act->need_response); - $handler->handleAll($a); - // 被阻断时候,就不发送了 - if ($handler->getStatus() === AnnotationHandler::STATUS_INTERRUPTED) { - return false; - } - - // 调用机器人连接发送 Action - if (container()->has('ws.message.event')) { - /** @var WebSocketMessageEvent $ws */ - $ws = container()->get('ws.message.event'); - return $ws->send(json_encode($a->jsonSerialize())); - } - // 如果是 HTTP WebHook 的形式,那么直接调用 Response - if (container()->has('http.request.event')) { - /** @var HttpRequestEvent $event */ - $event = container()->get('http.request.event'); - $response = HttpFactory::createResponse(headers: ['Content-Type' => 'application/json'], body: json_encode([$a->jsonSerialize()])); - $event->withResponse($response); - return true; - } - throw new OneBot12Exception('No bot connection found.'); - } } diff --git a/src/ZM/Context/Trait/BotActionTrait.php b/src/ZM/Context/Trait/BotActionTrait.php new file mode 100644 index 00000000..f8db6c0e --- /dev/null +++ b/src/ZM/Context/Trait/BotActionTrait.php @@ -0,0 +1,81 @@ +sendAction(Utils::camelToSeparator(__FUNCTION__), $params, $this->self); + } + + /** + * 发送机器人动作 + * + * @throws \Throwable + */ + public function sendAction(string $action, array $params = [], ?array $self = null): bool|ActionResponse + { + // 声明 Action 对象 + $a = new Action($action, $params, ob_uuidgen(), $self); + self::$echo_id_list[$a->echo] = $a; + // 调用事件在回复之前的回调 + $handler = new AnnotationHandler(BotAction::class); + container()->set(Action::class, $a); + $handler->setRuleCallback(fn (BotAction $act) => $act->action === '' || $act->action === $action && !$act->need_response); + $handler->handleAll($a); + // 被阻断时候,就不发送了 + if ($handler->getStatus() === AnnotationHandler::STATUS_INTERRUPTED) { + return false; + } + + // 调用机器人连接发送 Action + if ($this->base_event instanceof WebSocketMessageEvent) { + $result = $this->base_event->send(json_encode($a->jsonSerialize())); + } + if (!isset($result) && container()->has('ws.message.event')) { + $result = container()->get('ws.message.event')->send(json_encode($a->jsonSerialize())); + } + // 如果是 HTTP WebHook 的形式,那么直接调用 Response + if (!isset($result) && $this->base_event instanceof HttpRequestEvent) { + $response = HttpFactory::createResponse(headers: ['Content-Type' => 'application/json'], body: json_encode([$a->jsonSerialize()])); + $this->base_event->withResponse($response); + $result = true; + } + if (!isset($result) && container()->has('http.request.event')) { + $response = HttpFactory::createResponse(headers: ['Content-Type' => 'application/json'], body: json_encode([$a->jsonSerialize()])); + container()->get('http.request.event')->withResponse($response); + $result = true; + } + if (isset($result)) { + return $result; + } + /* TODO: 协程支持 + if (($result ?? false) === true && ($co = Adaptive::getCoroutine()) !== null) { + return $result ?? false; + }*/ + throw new OneBot12Exception('No bot connection found.'); + } +} diff --git a/src/ZM/Utils/ZMUtil.php b/src/ZM/Utils/ZMUtil.php index 6e7fce98..c71f6853 100644 --- a/src/ZM/Utils/ZMUtil.php +++ b/src/ZM/Utils/ZMUtil.php @@ -8,7 +8,9 @@ class ZMUtil { /** * 获取 composer.json 并转为数组进行读取使用 - * @param null|string $path 路径 + * + * @param null|string $path 路径 + * @throws \JsonException */ public static function getComposerMetadata(?string $path = null): ?array { diff --git a/src/ZM/ZMApplication.php b/src/ZM/ZMApplication.php index 6d811460..c8ee8d6b 100644 --- a/src/ZM/ZMApplication.php +++ b/src/ZM/ZMApplication.php @@ -12,11 +12,14 @@ use ZM\Plugin\ZMPlugin; class ZMApplication extends ZMPlugin { /** @var null|ZMApplication 存储单例类的变量 */ - private static ?ZMApplication $obj; + private static ?ZMApplication $obj = null; /** @var array 存储要传入的args */ private array $args = []; + /** + * @throws SingletonViolationException + */ public function __construct(mixed $dir = null) { if (self::$obj !== null) { From d7e815d670ac6d25e1e6689fb427c968c7de4075 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Fri, 30 Dec 2022 16:13:57 +0800 Subject: [PATCH 2/3] fix windows CtrlC capture bug --- src/ZM/Event/Listener/SignalListener.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZM/Event/Listener/SignalListener.php b/src/ZM/Event/Listener/SignalListener.php index 3e122b05..4b21d715 100644 --- a/src/ZM/Event/Listener/SignalListener.php +++ b/src/ZM/Event/Listener/SignalListener.php @@ -119,10 +119,10 @@ class SignalListener } echo "\r"; logger()->notice('请再按 {count} 次 Ctrl+C 以强制杀死进程', ['count' => 5 - self::$manager_kill_time]); - return; } ++self::$manager_kill_time; if (self::$manager_kill_time === 1) { + logger()->notice('Keyboard interrupt, shutting down server...'); Framework::getInstance()->stop(); } } From 96879bf415e1c5f09d4a1a4d1584b27c031c0ba9 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Fri, 30 Dec 2022 16:15:22 +0800 Subject: [PATCH 3/3] update some doc and types --- config/global.php | 6 ----- src/Globals/global_functions.php | 3 ++- src/ZM/Event/Listener/WorkerEventListener.php | 26 ++++++++++++++----- src/ZM/Framework.php | 4 ++- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/config/global.php b/config/global.php index 8d3f9cce..b8700668 100644 --- a/config/global.php +++ b/config/global.php @@ -18,12 +18,6 @@ $config['servers'] = [ 'type' => 'http', 'flag' => 20002, ], - [ - 'host' => '0.0.0.0', - 'port' => 20003, - 'type' => 'http', - 'flag' => 20003, - ], ]; /* Workerman 驱动相关配置 */ diff --git a/src/Globals/global_functions.php b/src/Globals/global_functions.php index 55b1747f..a179240d 100644 --- a/src/Globals/global_functions.php +++ b/src/Globals/global_functions.php @@ -12,6 +12,7 @@ use ZM\Container\ContainerHolder; use ZM\Logger\ConsoleLogger; use ZM\Middleware\MiddlewareHandler; use ZM\Store\Database\DBException; +use ZM\Store\Database\DBQueryBuilder; use ZM\Store\Database\DBWrapper; // 防止重复引用引发报错 @@ -209,7 +210,7 @@ function db(string $name = '') * * @throws DBException */ -function sql_builder(string $name = '') +function sql_builder(string $name = ''): DBQueryBuilder { return (new DBWrapper($name))->createQueryBuilder(); } diff --git a/src/ZM/Event/Listener/WorkerEventListener.php b/src/ZM/Event/Listener/WorkerEventListener.php index 22c88d5c..6d051e87 100644 --- a/src/ZM/Event/Listener/WorkerEventListener.php +++ b/src/ZM/Event/Listener/WorkerEventListener.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace ZM\Event\Listener; +use OneBot\Driver\Coroutine\Adaptive; use OneBot\Driver\Process\ProcessManager; use OneBot\Util\Singleton; use ZM\Annotation\AnnotationHandler; @@ -35,6 +36,8 @@ class WorkerEventListener // 自注册一下,刷新当前进程的logger进程banner ob_logger_register(ob_logger()); + Adaptive::initWithDriver(Framework::getInstance()->getDriver()); + // 如果没有引入参数disable-safe-exit,则监听 Ctrl+C if (!Framework::getInstance()->getArgv()['disable-safe-exit'] && PHP_OS_FAMILY !== 'Windows') { SignalListener::getInstance()->signalWorker(); @@ -42,12 +45,10 @@ class WorkerEventListener // Windows 环境下,为了监听 Ctrl+C,只能开启终端输入 if (PHP_OS_FAMILY === 'Windows') { + logger()->debug('监听Windows的键盘输入'); sapi_windows_set_ctrl_handler([SignalListener::getInstance(), 'signalWindowsCtrlC']); - Framework::getInstance()->getDriver()->getEventLoop()->addReadEvent(STDIN, function ($x) {}); } - logger()->debug('Worker #' . ProcessManager::getProcessId() . ' started'); - // 设置 Worker 进程的状态和 ID 等信息 if (($name = Framework::getInstance()->getDriver()->getName()) === 'swoole') { /* @phpstan-ignore-next-line */ @@ -88,17 +89,23 @@ class WorkerEventListener $this->initUserPlugins(); // handle @Init annotation - $this->dispatchInit(); - + Adaptive::getCoroutine()->create(function () { + $this->dispatchInit(); + }); // 回显 debug 日志:进程占用的内存 $memory_total = memory_get_usage() / 1024 / 1024; logger()->debug('Worker process used ' . round($memory_total, 3) . ' MB'); } + public function onWorkerStart1(): void + { + logger()->debug('Worker #' . ProcessManager::getProcessId() . ' started'); + } + /** * @throws ZMKnownException */ - public function onWorkerStop999() + public function onWorkerStop999(): void { logger()->debug('Worker #' . ProcessManager::getProcessId() . ' stopping'); if (DIRECTORY_SEPARATOR !== '\\') { @@ -110,11 +117,16 @@ class WorkerEventListener } } + public function onWorkerStop1(): void + { + logger()->debug('Worker #' . ProcessManager::getProcessId() . ' stopped'); + } + /** * 加载用户代码资源,包括普通插件、单文件插件、Composer 插件等 * @throws \Throwable */ - private function initUserPlugins() + private function initUserPlugins(): void { logger()->debug('Loading user sources'); diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index 035527d6..67b0732f 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -46,7 +46,7 @@ class Framework public const VERSION_ID = 650; /** @var string 版本名称 */ - public const VERSION = '3.0.0-beta2'; + public const VERSION = '3.0.0-beta3'; /** @var array 传入的参数 */ protected array $argv; @@ -228,7 +228,9 @@ class Framework // 添加框架需要监听的顶层事件监听器 // worker 事件 ob_event_provider()->addEventListener(WorkerStartEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStart999'], 999); + ob_event_provider()->addEventListener(WorkerStartEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStart1'], 1); ob_event_provider()->addEventListener(WorkerStopEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStop999'], 999); + ob_event_provider()->addEventListener(WorkerStopEvent::getName(), [WorkerEventListener::getInstance(), 'onWorkerStop1'], 1); // Http 事件 ob_event_provider()->addEventListener(HttpRequestEvent::getName(), [HttpEventListener::getInstance(), 'onRequest999'], 999); ob_event_provider()->addEventListener(HttpRequestEvent::getName(), [HttpEventListener::getInstance(), 'onRequest1'], 1);