update to build 385

add CQObject for CQ
add after-stop action(set terminal level 0)
update global.php modules, add http_proxy_server
add MessageUtil.php for message parsing
add RouteManager::addStaticFileRoute() for quick handling static file
finish onTask function finally!!
add TaskManager::runTask()
This commit is contained in:
jerry 2021-03-15 02:54:16 +08:00
parent 7dc39e6ada
commit f908513dca
16 changed files with 243 additions and 24 deletions

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -0,0 +1,36 @@
<?php
namespace ZM\Annotation\Swoole;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\Interfaces\Rule;
/**
* Class OnTask
* @package ZM\Annotation\Swoole
* @Annotation
* @Target("METHOD")
*/
class OnTask extends AnnotationBase implements Rule
{
/**
* @var string
* @Required()
*/
public $task_name;
/**
* @var string
*/
public $rule = "";
/**
* @return mixed
*/
public function getRule() {
return $this->rule;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace ZM\Annotation\Swoole;
use Doctrine\Common\Annotations\Annotation\Target;
/**
* Class OnTaskEvent
* @package ZM\Annotation\Swoole
* @Annotation
* @Target("METHOD")
*/
class OnTaskEvent extends OnSwooleEventBase
{
}

View File

@ -18,8 +18,8 @@ use ZM\Utils\DataProvider;
class ConsoleApplication extends Application
{
const VERSION_ID = 384;
const VERSION = "2.2.11";
const VERSION_ID = 385;
const VERSION = "2.3.0";
public function __construct(string $name = 'UNKNOWN') {
define("ZM_VERSION_ID", self::VERSION_ID);

View File

@ -0,0 +1,26 @@
<?php
namespace ZM\Entity;
class CQObject
{
public $type;
public $params;
public $start;
public $end;
public function __construct($type = "", $params = [], $start = 0, $end = 0) {
if ($type !== "") {
$this->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"]);
}
}

View File

@ -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;

View File

@ -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;
}
/**

View File

@ -196,6 +196,8 @@ class Framework
public function start() {
self::$server->start();
zm_atomic("server_is_stopped")->set(1);
Console::setLevel(0);
}
/**

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -0,0 +1,35 @@
<?php
namespace ZM\Utils;
use ZM\API\CQ;
use ZM\Console\Console;
use ZM\Requests\ZMRequest;
class MessageUtil
{
public static function downloadCQImage($msg, $path = null) {
$path = $path ?? DataProvider::getDataFolder() . "images/";
if (!is_dir($path)) mkdir($path);
$path = realpath($path);
if ($path === false) {
Console::warning("指定的路径错误不存在!");
return false;
}
$files = [];
$cq = CQ::getAllCQ($msg, true);
foreach ($cq as $v) {
if ($v->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;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace ZM\Utils;
class TaskManager
{
public static function runTask($task_name, $timeout = -1, ...$params) {
$r = server()->taskwait(["task" => $task_name, "params" => $params], $timeout);
return $r === false ? false : $r["result"];
}
}