diff --git a/config/global.php b/config/global.php index 4b744ab3..5a55d125 100644 --- a/config/global.php +++ b/config/global.php @@ -109,15 +109,25 @@ $config['static_file_server'] = [ /** 注册 Swoole Server 事件注解的类列表 */ $config['server_event_handler_class'] = [ - \ZM\Event\ServerEventHandler::class, + // 这里添加例如 \ZM\Event\ServerEventHandler::class 这样的启动注解类 ]; /** 服务器启用的外部第三方和内部插件 */ $config['modules'] = [ - 'onebot' => [ + 'onebot' => [ // 机器人解析模块,关闭后无法使用如@CQCommand等注解 'status' => true, 'single_bot_mode' => false - ], // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 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/component/cqcode.md b/docs/component/cqcode.md index c2c5606f..e32c113c 100644 --- a/docs/component/cqcode.md +++ b/docs/component/cqcode.md @@ -128,8 +128,9 @@ $str = CQ::removeCQ("[CQ:at,qq=all]这是带表情的全体消息[CQ:face,id=8]" 解析 CQ 码。 -- 参数:`getCQ($msg);`:要解析出 CQ 码的消息。 -- 返回:`数组 | null`,见下表 +- 定义:`getCQ($msg, $is_object = false)` +- 参数 `$is_object` 为 true 时,返回一个 `\ZM\Entity\CQObject` 对象,此对象的属性和下表相同。(2.3.0+ 版本可用) +- 返回:`数组 | CQObject | null`,见下表。 | 键名 | 说明 | | ------ | ------------------------------------------------------------ | @@ -140,6 +141,10 @@ $str = CQ::removeCQ("[CQ:at,qq=all]这是带表情的全体消息[CQ:face,id=8]" ### CQ::getAllCQ() +定义:`CQ::getAllCQ($msg, $is_object = false)` + +参数 `$is_object` 为 true 时,返回一个 `\ZM\Entity\CQObject[]` 对象数组,此对象的属性和上面的表格内相同。(2.3.0+ 版本可用) + 解析 CQ 码,和 `getCQ()` 的区别是,这个会将字符串中的所有 CQ 码都解析出来,并以同样上方解析出来的数组格式返回。 ```php diff --git a/docs/update/v2.md b/docs/update/v2.md index 38e7a5af..0e162191 100644 --- a/docs/update/v2.md +++ b/docs/update/v2.md @@ -1,5 +1,15 @@ # 更新日志(v2 版本) +## 2.2.11 + +> 更新时间:2021.3.13 + +- 新增:内部 ID 版本号(ZM_VERSION_ID) +- 优化:启动时 log 的等级 +- 移除:终端输入命令 +- 修复:纯 HTTP 服务器的启动 bug +- 新增:`zm_timer` 的报错处理,防止服务器直接崩掉 + ## v2.2.10 > 更新时间:2021.3.8 diff --git a/src/ZM/API/CQ.php b/src/ZM/API/CQ.php index d6f7bf8d..77a7da15 100644 --- a/src/ZM/API/CQ.php +++ b/src/ZM/API/CQ.php @@ -5,6 +5,7 @@ namespace ZM\API; use ZM\Console\Console; +use ZM\Entity\CQObject; class CQ { @@ -305,9 +306,10 @@ class CQ /** * 获取消息中第一个CQ码 * @param $msg - * @return array|null + * @param bool $is_object + * @return array|CQObject|null */ - public static function getCQ($msg) { + public static function getCQ($msg, $is_object = false) { if (($head = mb_strpos($msg, "[CQ:")) !== false) { $key_offset = mb_substr($msg, $head); $close = mb_strpos($key_offset, "]"); @@ -322,7 +324,7 @@ class CQ } $cq["start"] = $head; $cq["end"] = $close + $head; - return $cq; + return !$is_object ? $cq : CQObject::fromArray($cq); } else { return null; } @@ -331,9 +333,10 @@ class CQ /** * 获取消息中所有的CQ码 * @param $msg - * @return array + * @param bool $is_object + * @return array|CQObject[] */ - public static function getAllCQ($msg) { + public static function getAllCQ($msg, $is_object = false) { $cqs = []; $offset = 0; while (($head = mb_strpos(($submsg = mb_substr($msg, $offset)), "[CQ:")) !== false) { @@ -352,7 +355,7 @@ class CQ $cq["start"] = $offset + $head; $cq["end"] = $offset + $tmpmsg + $head; $offset += $tmpmsg + 1; - $cqs[] = $cq; + $cqs[] = (!$is_object ? $cq : CQObject::fromArray($cq)); } return $cqs; } diff --git a/src/ZM/Annotation/Swoole/OnTask.php b/src/ZM/Annotation/Swoole/OnTask.php new file mode 100644 index 00000000..83565c09 --- /dev/null +++ b/src/ZM/Annotation/Swoole/OnTask.php @@ -0,0 +1,36 @@ +rule; + } +} \ No newline at end of file diff --git a/src/ZM/Annotation/Swoole/OnTaskEvent.php b/src/ZM/Annotation/Swoole/OnTaskEvent.php new file mode 100644 index 00000000..32740058 --- /dev/null +++ b/src/ZM/Annotation/Swoole/OnTaskEvent.php @@ -0,0 +1,16 @@ +type = $type; + $this->params = $params; + $this->start = $start; + $this->end = $end; + } + } + + public static function fromArray($arr) { + return new CQObject($arr["type"], $arr["params"] ?? [], $arr["start"], $arr["end"]); + } +} \ No newline at end of file diff --git a/src/ZM/Event/EventDispatcher.php b/src/ZM/Event/EventDispatcher.php index cdc499fd..be164d98 100644 --- a/src/ZM/Event/EventDispatcher.php +++ b/src/ZM/Event/EventDispatcher.php @@ -100,6 +100,7 @@ class EventDispatcher } } if ($this->status === self::STATUS_RULE_FAILED) $this->status = self::STATUS_NORMAL; + //TODO:没有过滤before的false,可能会导致一些问题,先观望一下 } catch (InterruptException $e) { $this->store = $e->return_var; $this->status = self::STATUS_INTERRUPTED; diff --git a/src/ZM/Event/ServerEventHandler.php b/src/ZM/Event/ServerEventHandler.php index 316af3aa..2f058a98 100644 --- a/src/ZM/Event/ServerEventHandler.php +++ b/src/ZM/Event/ServerEventHandler.php @@ -17,6 +17,7 @@ use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Event; use Swoole\Process; +use Throwable; use ZM\Annotation\AnnotationParser; use ZM\Annotation\Http\RequestMapping; use ZM\Annotation\Swoole\OnCloseEvent; @@ -26,6 +27,8 @@ use ZM\Annotation\Swoole\OnPipeMessageEvent; use ZM\Annotation\Swoole\OnRequestEvent; use ZM\Annotation\Swoole\OnStart; use ZM\Annotation\Swoole\OnSwooleEvent; +use ZM\Annotation\Swoole\OnTask; +use ZM\Annotation\Swoole\OnTaskEvent; use ZM\Config\ZMConfig; use ZM\ConnectionManager\ManagerGM; use ZM\Console\Console; @@ -259,7 +262,7 @@ class ServerEventHandler try { Framework::$server = $server; $this->loadAnnotations(); - Console::debug("TaskWorker #" . $server->worker_id . " 已启动"); + Console::success("TaskWorker #" . $server->worker_id . " 已启动"); } catch (Exception $e) { Console::error("Worker加载出错!停止服务!"); Console::error($e->getMessage() . "\n" . $e->getTraceAsString()); @@ -594,20 +597,54 @@ class ServerEventHandler * @SwooleHandler("task") * @param Server|null $server * @param Server\Task $task - * @return mixed * @noinspection PhpUnusedParameterInspection + * @return null */ public function onTask(?Server $server, Server\Task $task) { - $data = $task->data; - switch ($data["action"]) { - case "runMethod": - $c = $data["class"]; - $ss = new $c(); - $method = $data["method"]; - $ps = $data["params"]; - $task->finish($ss->$method(...$ps)); + if (isset($task->data["task"])) { + $dispatcher = new EventDispatcher(OnTask::class); + $dispatcher->setRuleFunction(function ($v) use ($task) { + /** @var OnTask $v */ + return $v->task_name == $task->data["task"]; + }); + $dispatcher->setReturnFunction(function ($return) { + EventDispatcher::interrupt($return); + }); + $params = $task->data["params"]; + try { + $dispatcher->dispatchEvents(...$params); + } catch (Throwable $e) { + $finish["throw"] = $e; + } + if ($dispatcher->status === EventDispatcher::STATUS_EXCEPTION) { + $finish["result"] = null; + $finish["retcode"] = -1; + } else { + $finish["result"] = $dispatcher->store; + $finish["retcode"] = 0; + } + if (zm_atomic("server_is_stopped")->get() === 1) { + return; + } + $task->finish($finish); + } else { + try { + $dispatcher = new EventDispatcher(OnTaskEvent::class); + $dispatcher->setRuleFunction(function ($v) { + /** @var OnTaskEvent $v */ + return eval("return " . $v->getRule() . ";"); + }); + $dispatcher->dispatchEvents(); + } catch (Exception $e) { + $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; + Console::error("Uncaught exception " . get_class($e) . " when calling \"task\": " . $error_msg); + Console::trace(); + } catch (Error $e) { + $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; + Console::error("Uncaught " . get_class($e) . " when calling \"task\": " . $error_msg); + Console::trace(); + } } - return null; } /** diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index afd18c03..80452334 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -196,6 +196,8 @@ class Framework public function start() { self::$server->start(); + zm_atomic("server_is_stopped")->set(1); + Console::setLevel(0); } /** diff --git a/src/ZM/Http/RouteManager.php b/src/ZM/Http/RouteManager.php index 72b127f5..bbe7be16 100644 --- a/src/ZM/Http/RouteManager.php +++ b/src/ZM/Http/RouteManager.php @@ -9,6 +9,7 @@ use Symfony\Component\Routing\RouteCollection; use ZM\Annotation\Http\Controller; use ZM\Annotation\Http\RequestMapping; use ZM\Console\Console; +use ZM\Store\LightCacheInside; class RouteManager { @@ -34,4 +35,26 @@ class RouteManager self::$routes->add(md5($route_name), $route); } + + public static function addStaticFileRoute($route, $path) { + $tail = trim($route, "/"); + $route_name = ($tail === "" ? "" : "/") . $tail . "/{filename}"; + Console::debug("添加静态文件路由:" . $route_name); + $route = new Route($route_name, ['_class' => RouteManager::class, '_method' => "onStaticRoute"]); + //echo $path.PHP_EOL; + LightCacheInside::set("static_route", $route->getPath(), $path); + + self::$routes->add(md5($route_name), $route); + } + + public function onStaticRoute($params) { + $route_path = self::$routes->get($params["_route"])->getPath(); + if(($path = LightCacheInside::get("static_route", $route_path)) === null) { + ctx()->getResponse()->endWithStatus(404); + return false; + } + unset($params["_route"]); + $obj = array_shift($params); + return new StaticFileHandler($obj, $path); + } } \ No newline at end of file diff --git a/src/ZM/Store/LightCacheInside.php b/src/ZM/Store/LightCacheInside.php index deb6fa79..4f10e301 100644 --- a/src/ZM/Store/LightCacheInside.php +++ b/src/ZM/Store/LightCacheInside.php @@ -18,6 +18,7 @@ class LightCacheInside public static function init() { self::createTable("wait_api", 3, 65536); //用于存协程等待的状态内容的 self::createTable("connect", 3, 64); //用于存单机器人模式下的机器人fd的 + self::createTable("static_route", 64, 256);//用于存储 //self::createTable("worker_start", 2, 1024);//用于存启动服务器时的状态的 return true; } diff --git a/src/ZM/Store/ZMAtomic.php b/src/ZM/Store/ZMAtomic.php index 6c594389..c4dbee69 100644 --- a/src/ZM/Store/ZMAtomic.php +++ b/src/ZM/Store/ZMAtomic.php @@ -31,6 +31,7 @@ class ZMAtomic self::$atomics["_int_is_reload"] = new Atomic(0); 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 < 10; ++$i) { self::$atomics["_tmp_" . $i] = new Atomic(0); } diff --git a/src/ZM/Utils/MessageUtil.php b/src/ZM/Utils/MessageUtil.php new file mode 100644 index 00000000..93a47f1a --- /dev/null +++ b/src/ZM/Utils/MessageUtil.php @@ -0,0 +1,35 @@ +type == "image") { + $result = ZMRequest::downloadFile($v->params["url"], $path . "/" . $v->params["file"]); + if ($result === false) { + Console::warning("图片 " . $v->params["url"] . " 下载失败!"); + return false; + } + $files[] = $path . "/" . $v->params["file"]; + } + } + return $files; + } +} \ No newline at end of file diff --git a/src/ZM/Utils/TaskManager.php b/src/ZM/Utils/TaskManager.php new file mode 100644 index 00000000..9bb5974d --- /dev/null +++ b/src/ZM/Utils/TaskManager.php @@ -0,0 +1,13 @@ +taskwait(["task" => $task_name, "params" => $params], $timeout); + return $r === false ? false : $r["result"]; + } +} \ No newline at end of file