From 09220825cfe898d3cd9f6d0324412158f31650ee Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Tue, 16 Nov 2021 15:41:01 +0800 Subject: [PATCH] update to build 427 --- config/global.php | 7 +- docs/component/common/event-tracer.md | 104 ++++++++++++++++++ docs/update/build-update.md | 15 +++ src/Module/Middleware/TimerMiddleware.php | 4 +- src/ZM/API/CQAPI.php | 13 +++ src/ZM/Annotation/AnnotationParser.php | 20 +++- src/ZM/Command/Daemon/DaemonReloadCommand.php | 2 +- src/ZM/Command/Daemon/DaemonStatusCommand.php | 18 +-- src/ZM/Command/Daemon/DaemonStopCommand.php | 2 +- src/ZM/Command/RunServerCommand.php | 13 +++ src/ZM/Command/Server/ServerReloadCommand.php | 25 +++++ src/ZM/Command/Server/ServerStatusCommand.php | 34 ++++++ src/ZM/Command/Server/ServerStopCommand.php | 35 ++++++ src/ZM/ConsoleApplication.php | 14 ++- src/ZM/Context/Context.php | 23 +++- src/ZM/Event/EventManager.php | 4 + src/ZM/Event/SwooleEvent/OnBeforeReload.php | 5 +- src/ZM/Event/SwooleEvent/OnShutdown.php | 5 + src/ZM/Event/SwooleEvent/OnStart.php | 13 +-- src/ZM/Utils/MessageUtil.php | 73 ++++++++++-- zhamao | 0 21 files changed, 388 insertions(+), 41 deletions(-) create mode 100644 docs/component/common/event-tracer.md create mode 100644 src/ZM/Command/Server/ServerReloadCommand.php create mode 100644 src/ZM/Command/Server/ServerStatusCommand.php create mode 100644 src/ZM/Command/Server/ServerStopCommand.php mode change 100755 => 100644 zhamao diff --git a/config/global.php b/config/global.php index 115b4719..603f5be2 100644 --- a/config/global.php +++ b/config/global.php @@ -39,7 +39,9 @@ $config['swoole'] = [ $config['runtime'] = [ 'swoole_coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL), 'swoole_server_mode' => SWOOLE_PROCESS, - 'middleware_error_policy' => 1 + 'middleware_error_policy' => 1, + 'reload_delay_time' => 800, + 'global_middleware_binding' => [] ]; /** 轻量字符串缓存,默认开启 */ @@ -120,7 +122,8 @@ $config['static_file_server'] = [ $config['onebot'] = [ 'status' => true, 'single_bot_mode' => false, - 'message_level' => 99 + 'message_level' => 99, + 'message_convert_string' => true ]; /** 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) */ diff --git a/docs/component/common/event-tracer.md b/docs/component/common/event-tracer.md new file mode 100644 index 00000000..6e8aae1e --- /dev/null +++ b/docs/component/common/event-tracer.md @@ -0,0 +1,104 @@ +# 事件跟踪器及调试 + +众所周知,炸毛框架中的事件由内置的事件分发器(EventDispatcher)负责分发,但调试事件分发在之前的版本比较困难,例如不能获取到事件如何被调用,以及事件如何被捕获。 + +EventTracer 的作用是记录事件的调用顺序,以便于调试。 + +命名空间使用指南:`use ZM\Event\EventTracer;` + +## EventTracer::getCurrentEvent() - 获取当前注解事件对象 + +```php +/** + * @OnStart() + */ +public function onStart() { + zm_dump(EventTracer::getCurrentEvent()); +} +/* +^ ZM\Annotation\Swoole\OnStart^ {#192 + +worker_id: 0 + +method: "onStart" + +class: "Module\Example\Hello" +} +*/ +``` + +这里这个方法必须在注解事件内执行,如果在注解事件外执行,将会返回 `null`。 + +## EventTracer::getCurrentEventMiddlewares() - 获取当前注解事件的中间件们 + +```php +/** + * @OnStart() + * @Middleware("timer") + */ +public function onStart() { + zm_dump(EventTracer::getCurrentEventMiddlewares()); +} +/* +^ array:1 [ + 0 => ZM\Annotation\Http\Middleware^ {#194 + +middleware: "timer" + +params: [] + +method: "onStart" + +class: "Module\Example\Hello" + } +] +*/ +``` + +返回值为当前注解事件的中间件们,如果没有注解中间件,返回 `[]`。 + +## EventTracer::getEventTraceList() - 获取注解事件的列表 + +此处返回的是 `getCurrentEvent()` 相同的对象,但是返回的是一个数组,数组中的元素是注解事件。 + +```php +/** + * 一个简单随机数的功能demo + * 问法1:随机数 1 20 + * 问法2:从1到20的随机数 + * @CQCommand("随机数") + * @Middleware("timer") + * @CQCommand(pattern="*从*到*的随机数") + * @return string + */ +public function randNum() { + // 此处为随机数代码 + zm_dump(EventTracer::getEventTraceList()); + return "随机数:" . rand(1, 20); +} + +/* +^ array:2 [ + 0 => ZM\Annotation\CQ\CQCommand^ {#193 + +match: "" + +pattern: "*从*到*的随机数" + +regex: "" + +start_with: "" + +end_with: "" + +keyword: "" + +alias: [] + +message_type: "" + +user_id: 0 + +group_id: 0 + +discuss_id: 0 + +level: 20 + +method: "randNum" + +class: "Module\Example\Hello" + } + 1 => ZM\Annotation\Swoole\OnMessageEvent^ {#165 + +connect_type: "default" + +rule: "connectIsQQ()" + +level: 99 + +method: "handleByEvent" + +class: "ZM\Module\QQBot" + } +] +*/ +``` + +## EventDispatcher::enableEventTrace() - 启用事件跟踪器 + +还没写完,不着急。 \ No newline at end of file diff --git a/docs/update/build-update.md b/docs/update/build-update.md index ba48847d..3e2805a4 100644 --- a/docs/update/build-update.md +++ b/docs/update/build-update.md @@ -4,6 +4,21 @@ 同时此处将只使用 build 版本号进行区分。 +## build 427 (2021-11-16) + +- 新增全局中间件,可在全局配置文件中设置 +- 修复部分 Typo +- 新增指令 `server:status`、`server:reload`、`server:stop` 可用在新开终端中查看框架运行状态、重启和退出 +- 新增支持 `array` 格式的消息 +- 上下文 Context 对象新增 `getOriginMessage()` 用于获取原消息,`getMessage()` 如果在设置了转换后,将默认转换消息为字符串格式保持与旧模块兼容 +- OneBot API 新增全局过滤器,可用作 Action 过滤重写等操作 +- 配置文件新增 `runtime.reload_delay_time`,用于可配置重载 Worker 等待的时间(毫秒) +- 配置文件新增 `runtime.global_middleware_binding`,用于配置全局中间件 +- 配置文件新增 `onebot.message_convert_string`,用于配置是否转换数组格式为字符串,保证与前版本的兼容性(默认为 true) +- MessageUtil 消息工具类新增方法:`strToArray($msg, bool $ignore_space = true, bool $trim_text = false)` +- MessageUtil 消息工具类新增方法:`arrayToStr(array $array)` +- 新增框架启动多次监测功能,无法使用同一个框架项目同时启动两个框架 + ## build 426 (2021-11-10) - 修复 CQ 码的解析函数 Bug(#52) diff --git a/src/Module/Middleware/TimerMiddleware.php b/src/Module/Middleware/TimerMiddleware.php index c99cb2b0..a1a2b504 100644 --- a/src/Module/Middleware/TimerMiddleware.php +++ b/src/Module/Middleware/TimerMiddleware.php @@ -33,7 +33,7 @@ class TimerMiddleware implements MiddlewareInterface * @HandleAfter() */ public function onAfter() { - Console::info("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms."); + Console::info("Using " . round((microtime(true) - $this->starttime) * 1000, 3) . " ms."); } /** @@ -42,7 +42,7 @@ class TimerMiddleware implements MiddlewareInterface * @throws Exception */ public function onException(Exception $e) { - Console::error("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms but an Exception occurred."); + Console::error("Using " . round((microtime(true) - $this->starttime) * 1000, 3) . " ms but an Exception occurred."); throw $e; } } diff --git a/src/ZM/API/CQAPI.php b/src/ZM/API/CQAPI.php index d5e7cce5..2b24d55e 100644 --- a/src/ZM/API/CQAPI.php +++ b/src/ZM/API/CQAPI.php @@ -3,6 +3,7 @@ namespace ZM\API; +use Closure; use ZM\Console\Console; use ZM\Store\LightCacheInside; use ZM\Store\Lock\SpinLock; @@ -11,6 +12,13 @@ use ZM\Utils\CoMessage; trait CQAPI { + /** @var null|Closure */ + private static $filter = null; + + public static function registerFilter(callable $callable) { + self::$filter = $callable; + } + /** * @param $connection * @param $reply @@ -18,6 +26,11 @@ trait CQAPI * @return bool|array */ private function processAPI($connection, $reply, $function = null) { + if (is_callable(self::$filter)) { + $reply2 = call_user_func(self::$filter, $reply); + if (is_bool($reply2)) return $reply2; + else $reply = $reply2; + } if ($connection->getOption("type") === CONN_WEBSOCKET) return $this->processWebsocketAPI($connection, $reply, $function); else diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php index eec5a12a..c335de14 100644 --- a/src/ZM/Annotation/AnnotationParser.php +++ b/src/ZM/Annotation/AnnotationParser.php @@ -4,6 +4,7 @@ namespace ZM\Annotation; use Doctrine\Common\Annotations\AnnotationReader; +use ZM\Annotation\CQ\CQCommand; use ZM\Annotation\Interfaces\ErgodicAnnotation; use ZM\Config\ZMConfig; use ZM\Console\Console; @@ -98,7 +99,6 @@ class AnnotationParser if ($vs instanceof ErgodicAnnotation) { foreach (($this->annotation_map[$v]["methods"] ?? []) as $method) { $copy = clone $vs; - /** @noinspection PhpUndefinedFieldInspection */ $copy->method = $method->getName(); $this->annotation_map[$v]["methods_annotations"][$method->getName()][] = $copy; } @@ -116,12 +116,27 @@ class AnnotationParser } } + $inserted = []; + //预处理3:处理每个函数上面的特殊注解,就是需要操作一些东西的 foreach (($this->annotation_map[$v]["methods_annotations"] ?? []) as $method_name => $methods_annotations) { foreach ($methods_annotations as $method_anno) { /** @var AnnotationBase $method_anno */ $method_anno->class = $v; $method_anno->method = $method_name; + if (!($method_anno instanceof Middleware) && ($middlewares = ZMConfig::get("global", "runtime")["global_middleware_binding"][get_class($method_anno)] ?? []) !== []) { + if (!isset($inserted[$v][$method_name])) { + // 在这里在其他中间件前插入插入全局的中间件 + foreach ($middlewares as $middleware) { + $mid_class = new Middleware(); + $mid_class->middleware = $middleware; + $mid_class->class = $v; + $mid_class->method = $method_name; + $this->middleware_map[$v][$method_name][] = $mid_class; + } + $inserted[$v][$method_name] = true; + } + } if ($method_anno instanceof RequestMapping) { RouteManager::importRouteByAnnotation($method_anno, $method_name, $v, $methods_annotations); } elseif ($method_anno instanceof Middleware) { @@ -134,9 +149,10 @@ class AnnotationParser Console::debug("解析注解完毕!"); } - public function generateAnnotationEvents() { + public function generateAnnotationEvents(): array { $o = []; foreach ($this->annotation_map as $obj) { + // 这里的ErgodicAnnotation是为了解决类上的注解可穿透到方法上的问题 foreach (($obj["class_annotations"] ?? []) as $class_annotation) { if ($class_annotation instanceof ErgodicAnnotation) continue; else $o[get_class($class_annotation)][] = $class_annotation; diff --git a/src/ZM/Command/Daemon/DaemonReloadCommand.php b/src/ZM/Command/Daemon/DaemonReloadCommand.php index 7ff98c5a..e06cce56 100644 --- a/src/ZM/Command/Daemon/DaemonReloadCommand.php +++ b/src/ZM/Command/Daemon/DaemonReloadCommand.php @@ -12,7 +12,7 @@ class DaemonReloadCommand extends DaemonCommand protected static $defaultName = 'daemon:reload'; protected function configure() { - $this->setDescription("重载守护进程下的用户代码(仅限--daemon模式可用)"); + $this->setDescription("重载框架"); } protected function execute(InputInterface $input, OutputInterface $output): int { diff --git a/src/ZM/Command/Daemon/DaemonStatusCommand.php b/src/ZM/Command/Daemon/DaemonStatusCommand.php index 68d77239..0ab44a8c 100644 --- a/src/ZM/Command/Daemon/DaemonStatusCommand.php +++ b/src/ZM/Command/Daemon/DaemonStatusCommand.php @@ -11,18 +11,20 @@ class DaemonStatusCommand extends DaemonCommand protected static $defaultName = 'daemon:status'; protected function configure() { - $this->setDescription("查看守护进程框架的运行状态(仅限--daemon模式可用)"); + $this->setDescription("查看框架的运行状态"); } protected function execute(InputInterface $input, OutputInterface $output): int { parent::execute($input, $output); - $output->writeln("框架运行中,pid:" . $this->daemon_file["pid"] . ""); - $output->writeln("----- 以下是stdout内容 -----"); - $stdout = file_get_contents($this->daemon_file["stdout"]); - $stdout = explode("\n", $stdout); - for ($i = 15; $i > 0; --$i) { - if (isset($stdout[count($stdout) - $i])) - echo $stdout[count($stdout) - $i] . PHP_EOL; + $output->writeln("框架" . ($this->daemon_file["daemon"] ? "以守护进程模式" : "") . "运行中,pid:" . $this->daemon_file["pid"] . ""); + if ($this->daemon_file["daemon"]) { + $output->writeln("----- 以下是stdout内容 -----"); + $stdout = file_get_contents($this->daemon_file["stdout"]); + $stdout = explode("\n", $stdout); + for ($i = 15; $i > 0; --$i) { + if (isset($stdout[count($stdout) - $i])) + echo $stdout[count($stdout) - $i] . PHP_EOL; + } } return 0; } diff --git a/src/ZM/Command/Daemon/DaemonStopCommand.php b/src/ZM/Command/Daemon/DaemonStopCommand.php index 1cac85b3..959def97 100644 --- a/src/ZM/Command/Daemon/DaemonStopCommand.php +++ b/src/ZM/Command/Daemon/DaemonStopCommand.php @@ -13,7 +13,7 @@ class DaemonStopCommand extends DaemonCommand protected static $defaultName = 'daemon:stop'; protected function configure() { - $this->setDescription("停止守护进程下运行的框架(仅限--daemon模式可用)"); + $this->setDescription("停止运行的框架"); } protected function execute(InputInterface $input, OutputInterface $output): int { diff --git a/src/ZM/Command/RunServerCommand.php b/src/ZM/Command/RunServerCommand.php index b62361e6..819d57f9 100644 --- a/src/ZM/Command/RunServerCommand.php +++ b/src/ZM/Command/RunServerCommand.php @@ -8,12 +8,14 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use ZM\Framework; +use ZM\Utils\DataProvider; class RunServerCommand extends Command { protected static $defaultName = 'server'; protected function configure() { + $this->setAliases(['server:start']); $this->setDefinition([ new InputOption("debug-mode", "D", null, "开启调试模式 (这将关闭协程化)"), new InputOption("log-debug", null, null, "调整消息等级到debug (log-level=4)"), @@ -47,6 +49,17 @@ class RunServerCommand extends Command return 1; } } + $pid_path = DataProvider::getWorkingDir() . "/.daemon_pid"; + if (file_exists($pid_path)) { + $pid = json_decode(file_get_contents($pid_path), true)["pid"] ?? null; + if ($pid !== null && posix_getsid($pid) !== false) { + $output->writeln("检测到已经在 pid: $pid 进程启动了框架!"); + $output->writeln("不可以同时启动两个框架!"); + return 1; + } else { + unlink($pid_path); + } + } (new Framework($input->getOptions()))->start(); return 0; } diff --git a/src/ZM/Command/Server/ServerReloadCommand.php b/src/ZM/Command/Server/ServerReloadCommand.php new file mode 100644 index 00000000..7885949c --- /dev/null +++ b/src/ZM/Command/Server/ServerReloadCommand.php @@ -0,0 +1,25 @@ +setDescription("重载框架"); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + parent::execute($input, $output); + Process::kill(intval($this->daemon_file["pid"]), SIGUSR1); + $output->writeln("成功重载!"); + return 0; + } +} diff --git a/src/ZM/Command/Server/ServerStatusCommand.php b/src/ZM/Command/Server/ServerStatusCommand.php new file mode 100644 index 00000000..21838fe0 --- /dev/null +++ b/src/ZM/Command/Server/ServerStatusCommand.php @@ -0,0 +1,34 @@ +setDescription("查看框架的运行状态"); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + parent::execute($input, $output); + $output->writeln("框架" . ($this->daemon_file["daemon"] ? "以守护进程模式" : "") . "运行中,pid:" . $this->daemon_file["pid"] . ""); + if ($this->daemon_file["daemon"]) { + $output->writeln("----- 以下是stdout内容 -----"); + $stdout = file_get_contents($this->daemon_file["stdout"]); + $stdout = explode("\n", $stdout); + for ($i = 15; $i > 0; --$i) { + if (isset($stdout[count($stdout) - $i])) + echo $stdout[count($stdout) - $i] . PHP_EOL; + } + } + return 0; + } +} +{ + +} \ No newline at end of file diff --git a/src/ZM/Command/Server/ServerStopCommand.php b/src/ZM/Command/Server/ServerStopCommand.php new file mode 100644 index 00000000..91a447c6 --- /dev/null +++ b/src/ZM/Command/Server/ServerStopCommand.php @@ -0,0 +1,35 @@ +setDescription("停止运行的框架"); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + parent::execute($input, $output); + Process::kill(intval($this->daemon_file["pid"]), SIGTERM); + $i = 10; + while (file_exists(DataProvider::getWorkingDir() . "/.daemon_pid") && $i > 0) { + sleep(1); + --$i; + } + if ($i === 0) { + $output->writeln("停止失败,请检查进程pid #" . $this->daemon_file["pid"] . " 是否响应!"); + } else { + $output->writeln("成功停止!"); + } + return 0; + } +} diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php index 0274bb2d..fcb6081b 100644 --- a/src/ZM/ConsoleApplication.php +++ b/src/ZM/ConsoleApplication.php @@ -21,14 +21,17 @@ use ZM\Command\RunServerCommand; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +use ZM\Command\Server\ServerReloadCommand; +use ZM\Command\Server\ServerStatusCommand; +use ZM\Command\Server\ServerStopCommand; use ZM\Exception\InitException; class ConsoleApplication extends Application { private static $obj = null; - const VERSION_ID = 426; - const VERSION = "2.5.8"; + const VERSION_ID = 427; + const VERSION = "2.6.0"; /** * @throws InitException @@ -46,8 +49,8 @@ class ConsoleApplication extends Application * @return ConsoleApplication * @throws InitException */ - public function initEnv($with_default_cmd = ""): ConsoleApplication { - if (defined("WORKDING_DIR")) throw new InitException(); + public function initEnv(string $with_default_cmd = ""): ConsoleApplication { + if (defined("WORKING_DIR")) throw new InitException(); _zm_env_check(); @@ -90,6 +93,9 @@ class ConsoleApplication extends Application new DaemonReloadCommand(), new DaemonStopCommand(), new RunServerCommand(), //运行主服务的指令控制器 + new ServerStatusCommand(), + new ServerStopCommand(), + new ServerReloadCommand(), new PureHttpCommand(), //纯HTTP服务器指令 new SystemdGenerateCommand() ]); diff --git a/src/ZM/Context/Context.php b/src/ZM/Context/Context.php index 151b7314..92912df5 100644 --- a/src/ZM/Context/Context.php +++ b/src/ZM/Context/Context.php @@ -9,6 +9,7 @@ use Swoole\Coroutine; use Swoole\Http\Request; use Swoole\WebSocket\Frame; use Swoole\WebSocket\Server; +use ZM\Config\ZMConfig; use ZM\ConnectionManager\ConnectionObject; use ZM\ConnectionManager\ManagerGM; use ZM\Console\Console; @@ -19,6 +20,7 @@ use ZM\Exception\WaitTimeoutException; use ZM\Http\Response; use ZM\API\ZMRobot; use ZM\Utils\CoMessage; +use ZM\Utils\MessageUtil; class Context implements ContextInterface { @@ -56,8 +58,8 @@ class Context implements ContextInterface */ public function getResponse(): ?Response { return self::$context[$this->cid]["response"] ?? null; } - /** @return ConnectionObject|null|Response */ - public function getConnection() { return ManagerGM::get($this->getFd()); } + /** @return ConnectionObject|null */ + public function getConnection(): ?ConnectionObject { return ManagerGM::get($this->getFd()); } /** * @return int|null @@ -72,9 +74,20 @@ class Context implements ContextInterface return $conn instanceof ConnectionObject ? new ZMRobot($conn) : null; } - public function getMessage() { return self::$context[$this->cid]["data"]["message"] ?? null; } + public function getMessage() { + if ((ZMConfig::get("global", "onebot")["message_convert_string"] ?? true) === true && is_array($msg = $this->getOriginMessage())) { + return MessageUtil::arrayToStr($msg); + } else { + return self::$context[$this->cid]["data"]["message"] ?? null; + } + } - public function setMessage($msg) { self::$context[$this->cid]["data"]["message"] = $msg; } + public function setMessage($msg) { + if (is_string($msg) && is_array($this->getOriginMessage())) { + $msg = MessageUtil::strToArray($msg); + } + self::$context[$this->cid]["data"]["message"] = $msg; + } public function getUserId() { return $this->getData()["user_id"] ?? null; } @@ -239,4 +252,6 @@ class Context implements ContextInterface public function copy() { return self::$context[$this->cid]; } public function getOption() { return self::getCache("match"); } + + public function getOriginMessage() { return self::$context[$this->cid]["data"]["message"] ?? null; } } diff --git a/src/ZM/Event/EventManager.php b/src/ZM/Event/EventManager.php index 2580d42e..3f6620ae 100644 --- a/src/ZM/Event/EventManager.php +++ b/src/ZM/Event/EventManager.php @@ -12,6 +12,7 @@ use ZM\Annotation\AnnotationParser; use ZM\Annotation\Swoole\OnTick; use ZM\Config\ZMConfig; use ZM\Console\Console; +use ZM\Exception\AnnotationException; use ZM\Store\LightCache; use ZM\Store\ZMAtomic; @@ -32,6 +33,9 @@ class EventManager (new AnnotationParser())->sortByLevel(self::$events, $event_name); } + /** + * @throws AnnotationException + */ public static function loadEventByParser(AnnotationParser $parser) { self::$events = $parser->generateAnnotationEvents(); self::$middlewares = $parser->getMiddlewares(); diff --git a/src/ZM/Event/SwooleEvent/OnBeforeReload.php b/src/ZM/Event/SwooleEvent/OnBeforeReload.php index c5673569..af59ac1c 100644 --- a/src/ZM/Event/SwooleEvent/OnBeforeReload.php +++ b/src/ZM/Event/SwooleEvent/OnBeforeReload.php @@ -7,6 +7,7 @@ namespace ZM\Event\SwooleEvent; use Swoole\Process; use Swoole\WebSocket\Server; use ZM\Annotation\Swoole\SwooleHandler; +use ZM\Config\ZMConfig; use ZM\Console\Console; use ZM\Event\SwooleEvent; @@ -22,7 +23,7 @@ class OnBeforeReload implements SwooleEvent for ($i = 0; $i < ZM_WORKER_NUM; ++$i) { Process::kill(zm_atomic("_#worker_" . $i)->get(), SIGUSR1); } - - usleep(800 * 1000); + $conf = ZMConfig::get("global", "runtime")["reload_delay_time"] ?? 800; + if ($conf !== 0) usleep($conf * 1000); } } \ No newline at end of file diff --git a/src/ZM/Event/SwooleEvent/OnShutdown.php b/src/ZM/Event/SwooleEvent/OnShutdown.php index 2996929a..b997618b 100644 --- a/src/ZM/Event/SwooleEvent/OnShutdown.php +++ b/src/ZM/Event/SwooleEvent/OnShutdown.php @@ -8,6 +8,7 @@ use Swoole\Server; use ZM\Annotation\Swoole\SwooleHandler; use ZM\Console\Console; use ZM\Event\SwooleEvent; +use ZM\Utils\DataProvider; /** * Class OnShutdown @@ -18,5 +19,9 @@ class OnShutdown implements SwooleEvent { public function onCall(Server $server) { Console::verbose("正在关闭 Master 进程,pid=" . posix_getpid()); + $pid_path = DataProvider::getWorkingDir() . "/.daemon_pid"; + if (file_exists($pid_path)) { + unlink($pid_path); + } } } \ No newline at end of file diff --git a/src/ZM/Event/SwooleEvent/OnStart.php b/src/ZM/Event/SwooleEvent/OnStart.php index 84865ba9..400a8383 100644 --- a/src/ZM/Event/SwooleEvent/OnStart.php +++ b/src/ZM/Event/SwooleEvent/OnStart.php @@ -25,13 +25,12 @@ class OnStart implements SwooleEvent if (!Framework::$argv["disable-safe-exit"]) { SignalListener::signalMaster($server); } - if (Framework::$argv["daemon"]) { - $daemon_data = json_encode([ - "pid" => $server->master_pid, - "stdout" => ZMConfig::get("global")["swoole"]["log_file"] - ], 128 | 256); - file_put_contents(DataProvider::getWorkingDir() . "/.daemon_pid", $daemon_data); - } + $daemon_data = json_encode([ + "pid" => $server->master_pid, + "stdout" => ZMConfig::get("global")["swoole"]["log_file"], + "daemon" => (bool)Framework::$argv["daemon"] + ], 128 | 256); + file_put_contents(DataProvider::getWorkingDir() . "/.daemon_pid", $daemon_data); } diff --git a/src/ZM/Utils/MessageUtil.php b/src/ZM/Utils/MessageUtil.php index e9411e81..f3a7b10b 100644 --- a/src/ZM/Utils/MessageUtil.php +++ b/src/ZM/Utils/MessageUtil.php @@ -59,7 +59,7 @@ class MessageUtil return false; } - public static function isAtMe($msg, $me_id) { + public static function isAtMe($msg, $me_id): bool { return strpos($msg, CQ::at($me_id)) !== false; } @@ -72,7 +72,7 @@ class MessageUtil * @param int $type * @return string */ - public static function getImageCQFromLocal($file, $type = 0): string { + public static function getImageCQFromLocal($file, int $type = 0): string { switch ($type) { case 0: return CQ::image("base64://" . base64_encode(file_get_contents($file))); @@ -90,7 +90,7 @@ class MessageUtil * @param $msg * @return array|string[] */ - public static function splitCommand($msg) { + public static function splitCommand($msg): array { $word = explodeMsg(str_replace("\r", "", $msg)); if (empty($word)) $word = [""]; if (count(explode("\n", $word[0])) >= 2) { @@ -98,7 +98,7 @@ class MessageUtil $first = split_explode(" ", array_shift($enter)); $word = array_merge($first, $enter); foreach ($word as $k => $v) { - $word[$k] = trim($word[$k]); + $word[$k] = trim($v); } } return $word; @@ -109,15 +109,15 @@ class MessageUtil * @param $obj * @return MatchResult */ - public static function matchCommand($msg, $obj) { + public static function matchCommand($msg, $obj): MatchResult { $ls = EventManager::$events[CQCommand::class] ?? []; $word = self::splitCommand($msg); $matched = new MatchResult(); foreach ($ls as $v) { if (array_diff([$v->match, $v->pattern, $v->regex, $v->keyword, $v->end_with, $v->start_with], [""]) == []) continue; - elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == $obj["user_id"])) && - ($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == ($obj["group_id"] ?? 0))) && - ($v->message_type == '' || ($v->message_type != '' && $v->message_type == $obj["message_type"])) + elseif (($v->user_id == 0 || ($v->user_id == $obj["user_id"])) && + ($v->group_id == 0 || ($v->group_id == ($obj["group_id"] ?? 0))) && + ($v->message_type == '' || ($v->message_type == $obj["message_type"])) ) { if (($word[0] != "" && $v->match == $word[0]) || in_array($word[0], $v->alias)) { array_shift($word); @@ -166,4 +166,61 @@ class MessageUtil ProcessManager::sendActionToWorker($i, "add_short_command", [$command, $reply]); } } + + /** + * 字符串转数组 + * @param $msg + * @param bool $ignore_space + * @param false $trim_text + * @return array + */ + public static function strToArray($msg, bool $ignore_space = true, bool $trim_text = false): array { + $arr = []; + while (($rear = mb_strstr($msg, '[CQ:')) !== false && ($end = mb_strstr($rear, ']', true)) !== false) { + // 把 [CQ: 前面的文字生成段落 + $front = mb_strstr($msg, '[CQ:', true); + // 如果去掉空格都还有文字,或者不去掉空格有字符,且不忽略空格,则生成段落,否则不生成 + if (($trim_front = trim($front)) !== '' || ($front !== '' && !$ignore_space)) { + $arr[] = ['type' => 'text', 'data' => ['text' => CQ::decode($trim_text ? $trim_front : $front)]]; + } + // 处理 CQ 码 + $content = mb_substr($end, 4); + $cq = explode(",", $content); + $object_type = array_shift($cq); + $object_params = []; + foreach ($cq as $v) { + $key = mb_strstr($v, "=", true); + $object_params[$key] = CQ::decode(mb_substr(mb_strstr($v, "="), 1), true); + } + $arr[] = ["type" => $object_type, "data" => $object_params]; + $msg = mb_substr(mb_strstr($rear, ']'), 1); + } + if (($trim_msg = trim($msg)) !== '' || ($msg !== '' && !$ignore_space)) { + $arr[] = ['type' => 'text', 'data' => ['text' => CQ::decode($trim_text ? $trim_msg : $msg)]]; + } + return $arr; + } + + /** + * 数组转字符串 + * 纪念一下,这段代码完全由AI生成,没有人知道它是怎么写的,这句话是我自己写的,不知道是不是有人知道的 + * @param array $array + * @return string + * @author Copilot + */ + public static function arrayToStr(array $array): string { + $str = ""; + foreach ($array as $v) { + if ($v['type'] == 'text') { + $str .= $v['data']['text']; + } else { + $str .= "[CQ:" . $v['type']; + foreach ($v['data'] as $key => $value) { + $str .= "," . $key . "=" . CQ::encode($value, true); + } + $str .= "]"; + } + } + return $str; + } } \ No newline at end of file diff --git a/zhamao b/zhamao old mode 100755 new mode 100644