mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-03-18 05:04:51 +08:00
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:
parent
7dc39e6ada
commit
f908513dca
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
36
src/ZM/Annotation/Swoole/OnTask.php
Normal file
36
src/ZM/Annotation/Swoole/OnTask.php
Normal 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;
|
||||
}
|
||||
}
|
||||
16
src/ZM/Annotation/Swoole/OnTaskEvent.php
Normal file
16
src/ZM/Annotation/Swoole/OnTaskEvent.php
Normal 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
|
||||
{
|
||||
}
|
||||
@ -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);
|
||||
|
||||
26
src/ZM/Entity/CQObject.php
Normal file
26
src/ZM/Entity/CQObject.php
Normal 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"]);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -196,6 +196,8 @@ class Framework
|
||||
|
||||
public function start() {
|
||||
self::$server->start();
|
||||
zm_atomic("server_is_stopped")->set(1);
|
||||
Console::setLevel(0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
35
src/ZM/Utils/MessageUtil.php
Normal file
35
src/ZM/Utils/MessageUtil.php
Normal 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;
|
||||
}
|
||||
}
|
||||
13
src/ZM/Utils/TaskManager.php
Normal file
13
src/ZM/Utils/TaskManager.php
Normal 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"];
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user