2020-03-02 16:14:20 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace ZM\Event;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use Co;
|
2020-05-08 16:37:38 +08:00
|
|
|
|
use Doctrine\Common\Annotations\AnnotationException;
|
2020-03-29 16:29:02 +08:00
|
|
|
|
use Error;
|
2020-03-02 16:14:20 +08:00
|
|
|
|
use Exception;
|
|
|
|
|
|
use Framework\Console;
|
|
|
|
|
|
use Framework\ZMBuf;
|
|
|
|
|
|
use ZM\Event\Swoole\{MessageEvent, RequestEvent, WorkerStartEvent, WSCloseEvent, WSOpenEvent};
|
2020-05-23 17:23:29 +08:00
|
|
|
|
use Swoole\Http\Request;
|
2020-05-06 17:26:50 +08:00
|
|
|
|
use Swoole\Server;
|
|
|
|
|
|
use Swoole\WebSocket\Frame;
|
2020-05-08 16:37:38 +08:00
|
|
|
|
use ZM\Annotation\CQ\CQAPIResponse;
|
|
|
|
|
|
use ZM\Annotation\CQ\CQAPISend;
|
|
|
|
|
|
use ZM\Annotation\Http\MiddlewareClass;
|
2020-05-06 17:26:50 +08:00
|
|
|
|
use ZM\Connection\ConnectionManager;
|
2020-05-08 16:37:38 +08:00
|
|
|
|
use ZM\Connection\CQConnection;
|
|
|
|
|
|
use ZM\Http\MiddlewareInterface;
|
2020-03-02 16:14:20 +08:00
|
|
|
|
use ZM\Http\Response;
|
2020-05-02 23:27:26 +08:00
|
|
|
|
use Framework\DataProvider;
|
2020-03-02 16:14:20 +08:00
|
|
|
|
use ZM\Utils\ZMUtil;
|
|
|
|
|
|
|
|
|
|
|
|
class EventHandler
|
|
|
|
|
|
{
|
2020-05-08 16:37:38 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @param $event_name
|
|
|
|
|
|
* @param $param0
|
|
|
|
|
|
* @param null $param1
|
|
|
|
|
|
* @throws AnnotationException
|
|
|
|
|
|
*/
|
2020-03-02 16:14:20 +08:00
|
|
|
|
public static function callSwooleEvent($event_name, $param0, $param1 = null) {
|
2020-03-12 18:31:35 +08:00
|
|
|
|
//$starttime = microtime(true);
|
2020-05-06 17:26:50 +08:00
|
|
|
|
unset(ZMBuf::$context[Co::getCid()]);
|
2020-03-02 16:14:20 +08:00
|
|
|
|
$event_name = strtolower($event_name);
|
|
|
|
|
|
switch ($event_name) {
|
|
|
|
|
|
case "workerstart":
|
|
|
|
|
|
try {
|
2020-04-29 15:29:56 +08:00
|
|
|
|
register_shutdown_function(function () {
|
|
|
|
|
|
$error = error_get_last();
|
|
|
|
|
|
if ($error["type"] != 0) {
|
|
|
|
|
|
Console::error("Internal fatal error: " . $error["message"] . " at " . $error["file"] . "({$error["line"]})");
|
|
|
|
|
|
}
|
|
|
|
|
|
DataProvider::saveBuffer();
|
|
|
|
|
|
ZMBuf::$server->shutdown();
|
|
|
|
|
|
});
|
2020-05-23 17:23:29 +08:00
|
|
|
|
$r = (new WorkerStartEvent($param0, $param1))->onActivate();
|
2020-03-02 16:14:20 +08:00
|
|
|
|
Console::log("\n=== Worker #" . $param0->worker_id . " 已启动 ===\n", "gold");
|
2020-05-23 17:23:29 +08:00
|
|
|
|
$r->onAfter();
|
2020-05-10 18:25:51 +08:00
|
|
|
|
self::startTick();
|
2020-03-02 16:14:20 +08:00
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
|
Console::error("Worker加载出错!停止服务!");
|
|
|
|
|
|
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
|
|
|
|
|
|
ZMUtil::stop();
|
|
|
|
|
|
return;
|
2020-04-29 15:29:56 +08:00
|
|
|
|
} catch (Error $e) {
|
2020-03-29 16:29:02 +08:00
|
|
|
|
var_export($e);
|
|
|
|
|
|
ZMUtil::stop();
|
2020-03-02 16:14:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "message":
|
2020-05-06 17:26:50 +08:00
|
|
|
|
/** @var Frame $param1 */
|
|
|
|
|
|
/** @var Server $param0 */
|
|
|
|
|
|
$conn = ConnectionManager::get($param1->fd);
|
|
|
|
|
|
set_coroutine_params(["server" => $param0, "frame" => $param1, "connection" => $conn]);
|
2020-05-10 14:11:32 +08:00
|
|
|
|
try {
|
|
|
|
|
|
(new MessageEvent($param0, $param1))->onActivate()->onAfter();
|
|
|
|
|
|
} catch (Error $e) {
|
|
|
|
|
|
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
|
|
|
|
|
Console::error("Fatal error when calling $event_name: " . $error_msg);
|
2020-05-23 17:23:29 +08:00
|
|
|
|
Console::stackTrace();
|
2020-05-10 14:11:32 +08:00
|
|
|
|
}
|
2020-03-02 16:14:20 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case "request":
|
|
|
|
|
|
try {
|
2020-05-06 17:26:50 +08:00
|
|
|
|
set_coroutine_params(["request" => $param0, "response" => $param1]);
|
2020-03-02 16:14:20 +08:00
|
|
|
|
(new RequestEvent($param0, $param1))->onActivate()->onAfter();
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
|
/** @var Response $param1 */
|
|
|
|
|
|
$param1->status(500);
|
2020-04-29 15:29:56 +08:00
|
|
|
|
Console::info($param0->server["remote_addr"] . ":" . $param0->server["remote_port"] .
|
|
|
|
|
|
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
|
2020-04-14 23:46:42 +08:00
|
|
|
|
);
|
2020-03-02 16:14:20 +08:00
|
|
|
|
if (!$param1->isEnd()) $param1->end("Internal server error: " . $e->getMessage());
|
2020-06-10 14:39:30 +08:00
|
|
|
|
Console::error("Internal server exception (500), caused by ".get_class($e));
|
2020-03-02 16:14:20 +08:00
|
|
|
|
Console::log($e->getTraceAsString(), "gray");
|
2020-05-10 14:11:32 +08:00
|
|
|
|
} catch (Error $e) {
|
|
|
|
|
|
/** @var Response $param1 */
|
|
|
|
|
|
$param1->status(500);
|
|
|
|
|
|
Console::info($param0->server["remote_addr"] . ":" . $param0->server["remote_port"] .
|
|
|
|
|
|
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
|
|
|
|
|
|
);
|
|
|
|
|
|
$doc = "Internal server error<br>";
|
|
|
|
|
|
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
|
|
|
|
|
if (ZMBuf::$atomics["info_level"]->get() >= 4) $doc .= $error_msg;
|
|
|
|
|
|
if (!$param1->isEnd()) $param1->end($doc);
|
|
|
|
|
|
Console::error("Internal server error (500): " . $error_msg);
|
|
|
|
|
|
Console::log($e->getTraceAsString(), "gray");
|
2020-03-02 16:14:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "open":
|
2020-05-23 17:23:29 +08:00
|
|
|
|
/** @var Request $param1 */
|
|
|
|
|
|
set_coroutine_params(["server" => $param0, "request" => $param1, "fd" => $param1->fd]);
|
2020-05-10 14:11:32 +08:00
|
|
|
|
try {
|
|
|
|
|
|
(new WSOpenEvent($param0, $param1))->onActivate()->onAfter();
|
|
|
|
|
|
} catch (Error $e) {
|
|
|
|
|
|
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
|
|
|
|
|
Console::error("Fatal error when calling $event_name: " . $error_msg);
|
2020-05-23 17:23:29 +08:00
|
|
|
|
Console::stackTrace();
|
2020-05-10 14:11:32 +08:00
|
|
|
|
}
|
2020-03-02 16:14:20 +08:00
|
|
|
|
break;
|
|
|
|
|
|
case "close":
|
2020-05-06 17:26:50 +08:00
|
|
|
|
set_coroutine_params(["server" => $param0, "fd" => $param1]);
|
2020-05-10 14:11:32 +08:00
|
|
|
|
try {
|
|
|
|
|
|
(new WSCloseEvent($param0, $param1))->onActivate()->onAfter();
|
|
|
|
|
|
} catch (Error $e) {
|
|
|
|
|
|
$error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")";
|
|
|
|
|
|
Console::error("Fatal error when calling $event_name: " . $error_msg);
|
2020-05-23 17:23:29 +08:00
|
|
|
|
Console::stackTrace();
|
2020-05-10 14:11:32 +08:00
|
|
|
|
}
|
2020-03-02 16:14:20 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2020-03-05 12:10:08 +08:00
|
|
|
|
//Console::info(Console::setColor("Event: " . $event_name . " 运行了 " . round(microtime(true) - $starttime, 5) . " 秒", "gold"));
|
2020-03-02 16:14:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-05-08 16:37:38 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @param $event_data
|
|
|
|
|
|
* @param $conn_or_response
|
|
|
|
|
|
* @param int $level
|
|
|
|
|
|
* @return bool
|
|
|
|
|
|
* @throws AnnotationException
|
|
|
|
|
|
*/
|
2020-03-09 00:33:04 +08:00
|
|
|
|
public static function callCQEvent($event_data, $conn_or_response, int $level = 0) {
|
2020-05-23 17:23:29 +08:00
|
|
|
|
ctx()->setCache("level",$level);
|
2020-03-02 16:14:20 +08:00
|
|
|
|
if ($level >= 5) {
|
|
|
|
|
|
Console::warning("Recursive call reached " . $level . " times");
|
|
|
|
|
|
Console::stackTrace();
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
$starttime = microtime(true);
|
|
|
|
|
|
switch ($event_data["post_type"]) {
|
|
|
|
|
|
case "message":
|
2020-03-09 00:33:04 +08:00
|
|
|
|
$event = new CQ\MessageEvent($event_data, $conn_or_response, $level);
|
2020-03-02 16:14:20 +08:00
|
|
|
|
if ($event->onBefore()) $event->onActivate();
|
|
|
|
|
|
$event->onAfter();
|
|
|
|
|
|
return $event->hasReply();
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "notice":
|
2020-03-09 00:33:04 +08:00
|
|
|
|
$event = new CQ\NoticeEvent($event_data, $conn_or_response, $level);
|
|
|
|
|
|
if ($event->onBefore()) $event->onActivate();
|
2020-03-02 16:14:20 +08:00
|
|
|
|
$event->onAfter();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
case "request":
|
2020-03-09 00:33:04 +08:00
|
|
|
|
$event = new CQ\RequestEvent($event_data, $conn_or_response, $level);
|
|
|
|
|
|
if ($event->onBefore()) $event->onActivate();
|
2020-03-02 16:14:20 +08:00
|
|
|
|
$event->onAfter();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
case "meta_event":
|
2020-03-09 00:33:04 +08:00
|
|
|
|
$event = new CQ\MetaEvent($event_data, $conn_or_response, $level);
|
|
|
|
|
|
if ($event->onBefore()) $event->onActivate();
|
2020-03-02 16:14:20 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
unset($starttime);
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-05-23 17:23:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @param $req
|
|
|
|
|
|
* @throws AnnotationException
|
|
|
|
|
|
*/
|
2020-03-02 16:14:20 +08:00
|
|
|
|
public static function callCQResponse($req) {
|
2020-05-23 17:23:29 +08:00
|
|
|
|
Console::debug("收到来自API连接的回复:".json_encode($req, 128|256));
|
2020-05-08 16:37:38 +08:00
|
|
|
|
$status = $req["status"];
|
|
|
|
|
|
$retcode = $req["retcode"];
|
|
|
|
|
|
$data = $req["data"];
|
2020-03-09 00:33:04 +08:00
|
|
|
|
if (isset($req["echo"]) && ZMBuf::array_key_exists("sent_api", $req["echo"])) {
|
2020-03-02 16:14:20 +08:00
|
|
|
|
$origin = ZMBuf::get("sent_api")[$req["echo"]];
|
|
|
|
|
|
$self_id = $origin["self_id"];
|
|
|
|
|
|
$response = [
|
|
|
|
|
|
"status" => $status,
|
|
|
|
|
|
"retcode" => $retcode,
|
|
|
|
|
|
"data" => $data,
|
2020-05-23 17:23:29 +08:00
|
|
|
|
"self_id" => $self_id,
|
|
|
|
|
|
"echo" => $req["echo"]
|
2020-03-02 16:14:20 +08:00
|
|
|
|
];
|
2020-05-23 17:23:29 +08:00
|
|
|
|
set_coroutine_params(["cq_response" => $response]);
|
2020-05-08 16:37:38 +08:00
|
|
|
|
if (isset(ZMBuf::$events[CQAPIResponse::class][$req["retcode"]])) {
|
|
|
|
|
|
list($c, $method) = ZMBuf::$events[CQAPIResponse::class][$req["retcode"]];
|
|
|
|
|
|
$class = new $c(["data" => $origin["data"]]);
|
|
|
|
|
|
call_user_func_array([$class, $method], [$origin["data"], $req]);
|
|
|
|
|
|
}
|
2020-05-23 17:23:29 +08:00
|
|
|
|
$origin_ctx = ctx()->copy();
|
|
|
|
|
|
ctx()->setCache("action", $origin["data"]["action"] ?? "unknown");
|
|
|
|
|
|
ctx()->setData($origin["data"]);
|
|
|
|
|
|
foreach (ZMBuf::$events[CQAPISend::class] ?? [] as $k => $v) {
|
|
|
|
|
|
if (($v->action == "" || $v->action == ctx()->getCache("action")) && $v->with_result) {
|
|
|
|
|
|
$c = $v->class;
|
|
|
|
|
|
self::callWithMiddleware($c, $v->method, context()->copy(), [ctx()->getCache("action"), $origin["data"]["params"] ?? [], ctx()->getRobotId()]);
|
|
|
|
|
|
if (context()->getCache("block_continue") === true) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
set_coroutine_params($origin_ctx);
|
2020-03-02 16:14:20 +08:00
|
|
|
|
if (($origin["func"] ?? null) !== null) {
|
|
|
|
|
|
call_user_func($origin["func"], $response, $origin["data"]);
|
|
|
|
|
|
} elseif (($origin["coroutine"] ?? false) !== false) {
|
|
|
|
|
|
$p = ZMBuf::get("sent_api");
|
|
|
|
|
|
$p[$req["echo"]]["result"] = $response;
|
|
|
|
|
|
ZMBuf::set("sent_api", $p);
|
|
|
|
|
|
Co::resume($origin['coroutine']);
|
|
|
|
|
|
}
|
|
|
|
|
|
ZMBuf::unsetByValue("sent_api", $req["echo"]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2020-05-08 16:37:38 +08:00
|
|
|
|
|
|
|
|
|
|
public static function callCQAPISend($reply, ?CQConnection $connection) {
|
|
|
|
|
|
$action = $reply["action"] ?? null;
|
|
|
|
|
|
if ($action === null) {
|
|
|
|
|
|
Console::warning("API 激活事件异常!");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2020-05-10 14:40:18 +08:00
|
|
|
|
if (ctx() === null) $content = [];
|
|
|
|
|
|
else $content = ctx()->copy();
|
2020-05-08 16:37:38 +08:00
|
|
|
|
go(function () use ($action, $reply, $connection, $content) {
|
|
|
|
|
|
set_coroutine_params($content);
|
|
|
|
|
|
context()->setCache("action", $action);
|
|
|
|
|
|
context()->setCache("reply", $reply);
|
|
|
|
|
|
foreach (ZMBuf::$events[CQAPISend::class] ?? [] as $k => $v) {
|
2020-05-23 17:23:29 +08:00
|
|
|
|
if (($v->action == "" || $v->action == $action) && !$v->with_result) {
|
2020-05-08 16:37:38 +08:00
|
|
|
|
$c = $v->class;
|
|
|
|
|
|
self::callWithMiddleware($c, $v->method, context()->copy(), [$reply["action"], $reply["params"] ?? [], $connection->getQQ()]);
|
|
|
|
|
|
if (context()->getCache("block_continue") === true) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param $c
|
|
|
|
|
|
* @param $method
|
|
|
|
|
|
* @param array $class_construct
|
|
|
|
|
|
* @param array $func_args
|
|
|
|
|
|
* @param null $after_call
|
|
|
|
|
|
* @return mixed|null
|
|
|
|
|
|
* @throws AnnotationException
|
|
|
|
|
|
* @throws Exception
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static function callWithMiddleware($c, $method, array $class_construct, array $func_args, $after_call = null) {
|
|
|
|
|
|
$return_value = null;
|
|
|
|
|
|
$plain_class = is_object($c) ? get_class($c) : $c;
|
|
|
|
|
|
if (isset(ZMBuf::$events[MiddlewareInterface::class][$plain_class][$method])) {
|
|
|
|
|
|
$middlewares = ZMBuf::$events[MiddlewareInterface::class][$plain_class][$method];
|
|
|
|
|
|
$before_result = true;
|
|
|
|
|
|
$r = [];
|
|
|
|
|
|
foreach ($middlewares as $k => $middleware) {
|
|
|
|
|
|
if (!isset(ZMBuf::$events[MiddlewareClass::class][$middleware])) throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$middleware}\"!");
|
|
|
|
|
|
$middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middleware];
|
|
|
|
|
|
$before = $middleware_obj["class"];
|
|
|
|
|
|
$r[$k] = new $before();
|
|
|
|
|
|
$r[$k]->class = is_object($c) ? get_class($c) : $c;
|
|
|
|
|
|
$r[$k]->method = $method;
|
|
|
|
|
|
if (isset($middleware_obj["before"])) {
|
|
|
|
|
|
$before_result = call_user_func_array([$r[$k], $middleware_obj["before"]], $func_args);
|
|
|
|
|
|
if ($before_result === false) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if ($before_result) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (is_object($c)) $class = $c;
|
2020-05-10 18:25:51 +08:00
|
|
|
|
elseif ($class_construct == []) $class = ZMUtil::getModInstance($c);
|
2020-05-08 16:37:38 +08:00
|
|
|
|
else $class = new $c($class_construct);
|
|
|
|
|
|
$result = call_user_func_array([$class, $method], $func_args);
|
|
|
|
|
|
if (is_callable($after_call))
|
|
|
|
|
|
$return_value = call_user_func_array($after_call, [$result]);
|
|
|
|
|
|
} catch (Exception $e) {
|
|
|
|
|
|
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
|
|
|
|
|
|
$middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middlewares[$i]];
|
|
|
|
|
|
if (!isset($middleware_obj["exceptions"])) continue;
|
|
|
|
|
|
foreach ($middleware_obj["exceptions"] as $name => $method) {
|
|
|
|
|
|
if ($e instanceof $name) {
|
2020-05-10 18:25:51 +08:00
|
|
|
|
$r[$i]->$method($e);
|
2020-05-08 16:37:38 +08:00
|
|
|
|
context()->setCache("block_continue", true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (context()->getCache("block_continue") === true) return $return_value;
|
|
|
|
|
|
}
|
|
|
|
|
|
throw $e;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
for ($i = count($middlewares) - 1; $i >= 0; --$i) {
|
|
|
|
|
|
$middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middlewares[$i]];
|
|
|
|
|
|
if (isset($middleware_obj["after"], $r[$i]))
|
|
|
|
|
|
call_user_func_array([$r[$i], $middleware_obj["after"]], $func_args);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (is_object($c)) $class = $c;
|
2020-05-10 18:25:51 +08:00
|
|
|
|
elseif ($class_construct == []) $class = ZMUtil::getModInstance($c);
|
2020-05-08 16:37:38 +08:00
|
|
|
|
else $class = new $c($class_construct);
|
|
|
|
|
|
$result = call_user_func_array([$class, $method], $func_args);
|
|
|
|
|
|
if (is_callable($after_call))
|
|
|
|
|
|
$return_value = call_user_func_array($after_call, [$result]);
|
|
|
|
|
|
}
|
|
|
|
|
|
return $return_value;
|
|
|
|
|
|
}
|
2020-05-10 18:25:51 +08:00
|
|
|
|
|
|
|
|
|
|
private static function startTick() {
|
|
|
|
|
|
Console::debug("Starting " . count(ZMBuf::get("paused_tick", [])) . " custom tick function");
|
|
|
|
|
|
foreach (ZMBuf::get("paused_tick", []) as $cid) {
|
|
|
|
|
|
Co::resume($cid);
|
|
|
|
|
|
}
|
2020-05-23 17:23:29 +08:00
|
|
|
|
|
2020-05-10 18:25:51 +08:00
|
|
|
|
}
|
2020-03-29 16:29:02 +08:00
|
|
|
|
}
|