diff --git a/composer.json b/composer.json index a0451b95..33da5296 100644 --- a/composer.json +++ b/composer.json @@ -3,8 +3,12 @@ "description": "High performance QQ robot and web server development framework", "minimum-stability": "stable", "license": "Apache-2.0", - "version": "2.0.3", - "extra": [], + "version": "2.1.0", + "extra": { + "exclude_annotate": [ + "src/ZM" + ] + }, "authors": [ { "name": "whale", @@ -41,7 +45,9 @@ }, "autoload": { "psr-4": { - "ZM\\": "src/ZM" + "ZM\\": "src/ZM", + "Module\\": "src/Module", + "Custom\\": "src/Custom" }, "files": [ "src/ZM/global_functions.php" diff --git a/docs/assets/img/Untitled Diagram.png b/docs/assets/img/Untitled Diagram.png new file mode 100644 index 00000000..841f2dd4 Binary files /dev/null and b/docs/assets/img/Untitled Diagram.png differ diff --git a/docs/assets/img/diagram3.dbb4e32e.png b/docs/assets/img/diagram3.dbb4e32e.png new file mode 100644 index 00000000..7baa6733 Binary files /dev/null and b/docs/assets/img/diagram3.dbb4e32e.png differ diff --git a/docs/assets/img/diagram4.16ce39ca.png b/docs/assets/img/diagram4.16ce39ca.png new file mode 100644 index 00000000..ea88b527 Binary files /dev/null and b/docs/assets/img/diagram4.16ce39ca.png differ diff --git a/src/Custom/global_function.php b/src/Custom/global_function.php index 3fa7ef70..3965f5ba 100644 --- a/src/Custom/global_function.php +++ b/src/Custom/global_function.php @@ -1,4 +1,4 @@ -get(); - $r1 = ctx()->getArgs(ZM_MATCH_FIRST, "请说出你想设置的操作[r/w]"); - switch ($r1) { - case "r": - $k = ctx()->getArgs(ZM_MATCH_FIRST, "请说出你想读取的键名"); - $result = $redis->get($k); - ctx()->reply("结果:" . $result); - break; - case "w": - $k = ctx()->getArgs(ZM_MATCH_FIRST, "请说出你想写入的键名"); - $v = ctx()->getArgs(ZM_MATCH_FIRST, "请说出你想写入的字符串"); - $result = $redis->set($k, $v); - ctx()->reply("结果:" . ($result ? "成功" : "失败")); - break; - } - } - /** * 使用命令 .reload 发给机器人远程重载,注意将 user_id 换成你自己的 QQ * @CQCommand(".reload",user_id=627577391) @@ -116,7 +94,7 @@ class Hello /** * 在机器人连接后向终端输出信息 - * @OnSwooleEvent("open",rule="connectIsQQ()") + * @OnOpenEvent("qq") * @param $conn */ public function onConnect(ConnectionObject $conn) { @@ -125,7 +103,7 @@ class Hello /** * 在机器人断开连接后向终端输出信息 - * @OnSwooleEvent("close",rule="connectIsQQ()") + * @OnCloseEvent("qq") * @param ConnectionObject $conn */ public function onDisconnect(ConnectionObject $conn) { @@ -134,7 +112,7 @@ class Hello /** * 阻止 Chrome 自动请求 /favicon.ico 导致的多条请求并发和干扰 - * @OnSwooleEvent("request",rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200) + * @OnRequestEvent(rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200) */ public function onRequest() { EventDispatcher::interrupt(); @@ -142,7 +120,7 @@ class Hello /** * 框架会默认关闭未知的WebSocket链接,因为这个绑定的事件,你可以根据你自己的需求进行修改 - * @OnSwooleEvent(type="open",rule="connectIsDefault()") + * @OnOpenEvent("default") */ public function closeUnknownConn() { Console::info("Unknown connection , I will close it."); diff --git a/src/Module/Middleware/TimerMiddleware.php b/src/Module/Middleware/TimerMiddleware.php index d90d2570..95661b94 100644 --- a/src/Module/Middleware/TimerMiddleware.php +++ b/src/Module/Middleware/TimerMiddleware.php @@ -2,6 +2,7 @@ namespace Module\Middleware; +use Exception; use ZM\Annotation\Http\HandleAfter; use ZM\Annotation\Http\HandleBefore; use ZM\Annotation\Http\HandleException; @@ -37,8 +38,11 @@ class TimerMiddleware implements MiddlewareInterface /** * @HandleException(\Exception::class) + * @param Exception $e + * @throws Exception */ - public function onException() { + public function onException(Exception $e) { Console::error("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms but an Exception occurred."); + throw $e; } } diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php index 0024768a..d2c8aa0a 100644 --- a/src/ZM/Annotation/AnnotationParser.php +++ b/src/ZM/Annotation/AnnotationParser.php @@ -33,7 +33,7 @@ class AnnotationParser */ public function __construct() { $this->start_time = microtime(true); - $this->loadAnnotationClasses(); + //$this->loadAnnotationClasses(); $this->req_mapping[0] = [ 'id' => 0, 'pid' => -1, @@ -100,7 +100,7 @@ class AnnotationParser unset($this->annotation_map[$v]); continue 2; } elseif ($vs instanceof MiddlewareClass) { - Console::verbose("正在注册中间件 " . $reflection_class->getName()); + Console::debug("正在注册中间件 " . $reflection_class->getName()); $rs = $this->registerMiddleware($vs, $reflection_class); $this->middlewares[$rs["name"]] = $rs; } @@ -297,7 +297,7 @@ class AnnotationParser return $result; } - private function sortByLevel(&$events, string $class_name, $prefix = "") { + public function sortByLevel(&$events, string $class_name, $prefix = "") { if (is_a($class_name, Level::class, true)) { $class_name .= $prefix; usort($events[$class_name], function ($a, $b) { diff --git a/src/ZM/Annotation/Swoole/OnCloseEvent.php b/src/ZM/Annotation/Swoole/OnCloseEvent.php new file mode 100644 index 00000000..d0ea70ee --- /dev/null +++ b/src/ZM/Annotation/Swoole/OnCloseEvent.php @@ -0,0 +1,22 @@ +type = $type; } - - /** - * @return string - */ - public function getRule(): string { - return $this->rule; - } - - /** - * @param string $rule - */ - public function setRule(string $rule) { - $this->rule = $rule; - } - - /** - * @return int - */ - public function getLevel(): int { - return $this->level; - } - - /** - * @param int $level - */ - public function setLevel(int $level) { - $this->level = $level; - } - } diff --git a/src/ZM/Annotation/Swoole/OnSwooleEventBase.php b/src/ZM/Annotation/Swoole/OnSwooleEventBase.php new file mode 100644 index 00000000..e744bca4 --- /dev/null +++ b/src/ZM/Annotation/Swoole/OnSwooleEventBase.php @@ -0,0 +1,49 @@ +rule !== "" ? $this->rule : true; + } + + /** + * @param string $rule + */ + public function setRule(string $rule) { + $this->rule = $rule; + } + + /** + * @return int + */ + public function getLevel(): int { + return $this->level; + } + + /** + * @param int $level + */ + public function setLevel(int $level) { + $this->level = $level; + } +} \ No newline at end of file diff --git a/src/ZM/Event/EventDispatcher.php b/src/ZM/Event/EventDispatcher.php index 0c5e8489..21bb3eab 100644 --- a/src/ZM/Event/EventDispatcher.php +++ b/src/ZM/Event/EventDispatcher.php @@ -5,8 +5,8 @@ namespace ZM\Event; use Doctrine\Common\Annotations\AnnotationException; +use Error; use Exception; -use ZM\Annotation\AnnotationBase; use ZM\Console\Console; use ZM\Exception\InterruptException; use ZM\Exception\ZMException; @@ -17,6 +17,12 @@ use ZM\Utils\ZMUtil; class EventDispatcher { + const STATUS_NORMAL = 0; //正常结束 + const STATUS_INTERRUPTED = 1; //被interrupt了,不管在什么地方 + const STATUS_EXCEPTION = 2; //执行过程中抛出了异常 + const STATUS_BEFORE_FAILED = 3; //中间件HandleBefore返回了false,所以不执行此方法 + const STATUS_RULE_FAILED = 4; //判断事件执行的规则函数判定为false,所以不执行此方法 + /** @var string */ private $class; /** @var null|callable */ @@ -27,6 +33,10 @@ class EventDispatcher private $log = false; /** @var int */ private $eid = 0; + /** @var int */ + public $status = self::STATUS_NORMAL; + /** @var mixed */ + public $store = null; /** * @param null $return_var @@ -74,41 +84,53 @@ class EventDispatcher return $this; } + /** + * @param mixed ...$params + * @throws Exception + * @throws InterruptException + */ public function dispatchEvents(...$params) { try { foreach ((EventManager::$events[$this->class] ?? []) as $v) { - $result = $this->dispatchEvent($v, $this->rule, ...$params); + $this->dispatchEvent($v, $this->rule, ...$params); if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 分发结束。"); - if ($result !== false && is_callable($this->return_func)) { + if($this->status == self::STATUS_BEFORE_FAILED || $this->status == self::STATUS_RULE_FAILED) continue; + if (is_callable($this->return_func) && $this->status === self::STATUS_NORMAL) { if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 正在执行返回值处理函数 ..."); - ($this->return_func)($result); + ($this->return_func)($this->store); } } - return true; + if($this->status === self::STATUS_RULE_FAILED) $this->status = self::STATUS_NORMAL; } catch (InterruptException $e) { - return $e->return_var; - } catch (AnnotationException $e) { - return false; + $this->store = $e->return_var; + $this->status = self::STATUS_INTERRUPTED; + } catch (Exception $e) { + $this->status = self::STATUS_EXCEPTION; + throw $e; + } catch (Error $e) { + $this->status = self::STATUS_EXCEPTION; + throw $e; } } /** - * @param AnnotationBase|null $v + * @param mixed $v * @param null $rule_func * @param mixed ...$params * @throws AnnotationException * @throws InterruptException - * @return mixed + * @return bool */ - public function dispatchEvent(?AnnotationBase $v, $rule_func = null, ...$params) { + public function dispatchEvent($v, $rule_func = null, ...$params) { $q_c = $v->class; $q_f = $v->method; - if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在判断 " . $q_c . "::" . $q_f . " 方法下的 rule ..."); + if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在判断 " . $q_c . "::" . $q_f . " 方法下的 ruleFunc ..."); if ($rule_func !== null && !$rule_func($v)) { - if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 rule 判断为 false, 拒绝执行此方法。"); + if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 ruleFunc 判断为 false, 拒绝执行此方法。"); + $this->status = self::STATUS_RULE_FAILED; return false; } - if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 rule 为真,继续执行方法本身 ..."); + if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 ruleFunc 为真,继续执行方法本身 ..."); if (isset(EventManager::$middleware_map[$q_c][$q_f])) { $middlewares = EventManager::$middleware_map[$q_c][$q_f]; if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法还绑定了 Middleware:" . implode(", ", $middlewares)); @@ -138,7 +160,7 @@ class EventDispatcher try { $q_o = ZMUtil::getModInstance($q_c); if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在执行方法 " . $q_c . "::" . $q_f . " ..."); - $result = $q_o->$q_f(...$params); + $this->store = $q_o->$q_f(...$params); } catch (Exception $e) { if ($e instanceof InterruptException) { if ($this->log) Console::verbose("[事件分发{$this->eid}] 检测到事件阻断调用,正在跳出事件分发器 ..."); @@ -166,13 +188,17 @@ class EventDispatcher if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 后置事件执行完毕!"); } } - return $result; + $this->status = self::STATUS_NORMAL; + return true; } + $this->status = self::STATUS_BEFORE_FAILED; return false; } else { $q_o = ZMUtil::getModInstance($q_c); if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在执行方法 " . $q_c . "::" . $q_f . " ..."); - return $q_o->$q_f(...$params); + $this->store = $q_o->$q_f(...$params); + $this->status = self::STATUS_NORMAL; + return true; } } } diff --git a/src/ZM/Event/EventManager.php b/src/ZM/Event/EventManager.php index 3fea9f3e..a0404a29 100644 --- a/src/ZM/Event/EventManager.php +++ b/src/ZM/Event/EventManager.php @@ -22,6 +22,7 @@ class EventManager public static function addEvent($event_name, ?AnnotationBase $event_obj) { self::$events[$event_name][] = $event_obj; + (new AnnotationParser())->sortByLevel(self::$events, $event_name); } public static function loadEventByParser(AnnotationParser $parser) { diff --git a/src/ZM/Event/ServerEventHandler.php b/src/ZM/Event/ServerEventHandler.php index a887480c..39d7b3bb 100644 --- a/src/ZM/Event/ServerEventHandler.php +++ b/src/ZM/Event/ServerEventHandler.php @@ -9,6 +9,7 @@ use Error; use Exception; use PDO; use ReflectionException; +use Swoole\Coroutine; use Swoole\Database\PDOConfig; use Swoole\Database\PDOPool; use Swoole\Event; @@ -16,6 +17,10 @@ use Swoole\Process; use Swoole\Timer; 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\OnRequestEvent; use ZM\Annotation\Swoole\OnStart; use ZM\Annotation\Swoole\OnSwooleEvent; use ZM\Config\ZMConfig; @@ -70,6 +75,40 @@ class ServerEventHandler /** @noinspection PhpUndefinedFieldInspection */ Event::del(Framework::$server->inotify); ZMUtil::stop(); }); + set_error_handler(function ($error_no, $error_msg, $error_file, $error_line) { + switch ($error_no) { + case E_WARNING: + $level_tips = 'PHP Warning: '; + break; + case E_NOTICE: + $level_tips = 'PHP Notice: '; + break; + case E_DEPRECATED: + $level_tips = 'PHP Deprecated: '; + break; + case E_USER_ERROR: + $level_tips = 'User Error: '; + break; + case E_USER_WARNING: + $level_tips = 'User Warning: '; + break; + case E_USER_NOTICE: + $level_tips = 'User Notice: '; + break; + case E_USER_DEPRECATED: + $level_tips = 'User Deprecated: '; + break; + case E_STRICT: + $level_tips = 'PHP Strict: '; + break; + default: + $level_tips = 'Unkonw Type Error: '; + break; + } // do some handle + $error = $level_tips . $error_msg . ' in ' . $error_file . ' on ' . $error_line; + Console::warning($error); // 如果 return false 则错误会继续递交给 PHP 标准错误处理 / + return true; + }, E_ALL | E_STRICT); if (Framework::$argv["watch"]) { if (extension_loaded('inotify')) { Console::warning("Enabled File watcher, do not use in production."); @@ -114,7 +153,7 @@ class ServerEventHandler Console::debug("正在关闭 " . ($server->taskworker ? "Task" : "") . "Worker 进程 " . Console::setColor("#" . \server()->worker_id, "gold") . TermColor::frontColor256(59) . ", pid=" . posix_getpid()); server()->stop($worker_id); }); - unset(Context::$context[Co::getCid()]); + unset(Context::$context[Coroutine::getCid()]); if ($server->taskworker === false) { try { register_shutdown_function(function () use ($server) { @@ -211,7 +250,8 @@ class ServerEventHandler return server()->worker_id === $v->worker_id || $v->worker_id === -1; }); $dispatcher->dispatchEvents($server, $worker_id); - Console::debug("@OnStart 执行完毕"); + if ($dispatcher->status === EventDispatcher::STATUS_NORMAL) Console::debug("@OnStart 执行完毕"); + else Console::warning("@OnStart 执行异常!"); } catch (Exception $e) { Console::error("Worker加载出错!停止服务!"); Console::error($e->getMessage() . "\n" . $e->getTraceAsString()); @@ -251,9 +291,15 @@ class ServerEventHandler public function onMessage($server, Frame $frame) { Console::debug("Calling Swoole \"message\" from fd=" . $frame->fd.": ".TermColor::ITALIC.$frame->data.TermColor::RESET); - unset(Context::$context[Co::getCid()]); + unset(Context::$context[\Swoole\Coroutine::getCid()]); $conn = ManagerGM::get($frame->fd); set_coroutine_params(["server" => $server, "frame" => $frame, "connection" => $conn]); + $dispatcher1 = new EventDispatcher(OnMessageEvent::class); + $dispatcher1->setRuleFunction(function($v) { + return ctx()->getConnection()->getName() == $v->connect_type && eval("return " . $v->getRule() . ";"); + }); + + $dispatcher = new EventDispatcher(OnSwooleEvent::class); $dispatcher->setRuleFunction(function ($v) { if ($v->getRule() == '') { @@ -268,6 +314,7 @@ class ServerEventHandler }); try { //$starttime = microtime(true); + $dispatcher1->dispatchEvents($conn); $dispatcher->dispatchEvents($conn); //Console::success("Used ".round((microtime(true) - $starttime) * 1000, 3)." ms!"); } catch (Exception $e) { @@ -293,6 +340,11 @@ class ServerEventHandler Console::debug("Calling Swoole \"request\" event from fd=" . $request->fd); set_coroutine_params(["request" => $request, "response" => $response]); + $dis1 = new EventDispatcher(OnRequestEvent::class); + $dis1->setRuleFunction(function($v) { + return eval("return ".$v->getRule().";") ? true : false; + }); + $dis = new EventDispatcher(OnSwooleEvent::class); $dis->setRuleFunction(function ($v) { if ($v->getRule() == '') { @@ -305,8 +357,10 @@ class ServerEventHandler }); try { - $no_interrupt = $dis->dispatchEvents($request, $response); - if ($no_interrupt !== null) { + $dis1->dispatchEvents($request, $response); + $dis->dispatchEvents($request, $response); + var_dump($dis->status); + if ($dis->status === EventDispatcher::STATUS_NORMAL && $dis1->status === EventDispatcher::STATUS_NORMAL) { $result = HttpUtil::parseUri($request, $response, $request->server["request_uri"], $node, $params); if ($result === true) { ctx()->setCache("params", $params); @@ -318,8 +372,8 @@ class ServerEventHandler $div->request_method = $node["request_method"]; $div->class = $node["class"]; //Console::success("正在执行路由:".$node["method"]); - $r = $dispatcher->dispatchEvent($div, null, $params, $request, $response); - if (is_string($r) && !$response->isEnd()) $response->end($r); + $dispatcher->dispatchEvent($div, null, $params, $request, $response); + if (is_string($dispatcher->store) && !$response->isEnd()) $response->end($dispatcher->store); } } if (!$response->isEnd()) { @@ -371,6 +425,11 @@ class ServerEventHandler set_coroutine_params(["server" => $server, "request" => $request, "connection" => $conn, "fd" => $request->fd]); $conn->setOption("connect_id", strval($request->header["x-self-id"] ?? "")); + $dispatcher1 = new EventDispatcher(OnOpenEvent::class); + $dispatcher1->setRuleFunction(function($v) { + return ctx()->getConnection()->getName() == $v->connect_type && eval("return ".$v->getRule().";"); + }); + $dispatcher = new EventDispatcher(OnSwooleEvent::class); $dispatcher->setRuleFunction(function ($v) { if ($v->getRule() == '') { @@ -387,6 +446,7 @@ class ServerEventHandler LightCacheInside::set("connect", "conn_fd", $request->fd); } } + $dispatcher1->dispatchEvents($conn); $dispatcher->dispatchEvents($conn); } catch (Exception $e) { $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; @@ -412,6 +472,10 @@ class ServerEventHandler Console::debug("Calling Swoole \"close\" event from fd=" . $fd); set_coroutine_params(["server" => $server, "connection" => $conn, "fd" => $fd]); + $dispatcher1 = new EventDispatcher(OnCloseEvent::class); + $dispatcher1->setRuleFunction(function($v){ + return $v->connect_type == ctx()->getConnection()->getName() && eval("return " . $v->getRule() . ";"); + }); $dispatcher = new EventDispatcher(OnSwooleEvent::class); $dispatcher->setRuleFunction(function ($v) { @@ -429,6 +493,7 @@ class ServerEventHandler LightCacheInside::set("connect", "conn_fd", -1); } } + $dispatcher1->dispatchEvents($conn); $dispatcher->dispatchEvents($conn); } catch (Exception $e) { $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; @@ -505,7 +570,16 @@ class ServerEventHandler //加载各个模块的注解类,以及反射 Console::debug("检索Module中"); $parser = new AnnotationParser(); - $parser->addRegisterPath(DataProvider::getWorkingDir() . "/src/Module/", "Module"); + $path = DataProvider::getWorkingDir()."/src/"; + $dir = scandir($path); + unset($dir[0], $dir[1]); + $composer = json_decode(file_get_contents(__DIR__."/../../../composer.json"), true); + foreach($dir as $v) { + if(is_dir($path."/".$v) && isset($composer["autoload"]["psr-4"][$v."\\"]) && !in_array($composer["autoload"]["psr-4"][$v."\\"], $composer["extra"]["exclude_annotate"])) { + Console::verbose("Add ".$v . " to register path"); + $parser->addRegisterPath(DataProvider::getWorkingDir()."/src/".$v."/", $v); + } + } $parser->registerMods(); EventManager::loadEventByParser($parser); //加载事件 @@ -518,14 +592,14 @@ class ServerEventHandler //加载插件 $plugins = ZMConfig::get("global", "modules") ?? []; - if (!isset($plugins["onebot"])) $plugins["onebot"] = ["status" => true, "single_bot_mode" => false]; + if (!isset($plugins["onebot"])) $plugins["onebot"] = ["status" => true, "single_bot_mode" => false, "message_level" => 99999]; if ($plugins["onebot"]) { $obj = new OnSwooleEvent(); $obj->class = QQBot::class; $obj->method = 'handle'; $obj->type = 'message'; - $obj->level = 99999; + $obj->level = $plugins["onebot"]["message_level"] ?? 99999; $obj->rule = 'connectIsQQ()'; EventManager::addEvent(OnSwooleEvent::class, $obj); if ($plugins["onebot"]["single_bot_mode"]) { @@ -536,7 +610,6 @@ class ServerEventHandler } //TODO: 编写加载外部插件的方式 - $this->loadExternalModules($plugins); } private function addWatcher($maindir, $fd) { @@ -550,11 +623,4 @@ class ServerEventHandler } } } - - private function loadExternalModules($plugins) { - foreach ($plugins as $k => $v) { - if ($k == "onebot") continue; - - } - } } diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index c9258650..55d373c4 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -55,9 +55,9 @@ class Framework ZMAtomic::init(); try { $sw = ZMConfig::get("global"); - if(!is_dir($sw["zm_data"])) mkdir($sw["zm_data"]); - if(!is_dir($sw["config_dir"])) mkdir($sw["config_dir"]); - if(!is_dir($sw["crash_dir"])) mkdir($sw["crash_dir"]); + if (!is_dir($sw["zm_data"])) mkdir($sw["zm_data"]); + if (!is_dir($sw["config_dir"])) mkdir($sw["config_dir"]); + if (!is_dir($sw["crash_dir"])) mkdir($sw["crash_dir"]); ManagerGM::init(ZMConfig::get("global", "swoole")["max_connection"] ?? 2048, 0.5, [ [ "key" => "connect_id", @@ -174,11 +174,9 @@ class Framework } } foreach ($event_list as $k => $v) { - self::$server->on($k, function (...$param) use ($v) { - $c = ZMUtil::getModInstance($v->class); - $m = $v->method; - $c->$m(...$param); - }); + $c = ZMUtil::getModInstance($v->class); + $m = $v->method; + self::$server->on($k, function (...$param) use ($c, $m) { $c->$m(...$param); }); } } diff --git a/src/ZM/Module/QQBot.php b/src/ZM/Module/QQBot.php index 02eebf3a..2e450a5b 100644 --- a/src/ZM/Module/QQBot.php +++ b/src/ZM/Module/QQBot.php @@ -35,7 +35,10 @@ class QQBot set_coroutine_params(["data" => $data]); ctx()->setCache("level", 0); //Console::debug("Calling CQ Event from fd=" . ctx()->getConnection()->getFd()); - $this->dispatchBeforeEvents($data); // >= 200 的level before在这里执行 + if ($data["post_type"] != "meta_event") { + $r = $this->dispatchBeforeEvents($data); // before在这里执行,元事件不执行before为减少不必要的调试日志 + if ($r->store === "block") EventDispatcher::interrupt(); + } if (CoMessage::resumeByWS()) { EventDispatcher::interrupt(); } @@ -49,17 +52,27 @@ class QQBot } } + /** + * @param $data + * @return EventDispatcher + * @throws InterruptException + */ public function dispatchBeforeEvents($data) { $before = new EventDispatcher(CQBefore::class); $before->setRuleFunction(function ($v) use ($data) { return $v->cq_event == $data["post_type"]; }); $before->setReturnFunction(function ($result) { - if (!$result) EventDispatcher::interrupt(); + if (!$result) EventDispatcher::interrupt("block"); }); $before->dispatchEvents($data); + return $before; } + /** + * @param $data + * @throws InterruptException + */ private function dispatchEvents($data) { //Console::warning("最xia数据包:".json_encode($data)); switch ($data["post_type"]) { @@ -73,16 +86,15 @@ class QQBot $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; + 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)) { + if (($word[0] != "" && $v->match == $word[0]) || in_array($word[0], $v->alias)) { array_shift($word); ctx()->setCache("match", $word); return true; @@ -95,14 +107,14 @@ class QQBot } elseif ($v->keyword != "" && mb_strpos(ctx()->getMessage(), $v->keyword) !== false) { ctx()->setCache("match", explode($v->keyword, ctx()->getMessage())); return true; - }elseif ($v->pattern != "") { + } elseif ($v->pattern != "") { $match = matchArgs($v->pattern, ctx()->getMessage()); - if($match !== false) { + if ($match !== false) { ctx()->setCache("match", $match); return true; } } elseif ($v->regex != "") { - if(preg_match("/" . $v->regex . "/u", ctx()->getMessage(), $word2) != 0) { + if (preg_match("/" . $v->regex . "/u", ctx()->getMessage(), $word2) != 0) { ctx()->setCache("match", $word2); return true; } @@ -112,10 +124,10 @@ class QQBot }); $dispatcher->setReturnFunction(function ($result) { if (is_string($result)) ctx()->reply($result); - EventDispatcher::interrupt(); + if (ctx()->getCache("has_reply") === true) EventDispatcher::interrupt(); }); - $r = $dispatcher->dispatchEvents(); - if ($r === null) EventDispatcher::interrupt(); + $dispatcher->dispatchEvents(); + if ($dispatcher->status == EventDispatcher::STATUS_INTERRUPTED) EventDispatcher::interrupt(); //分发CQMessage事件 $msg_dispatcher = new EventDispatcher(CQMessage::class); diff --git a/src/ZM/global_defines.php b/src/ZM/global_defines.php index 9612da44..c88a819e 100644 --- a/src/ZM/global_defines.php +++ b/src/ZM/global_defines.php @@ -1,4 +1,4 @@ -