diff --git a/composer.json b/composer.json
index a043fc23..852f7116 100644
--- a/composer.json
+++ b/composer.json
@@ -3,7 +3,7 @@
"description": "High performance QQ robot and web server development framework",
"minimum-stability": "stable",
"license": "Apache-2.0",
- "version": "2.0.0-b4",
+ "version": "2.0.0-b5",
"extra": {},
"authors": [
{
@@ -35,6 +35,10 @@
"symfony/routing": "^5.1",
"symfony/polyfill-php80": "^1.20"
},
+ "suggest": {
+ "ext-ctype": "*",
+ "ext-mbstring": "*"
+ },
"autoload": {
"psr-4": {
"ZM\\": "src/ZM",
@@ -57,4 +61,4 @@
"phpunit/phpunit": "^9.3",
"swoole/ide-helper": "@dev"
}
-}
\ No newline at end of file
+}
diff --git a/config/global.php b/config/global.php
index eed3fef0..03832714 100644
--- a/config/global.php
+++ b/config/global.php
@@ -50,8 +50,6 @@ $config['sql_config'] = [
'sql_username' => 'name',
'sql_database' => 'db_name',
'sql_password' => '',
- 'sql_enable_cache' => true,
- 'sql_reset_cache' => '0300',
'sql_options' => [
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false
@@ -115,7 +113,10 @@ $config['command_register_class'] = [
/** 服务器启用的外部第三方和内部插件 */
$config['modules'] = [
- 'onebot' => true, // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭
+ 'onebot' => [
+ 'status' => true,
+ 'single_bot_mode' => false
+ ], // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭
];
return $config;
diff --git a/resources/html/subdir/index.html b/resources/html/subdir/index.html
new file mode 100644
index 00000000..5c370979
--- /dev/null
+++ b/resources/html/subdir/index.html
@@ -0,0 +1,10 @@
+
+
+
+
+ Example page
+
+
+
+
+
diff --git a/src/Module/Example/Hello.php b/src/Module/Example/Hello.php
index 40a41545..858ffe88 100644
--- a/src/Module/Example/Hello.php
+++ b/src/Module/Example/Hello.php
@@ -8,7 +8,9 @@ use ZM\ConnectionManager\ConnectionObject;
use ZM\Console\Console;
use ZM\Annotation\CQ\CQCommand;
use ZM\Annotation\Http\RequestMapping;
+use ZM\Event\EventDispatcher;
use ZM\Store\Redis\ZMRedis;
+use ZM\Utils\ZMUtil;
/**
* Class Hello
@@ -40,11 +42,20 @@ class Hello
}
}
+ /**
+ * 使用命令 .reload 发给机器人远程重载,注意将 user_id 换成你自己的 QQ
+ * @CQCommand(".reload",user_id=627577391)
+ */
+ public function reload() {
+ ctx()->reply("重启中...");
+ ZMUtil::reload();
+ }
+
/**
* @CQCommand("我是谁")
*/
public function whoami() {
- $user = ctx()->getRobot()->setCallback(true)->getLoginInfo();
+ $user = ctx()->getRobot()->getLoginInfo();
return "你是" . $user["data"]["nickname"] . ",QQ号是" . $user["data"]["user_id"];
}
@@ -121,6 +132,14 @@ class Hello
Console::info("机器人 " . $conn->getOption("connect_id") . " 已断开连接!");
}
+ /**
+ * 阻止 Chrome 自动请求 /favicon.ico 导致的多条请求并发和干扰
+ * @OnSwooleEvent("request",rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200)
+ */
+ public function onRequest() {
+ EventDispatcher::interrupt();
+ }
+
/**
* 框架会默认关闭未知的WebSocket链接,因为这个绑定的事件,你可以根据你自己的需求进行修改
* @OnSwooleEvent(type="open",rule="connectIsDefault()")
diff --git a/src/Module/Middleware/TimerMiddleware.php b/src/Module/Middleware/TimerMiddleware.php
index 34e21c7b..d90d2570 100644
--- a/src/Module/Middleware/TimerMiddleware.php
+++ b/src/Module/Middleware/TimerMiddleware.php
@@ -2,8 +2,9 @@
namespace Module\Middleware;
-use ZM\Annotation\Http\After;
-use ZM\Annotation\Http\Before;
+use ZM\Annotation\Http\HandleAfter;
+use ZM\Annotation\Http\HandleBefore;
+use ZM\Annotation\Http\HandleException;
use ZM\Annotation\Http\MiddlewareClass;
use ZM\Console\Console;
use ZM\Http\MiddlewareInterface;
@@ -19,7 +20,7 @@ class TimerMiddleware implements MiddlewareInterface
private $starttime;
/**
- * @Before()
+ * @HandleBefore()
* @return bool
*/
public function onBefore() {
@@ -28,9 +29,16 @@ class TimerMiddleware implements MiddlewareInterface
}
/**
- * @After()
+ * @HandleAfter()
*/
public function onAfter() {
Console::info("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms.");
}
+
+ /**
+ * @HandleException(\Exception::class)
+ */
+ public function onException() {
+ Console::error("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms but an Exception occurred.");
+ }
}
diff --git a/src/ZM/API/ZMRobot.php b/src/ZM/API/ZMRobot.php
index 126382bb..45230948 100644
--- a/src/ZM/API/ZMRobot.php
+++ b/src/ZM/API/ZMRobot.php
@@ -24,7 +24,7 @@ class ZMRobot
/** @var ConnectionObject|null */
private $connection;
- private $callback = null;
+ private $callback = true;
private $prefix = 0;
/**
@@ -50,6 +50,9 @@ class ZMRobot
return new ZMRobot($r[array_rand($r)]);
}
+ public static function getFirst() {
+ }
+
/**
* @return ZMRobot[]
*/
diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php
index 6bcb7eaf..0024768a 100644
--- a/src/ZM/Annotation/AnnotationParser.php
+++ b/src/ZM/Annotation/AnnotationParser.php
@@ -9,7 +9,7 @@ use ZM\Console\Console;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
-use ZM\Annotation\Http\{After, Before, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping};
+use ZM\Annotation\Http\{HandleAfter, HandleBefore, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping};
use ZM\Annotation\Interfaces\Level;
use ZM\Annotation\Module\Closed;
use ZM\Utils\DataProvider;
@@ -287,8 +287,8 @@ class AnnotationParser
foreach ($reflection_class->getMethods() as $vss) {
$method_annotations = $this->reader->getMethodAnnotations($vss);
foreach ($method_annotations as $vsss) {
- if ($vsss instanceof Before) $result["before"] = $vss->getName();
- if ($vsss instanceof After) $result["after"] = $vss->getName();
+ if ($vsss instanceof HandleBefore) $result["before"] = $vss->getName();
+ if ($vsss instanceof HandleAfter) $result["after"] = $vss->getName();
if ($vsss instanceof HandleException) {
$result["exceptions"][$vsss->class_name] = $vss->getName();
}
diff --git a/src/ZM/Annotation/CQ/CQCommand.php b/src/ZM/Annotation/CQ/CQCommand.php
index 6a6d4021..a7ec162b 100644
--- a/src/ZM/Annotation/CQ/CQCommand.php
+++ b/src/ZM/Annotation/CQ/CQCommand.php
@@ -25,6 +25,8 @@ class CQCommand extends AnnotationBase implements Level
public $start_with = "";
/** @var string */
public $end_with = "";
+ /** @var string */
+ public $keyword = "";
/** @var string[] */
public $alias = [];
/** @var string */
diff --git a/src/ZM/Annotation/Http/After.php b/src/ZM/Annotation/Http/HandleAfter.php
similarity index 80%
rename from src/ZM/Annotation/Http/After.php
rename to src/ZM/Annotation/Http/HandleAfter.php
index ef24db54..9d2e43a4 100644
--- a/src/ZM/Annotation/Http/After.php
+++ b/src/ZM/Annotation/Http/HandleAfter.php
@@ -9,11 +9,11 @@ use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
- * Class After
+ * Class HandleAfter
* @package ZM\Annotation\Http
* @Annotation
* @Target("METHOD")
*/
-class After extends AnnotationBase
+class HandleAfter extends AnnotationBase
{
-}
\ No newline at end of file
+}
diff --git a/src/ZM/Annotation/Http/Before.php b/src/ZM/Annotation/Http/HandleBefore.php
similarity index 65%
rename from src/ZM/Annotation/Http/Before.php
rename to src/ZM/Annotation/Http/HandleBefore.php
index f09cfc40..90a092a5 100644
--- a/src/ZM/Annotation/Http/Before.php
+++ b/src/ZM/Annotation/Http/HandleBefore.php
@@ -4,16 +4,15 @@
namespace ZM\Annotation\Http;
-use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
- * Class Before
+ * Class HandleBefore
* @package ZM\Annotation\Http
* @Annotation
* @Target("METHOD")
*/
-class Before extends AnnotationBase
+class HandleBefore extends AnnotationBase
{
-}
\ No newline at end of file
+}
diff --git a/src/ZM/Annotation/Swoole/ZMSetup.php b/src/ZM/Annotation/Swoole/OnSetup.php
similarity index 85%
rename from src/ZM/Annotation/Swoole/ZMSetup.php
rename to src/ZM/Annotation/Swoole/OnSetup.php
index b3165937..654002b6 100644
--- a/src/ZM/Annotation/Swoole/ZMSetup.php
+++ b/src/ZM/Annotation/Swoole/OnSetup.php
@@ -13,6 +13,6 @@ use ZM\Annotation\AnnotationBase;
* @Annotation
* @Target("METHOD")
*/
-class ZMSetup extends AnnotationBase
+class OnSetup extends AnnotationBase
{
}
diff --git a/src/ZM/Annotation/Swoole/OnWorkerStart.php b/src/ZM/Annotation/Swoole/OnStart.php
similarity index 86%
rename from src/ZM/Annotation/Swoole/OnWorkerStart.php
rename to src/ZM/Annotation/Swoole/OnStart.php
index 1eb0f14d..e95e6275 100644
--- a/src/ZM/Annotation/Swoole/OnWorkerStart.php
+++ b/src/ZM/Annotation/Swoole/OnStart.php
@@ -12,7 +12,7 @@ use ZM\Annotation\AnnotationBase;
* @Annotation
* @Target("ALL")
*/
-class OnWorkerStart extends AnnotationBase
+class OnStart extends AnnotationBase
{
/**
* @var int
diff --git a/src/ZM/Annotation/Swoole/SwooleEventAfter.php b/src/ZM/Annotation/Swoole/SwooleEventAfter.php
deleted file mode 100644
index 6626a69e..00000000
--- a/src/ZM/Annotation/Swoole/SwooleEventAfter.php
+++ /dev/null
@@ -1,76 +0,0 @@
-type;
- }
-
- /**
- * @param string $type
- */
- public function setType(string $type) {
- $this->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;
- }
-
-
-}
\ No newline at end of file
diff --git a/src/ZM/Annotation/Swoole/HandleEvent.php b/src/ZM/Annotation/Swoole/SwooleHandler.php
similarity index 83%
rename from src/ZM/Annotation/Swoole/HandleEvent.php
rename to src/ZM/Annotation/Swoole/SwooleHandler.php
index 80a0fac1..d70fb7d0 100644
--- a/src/ZM/Annotation/Swoole/HandleEvent.php
+++ b/src/ZM/Annotation/Swoole/SwooleHandler.php
@@ -9,12 +9,12 @@ use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
- * Class HandleEvent
+ * Class SwooleHandler
* @package ZM\Annotation\Swoole
* @Annotation
* @Target("METHOD")
*/
-class HandleEvent extends AnnotationBase
+class SwooleHandler extends AnnotationBase
{
/**
* @var string
diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php
index 08bece9a..f19ab656 100644
--- a/src/ZM/ConsoleApplication.php
+++ b/src/ZM/ConsoleApplication.php
@@ -68,9 +68,6 @@ class ConsoleApplication extends Application
}
}
- if (!is_dir(DataProvider::getWorkingDir() . '/src/')) {
- die("Unable to find source directory.\nMaybe you need to run \"init\"?");
- }
ZMConfig::setDirectory(DataProvider::getWorkingDir() . '/config');
ZMConfig::env($args["env"] ?? "");
if (ZMConfig::get("global") === false) {
diff --git a/src/ZM/Context/Context.php b/src/ZM/Context/Context.php
index 6ad6fa12..e1de0e1e 100644
--- a/src/ZM/Context/Context.php
+++ b/src/ZM/Context/Context.php
@@ -149,7 +149,7 @@ class Context implements ContextInterface
} catch (Exception $e) {
$r = false;
}
- if($r === false) {
+ if ($r === false) {
throw new WaitTimeoutException($this, $timeout_prompt);
}
return $r["message"];
@@ -203,7 +203,6 @@ class Context implements ContextInterface
switch ($mode) {
case ZM_MATCH_ALL:
$p = $arg;
- array_shift($p);
return trim(implode(" ", $p)) == "" ? $this->waitMessage($prompt_msg) : trim(implode(" ", $p));
case ZM_MATCH_NUMBER:
foreach ($arg as $k => $v) {
@@ -215,9 +214,9 @@ class Context implements ContextInterface
}
return $this->waitMessage($prompt_msg);
case ZM_MATCH_FIRST:
- if (isset($arg[1])) {
- $a = $arg[1];
- array_splice($arg, 1, 1);
+ if (isset($arg[0])) {
+ $a = $arg[0];
+ array_splice($arg, 0, 1);
ctx()->setCache("match", $arg);
return $a;
} else {
@@ -227,6 +226,22 @@ class Context implements ContextInterface
throw new InvalidArgumentException();
}
+ /**
+ * @param string $prompt_msg
+ * @return int|mixed|string
+ * @throws InvalidArgumentException
+ * @throws WaitTimeoutException
+ */
+ public function getNextArg($prompt_msg = "") { return $this->getArgs(ZM_MATCH_FIRST, $prompt_msg); }
+
+ /**
+ * @param string $prompt_msg
+ * @return int|mixed|string
+ * @throws InvalidArgumentException
+ * @throws WaitTimeoutException
+ */
+ public function getFullArg($prompt_msg = "") { return $this->getArgs(ZM_MATCH_ALL, $prompt_msg); }
+
public function cloneFromParent() {
set_coroutine_params(self::$context[Co::getPcid()] ?? self::$context[$this->cid]);
return context();
diff --git a/src/ZM/Context/ContextInterface.php b/src/ZM/Context/ContextInterface.php
index 94689d37..00cbda60 100644
--- a/src/ZM/Context/ContextInterface.php
+++ b/src/ZM/Context/ContextInterface.php
@@ -103,6 +103,10 @@ interface ContextInterface
*/
public function getArgs($mode, $prompt_msg);
+ public function getNextArg($prompt_msg = "");
+
+ public function getFullArg($prompt_msg = "");
+
public function setCache($key, $value);
/**
diff --git a/src/ZM/Event/EventDispatcher.php b/src/ZM/Event/EventDispatcher.php
index 5f5339bf..8bbd3654 100644
--- a/src/ZM/Event/EventDispatcher.php
+++ b/src/ZM/Event/EventDispatcher.php
@@ -7,8 +7,12 @@ namespace ZM\Event;
use Doctrine\Common\Annotations\AnnotationException;
use Exception;
use ZM\Annotation\AnnotationBase;
-use ZM\Annotation\CQ\CQMetaEvent;
+use ZM\Console\Console;
use ZM\Exception\InterruptException;
+use ZM\Exception\ZMException;
+use ZM\Store\LightCacheInside;
+use ZM\Store\Lock\SpinLock;
+use ZM\Store\ZMAtomic;
use ZM\Utils\ZMUtil;
class EventDispatcher
@@ -19,6 +23,10 @@ class EventDispatcher
private $rule = null;
/** @var null|callable */
private $return_func = null;
+ /** @var bool */
+ private $log = false;
+ /** @var int */
+ private $eid = 0;
/**
* @throws InterruptException
@@ -27,8 +35,32 @@ class EventDispatcher
throw new InterruptException('interrupt');
}
+ public static function enableEventTrace($event_class) {
+ SpinLock::lock("_event_trace");
+ $list = LightCacheInside::get("wait_api", "event_trace");
+ $list[$event_class] = true;
+ LightCacheInside::set("wait_api", "event_trace", $list);
+ SpinLock::unlock("_event_trace");
+ }
+
+ public static function disableEventTrace($event_class) {
+ SpinLock::lock("_event_trace");
+ $list = LightCacheInside::get("wait_api", "event_trace");
+ unset($list[$event_class]);
+ LightCacheInside::set("wait_api", "event_trace", $list);
+ SpinLock::unlock("_event_trace");
+ }
+
public function __construct(string $class = '') {
$this->class = $class;
+ try {
+ $this->eid = ZMAtomic::get("_event_id")->add(1);
+ $list = LightCacheInside::get("wait_api", "event_trace");
+ } catch (ZMException $e) {
+ $list = [];
+ }
+ if (isset($list[$class])) $this->log = true;
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] 开始分发事件: " . $class);
}
public function setRuleFunction(callable $rule = null) {
@@ -43,10 +75,13 @@ class EventDispatcher
public function dispatchEvents(...$params) {
try {
-
foreach ((EventManager::$events[$this->class] ?? []) as $v) {
$result = $this->dispatchEvent($v, $this->rule, ...$params);
- if ($result !== false && is_callable($this->return_func)) ($this->return_func)($result);
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 分发结束。");
+ if ($result !== false && is_callable($this->return_func)) {
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 正在执行返回值处理函数 ...");
+ ($this->return_func)($result);
+ }
}
return true;
} catch (InterruptException $e) {
@@ -67,9 +102,15 @@ class EventDispatcher
public function dispatchEvent(?AnnotationBase $v, $rule_func = null, ...$params) {
$q_c = $v->class;
$q_f = $v->method;
- if ($rule_func !== null && !$rule_func($v)) return false;
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在判断 " . $q_c . "::" . $q_f . " 方法下的 rule ...");
+ if ($rule_func !== null && !$rule_func($v)) {
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 rule 判断为 false, 拒绝执行此方法。");
+ return false;
+ }
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 rule 为真,继续执行方法本身 ...");
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));
$before_result = true;
$r = [];
foreach ($middlewares as $k => $middleware) {
@@ -81,22 +122,34 @@ class EventDispatcher
$r[$k]->class = $q_c;
$r[$k]->method = $q_f;
if (isset($middleware_obj["before"])) {
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 存在前置事件,执行中 ...");
$rs = $middleware_obj["before"];
$before_result = $r[$k]->$rs(...$params);
- if ($before_result === false) break;
+ if ($before_result === false) {
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 前置事件为 false,停止执行原事件,开始执行下一事件。");
+ break;
+ } else {
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 前置事件为 true,继续执行原事件。");
+ }
}
}
if ($before_result) {
try {
$q_o = ZMUtil::getModInstance($q_c);
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在执行方法 " . $q_c . "::" . $q_f . " ...");
$result = $q_o->$q_f(...$params);
} catch (Exception $e) {
- if ($e instanceof InterruptException) throw $e;
+ if ($e instanceof InterruptException) {
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] 检测到事件阻断调用,正在跳出事件分发器 ...");
+ throw $e;
+ }
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] 方法 " . $q_c . "::" . $q_f . " 执行过程中抛出了异常,正在倒序查找 Middleware 中的捕获方法 ...");
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
$middleware_obj = EventManager::$middlewares[$middlewares[$i]];
if (!isset($middleware_obj["exceptions"])) continue;
foreach ($middleware_obj["exceptions"] as $name => $method) {
if ($e instanceof $name) {
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] 方法 " . $q_c . "::" . $q_f . " 的异常 " . get_class($e) . " 被 Middleware:" . $middlewares[$i] . " 下的 " . get_class($r[$i]) . "::" . $method . " 捕获。");
$r[$i]->$method($e);
self::interrupt();
}
@@ -107,7 +160,9 @@ class EventDispatcher
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
$middleware_obj = EventManager::$middlewares[$middlewares[$i]];
if (isset($middleware_obj["after"], $r[$i])) {
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 存在后置事件,执行中 ...");
$r[$i]->{$middleware_obj["after"]}(...$params);
+ if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 后置事件执行完毕!");
}
}
return $result;
@@ -115,6 +170,7 @@ class EventDispatcher
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);
}
}
diff --git a/src/ZM/Event/ServerEventHandler.php b/src/ZM/Event/ServerEventHandler.php
index 6783e1be..ddb3827d 100644
--- a/src/ZM/Event/ServerEventHandler.php
+++ b/src/ZM/Event/ServerEventHandler.php
@@ -16,7 +16,7 @@ use Swoole\Process;
use Swoole\Timer;
use ZM\Annotation\AnnotationParser;
use ZM\Annotation\Http\RequestMapping;
-use ZM\Annotation\Swoole\OnWorkerStart;
+use ZM\Annotation\Swoole\OnStart;
use ZM\Annotation\Swoole\OnSwooleEvent;
use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ManagerGM;
@@ -24,7 +24,7 @@ use ZM\Console\Console;
use Swoole\Http\Request;
use Swoole\Server;
use Swoole\WebSocket\Frame;
-use ZM\Annotation\Swoole\HandleEvent;
+use ZM\Annotation\Swoole\SwooleHandler;
use ZM\Console\TermColor;
use ZM\Context\Context;
use ZM\Context\ContextInterface;
@@ -33,6 +33,7 @@ use ZM\Exception\DbException;
use ZM\Framework;
use ZM\Http\Response;
use ZM\Module\QQBot;
+use ZM\Store\LightCacheInside;
use ZM\Store\MySQL\SqlPoolStorage;
use ZM\Store\Redis\ZMRedisPool;
use ZM\Store\ZMBuf;
@@ -44,7 +45,7 @@ use ZM\Utils\ZMUtil;
class ServerEventHandler
{
/**
- * @HandleEvent("start")
+ * @SwooleHandler("start")
*/
public function onStart() {
global $terminal_id;
@@ -87,14 +88,14 @@ class ServerEventHandler
}
/**
- * @HandleEvent("shutdown")
+ * @SwooleHandler("shutdown")
*/
public function onShutdown() {
Console::debug("正在关闭 Master 进程,pid=" . posix_getpid());
}
/**
- * @HandleEvent("WorkerStop")
+ * @SwooleHandler("WorkerStop")
* @param $server
* @param $worker_id
*/
@@ -103,7 +104,7 @@ class ServerEventHandler
}
/**
- * @HandleEvent("WorkerStart")
+ * @SwooleHandler("WorkerStart")
* @param Server $server
* @param $worker_id
*/
@@ -205,12 +206,12 @@ class ServerEventHandler
EventManager::registerTimerTick(); //启动计时器
//ZMBuf::unsetCache("wait_start");
set_coroutine_params(["server" => $server, "worker_id" => $worker_id]);
- $dispatcher = new EventDispatcher(OnWorkerStart::class);
+ $dispatcher = new EventDispatcher(OnStart::class);
$dispatcher->setRuleFunction(function ($v) {
return server()->worker_id === $v->worker_id || $v->worker_id === -1;
});
$dispatcher->dispatchEvents($server, $worker_id);
- Console::debug("@OnWorkerStart 执行完毕");
+ Console::debug("@OnStart 执行完毕");
} catch (Exception $e) {
Console::error("Worker加载出错!停止服务!");
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
@@ -243,7 +244,7 @@ class ServerEventHandler
}
/**
- * @HandleEvent("message")
+ * @SwooleHandler("message")
* @param $server
* @param Frame $frame
*/
@@ -266,9 +267,9 @@ class ServerEventHandler
}
});
try {
- $starttime = microtime(true);
+ //$starttime = microtime(true);
$dispatcher->dispatchEvents($conn);
- Console::success("Used ".round((microtime(true) - $starttime) * 1000, 3)." ms!");
+ //Console::success("Used ".round((microtime(true) - $starttime) * 1000, 3)." ms!");
} catch (Exception $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
Console::error("Uncaught exception " . get_class($e) . " when calling \"message\": " . $error_msg);
@@ -282,7 +283,7 @@ class ServerEventHandler
}
/**
- * @HandleEvent("request")
+ * @SwooleHandler("request")
* @param $request
* @param $response
*/
@@ -356,7 +357,7 @@ class ServerEventHandler
}
/**
- * @HandleEvent("open")
+ * @SwooleHandler("open")
* @param $server
* @param Request $request
*/
@@ -369,6 +370,7 @@ class ServerEventHandler
$conn = ManagerGM::get($request->fd);
set_coroutine_params(["server" => $server, "request" => $request, "connection" => $conn, "fd" => $request->fd]);
$conn->setOption("connect_id", strval($request->header["x-self-id"]) ?? "");
+
$dispatcher = new EventDispatcher(OnSwooleEvent::class);
$dispatcher->setRuleFunction(function ($v) {
if ($v->getRule() == '') {
@@ -380,6 +382,11 @@ class ServerEventHandler
}
});
try {
+ if ($conn->getName() === 'qq' && ZMConfig::get("global", "modules")["onebot"]["status"] === true) {
+ if (ZMConfig::get("global", "modules")["onebot"]["single_bot_mode"]) {
+ LightCacheInside::set("connect", "conn_fd", $request->fd);
+ }
+ }
$dispatcher->dispatchEvents($conn);
} catch (Exception $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
@@ -394,7 +401,7 @@ class ServerEventHandler
}
/**
- * @HandleEvent("close")
+ * @SwooleHandler("close")
* @param $server
* @param $fd
*/
@@ -404,6 +411,8 @@ class ServerEventHandler
if ($conn === null) return;
Console::debug("Calling Swoole \"close\" event from fd=" . $fd);
set_coroutine_params(["server" => $server, "connection" => $conn, "fd" => $fd]);
+
+
$dispatcher = new EventDispatcher(OnSwooleEvent::class);
$dispatcher->setRuleFunction(function ($v) {
if ($v->getRule() == '') {
@@ -415,6 +424,11 @@ class ServerEventHandler
}
});
try {
+ if ($conn->getName() === 'qq' && ZMConfig::get("global", "modules")["onebot"]["status"] === true) {
+ if (ZMConfig::get("global", "modules")["onebot"]["single_bot_mode"]) {
+ LightCacheInside::set("connect", "conn_fd", -1);
+ }
+ }
$dispatcher->dispatchEvents($conn);
} catch (Exception $e) {
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
@@ -429,7 +443,7 @@ class ServerEventHandler
}
/**
- * @HandleEvent("pipeMessage")
+ * @SwooleHandler("pipeMessage")
* @param $server
* @param $src_worker_id
* @param $data
@@ -462,7 +476,7 @@ class ServerEventHandler
}
/**
- * @HandleEvent("task")
+ * @SwooleHandler("task")
*/
public function onTask() {
}
@@ -504,7 +518,7 @@ class ServerEventHandler
//加载插件
$plugins = ZMConfig::get("global", "modules") ?? [];
- if (!isset($plugins["onebot"])) $plugins["onebot"] = true;
+ if (!isset($plugins["onebot"])) $plugins["onebot"] = ["status" => true, "single_bot_mode" => false];
if ($plugins["onebot"]) {
$obj = new OnSwooleEvent();
@@ -514,6 +528,11 @@ class ServerEventHandler
$obj->level = 99999;
$obj->rule = 'connectIsQQ()';
EventManager::addEvent(OnSwooleEvent::class, $obj);
+ if ($plugins["onebot"]["single_bot_mode"]) {
+ LightCacheInside::set("connect", "conn_fd", -1);
+ } else {
+ LightCacheInside::set("connect", "conn_fd", -2);
+ }
}
//TODO: 编写加载外部插件的方式
diff --git a/src/ZM/Exception/DbException.php b/src/ZM/Exception/DbException.php
index d663eacc..007d9ffe 100644
--- a/src/ZM/Exception/DbException.php
+++ b/src/ZM/Exception/DbException.php
@@ -4,9 +4,7 @@
namespace ZM\Exception;
-use Exception;
-
class DbException extends ZMException
{
-}
\ No newline at end of file
+}
diff --git a/src/ZM/Exception/InterruptException.php b/src/ZM/Exception/InterruptException.php
index f5d00aa8..b5b5a5f8 100644
--- a/src/ZM/Exception/InterruptException.php
+++ b/src/ZM/Exception/InterruptException.php
@@ -4,8 +4,6 @@
namespace ZM\Exception;
-use Exception;
-
class InterruptException extends ZMException
{
diff --git a/src/ZM/Exception/InvalidArgumentException.php b/src/ZM/Exception/InvalidArgumentException.php
index 7ccc3b70..0052a29e 100644
--- a/src/ZM/Exception/InvalidArgumentException.php
+++ b/src/ZM/Exception/InvalidArgumentException.php
@@ -4,9 +4,7 @@
namespace ZM\Exception;
-use Exception;
-
class InvalidArgumentException extends ZMException
{
-}
\ No newline at end of file
+}
diff --git a/src/ZM/Exception/NotInitializedException.php b/src/ZM/Exception/NotInitializedException.php
index f1bf753d..6f56e830 100644
--- a/src/ZM/Exception/NotInitializedException.php
+++ b/src/ZM/Exception/NotInitializedException.php
@@ -4,8 +4,6 @@
namespace ZM\Exception;
-use Exception;
-
class NotInitializedException extends ZMException
{
diff --git a/src/ZM/Exception/RobotNotFoundException.php b/src/ZM/Exception/RobotNotFoundException.php
index 9b734260..49880dda 100644
--- a/src/ZM/Exception/RobotNotFoundException.php
+++ b/src/ZM/Exception/RobotNotFoundException.php
@@ -4,7 +4,6 @@
namespace ZM\Exception;
-use Exception;
use Throwable;
/**
diff --git a/src/ZM/Exception/WaitTimeoutException.php b/src/ZM/Exception/WaitTimeoutException.php
index c9d18255..76472561 100644
--- a/src/ZM/Exception/WaitTimeoutException.php
+++ b/src/ZM/Exception/WaitTimeoutException.php
@@ -4,7 +4,6 @@
namespace ZM\Exception;
-use Exception;
use Throwable;
class WaitTimeoutException extends ZMException
diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php
index b575733e..aef733f7 100644
--- a/src/ZM/Framework.php
+++ b/src/ZM/Framework.php
@@ -6,7 +6,7 @@ namespace ZM;
use Doctrine\Common\Annotations\AnnotationReader;
use Exception;
-use ZM\Annotation\Swoole\ZMSetup;
+use ZM\Annotation\Swoole\OnSetup;
use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ManagerGM;
use ZM\Event\ServerEventHandler;
@@ -20,7 +20,7 @@ use ReflectionException;
use ReflectionMethod;
use Swoole\Runtime;
use Swoole\WebSocket\Server;
-use ZM\Annotation\Swoole\HandleEvent;
+use ZM\Annotation\Swoole\SwooleHandler;
use ZM\Console\Console;
use ZM\Utils\ZMUtil;
@@ -67,7 +67,7 @@ class Framework
self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port"));
$this->server_set = ZMConfig::get("global", "swoole");
Console::init(
- ZMConfig::get("global", "info_level"),
+ ZMConfig::get("global", "info_level") ?? 2,
self::$server,
$args["log-theme"] ?? "default",
($o = ZMConfig::get("console_color")) === false ? [] : $o
@@ -150,11 +150,11 @@ class Framework
$method_annotations = $reader->getMethodAnnotations($vs);
if ($method_annotations != []) {
$annotation = $method_annotations[0];
- if ($annotation instanceof HandleEvent) {
+ if ($annotation instanceof SwooleHandler) {
$annotation->class = $v;
$annotation->method = $vs->getName();
$event_list[strtolower($annotation->event)] = $annotation;
- } elseif ($annotation instanceof ZMSetup) {
+ } elseif ($annotation instanceof OnSetup) {
$annotation->class = $v;
$annotation->method = $vs->getName();
$c = new $v();
diff --git a/src/ZM/Http/Response.php b/src/ZM/Http/Response.php
index 2c937f89..c8cae4b8 100644
--- a/src/ZM/Http/Response.php
+++ b/src/ZM/Http/Response.php
@@ -4,6 +4,8 @@
namespace ZM\Http;
+use ZM\Console\Console;
+
class Response
{
@@ -92,6 +94,7 @@ class Response
*/
public function status($http_code, $reason = null) {
$this->status_code = $http_code;
+ Console::trace();
return $this->response->status($http_code, $reason);
}
@@ -184,7 +187,8 @@ class Response
* @return mixed
*/
public function redirect($location, $http_code = null) {
- return $this->redirect($location, $http_code);
+ $this->is_end = true;
+ return $this->response->redirect($location, $http_code);
}
/**
diff --git a/src/ZM/Module/QQBot.php b/src/ZM/Module/QQBot.php
index 902c1db7..c37207a4 100644
--- a/src/ZM/Module/QQBot.php
+++ b/src/ZM/Module/QQBot.php
@@ -21,7 +21,6 @@ use ZM\Utils\CoMessage;
/**
* Class QQBot
* @package ZM\Module
- * @ExternalModule("onebot")
*/
class QQBot
{
@@ -80,12 +79,13 @@ class QQBot
//分发CQCommand事件
$dispatcher = new EventDispatcher(CQCommand::class);
$dispatcher->setRuleFunction(function (CQCommand $v) use ($word) {
- if ($v->match == "" && $v->pattern == "" && $v->regex == "") 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)) {
+ array_shift($word);
ctx()->setCache("match", $word);
return true;
} elseif ($v->start_with != "" && mb_strpos(ctx()->getMessage(), $v->start_with) === 0) {
@@ -94,6 +94,9 @@ class QQBot
} 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) {
diff --git a/src/ZM/Store/LightCache.php b/src/ZM/Store/LightCache.php
index b6f0c720..c28c61e5 100644
--- a/src/ZM/Store/LightCache.php
+++ b/src/ZM/Store/LightCache.php
@@ -92,7 +92,7 @@ class LightCache
} elseif (is_bool($value)) {
$data_type = "bool";
$value = json_encode($value);
- }else {
+ } else {
throw new Exception("Only can set string, array and int");
}
try {
@@ -126,7 +126,7 @@ class LightCache
throw new Exception("Only can set string, array and int");
}
try {
- if(self::$kv_table->get($key) === false) return false;
+ if (self::$kv_table->get($key) === false) return false;
return self::$kv_table->set($key, [
"value" => $value,
"data_type" => $data_type
@@ -170,7 +170,7 @@ class LightCache
}
public static function savePersistence() {
- if(self::$kv_table === null) return;
+ if (self::$kv_table === null) return;
$r = [];
foreach (self::$kv_table as $k => $v) {
if ($v["expire"] === -2) {
@@ -179,8 +179,10 @@ class LightCache
}
}
if(self::$config["persistence_path"] == "") return;
- $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\"!");
+ if (file_exists(self::$config["persistence_path"])) {
+ $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\"!");
+ }
}
private static function checkExpire($key) {
diff --git a/src/ZM/Store/LightCacheInside.php b/src/ZM/Store/LightCacheInside.php
index f3838cc7..c161eca0 100644
--- a/src/ZM/Store/LightCacheInside.php
+++ b/src/ZM/Store/LightCacheInside.php
@@ -6,6 +6,7 @@ namespace ZM\Store;
use Exception;
use Swoole\Table;
+use ZM\Exception\ZMException;
class LightCacheInside
{
@@ -15,9 +16,11 @@ class LightCacheInside
public static $last_error = '';
public static function init() {
- self::$kv_table["wait_api"] = new Table(2, 0);
+ self::$kv_table["wait_api"] = new Table(3, 0);
self::$kv_table["wait_api"]->column("value", Table::TYPE_STRING, 65536);
- $result = self::$kv_table["wait_api"]->create();
+ self::$kv_table["connect"] = new Table(8, 0);
+ self::$kv_table["connect"]->column("value", Table::TYPE_STRING, 256);
+ $result = self::$kv_table["wait_api"]->create() && self::$kv_table["connect"]->create();
if ($result === false) {
self::$last_error = '系统内存不足,申请失败';
return $result;
@@ -25,8 +28,14 @@ class LightCacheInside
return $result;
}
+ /**
+ * @param string $table
+ * @param string $key
+ * @return mixed|null
+ * @throws ZMException
+ */
public static function get(string $table, string $key) {
- if (!isset(self::$kv_table[$table])) throw new Exception("not initialized LightCache");
+ if (!isset(self::$kv_table[$table])) throw new ZMException("not initialized LightCache");
$r = self::$kv_table[$table]->get($key);
return $r === false ? null : json_decode($r["value"], true);
}
@@ -36,10 +45,10 @@ class LightCacheInside
* @param string $key
* @param string|array|int $value
* @return mixed
- * @throws Exception
+ * @throws ZMException
*/
public static function set(string $table, string $key, $value) {
- if (self::$kv_table === null) throw new Exception("not initialized LightCache");
+ if (self::$kv_table === null) throw new ZMException("not initialized LightCache");
try {
return self::$kv_table[$table]->set($key, [
"value" => json_encode($value, 256)
diff --git a/src/ZM/Store/ZMAtomic.php b/src/ZM/Store/ZMAtomic.php
index 850a465e..b4e88ff8 100644
--- a/src/ZM/Store/ZMAtomic.php
+++ b/src/ZM/Store/ZMAtomic.php
@@ -29,6 +29,7 @@ class ZMAtomic
}
self::$atomics["stop_signal"] = new Atomic(0);
self::$atomics["wait_msg_id"] = new Atomic(0);
+ self::$atomics["_event_id"] = new Atomic(0);
for ($i = 0; $i < 10; ++$i) {
self::$atomics["_tmp_" . $i] = new Atomic(0);
}
diff --git a/src/ZM/Utils/SingletonTrait.php b/src/ZM/Utils/SingletonTrait.php
new file mode 100644
index 00000000..a6649b61
--- /dev/null
+++ b/src/ZM/Utils/SingletonTrait.php
@@ -0,0 +1,24 @@
+connections as $v) {
- server()->close($v);
- }
LightCache::savePersistence();
//DataProvider::saveBuffer();
Timer::clearAll();
diff --git a/src/ZM/global_functions.php b/src/ZM/global_functions.php
index 605c74dd..3a4a6971 100644
--- a/src/ZM/global_functions.php
+++ b/src/ZM/global_functions.php
@@ -1,16 +1,20 @@
$v) {
- foreach($v as $ks => $vs) {
+ foreach (EventManager::$events as $event => $v) {
+ foreach ($v as $ks => $vs) {
//echo get_class($vs).": ".$vs->class." => ".$vs->method.PHP_EOL;
- if($vs->class == $s["class"] && $vs->method == $s["function"]) {
- $list[get_class($vs)][]=$vs;
+ if ($vs->class == $s["class"] && $vs->method == $s["function"]) {
+ $list[get_class($vs)][] = $vs;
}
}
}
@@ -227,7 +231,7 @@ function ctx() {
}
}
-function debug($msg) { Console::debug($msg); }
+function zm_debug($msg) { Console::debug($msg); }
function onebot_target_id_name($message_type) {
return ($message_type == "group" ? "group_id" : "user_id");
@@ -268,6 +272,22 @@ function server() {
return Framework::$server;
}
+/**
+ * @return ZMRobot
+ * @throws RobotNotFoundException
+ * @throws ZMException
+ */
+function bot() {
+ if (($conn = LightCacheInside::get("connect", "conn_fd")) == -2) {
+ return ZMRobot::getRandom();
+ } elseif ($conn != -1) {
+ if (($obj = ManagerGM::get($conn)) !== null) return new ZMRobot($obj);
+ else throw new RobotNotFoundException("单机器人连接模式可能连接了多个机器人!");
+ } else {
+ throw new RobotNotFoundException("没有任何机器人连接到框架!");
+ }
+}
+
diff --git a/test/ZMTest/PassedTest/AnnotationParserRegisterTest.php b/test/ZMTest/PassedTest/AnnotationParserRegisterTest.php
index d09b2d4b..3ade094b 100644
--- a/test/ZMTest/PassedTest/AnnotationParserRegisterTest.php
+++ b/test/ZMTest/PassedTest/AnnotationParserRegisterTest.php
@@ -9,7 +9,7 @@ use Module\Example\Hello;
use PHPUnit\Framework\TestCase;
use ReflectionException;
use ZM\Annotation\AnnotationParser;
-use ZM\Annotation\Swoole\OnWorkerStart;
+use ZM\Annotation\Swoole\OnStart;
use ZM\Console\Console;
class AnnotationParserRegisterTest extends TestCase
@@ -34,8 +34,8 @@ class AnnotationParserRegisterTest extends TestCase
public function testAnnotation() {
ob_start();
$gen = $this->parser->generateAnnotationEvents();
- $m = $gen[OnWorkerStart::class][0]->method;
- $class = $gen[OnWorkerStart::class][0]->class;
+ $m = $gen[OnStart::class][0]->method;
+ $class = $gen[OnStart::class][0]->class;
$c = new $class();
try {
$c->$m();