diff --git a/config/global.php b/config/global.php index 8237a76f..5a55d125 100644 --- a/config/global.php +++ b/config/global.php @@ -113,18 +113,21 @@ $config['server_event_handler_class'] = [ ]; /** 服务器启用的外部第三方和内部插件 */ -$config['modules']['onebot'] = [ - // 机器人解析模块,关闭后无法使用如@CQCommand等注解 - 'status' => true, - 'single_bot_mode' => false -]; - -$config['modules']['remote_terminal'] = [ - // 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) - 'status' => false, - 'host' => '127.0.0.1', - 'port' => 20002, - 'token' => '' +$config['modules'] = [ + 'onebot' => [ // 机器人解析模块,关闭后无法使用如@CQCommand等注解 + 'status' => true, + 'single_bot_mode' => false + ], + 'http_proxy_server' => [ // 一个内置的简单HTTP代理服务器,目前还没有认证功能,预计2.4.0版本完成 + 'status' => false, + 'host' => '0.0.0.0', + 'port' => 8083, + 'swoole_set_override' => [ + 'backlog' => 128, + 'buffer_output_size' => 1024 * 1024 * 128, + 'socket_buffer_size' => 1024 * 1024 * 1 + ] + ], ]; return $config; diff --git a/docs/advanced/inside-class.md b/docs/advanced/inside-class.md index fb300250..b71a30b9 100644 --- a/docs/advanced/inside-class.md +++ b/docs/advanced/inside-class.md @@ -27,44 +27,3 @@ TODO:先放一放。 ``` -## ZM\Entity\MatchObject - -此类是调用方法 `MessageUtil::matchCommand()` 返回的对象体,含有匹配成功与否和匹配到的注解相关的信息。 - -### 属性 - -- `$match`:`bool` 类型,返回匹配是否成功 -- `$object`:`CQCommand` 注解类,如果匹配成功则返回对应的 `@CQCommand` 信息 -- `match`:`array` 类型,如果匹配成功则返回匹配到的参数 - -```php -// 假设我有一个注解事件 @CQCommand(match="你好"),绑定的函数是 \Module\Example\Hello 下的 hello123() - -$obj = MessageUtil::matchCommand("你好 我叫顺溜 我今年二十八", ctx()->getData()); -/* 以下是返回信息,仅供参考 -$obj->match ==> true -$obj->object ==> \ZM\Annotation\CQ\CQCommand: ( - match: "你好", - pattern: "", - regex: "", - start_with: "", - end_with: "", - keyword: "", - alias: [], - message_type: "", - user_id: 0, - group_id: 0, - discuss_id: 0, - level: 20, - method: "hello123", - class: \Module\Example\Hello::class -) -$obj->match ==> [ - "我叫顺溜", - "我今年二十八" -] -*/ -``` - - - diff --git a/docs/advanced/task-worker.md b/docs/advanced/task-worker.md deleted file mode 100644 index 18495898..00000000 --- a/docs/advanced/task-worker.md +++ /dev/null @@ -1,3 +0,0 @@ -# 使用 TaskWorker 进程处理密集运算 - -> 新开个坑,有时间补上。(__填坑标记__) \ No newline at end of file diff --git a/docs/assets/img/image-20210321193956832.png b/docs/assets/img/image-20210321193956832.png deleted file mode 100644 index 3e28f407..00000000 Binary files a/docs/assets/img/image-20210321193956832.png and /dev/null differ diff --git a/docs/component/global-functions.md b/docs/component/global-functions.md index 897fbccc..f4e6e37e 100644 --- a/docs/component/global-functions.md +++ b/docs/component/global-functions.md @@ -134,6 +134,10 @@ set_coroutine_params(["data" => [ 别名:`context()`,获取当前协程的上下文,见 [上下文](/component/context/)。 +## zm_debug() + +同 `Console::debug($msg)`。 + ## zm_sleep() 协程版 `sleep()` 函数。 @@ -251,58 +255,4 @@ bot()->sendPrivateMsg(123456, "你好啊!!"); 定义:`getAllFdByConnectType(string $type = 'default'): array` -当 `$type` 为 `qq` 时,则返回所有 OneBot 机器人接入的 WebSocket 连接号。 - -## zm_dump() - -更漂亮地输出变量值,可替代 `var_dump()`。 - -```php -class Pass { - public $foo = 123; - public $bar = ["a", "b"]; -} -$pass = new Pass(); -$pass->obj = true; -zm_dump($pass); -``` - -image-20210321193956832 - -## zm_config() - -同 `ZMConfig::get()`。 - -定义:`zm_config($name, $key = null)`。 - -有关 ZMConfig 模块的说明,见 [指南 - 基本配置](/guide/basic-config/)。 - -```php -zm_config("global"); //等同于 ZMConfig::get("global"); -zm_config("global", "swoole"); //等同于 ZMConfig::get("global", "swoole"); -``` - -## zm_info() - -同 `Console::info($msg)`。 - -## zm_debug() - -同 `Console::debug($msg)`。 - -## zm_warning() - -同 `Console::warning($msg)`。 - -## zm_success() - -同 `Console::success($msg)`。 - -## zm_error() - -同 `Console::error($msg)`。 - -## zm_verbose() - -同 `Console::verbose($msg)`。 - +当 `$type` 为 `qq` 时,则返回所有 OneBot 机器人接入的 WebSocket 连接号。 \ No newline at end of file diff --git a/docs/component/message-util.md b/docs/component/message-util.md deleted file mode 100644 index 72f6507a..00000000 --- a/docs/component/message-util.md +++ /dev/null @@ -1,89 +0,0 @@ -# MessageUtil 消息处理工具类 - -类定义:`\ZM\Utils\MessageUtil` - -> 2.3.0 版本起可用。 - -这里放置一些机器人聊天消息处理的便捷静态方法,例如下载图片等。 - -## 方法 - -### downloadCQImage() - -下载用户消息中所带的所有图片,并返回文件路径。 - -定义:`downloadCQImage($msg, $path = null)` - -参数 `$msg` 为带图片的用户消息,例如 `你好啊!\n[CQ:image,file=a.jpg,url=https://zhamao.xin/file/hello.jpg]` - -参数 `$path` 为图片下载的路径,如果不填(默认 null)则指定为 `zm_data/images/` 目录,且不存在会自动创建。 - -```php -$r = MessageUtil::downloadCQImage("你好啊!\n[CQ:image,file=a.jpg,url=https://zhamao.xin/file/hello.jpg]"); -/* -$r == [ - "/path-to/zhamao-framework/zm_data/images/a.jpg" -]; -*/ -``` - -如果返回的是空数组 `[ ]`,则表明消息中没有图片。如果返回的是 `false`,则表明其中至少一张下载失败或路径有误。 - -### containsImage() - -检查消息中是否含有图片。 - -定义:`containsImage($msg)` - -返回:`bool`,你懂的,true 就是有,false 就没有。 - -```php -MessageUtil::containsImage("[CQ:image,file=a.jpg,url=http://xxx]"); // true -MessageUtil::containsImage("[CQ:face,id=140] 咦,这是一条带表情的消息"); // false -``` - -### getImageCQFromLocal() - -通过文件路径获取图片的发送 CQ 码。 - -定义:`getImageCQFromLocal($file, $type = 0)` - -参数 `$file` 为图片的绝对路径。 - -返回:图片的 CQ 码,如 `[CQ:image,file=xxxxx]` - -参数 `$type`: - -- `0`:以 base64 的方式发送图片,返回结果如 `[CQ:image,file=base64://xxxxxx]` -- `1`:以 `file://` 本地文件的方式发送图片,返回结果如 `[CQ:image,file=file:///path-to/images/a.jpg]` -- `2`:返回图片的 http:// CQ 码(默认为 /images/ 路径就是文件对应所在的目录),如 `[CQ:image,file=http://127.0.0.1:20001/images/a.jpg]` - -### splitCommand() - -切割用户消息为数组形式(`@CQCommand` 就是使用此方式切割的) - -定义:`splitCommand($msg): array` - -返回:数组,切分后的。 - -!!! tip "为什么不直接使用 explode 呢" - - 因为 `explode()` 只会简单粗暴的切割字符串,假设用户输入的消息中两个词中间有多个空格,则会有空的词出现。例如 `你好 我是一个长空格`。此函数会将多个空格当作一个空格来对待。 - -```php -MessageUtil::splitCommand("你好 我是傻瓜\n我是傻瓜二号"); // ["你好","我是傻瓜","我是傻瓜二号"] -MessageUtil::splitCommand("我有 三个空格"); // ["我有","三个空格"] -``` - -### matchCommand() - -匹配一条消息到 `@CQCommand` 规则的注解事件,返回要执行的类和函数位置。 - -定义:`matchCommand($msg, $obj)` - -参数 `$msg` 为消息内容。 - -参数 `$obj` 为事件的对象,可使用 `ctx()->getData()` 获取原先的事件体(仅限 OneBot 消息类型事件中使用) - -返回:`\ZM\Entity\MatchObject` 对象,含有匹配成功与否,匹配到的注解对象,匹配到的分割词等,见 [] - diff --git a/docs/component/route-manager.md b/docs/component/route-manager.md deleted file mode 100644 index 698e125d..00000000 --- a/docs/component/route-manager.md +++ /dev/null @@ -1,54 +0,0 @@ -# HTTP 路由管理 - -HTTP 路由管理器用作管理炸毛框架内 `@RequestMapping` 和静态目录的路由操作的,可在运行过程中编写添加路由。 - -类定义:`\ZM\Http\RouteManager` - -> 2.3.0 版本起可用。 - -!!! warning "注意" - - 因为炸毛框架的路由实现是不基于跨进程的共享内存的,所以每次使用这里面的工具函数都需要单独在所有 Worker 进程中执行一次,最好的办法就是在启动框架时执行(`@OnStart(-1)` 即可,代表此注解事件将在每个工作进程中都被执行一次)。 - -## 方法 - -### importRouteByAnnotation() - -通过注解类导入路由。(注:此方法一般为框架内部使用) - -定义:`importRouteByAnnotation(RequestMapping $vss, $method, $class, $methods_annotations)` - -参数 `$vss`:RequestMapping 注解类,类中定义 `route` 和 `request_method` 即可。 - -参数 `$method, $class`:执行的目标注解事件函数位置,比如 `$class = \Module\Example\Hello::class`,`$method = 'hitokoto'`。 - -参数 `$methods_annotations`:需要绑定的 Controller 注解类数组,一般数组内建议只带有一个 Controller,如 `[$controller]`。 - -### addStaticFileRoute() - -添加一个单目录(此目录下无子目录,只有文件)并绑定为一个路由。 - -定义:`addStaticFileRoute($route, $path)` - -参数 `$route`:绑定的目标路由,如 `/images/`。 - -参数 `$path`:绑定的文件目录位置,如 `/root/zhamao-framework-starter/zm_data/images/`。 - -```php -/** - * @OnStart(-1) - */ -public function onStart() { - RouteManager::addStaticFileRoute("/images/", DataProvider::getDataFolder()."images/"); -} -``` - -## 属性 - -### RouteManager::$routes - -此为存放路由树的变量,请谨慎操作。 - -定义:`\Symfony\Component\Routing\RouteCollection | null` - -炸毛框架使用了 Symfony 框架的 route 组件,有关详情请查阅 [文档](https://symfony.com/doc/current/routing.html)。 \ No newline at end of file diff --git a/docs/component/task-worker.md b/docs/component/task-worker.md deleted file mode 100644 index 77c955b8..00000000 --- a/docs/component/task-worker.md +++ /dev/null @@ -1,26 +0,0 @@ -# TaskManager 工作进程管理 - -此类管理的是 TaskWorker 相关工作。有关使用 TaskWorker 的教程,见 [进阶 - 使用 TaskWorker 进程处理密集运算](/advanced/task-worker) - -类定义:`\ZM\Utils\TaskManager` - -使用 TaskWorker 需要先在 `global.php` 配置文件中开启! - -## 方法 - -### runTask() - -在 TaskWorker 运行任务。 - -定义:`runTask($task_name, $timeout = -1, ...$params)` - -参数 `$task_name`:对应 `@OnTask` 注解绑定的任务函数。 - -参数 `$timeout`:等待任务函数最长运行的时间(秒),如果超过此时间将返回 false。 - -参数 `剩余`:将变量传入 TaskWorker 进程,除 Closure,资源类型外,可序列化的变量均可。 - -```php -TaskManager::runTask("heavy_task", 100, "param1", "param2"); -``` - diff --git a/docs/component/zmutil.md b/docs/component/zmutil.md index 68f0d795..1ee6cb78 100644 --- a/docs/component/zmutil.md +++ b/docs/component/zmutil.md @@ -21,47 +21,3 @@ class ASD{ ZMUtil::getModInstance(ASD::class)->test = 5; ``` -## ZMUtil::getReloadableFiles() - -返回可通过热重启(reload)来重新加载的 php 文件列表。 - -以下是示例模块下的例子(直接拉取最新的框架源码并运行框架后获取的)。 - -```php -array:31 [ - 94 => "src/ZM/Context/Context.php" - 95 => "src/ZM/Context/ContextInterface.php" - 96 => "src/ZM/Annotation/AnnotationParser.php" - 97 => "src/Custom/Annotation/Example.php" - 98 => "src/ZM/Annotation/Interfaces/CustomAnnotation.php" - 99 => "src/Module/Example/Hello.php" - 100 => "src/ZM/Annotation/Swoole/OnStart.php" - 101 => "src/ZM/Annotation/CQ/CQCommand.php" - 102 => "src/ZM/Annotation/Interfaces/Level.php" - 103 => "src/ZM/Annotation/Command/TerminalCommand.php" - 104 => "src/ZM/Annotation/Http/RequestMapping.php" - 105 => "src/ZM/Annotation/Http/RequestMethod.php" - 106 => "src/ZM/Annotation/Http/Middleware.php" - 107 => "src/ZM/Annotation/Interfaces/ErgodicAnnotation.php" - 108 => "src/ZM/Annotation/Swoole/OnOpenEvent.php" - 109 => "src/ZM/Annotation/Swoole/OnSwooleEventBase.php" - 110 => "src/ZM/Annotation/Interfaces/Rule.php" - 111 => "src/ZM/Annotation/Swoole/OnCloseEvent.php" - 112 => "src/ZM/Annotation/Swoole/OnRequestEvent.php" - 113 => "src/ZM/Http/RouteManager.php" - 114 => "vendor/symfony/routing/RouteCollection.php" - 115 => "vendor/symfony/routing/Route.php" - 116 => "src/Module/Middleware/TimerMiddleware.php" - 117 => "src/ZM/Http/MiddlewareInterface.php" - 118 => "src/ZM/Annotation/Http/MiddlewareClass.php" - 119 => "src/ZM/Annotation/Http/HandleBefore.php" - 120 => "src/ZM/Annotation/Http/HandleAfter.php" - 121 => "src/ZM/Annotation/Http/HandleException.php" - 122 => "src/ZM/Event/EventManager.php" - 123 => "src/ZM/Annotation/Swoole/OnSwooleEvent.php" - 124 => "src/ZM/Event/EventDispatcher.php" -] -``` - -> 为什么不能重载所有文件?因为框架是多进程模型,而重载相当于只重新启动了一次 Worker 进程,Manager 和 Master 进程未重启,所以被 Manager、Master 进程已经加载的 PHP 文件无法使用 reload 命令重新加载。详见 [进阶 - 进程间隔离](/advanced/multi-process/#_5)。 - diff --git a/docs/event/framework-annotations.md b/docs/event/framework-annotations.md index db8e3bcc..874d227d 100644 --- a/docs/event/framework-annotations.md +++ b/docs/event/framework-annotations.md @@ -262,27 +262,6 @@ 无。 -## TerminalCommand() - -添加一个远程终端的自定义命令。(2.4.0 版本起可用) - -### 属性 - -| 类型 | 值 | -| ---------- | --------------------------------------- | -| 名称 | `@TerminalCommand` | -| 触发前提 | 连接到远程终端可触发 | -| 命名空间 | `ZM\Annotation\Command\TerminalCommand` | -| 适用位置 | 方法 | -| 返回值处理 | 无 | - -### 注解参数 - -| 参数名称 | 参数范围 | 默认 | -| ----------- | ------------------------------ | ---- | -| command | `string`,**必填**,命令字符串 | | -| description | `string`,要显示的帮助文本 | 空 | - ## 示例1(机器人连接框架后输出信息) ```php @@ -427,6 +406,3 @@ public function onCrawl() { } ``` -## 示例6(创建一个远程终端命令并调试框架) - -> 开个坑,以后填。(__填坑标记__) \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 28aee039..9c7613ef 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -72,12 +72,9 @@ nav: - 事件分发器: event/event-dispatcher.md - 框架组件: - 框架组件: component/index.md + - 机器人 API: component/robot-api.md + - CQ 码(多媒体消息): component/cqcode.md - 上下文: component/context.md - - 聊天机器人组件: - - 机器人 API: component/robot-api.md - - CQ 码(多媒体消息): component/cqcode.md - - 机器人消息处理: component/message-util.md - - Token 验证: component/access-token.md - 存储: - LightCache 轻量缓存: component/light-cache.md - MySQL 数据库: component/mysql.md @@ -85,15 +82,13 @@ nav: - ZMAtomic 原子计数器: component/atomics.md - SpinLock 自旋锁: component/spin-lock.md - 文件管理: component/data-provider.md - - HTTP 服务器工具类: - - HTTP 和 WebSocket 客户端: component/zmrequest.md - - HTTP 路由管理: component/route-manager.md - 协程池: component/coroutine-pool.md - 单例类: component/singleton-trait.md - ZMUtil 杂项: component/zmutil.md - 全局方法: component/global-functions.md + - HTTP 和 WebSocket 客户端: component/zmrequest.md - Console 终端: component/console.md - - TaskWorker 管理: component/task-worker.md + - Token 验证: component/access-token.md - 进阶开发: - 进阶开发: advanced/index.md - 框架剖析: advanced/framework-structure.md @@ -102,7 +97,6 @@ nav: - 内部类文件手册: advanced/inside-class.md - 接入 WebSocket 客户端: advanced/connect-ws-client.md - 框架多进程: advanced/multi-process.md - - TaskWorker 提高并发: advanced/task-worker.md - 开发实战教程: - 编写管理员才能触发的功能: advanced/example/admin.md - FAQ: FAQ.md diff --git a/src/Module/Example/Hello.php b/src/Module/Example/Hello.php index 52f3aa9e..5abf3a96 100644 --- a/src/Module/Example/Hello.php +++ b/src/Module/Example/Hello.php @@ -2,16 +2,10 @@ namespace Module\Example; -use ZM\Annotation\Command\TerminalCommand; -use ZM\Annotation\CQ\CQBefore; -use ZM\Annotation\CQ\CQMessage; use ZM\Annotation\Http\Middleware; use ZM\Annotation\Swoole\OnCloseEvent; use ZM\Annotation\Swoole\OnOpenEvent; use ZM\Annotation\Swoole\OnRequestEvent; -use ZM\Annotation\Swoole\OnStart; -use ZM\API\CQ; -use ZM\API\TuringAPI; use ZM\ConnectionManager\ConnectionObject; use ZM\Console\Console; use ZM\Annotation\CQ\CQCommand; @@ -19,7 +13,6 @@ use ZM\Annotation\Http\RequestMapping; use ZM\Event\EventDispatcher; use ZM\Exception\InterruptException; use ZM\Requests\ZMRequest; -use ZM\Utils\MessageUtil; use ZM\Utils\ZMUtil; /** @@ -29,12 +22,6 @@ use ZM\Utils\ZMUtil; */ class Hello { - /** - * @OnStart() - */ - public function onStart() { - } - /* * 默认的图片监听路由对应目录,如需要使用可取消下面的注释,把上面的 /* 换成 /** * @OnStart(-1) @@ -80,34 +67,6 @@ class Hello return $obj["hitokoto"] . "\n----「" . $obj["from"] . "」"; } - /** - * @CQCommand(start_with="机器人",end_with="机器人",message_type="group") - * @CQMessage(message_type="private",level=1) - */ - public function turingAPI() { - $user_id = ctx()->getUserId(); - $api = "83513e3d316f44de9c952cda4c9aed30"; // 请在这里填入你的图灵机器人的apikey - if ($api === "") return false; //如果没有填入apikey则此功能关闭 - if (($this->_running_annotation ?? null) instanceof CQCommand) { - $msg = ctx()->getFullArg("我在!有什么事吗?"); - } else { - $msg = ctx()->getMessage(); - } - zm_dump($msg); - return TuringAPI::getTuringMsg($msg, $user_id, $api); - } - - /** - * @CQBefore("message") - */ - public function changeAt() { - if (MessageUtil::isAtMe(ctx()->getMessage(), ctx()->getRobotId())) { - $msg = str_replace(CQ::at(ctx()->getRobotId()), "", ctx()->getMessage()); - ctx()->setMessage("机器人 ".$msg); - } - return true; - } - /** * 一个简单随机数的功能demo * 问法1:随机数 1 20 diff --git a/src/ZM/API/TuringAPI.php b/src/ZM/API/TuringAPI.php deleted file mode 100644 index ab5de163..00000000 --- a/src/ZM/API/TuringAPI.php +++ /dev/null @@ -1,118 +0,0 @@ - 0, - "userInfo" => [ - "apiKey" => $api, - "userId" => $user_id - ] - ]; - if ($msg != "") { - $content["perception"]["inputText"]["text"] = $msg; - } - $msg = trim($msg); - if (mb_strlen($msg) < 1 && !isset($url)) return "请说出你想说的话"; - if (isset($url)) { - $content["perception"]["inputImage"]["url"] = $url; - $content["reqType"] = 1; - } - if (!isset($content["perception"])) return "请说出你想说的话"; - $client = new Client("openapi.tuling123.com", 80); - $client->setHeaders(["Content-type" => "application/json"]); - $client->post("/openapi/api/v2", json_encode($content, JSON_UNESCAPED_UNICODE)); - $api_return = json_decode($client->body, true); - if (!isset($api_return["intent"]["code"])) return "XD 哎呀,我脑子突然短路了,请稍后再问我吧!"; - $status = self::getResultStatus($api_return); - if ($status !== true) { - if ($status == "err:输入文本内容超长(上限150)") return "你的话太多了!!!"; - if ($api_return["intent"]["code"] == 4003) { - return "哎呀,我刚才有点走神了,可能忘记你说什么了,可以重说一遍吗"; - } - Console::error("图灵机器人发送错误!\n错误原始内容:" . $origin . "\n来自:" . $user_id . "\n错误信息:" . $status); - //echo json_encode($r, 128|256); - return "哎呀,我刚才有点走神了,要不一会儿换一种问题试试?"; - } - $result = $api_return["results"]; - //Console::info(Console::setColor(json_encode($result, 128 | 256), "green")); - $final = ""; - foreach ($result as $k => $v) { - switch ($v["resultType"]) { - case "url": - $final .= "\n" . $v["values"]["url"]; - break; - case "text": - $final .= "\n" . $v["values"]["text"]; - break; - case "image": - $final .= "\n" . CQ::image($v["values"]["image"]); - break; - } - } - return trim($final); - } - - public static function getResultStatus($r) { - switch ($r["intent"]["code"]) { - case 5000: - return "err:无解析结果"; - case 4000: - case 6000: - return "err:暂不支持该功能"; - case 4001: - return "err:加密方式错误"; - case 4005: - case 4002: - return "err:无功能权限"; - case 4003: - return "err:该apikey没有可用请求次数"; - case 4007: - return "err:apikey不合法"; - case 4100: - return "err:userid获取失败"; - case 4200: - return "err:上传格式错误"; - case 4300: - return "err:批量操作超过限制"; - case 4400: - return "err:没有上传合法userid"; - case 4500: - return "err:userid申请个数超过限制"; - case 4600: - return "err:输入内容为空"; - case 4602: - return "err:输入文本内容超长(上限150)"; - case 7002: - return "err:上传信息失败"; - case 8008: - return "err:服务器错误"; - default: - return true; - } - } -} \ No newline at end of file diff --git a/src/ZM/Annotation/Command/TerminalCommand.php b/src/ZM/Annotation/Command/TerminalCommand.php deleted file mode 100644 index 30034cb8..00000000 --- a/src/ZM/Annotation/Command/TerminalCommand.php +++ /dev/null @@ -1,27 +0,0 @@ -daemon_file["pid"])); + system("kill -TERM " . intval($this->daemon_file["pid"])); unlink(DataProvider::getWorkingDir() . "/.daemon_pid"); $output->writeln("成功停止!"); return Command::SUCCESS; diff --git a/src/ZM/Command/RunServerCommand.php b/src/ZM/Command/RunServerCommand.php index 6d4b77c2..c5a93d6d 100644 --- a/src/ZM/Command/RunServerCommand.php +++ b/src/ZM/Command/RunServerCommand.php @@ -23,7 +23,6 @@ class RunServerCommand extends Command new InputOption("log-error", null, null, "调整消息等级到error (log-level=0)"), new InputOption("log-theme", null, InputOption::VALUE_REQUIRED, "改变终端的主题配色"), new InputOption("disable-console-input", null, null, "禁止终端输入内容 (废弃)"), - new InputOption("remote-terminal", null, null, "启用远程终端,配置使用global.php中的"), new InputOption("disable-coroutine", null, null, "关闭协程Hook"), new InputOption("daemon", null, null, "以守护进程的方式运行框架"), new InputOption("watch", null, null, "监听 src/ 目录的文件变化并热更新"), diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php index bd0388ef..74fc50b9 100644 --- a/src/ZM/ConsoleApplication.php +++ b/src/ZM/ConsoleApplication.php @@ -18,8 +18,8 @@ use ZM\Utils\DataProvider; class ConsoleApplication extends Application { - const VERSION_ID = 397; - const VERSION = "2.3.4"; + const VERSION_ID = 398; + const VERSION = "2.3.5"; public function __construct(string $name = 'UNKNOWN') { define("ZM_VERSION_ID", self::VERSION_ID); @@ -100,7 +100,7 @@ class ConsoleApplication extends Application private function selfCheck(): bool { if (!extension_loaded("swoole")) die("Can not find swoole extension.\nSee: https://github.com/zhamao-robot/zhamao-framework/issues/19\n"); - if (version_compare(SWOOLE_VERSION, "4.5.0") == -1) die("You must install swoole version >= 4.5.0 !"); + if (version_compare(SWOOLE_VERSION, "4.4.13") == -1) die("You must install swoole version >= 4.4.13 !"); if (version_compare(PHP_VERSION, "7.2") == -1) die("PHP >= 7.2 required."); return true; } diff --git a/src/ZM/Entity/MatchResult.php b/src/ZM/Entity/MatchResult.php deleted file mode 100644 index d345f797..00000000 --- a/src/ZM/Entity/MatchResult.php +++ /dev/null @@ -1,17 +0,0 @@ -_running_annotation = $v; if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在执行方法 " . $q_c . "::" . $q_f . " ..."); $this->store = $q_o->$q_f(...$params); } catch (Exception $e) { @@ -189,7 +188,6 @@ class EventDispatcher return false; } else { $q_o = ZMUtil::getModInstance($q_c); - $q_o->_running_annotation = $v; if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在执行方法 " . $q_c . "::" . $q_f . " ..."); $this->store = $q_o->$q_f(...$params); $this->status = self::STATUS_NORMAL; diff --git a/src/ZM/Event/ServerEventHandler.php b/src/ZM/Event/ServerEventHandler.php index d0d766a1..1167e61c 100644 --- a/src/ZM/Event/ServerEventHandler.php +++ b/src/ZM/Event/ServerEventHandler.php @@ -17,13 +17,13 @@ use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Event; use Swoole\Process; -use Swoole\Timer; use Throwable; use ZM\Annotation\AnnotationParser; use ZM\Annotation\Http\RequestMapping; use ZM\Annotation\Swoole\OnCloseEvent; use ZM\Annotation\Swoole\OnMessageEvent; use ZM\Annotation\Swoole\OnOpenEvent; +use ZM\Annotation\Swoole\OnPipeMessageEvent; use ZM\Annotation\Swoole\OnRequestEvent; use ZM\Annotation\Swoole\OnStart; use ZM\Annotation\Swoole\OnSwooleEvent; @@ -49,9 +49,9 @@ use ZM\Store\LightCache; use ZM\Store\LightCacheInside; use ZM\Store\MySQL\SqlPoolStorage; use ZM\Store\Redis\ZMRedisPool; +use ZM\Store\WorkerCache; use ZM\Utils\DataProvider; use ZM\Utils\HttpUtil; -use ZM\Utils\ProcessManager; use ZM\Utils\ZMUtil; class ServerEventHandler @@ -83,7 +83,7 @@ class ServerEventHandler Process::signal(SIGINT, function () use ($r) { if (zm_atomic("_int_is_reload")->get() === 1) { zm_atomic("_int_is_reload")->set(0); - \server()->reload(); + ZMUtil::reload(); } else { echo "\r"; Console::warning("Server interrupted(SIGINT) on Master."); @@ -133,25 +133,17 @@ class ServerEventHandler if ($worker_id == (ZMConfig::get("worker_cache")["worker"] ?? 0)) { LightCache::savePersistence(); } - Console::verbose(($server->taskworker ? "Task" : "") . "Worker #$worker_id 已停止"); + Console::debug(($server->taskworker ? "Task" : "") . "Worker #$worker_id 已停止"); } /** * @SwooleHandler("WorkerStart") * @param Server $server * @param $worker_id - * @throws Exception */ public function onWorkerStart(Server $server, $worker_id) { - zm_atomic("_#worker_".$worker_id)->set($server->worker_pid); //if (ZMBuf::atomic("stop_signal")->get() != 0) return; - Process::signal(SIGUSR1, function() use ($worker_id){ - Timer::clearAll(); - ProcessManager::resumeAllWorkerCoroutines(); - }); Process::signal(SIGINT, function () use ($worker_id, $server) { - Timer::clearAll(); - ProcessManager::resumeAllWorkerCoroutines(); Console::debug("正在关闭 " . ($server->taskworker ? "Task" : "") . "Worker 进程 " . Console::setColor("#" . \server()->worker_id, "gold") . TermColor::frontColor256(59) . ", pid=" . posix_getpid()); server()->stop($worker_id); }); @@ -480,6 +472,7 @@ class ServerEventHandler Console::error("Uncaught " . get_class($e) . " when calling \"open\": " . $error_msg); Console::trace(); } + //EventHandler::callSwooleEvent("open", $server, $request); } /** @@ -535,27 +528,70 @@ class ServerEventHandler * @param $src_worker_id * @param $data * @throws Exception - * @noinspection PhpUnusedParameterInspection */ public function onPipeMessage(Server $server, $src_worker_id, $data) { //var_dump($data, $server->worker_id); //unset(Context::$context[Co::getCid()]); $data = json_decode($data, true); - ProcessManager::workerAction($src_worker_id, $data); - } - - /** - * @SwooleHandler("beforeReload") - */ - public function onBeforeReload() { - for($i = 0; $i < ZM_WORKER_NUM; ++$i) { - $pid = zm_atomic("_#worker_".$i)->get(); - Process::kill($pid, SIGUSR1); + switch ($data["action"] ?? '') { + case "resume_ws_message": + $obj = $data["data"]; + Co::resume($obj["coroutine"]); + break; + case "getWorkerCache": + $r = WorkerCache::get($data["key"]); + $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; + $server->sendMessage(json_encode($action, 256), $src_worker_id); + break; + case "setWorkerCache": + $r = WorkerCache::set($data["key"], $data["value"]); + $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; + $server->sendMessage(json_encode($action, 256), $src_worker_id); + break; + case "unsetWorkerCache": + $r = WorkerCache::unset($data["key"]); + $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; + $server->sendMessage(json_encode($action, 256), $src_worker_id); + break; + case "hasKeyWorkerCache": + $r = WorkerCache::hasKey($data["key"], $data["subkey"]); + $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; + $server->sendMessage(json_encode($action, 256), $src_worker_id); + break; + case "asyncAddWorkerCache": + WorkerCache::add($data["key"], $data["value"], true); + break; + case "asyncSubWorkerCache": + WorkerCache::sub($data["key"], $data["value"], true); + break; + case "asyncSetWorkerCache": + WorkerCache::set($data["key"], $data["value"], true); + break; + case "asyncUnsetWorkerCache": + WorkerCache::unset($data["key"], true); + break; + case "addWorkerCache": + $r = WorkerCache::add($data["key"], $data["value"]); + $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; + $server->sendMessage(json_encode($action, 256), $src_worker_id); + break; + case "subWorkerCache": + $r = WorkerCache::sub($data["key"], $data["value"]); + $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; + $server->sendMessage(json_encode($action, 256), $src_worker_id); + break; + case "returnWorkerCache": + WorkerCache::$transfer[$data["cid"]] = $data["value"]; + zm_resume($data["cid"]); + break; + default: + $dispatcher = new EventDispatcher(OnPipeMessageEvent::class); + $dispatcher->setRuleFunction(function (OnPipeMessageEvent $v) use ($data) { + return $v->action == $data["action"]; + }); + $dispatcher->dispatchEvents($data); + break; } - - Console::info(Console::setColor("Reloading server...", "gold")); - usleep(800 * 1000); - LightCacheInside::unset("wait_api", "wait_api"); } /** diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index 1b349f37..c164aeb0 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -5,9 +5,7 @@ namespace ZM; use Doctrine\Common\Annotations\AnnotationReader; -use Error; use Exception; -use Swoole\Server\Port; use ZM\Annotation\Swoole\OnSetup; use ZM\Config\ZMConfig; use ZM\ConnectionManager\ManagerGM; @@ -25,7 +23,6 @@ use Swoole\Runtime; use Swoole\WebSocket\Server; use ZM\Annotation\Swoole\SwooleHandler; use ZM\Console\Console; -use ZM\Utils\Terminal; use ZM\Utils\ZMUtil; class Framework @@ -38,16 +35,11 @@ class Framework * @var Server */ public static $server; - /** - * @var string[] - */ - public static $loaded_files = []; /** * @var array|bool|mixed|null */ private $server_set; - /** @noinspection PhpUnusedParameterInspection */ public function __construct($args = []) { $tty_width = $this->getTtyWidth(); @@ -62,6 +54,7 @@ class Framework //定义常量 include_once "global_defines.php"; + ZMAtomic::init(); try { $sw = ZMConfig::get("global"); if (!is_dir($sw["zm_data"])) mkdir($sw["zm_data"]); @@ -89,17 +82,11 @@ class Framework ($o = ZMConfig::get("console_color")) === false ? [] : $o ); - $worker = ZMConfig::get("global", "swoole")["worker_num"] ?? swoole_cpu_num(); - define("ZM_WORKER_NUM", $worker); - ZMAtomic::init(); $timezone = ZMConfig::get("global", "timezone") ?? "Asia/Shanghai"; date_default_timezone_set($timezone); $this->server_set = ZMConfig::get("global", "swoole"); - $this->server_set["log_level"] = SWOOLE_LOG_DEBUG; - $add_port = ZMConfig::get("global", "modules")["remote_terminal"]["status"] ?? false; - - $this->parseCliArgs(self::$argv, $add_port); + $this->parseCliArgs(self::$argv); // 打印初始信息 $out["listen"] = ZMConfig::get("global", "host") . ":" . ZMConfig::get("global", "port"); @@ -127,82 +114,12 @@ class Framework $out["php_version"] = PHP_VERSION; $out["swoole_version"] = SWOOLE_VERSION; } - if ($add_port) { - $conf = ZMConfig::get("global", "modules")["remote_terminal"]; - $out["terminal"] = $conf["host"] . ":" . $conf["port"]; - } $out["working_dir"] = DataProvider::getWorkingDir(); self::printProps($out, $tty_width, $args["log-theme"] === null); self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port")); - if ($add_port) { - $welcome_msg = Console::setColor("Welcome! You can use `help` for usage.", "green"); - /** @var Port $port */ - $port = self::$server->listen("127.0.0.1", 20002, SWOOLE_SOCK_TCP); - $port->set([ - 'open_http_protocol' => false - ]); - $port->on('connect', function (?\Swoole\Server $serv, $fd) use ($port, $welcome_msg) { - ManagerGM::pushConnect($fd, "terminal"); - $serv->send($fd, file_get_contents(working_dir() . "/config/motd.txt")); - if (!empty(ZMConfig::get("global", "modules")["remote_terminal"]["token"] ?? '')) { - $serv->send($fd, "Please input token: "); - } else { - $serv->send($fd, $welcome_msg . "\n>>> "); - } - }); - - $port->on('receive', function ($serv, $fd, $reactor_id, $data) use ($welcome_msg) { - ob_start(); - try { - $arr = LightCacheInside::get("light_array", "input_token") ?? []; - if (empty($arr[$fd] ?? '')) { - if (ZMConfig::get("global", "modules")["remote_terminal"]["token"] != '') { - $token = trim($data); - if ($token === ZMConfig::get("global", "modules")["remote_terminal"]["token"]) { - SpinLock::transaction("input_token", function () use ($fd, $token) { - $arr = LightCacheInside::get("light_array", "input_token"); - $arr[$fd] = $token; - LightCacheInside::set("light_array", "input_token", $arr); - }); - $serv->send($fd, Console::setColor("Auth success!!\n", "green")); - $serv->send($fd, $welcome_msg . "\n>>> "); - } else { - $serv->send($fd, Console::setColor("Auth failed!!\n", "red")); - $serv->close($fd); - } - return; - } - } - if (trim($data) == "exit" || trim($data) == "q") { - $serv->send($fd, Console::setColor("Bye!\n", "blue")); - $serv->close($fd); - return; - } - Terminal::executeCommand(trim($data)); - } catch (Exception $e) { - $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; - Console::error("Uncaught exception " . get_class($e) . " when calling \"open\": " . $error_msg); - Console::trace(); - } catch (Error $e) { - $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; - Console::error("Uncaught " . get_class($e) . " when calling \"open\": " . $error_msg); - Console::trace(); - } - - $r = ob_get_clean(); - if (!empty($r)) $serv->send($fd, $r); - if (!in_array(trim($data), ['r', 'reload', 'stop'])) $serv->send($fd, ">>> "); - }); - - $port->on('close', function ($serv, $fd) { - ManagerGM::popConnect($fd); - //echo "Client: Close.\n"; - }); - } - self::$server->set($this->server_set); Console::setServer(self::$server); self::printMotd($tty_width); @@ -278,9 +195,9 @@ class Framework } public function start() { - self::$loaded_files = get_included_files(); self::$server->start(); zm_atomic("server_is_stopped")->set(1); + Console::setLevel(0); } /** @@ -326,9 +243,9 @@ class Framework /** * 解析命令行的 $argv 参数们 * @param $args - * @param $add_port + * @throws Exception */ - private function parseCliArgs($args, &$add_port) { + private function parseCliArgs($args) { $coroutine_mode = true; global $terminal_id; $terminal_id = uuidgen(); @@ -379,9 +296,6 @@ class Framework Console::$theme = $y; } break; - case 'remote-terminal': - $add_port = true; - break; case 'show-php-ver': default: //Console::info("Calculating ".$x); @@ -390,7 +304,6 @@ class Framework } } if ($coroutine_mode) Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL); - else Runtime::enableCoroutine(false, SWOOLE_HOOK_ALL); } private static function writeNoDouble($k, $v, &$line_data, &$line_width, &$current_line, $colorful, $max_border) { diff --git a/src/ZM/Module/QQBot.php b/src/ZM/Module/QQBot.php index 9fbd9721..be54c454 100644 --- a/src/ZM/Module/QQBot.php +++ b/src/ZM/Module/QQBot.php @@ -15,7 +15,6 @@ use ZM\Event\EventDispatcher; use ZM\Exception\InterruptException; use ZM\Exception\WaitTimeoutException; use ZM\Utils\CoMessage; -use ZM\Utils\MessageUtil; /** * Class QQBot @@ -77,19 +76,58 @@ class QQBot //Console::warning("最xia数据包:".json_encode($data)); switch ($data["post_type"]) { case "message": + $word = explodeMsg(str_replace("\r", "", context()->getMessage())); + if (empty($word)) $word = [""]; + if (count(explode("\n", $word[0])) >= 2) { + $enter = explode("\n", context()->getMessage()); + $first = split_explode(" ", array_shift($enter)); + $word = array_merge($first, $enter); + foreach ($word as $k => $v) { + $word[$k] = trim($word[$k]); + } + } //分发CQCommand事件 $dispatcher = new EventDispatcher(CQCommand::class); + $dispatcher->setRuleFunction(function (CQCommand $v) use ($word) { + if (array_diff([$v->match, $v->pattern, $v->regex, $v->keyword, $v->end_with, $v->start_with], [""]) == []) return false; + elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == ctx()->getUserId())) && + ($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == (ctx()->getGroupId() ?? 0))) && + ($v->message_type == '' || ($v->message_type != '' && $v->message_type == ctx()->getMessageType())) + ) { + if (($word[0] != "" && $v->match == $word[0]) || in_array($word[0], $v->alias)) { + array_shift($word); + ctx()->setCache("match", $word); + return true; + } elseif ($v->start_with != "" && mb_strpos(ctx()->getMessage(), $v->start_with) === 0) { + ctx()->setCache("match", [mb_substr(ctx()->getMessage(), mb_strlen($v->start_with))]); + return true; + } elseif ($v->end_with != "" && strlen(ctx()->getMessage()) == (strripos(ctx()->getMessage(), $v->end_with) + strlen($v->end_with))) { + ctx()->setCache("match", [substr(ctx()->getMessage(), 0, strripos(ctx()->getMessage(), $v->end_with))]); + return true; + } elseif ($v->keyword != "" && mb_strpos(ctx()->getMessage(), $v->keyword) !== false) { + ctx()->setCache("match", explode($v->keyword, ctx()->getMessage())); + return true; + } elseif ($v->pattern != "") { + $match = matchArgs($v->pattern, ctx()->getMessage()); + if ($match !== false) { + ctx()->setCache("match", $match); + return true; + } + } elseif ($v->regex != "") { + if (preg_match("/" . $v->regex . "/u", ctx()->getMessage(), $word2) != 0) { + ctx()->setCache("match", $word2); + return true; + } + } + } + return false; + }); $dispatcher->setReturnFunction(function ($result) { if (is_string($result)) ctx()->reply($result); if (ctx()->getCache("has_reply") === true) EventDispatcher::interrupt(); }); - $s = MessageUtil::matchCommand(ctx()->getMessage(), ctx()->getData()); - if ($s->status !== false) { - if (!empty($s->match)) ctx()->setCache("match", $s->match); - $dispatcher->dispatchEvent($s->object, null); - if (is_string($dispatcher->store)) ctx()->reply($dispatcher->store); - if (ctx()->getCache("has_reply") === true) EventDispatcher::interrupt(); - } + $dispatcher->dispatchEvents(); + if ($dispatcher->status == EventDispatcher::STATUS_INTERRUPTED) EventDispatcher::interrupt(); //分发CQMessage事件 $msg_dispatcher = new EventDispatcher(CQMessage::class); diff --git a/src/ZM/Store/LightCache.php b/src/ZM/Store/LightCache.php index b88ca3dd..060cc0cf 100644 --- a/src/ZM/Store/LightCache.php +++ b/src/ZM/Store/LightCache.php @@ -7,6 +7,7 @@ namespace ZM\Store; use Exception; use Swoole\Table; use ZM\Annotation\Swoole\OnSave; +use ZM\Config\ZMConfig; use ZM\Console\Console; use ZM\Event\EventDispatcher; use ZM\Exception\ZMException; @@ -181,12 +182,17 @@ class LightCache } /** - * 这个只能在唯一一个工作进程中执行 + * @param false $only_worker * @throws Exception */ - public static function savePersistence() { - $dispatcher = new EventDispatcher(OnSave::class); - $dispatcher->dispatchEvents(); + public static function savePersistence($only_worker = false) { + // 下面将OnSave激活一下 + if (server()->worker_id == (ZMConfig::get("global", "worker_cache")["worker"] ?? 0)) { + $dispatcher = new EventDispatcher(OnSave::class); + $dispatcher->dispatchEvents(); + } + + if($only_worker) return; if (self::$kv_table === null) return; $r = []; @@ -201,7 +207,8 @@ class LightCache $r = file_put_contents(self::$config["persistence_path"], json_encode($r, 128 | 256)); if ($r === false) Console::error("Not saved, please check your \"persistence_path\"!"); } - Console::verbose("Saved."); + + } private static function checkExpire($key) { diff --git a/src/ZM/Store/LightCacheInside.php b/src/ZM/Store/LightCacheInside.php index 92c3ab38..b9bcc81a 100644 --- a/src/ZM/Store/LightCacheInside.php +++ b/src/ZM/Store/LightCacheInside.php @@ -18,7 +18,6 @@ class LightCacheInside self::createTable("wait_api", 3, 65536); self::createTable("connect", 3, 64); //用于存单机器人模式下的机器人fd的 self::createTable("static_route", 64, 256);//用于存储 - self::createTable("light_array", 8, 512, 0.6); } catch (ZMException $e) { return false; } //用于存协程等待的状态内容的 @@ -60,11 +59,10 @@ class LightCacheInside * @param $name * @param $size * @param $str_size - * @param int $conflict_proportion * @throws ZMException */ - private static function createTable($name, $size, $str_size, $conflict_proportion = 0) { - self::$kv_table[$name] = new Table($size, $conflict_proportion); + private static function createTable($name, $size, $str_size) { + self::$kv_table[$name] = new Table($size, 0); self::$kv_table[$name]->column("value", Table::TYPE_STRING, $str_size); $r = self::$kv_table[$name]->create(); if ($r === false) throw new ZMException("内存不足,创建静态表失败!"); diff --git a/src/ZM/Store/ZMAtomic.php b/src/ZM/Store/ZMAtomic.php index c1c8bed3..c2cb72a2 100644 --- a/src/ZM/Store/ZMAtomic.php +++ b/src/ZM/Store/ZMAtomic.php @@ -32,9 +32,6 @@ class ZMAtomic self::$atomics["wait_msg_id"] = new Atomic(0); self::$atomics["_event_id"] = new Atomic(0); self::$atomics["server_is_stopped"] = new Atomic(0); - for($i = 0; $i < ZM_WORKER_NUM; ++$i) { - self::$atomics["_#worker_".$i] = new Atomic(0); - } for ($i = 0; $i < 10; ++$i) { self::$atomics["_tmp_" . $i] = new Atomic(0); } diff --git a/src/ZM/Utils/MessageUtil.php b/src/ZM/Utils/MessageUtil.php index 79b48dff..1396d703 100644 --- a/src/ZM/Utils/MessageUtil.php +++ b/src/ZM/Utils/MessageUtil.php @@ -4,13 +4,9 @@ namespace ZM\Utils; -use ZM\Annotation\CQ\CQCommand; use ZM\API\CQ; use ZM\Config\ZMConfig; use ZM\Console\Console; -use ZM\Entity\MatchResult; -use ZM\Event\EventDispatcher; -use ZM\Event\EventManager; use ZM\Requests\ZMRequest; class MessageUtil @@ -59,10 +55,6 @@ class MessageUtil return false; } - public static function isAtMe($msg, $me_id) { - return strpos($msg, CQ::at($me_id)) !== false; - } - /** * 通过本地地址返回图片的 CQ 码 * type == 0 : 返回图片的 base64 CQ 码 @@ -84,80 +76,4 @@ class MessageUtil } return ""; } - - /** - * 分割字符,将用户消息通过空格或换行分割为数组 - * @param $msg - * @return array|string[] - */ - public static function splitCommand($msg) { - $word = explodeMsg(str_replace("\r", "", $msg)); - if (empty($word)) $word = [""]; - if (count(explode("\n", $word[0])) >= 2) { - $enter = explode("\n", $msg); - $first = split_explode(" ", array_shift($enter)); - $word = array_merge($first, $enter); - foreach ($word as $k => $v) { - $word[$k] = trim($word[$k]); - } - } - return $word; - } - - /** - * @param $msg - * @param $obj - * @return MatchResult - */ - public static function matchCommand($msg, $obj) { - $ls = EventManager::$events[CQCommand::class]; - $word = self::splitCommand($msg); - $matched = new MatchResult(); - foreach ($ls as $k => $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"])) - ) { - if (($word[0] != "" && $v->match == $word[0]) || in_array($word[0], $v->alias)) { - array_shift($word); - $matched->match = $word; - $matched->object = $v; - $matched->status = true; - break; - } elseif ($v->start_with != "" && mb_strpos($msg, $v->start_with) === 0) { - $matched->match = [mb_substr($msg, mb_strlen($v->start_with))]; - $matched->object = $v; - $matched->status = true; - break; - } elseif ($v->end_with != "" && strlen($msg) == (strripos($msg, $v->end_with) + strlen($v->end_with))) { - $matched->match = [substr($msg, 0, strripos($msg, $v->end_with))]; - $matched->object = $v; - $matched->status = true; - break; - } elseif ($v->keyword != "" && mb_strpos($msg, $v->keyword) !== false) { - $matched->match = explode($v->keyword, $msg); - $matched->object = $v; - $matched->status = true; - break; - } elseif ($v->pattern != "") { - $match = matchArgs($v->pattern, $msg); - if ($match !== false) { - $matched->match = $match; - $matched->object = $v; - $matched->status = true; - break; - } - } elseif ($v->regex != "") { - if (preg_match("/" . $v->regex . "/u", $msg, $word2) != 0) { - $matched->match = $word2; - $matched->object = $v; - $matched->status = true; - break; - } - } - } - } - return $matched; - } } \ No newline at end of file diff --git a/src/ZM/Utils/ProcessManager.php b/src/ZM/Utils/ProcessManager.php index 973f3d43..83b65ac5 100644 --- a/src/ZM/Utils/ProcessManager.php +++ b/src/ZM/Utils/ProcessManager.php @@ -4,110 +4,14 @@ namespace ZM\Utils; -use Co; -use ZM\Annotation\Swoole\OnPipeMessageEvent; -use ZM\Console\Console; -use ZM\Event\EventDispatcher; -use ZM\Store\LightCache; -use ZM\Store\LightCacheInside; -use ZM\Store\WorkerCache; - class ProcessManager { - public static function workerAction($src_worker_id, $data) { - $server = server(); - switch ($data["action"] ?? '') { - case "eval": - eval($data["data"]); - break; - case "call_static": - call_user_func_array([$data["data"]["class"], $data["data"]["method"]], $data["data"]["params"]); - break; - case "save_persistence": - LightCache::savePersistence(); - break; - case "resume_ws_message": - $obj = $data["data"]; - Co::resume($obj["coroutine"]); - break; - case "getWorkerCache": - $r = WorkerCache::get($data["key"]); - $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; - $server->sendMessage(json_encode($action, 256), $src_worker_id); - break; - case "setWorkerCache": - $r = WorkerCache::set($data["key"], $data["value"]); - $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; - $server->sendMessage(json_encode($action, 256), $src_worker_id); - break; - case "unsetWorkerCache": - $r = WorkerCache::unset($data["key"]); - $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; - $server->sendMessage(json_encode($action, 256), $src_worker_id); - break; - case "hasKeyWorkerCache": - $r = WorkerCache::hasKey($data["key"], $data["subkey"]); - $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; - $server->sendMessage(json_encode($action, 256), $src_worker_id); - break; - case "asyncAddWorkerCache": - WorkerCache::add($data["key"], $data["value"], true); - break; - case "asyncSubWorkerCache": - WorkerCache::sub($data["key"], $data["value"], true); - break; - case "asyncSetWorkerCache": - WorkerCache::set($data["key"], $data["value"], true); - break; - case "asyncUnsetWorkerCache": - WorkerCache::unset($data["key"], true); - break; - case "addWorkerCache": - $r = WorkerCache::add($data["key"], $data["value"]); - $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; - $server->sendMessage(json_encode($action, 256), $src_worker_id); - break; - case "subWorkerCache": - $r = WorkerCache::sub($data["key"], $data["value"]); - $action = ["action" => "returnWorkerCache", "cid" => $data["cid"], "value" => $r]; - $server->sendMessage(json_encode($action, 256), $src_worker_id); - break; - case "returnWorkerCache": - WorkerCache::$transfer[$data["cid"]] = $data["value"]; - zm_resume($data["cid"]); - break; - default: - $dispatcher = new EventDispatcher(OnPipeMessageEvent::class); - $dispatcher->setRuleFunction(function (OnPipeMessageEvent $v) use ($data) { - return $v->action == $data["action"]; - }); - $dispatcher->dispatchEvents($data); - break; - } - } - - public static function sendActionToWorker($worker_id, $action, $data) { - $obj = ["action" => $action, "data" => $data]; - if (server()->worker_id === -1 && server()->getManagerPid() != posix_getpid()) { - Console::warning("Cannot send worker action from master or manager process!"); - return; - } - if (server()->worker_id == $worker_id) { - self::workerAction($worker_id, $obj); - } else { - server()->sendMessage(json_encode($obj), $worker_id); - } - } - - public static function resumeAllWorkerCoroutines() { - if (server()->worker_id === -1) { - Console::warning("Cannot call '".__FUNCTION__."' in non-worker process!"); - return; - } - foreach ((LightCacheInside::get("wait_api", "wait_api") ?? []) as $k => $v) { - if (($v["result"] ?? false) === null && isset($v["coroutine"], $v["worker_id"])) { - if (server()->worker_id == $v["worker_id"]) Co::resume($v["coroutine"]); - } - } + public static function runOnTask($param, $timeout = 0.5, $dst_worker_id = -1) { + return server()->taskwait([ + "action" => "runMethod", + "class" => $param["class"], + "method" => $param["method"], + "params" => $param["params"] + ], $timeout, $dst_worker_id); } } \ No newline at end of file diff --git a/src/ZM/Utils/Terminal.php b/src/ZM/Utils/Terminal.php index f1eb1054..fc01cc82 100644 --- a/src/ZM/Utils/Terminal.php +++ b/src/ZM/Utils/Terminal.php @@ -6,38 +6,22 @@ namespace ZM\Utils; use Exception; use Psy\Shell; -use ZM\Annotation\Command\TerminalCommand; -use ZM\ConnectionManager\ManagerGM; +use Swoole\Event; use ZM\Console\Console; -use ZM\Event\EventDispatcher; -use ZM\Event\EventManager; use ZM\Framework; class Terminal { /** * @param string $cmd + * @param $resource * @return bool * @noinspection PhpMissingReturnTypeInspection * @noinspection PhpUnused - * @throws Exception */ - public static function executeCommand(string $cmd) { + public static function executeCommand(string $cmd, $resource) { $it = explodeMsg($cmd); switch ($it[0] ?? '') { - case 'help': - $help[] = "exit | q:\t断开远程终端"; - $help[] = "logtest:\t输出所有可以打印的log等级示例消息,用于测试Console"; - $help[] = "call:\t\t用于执行不需要参数的动态函数,比如 `call \Module\Example\Hello hitokoto`"; - $help[] = "level:\t\t设置log等级,例如 `level 0|1|2|3|4`"; - $help[] = "bc:\t\teval执行代码,但输入必须是将代码base64之后的,如 `bc em1faW5mbygn5L2g5aW9Jyk7`"; - $help[] = "stop:\t\t停止服务器"; - $help[] = "reload | r:\t热重启用户编写的模块代码"; - foreach(EventManager::$events[TerminalCommand::class] as $v) { - $help[]=$v->command.":\t\t".(empty($v->description) ? "<用户自定义指令>" : $v->description); - } - echo implode("\n", $help) . PHP_EOL; - return true; case 'logtest': Console::log(date("[H:i:s]") . " [L] This is normal msg. (0)"); Console::error("This is error msg. (0)"); @@ -51,8 +35,7 @@ class Terminal $class_name = $it[1]; $function_name = $it[2]; $class = new $class_name([]); - $r = $class->$function_name(); - if (is_string($r)) Console::success($r); + $class->$function_name(); return true; case 'psysh': if (Framework::$argv["disable-coroutine"]) { @@ -60,11 +43,6 @@ class Terminal } else Console::error("Only \"--disable-coroutine\" mode can use psysh!!!"); return true; - case 'level': - $level = intval(is_numeric($it[1] ?? 99) ? ($it[1] ?? 99) : 99); - if ($level > 4 || $level < 0) Console::warning("Usage: 'level 0|1|2|3|4'"); - else Console::setLevel($level) || Console::success("Success!!"); - break; case 'bc': $code = base64_decode($it[1] ?? '', true); try { @@ -79,6 +57,7 @@ class Terminal Console::log($it[2], $it[1]); return true; case 'stop': + Event::del($resource); ZMUtil::stop(); return false; case 'reload': @@ -88,35 +67,8 @@ class Terminal case '': return true; default: - $dispatcher = new EventDispatcher(TerminalCommand::class); - $dispatcher->setRuleFunction(function ($v) use ($it) { - /** @var TerminalCommand $v */ - return $v->command == $it[0]; - }); - $dispatcher->setReturnFunction(function () { - EventDispatcher::interrupt('none'); - }); - $dispatcher->dispatchEvents($it); - if ($dispatcher->store !== 'none') { - Console::info("Command not found: " . $cmd); - return true; - } - } - return false; - } - - public static function log($type, $log_msg) { - ob_start(); - if (!in_array($type, ["log", "info", "debug", "success", "warning", "error", "verbose"])) { - ob_get_clean(); - return; - } - Console::$type($log_msg); - $r = ob_get_clean(); - $all = ManagerGM::getAllByName("terminal"); - foreach ($all as $k => $v) { - server()->send($v->getFd(), "\r" . $r); - server()->send($v->getFd(), ">>> "); + Console::info("Command not found: " . $cmd); + return true; } } } diff --git a/src/ZM/Utils/ZMUtil.php b/src/ZM/Utils/ZMUtil.php index dc891515..0efc014b 100644 --- a/src/ZM/Utils/ZMUtil.php +++ b/src/ZM/Utils/ZMUtil.php @@ -4,10 +4,13 @@ namespace ZM\Utils; +use Co; use Exception; -use Swoole\Process; +use Swoole\Event; +use Swoole\Timer; use ZM\Console\Console; -use ZM\Framework; +use ZM\Store\LightCache; +use ZM\Store\LightCacheInside; use ZM\Store\Lock\SpinLock; use ZM\Store\ZMAtomic; use ZM\Store\ZMBuf; @@ -21,20 +24,39 @@ class ZMUtil if (SpinLock::tryLock("_stop_signal") === false) return; Console::warning(Console::setColor("Stopping server...", "red")); if (Console::getLevel() >= 4) Console::trace(); + LightCache::savePersistence(); + if (ZMBuf::$terminal !== null) + Event::del(ZMBuf::$terminal); ZMAtomic::get("stop_signal")->set(1); - for($i = 0; $i < ZM_WORKER_NUM; ++$i) { - Process::kill(zm_atomic("_#worker_".$i)->get(), SIGUSR1); + try { + LightCache::set('stop', 'OK'); + } catch (Exception $e) { } server()->shutdown(); server()->stop(); } /** + * @param int $delay * @throws Exception */ - public static function reload() { - zm_atomic("_int_is_reload")->set(1); - system("kill -INT " . intval(server()->master_pid)); + public static function reload($delay = 800) { + if (server()->worker_id !== -1) { + Console::info(server()->worker_id); + zm_atomic("_int_is_reload")->set(1); + system("kill -INT " . intval(server()->master_pid)); + return; + } + Console::info(Console::setColor("Reloading server...", "gold")); + usleep($delay * 1000); + foreach ((LightCacheInside::get("wait_api", "wait_api") ?? []) as $k => $v) { + if (($v["result"] ?? false) === null && isset($v["coroutine"])) Co::resume($v["coroutine"]); + } + LightCacheInside::unset("wait_api", "wait_api"); + LightCache::savePersistence(); + //DataProvider::saveBuffer(); + Timer::clearAll(); + server()->reload(); } public static function getModInstance($class) { @@ -47,22 +69,6 @@ class ZMUtil } public static function sendActionToWorker($target_id, $action, $data) { - Console::verbose($action . ": " . $data); server()->sendMessage(json_encode(["action" => $action, "data" => $data]), $target_id); } - - /** - * 在工作进程中返回可以通过reload重新加载的php文件列表 - * @return string[]|string[][] - */ - public static function getReloadableFiles() { - return array_map( - function ($x) { - return str_replace(DataProvider::getWorkingDir() . "/", "", $x); - }, array_diff( - get_included_files(), - Framework::$loaded_files - ) - ); - } } diff --git a/src/ZM/global_functions.php b/src/ZM/global_functions.php index 9b38f3cf..14e790dd 100644 --- a/src/ZM/global_functions.php +++ b/src/ZM/global_functions.php @@ -3,7 +3,6 @@ use Swoole\Atomic; use Swoole\Coroutine; use Swoole\WebSocket\Server; -use Symfony\Component\VarDumper\VarDumper; use ZM\API\ZMRobot; use ZM\Config\ZMConfig; use ZM\ConnectionManager\ManagerGM; @@ -244,6 +243,8 @@ function ctx(): ?ContextInterface { } } +function zm_debug($msg) { Console::debug($msg); } + function onebot_target_id_name($message_type): string { return ($message_type == "group" ? "group_id" : "user_id"); } @@ -254,21 +255,13 @@ function zm_sleep($s = 1): bool { return true; } -function zm_exec($cmd): array { - return System::exec($cmd); -} +function zm_exec($cmd): array { return System::exec($cmd); } -function zm_cid() { - return Co::getCid(); -} +function zm_cid() { return Co::getCid(); } -function zm_yield() { - Co::yield(); -} +function zm_yield() { Co::yield(); } -function zm_resume(int $cid) { - Co::resume($cid); -} +function zm_resume(int $cid) { Co::resume($cid); } function zm_timer_after($ms, callable $callable) { Swoole\Timer::after($ms, function () use ($callable) { @@ -294,14 +287,10 @@ function zm_timer_tick($ms, callable $callable) { if (zm_cid() === -1) { return go(function () use ($ms, $callable) { Console::debug("Adding extra timer tick of " . $ms . " ms"); - Swoole\Timer::tick($ms, function () use ($callable) { - call_with_catch($callable); - }); + Swoole\Timer::tick($ms, function () use ($callable) {call_with_catch($callable);}); }); } else { - return Swoole\Timer::tick($ms, function () use ($callable) { - call_with_catch($callable); - }); + return Swoole\Timer::tick($ms, function () use ($callable) {call_with_catch($callable);}); } } @@ -365,32 +354,3 @@ function working_dir() { elseif (LOAD_MODE == 2) return realpath('.'); return null; } - -/** @noinspection PhpMissingReturnTypeInspection */ -function zm_dump($var, ...$moreVars) { - VarDumper::dump($var); - - foreach ($moreVars as $v) { - VarDumper::dump($v); - } - - if (1 < func_num_args()) { - return func_get_args(); - } - - return $var; -} - -function zm_info($obj) { Console::info($obj); } - -function zm_warning($obj) { Console::warning($obj); } - -function zm_success($obj) { Console::success($obj); } - -function zm_debug($obj) { Console::debug($obj); } - -function zm_verbose($obj) { Console::verbose($obj); } - -function zm_error($obj) { Console::error($obj); } - -function zm_config($name, $key = null) { return ZMConfig::get($name, $key); }