update to 1.2 version

Generate systemd script
Default info_level set to 2
Modify & add some comment for Example module
Brand new Console
Add daemon command argument
Add #OnTick annotation
Add ZMRobot API class
This commit is contained in:
whale 2020-04-29 15:29:56 +08:00
parent e1983d6dd8
commit 169a751e0f
31 changed files with 872 additions and 200 deletions

View File

@ -4,7 +4,7 @@
[![zhamao License](https://img.shields.io/hexpm/l/plug.svg?maxAge=2592000)](https://github.com/zhamao-robot/zhamao-framework/blob/master/LICENSE)
[![版本](https://img.shields.io/badge/version-1.1-green.svg)]()
[![goto counter](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/goto.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=goto)
[![stupid counter](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/stupid.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=stupid)
[![TODO counter](https://img.shields.io/github/search/zhamao-robot/zhamao-framework/TODO.svg)](https://github.com/zhamao-robot/zhamao-framework/search?q=TODO)
一个异步、多平台兼容的 **聊天机器人** 框架。

View File

@ -1,6 +1,5 @@
#!/usr/bin/env php
<?php
<?php /** @since 1.2 */
switch ($argv[1] ?? '') {
case '--generate':
generate();
@ -16,19 +15,12 @@ switch ($argv[1] ?? '') {
}
function generate() {
$s = <<<EOL
[Unit]
Description=zhamao-framework Daemon
After=rc-local.service
[Service]
Type=simple
EOL;
$s .= "\nUser=".exec("whoami");
$s .= "\nGroup=".exec("groups | awk '{print $1}'");
$s = "[Unit]\nDescription=zhamao-framework Daemon\nAfter=rc-local.service\n\n[Service]\nType=simple";
$s .= "\nUser=" . exec("whoami");
$s .= "\nGroup=" . exec("groups | awk '{print $1}'");
$s .= "\nWorkingDirectory=" . getcwd();
$s .= "\nExecStart=$(which php) " .getcwd()."/bin/start --disable-console-input";
$s .= "\nExecStart=" . getcwd() . "/bin/start server --disable-console-input";
$s .= "\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\n";
file_put_contents(getcwd()."/resources/zhamao.service", $s);
echo "File successfully generated. Path: ".getcwd()."/resources/zhamao.service\n";
file_put_contents(getcwd() . "/resources/zhamao.service", $s);
echo "File successfully generated. Path: " . getcwd() . "/resources/zhamao.service\n";
}

View File

@ -3,7 +3,7 @@
"description": "high-performance intelligent assistant",
"minimum-stability": "stable",
"license": "proprietary",
"version": "1.0",
"version": "1.2.0",
"authors": [
{
"name": "whale",
@ -18,10 +18,11 @@
"swoole/ide-helper": "@dev",
"ext-mbstring": "*",
"swlib/saber": "^1.0",
"doctrine/annotations": "1.8",
"doctrine/annotations": "<1.10.2",
"ext-json": "*",
"ext-posix": "*",
"ext-ctype": "*"
"ext-ctype": "*",
"ext-pcntl": "*"
},
"repositories": {
"packagist": {

View File

@ -58,7 +58,7 @@ $config['init_atomics'] = [
'out_count' => 0, //消息发送调用send_*_msg的统计数量
'reload_time' => 0, //调用reload功能统计数量
'wait_msg_id' => 0, //协程挂起id自增
'info_level' => 0, //终端显示的log等级
'info_level' => 2, //终端显示的log等级
];
/** 自动保存的缓存保存时间(秒) */

View File

@ -14,8 +14,7 @@ use Exception;
class Console
{
static function setColor($string, $color = "")
{
static function setColor($string, $color = "") {
switch ($color) {
case "red":
return "\x1b[38;5;203m" . $string . "\x1b[m";
@ -25,6 +24,7 @@ class Console
return "\x1b[38;5;227m" . $string . "\x1b[m";
case "blue":
return "\033[34m" . $string . "\033[0m";
case "pink": // I really don't know what stupid color it is.
case "lightpurple":
return "\x1b[38;5;207m" . $string . "\x1b[m";
case "lightblue":
@ -33,8 +33,6 @@ class Console
return "\x1b[38;5;214m" . $string . "\x1b[m";
case "gray":
return "\x1b[38;5;59m" . $string . "\x1b[m";
case "pink":
return "\x1b[38;5;207m" . $string . "\x1b[m";
case "lightlightblue":
return "\x1b[38;5;63m" . $string . "\x1b[m";
default:
@ -42,9 +40,8 @@ class Console
}
}
static function error($obj, $head = null)
{
if ($head === null) $head = date("[H:i:s ") . "ERROR] ";
static function error($obj, $head = null) {
if ($head === null) $head = date("[H:i:s] ") . "[E] ";
if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
@ -58,90 +55,80 @@ class Console
echo(self::setColor($head . ($trace ?? "") . $obj, "red") . "\n");
}
static function warning($obj, $head = null)
{
if ($head === null) $head = date("[H:i:s") . " WARN] ";
static function warning($obj, $head = null) {
if ($head === null) $head = date("[H:i:s]") . " [W] ";
if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
}
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
if(ZMBuf::$atomics["info_level"]->get() >= 1) {
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
}
echo(self::setColor($head . ($trace ?? "") . $obj, "yellow") . "\n");
}
echo(self::setColor($head . ($trace ?? "") . $obj, "yellow") . "\n");
}
static function info($obj, $head = null)
{
if ($head === null) $head = date("[H:i:s ") . "INFO] ";
static function info($obj, $head = null) {
if ($head === null) $head = date("[H:i:s] ") . "[I] ";
if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
}
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
if(ZMBuf::$atomics["info_level"]->get() >= 2) {
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
}
echo(self::setColor($head . ($trace ?? "") . $obj, "lightblue") . "\n");
}
echo(self::setColor($head . ($trace ?? "") . $obj, "lightblue") . "\n");
}
static function log($obj, $color = "")
{
static function success($obj, $head = null) {
if ($head === null) $head = date("[H:i:s] ") . "[S] ";
if (ZMBuf::$info_level !== null && in_array(ZMBuf::$info_level->get(), [1, 2])) {
$trace = debug_backtrace()[1] ?? ['file' => '', 'function' => ''];
$trace = "[" . basename($trace["file"], ".php") . ":" . $trace["function"] . "] ";
}
if(ZMBuf::$atomics["info_level"]->get() >= 2) {
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
}
echo(self::setColor($head . ($trace ?? "") . $obj, "green") . "\n");
}
}
static function verbose($obj, $head = null) {
if($head === null) $head = date("[H:i:s] ") . "[V] ";
if(ZMBuf::$atomics["info_level"]->get() >= 3) {
if (!is_string($obj)) {
if (isset($trace)) {
var_dump($obj);
return;
} else $obj = "{Object}";
}
echo(self::setColor($head . ($trace ?? "") . $obj, "blue") . "\n");
}
}
static function debug($obj) {
debug($obj);
}
static function log($obj, $color = "") {
if (!is_string($obj)) var_dump($obj);
else echo(self::setColor($obj, $color) . "\n");
}
static function msg($obj, $self_id = "")
{
if (ZMBuf::$info_level !== null && ZMBuf::$info_level->get() == 3) {
if (!isset($obj["post_type"])) {
switch ($obj["action"]) {
case "send_private_msg":
$msg = Console::setColor(date("H:i:s ") . "[" . (ZMBuf::globals("robot_alias")[$self_id] ?? "Null") . "] ", "lightlightblue");
$msg .= Console::setColor("私聊↑(" . $obj["params"]["user_id"] . ")", "lightlightblue");
$msg .= Console::setColor(" > ", "gray");
$msg .= $obj["params"]["message"];
Console::log($msg);
break;
case "send_group_msg":
//TODO: 写新的控制台消息API消息处理
Console::log(Console::setColor("[" . date("H:i:s") . " GROUP:" . $obj["params"]["group_id"] . "] ", "blue") . Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray") . ($obj["params"]["message"] ?? ""));
break;
case "send_discuss_msg":
Console::log(Console::setColor("[" . date("H:i:s") . " DISCUSS:" . $obj["params"]["discuss_id"] . "] ", "blue") . Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray") . ($obj["params"]["message"] ?? ""));
break;
case "send_msg":
$obj["action"] = "send_" . $obj["message_type"] . "_msg";
self::msg($obj);
break;
case "send_wechat_msg":
Console::log(Console::setColor("[" . date("H:i:s") . " WECHAT] ", "blue") . Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray") . ($obj["params"]["message"] ?? ""));
break;
default:
break;
}
} else {
if ($obj["post_type"] == "message") {
switch ($obj["message_type"]) {
case "group":
//
//TODO: 写新的控制台消息event处理
case "private":
case "discuss":
case "wechat":
}
}
}
}
}
static function stackTrace()
{
static function stackTrace() {
$log = "Stack trace:\n";
$trace = debug_backtrace();
//array_shift($trace);
@ -165,8 +152,7 @@ class Console
echo $log;
}
static function listenConsole()
{
static function listenConsole() {
if (in_array('--disable-console-input', FrameworkLoader::$argv)) {
self::info("ConsoleCommand disabled.");
return;
@ -183,10 +169,18 @@ class Console
* @param string $cmd
* @return bool
*/
private static function executeCommand(string $cmd)
{
private static function executeCommand(string $cmd) {
$it = explodeMsg($cmd);
switch ($it[0] ?? '') {
case 'logtest':
Console::log(date("[H:i:s]"). " [L] This is normal msg. (0)");
Console::error("This is error msg. (0)");
Console::warning("This is warning msg. (1)");
Console::info("This is info msg. (2)");
Console::success("This is success msg. (2)");
Console::verbose("This is verbose msg. (3)");
Console::debug("This is debug msg. (4)");
return true;
case 'call':
$class_name = $it[1];
$function_name = $it[2];
@ -203,6 +197,9 @@ class Console
case 'echo':
Console::info($it[1]);
return true;
case 'color':
Console::log($it[2], $it[1]);
return true;
case 'stop':
ZMUtil::stop();
return false;
@ -218,9 +215,8 @@ class Console
}
}
public static function withSleep(string $string, int $int)
{
public static function withSleep(string $string, int $int) {
self::info($string);
sleep($int);
}
}
}

View File

@ -3,7 +3,10 @@
namespace Framework;
use Co;
use Swoole\Http\Request;
use Swoole\Runtime;
use Swoole\WebSocket\Frame;
use ZM\Event\EventHandler;
use Exception;
use Swoole\WebSocket\Server;
@ -32,7 +35,8 @@ class FrameworkLoader
/** @var Server */
private $server;
public function __construct($args = []) {
public function __construct($args = [])
{
if (self::$instance !== null) die("Cannot run two FrameworkLoader in one process!");
self::$instance = $this;
self::$argv = $args;
@ -52,17 +56,38 @@ class FrameworkLoader
try {
$this->server = new Server(self::$settings->get("host"), self::$settings->get("port"));
if (in_array("--remote-shell", $args)) RemoteShell::listen($this->server, "127.0.0.1");
$this->server->set(self::$settings->get("swoole"));
$settings = self::$settings->get("swoole");
if (in_array("--daemon", $args)) {
$settings["daemonize"] = 1;
Console::log("已启用守护进程,输出重定向到 " . $settings["log_file"]);
self::$argv[] = "--disable-console-input";
}
$this->server->set($settings);
$this->server->on("WorkerStart", [$this, "onWorkerStart"]);
$this->server->on("message", function ($server, $frame) { EventHandler::callSwooleEvent("message", $server, $frame); });
$this->server->on("message", function ($server, Frame $frame) {
Console::debug("Calling Swoole \"message\" event from fd=" . $frame->fd);
EventHandler::callSwooleEvent("message", $server, $frame);
});
$this->server->on("request", function ($request, $response) {
$response = new Response($response);
Console::debug("Receiving Http request event, cid=" . Co::getCid());
EventHandler::callSwooleEvent("request", $request, $response);
});
$this->server->on("open", function ($server, $request) { EventHandler::callSwooleEvent("open", $server, $request); });
$this->server->on("close", function ($server, $fd) { EventHandler::callSwooleEvent("close", $server, $fd); });
$this->server->on("open", function ($server, Request $request) {
Console::debug("Calling Swoole \"open\" event from fd=" . $request->fd);
EventHandler::callSwooleEvent("open", $server, $request);
});
$this->server->on("close", function ($server, $fd) {
Console::debug("Calling Swoole \"close\" event from fd=" . $fd);
EventHandler::callSwooleEvent("close", $server, $fd);
});
ZMBuf::initAtomic();
Console::info("host: ".self::$settings->get("host").", port: ".self::$settings->get("port"));
if (in_array("--log-error", $args)) ZMBuf::$atomics["info_level"]->set(0);
if (in_array("--log-warning", $args)) ZMBuf::$atomics["info_level"]->set(1);
if (in_array("--log-info", $args)) ZMBuf::$atomics["info_level"]->set(2);
if (in_array("--log-verbose", $args)) ZMBuf::$atomics["info_level"]->set(3);
if (in_array("--log-debug", $args)) ZMBuf::$atomics["info_level"]->set(4);
Console::log("host: " . self::$settings->get("host") . ", port: " . self::$settings->get("port") . ", log_level: " . ZMBuf::$atomics["info_level"]->get());
$this->server->start();
} catch (Exception $e) {
Console::error("Framework初始化出现错误请检查");
@ -71,15 +96,18 @@ class FrameworkLoader
}
}
private function requireGlobalFunctions() {
private function requireGlobalFunctions()
{
require __DIR__ . '/global_functions.php';
}
private function registerAutoloader(string $string) {
private function registerAutoloader(string $string)
{
if (!spl_autoload_register($string)) die("Failed to register autoloader named \"$string\" !");
}
private function defineProperties() {
private function defineProperties()
{
define("ZM_START_TIME", microtime(true));
define("ZM_DATA", self::$settings->get("zm_data"));
define("CONFIG_DIR", self::$settings->get("config_dir"));
@ -93,7 +121,8 @@ class FrameworkLoader
define("ZM_MATCH_SECOND", 3);
}
private function selfCheck() {
private function selfCheck()
{
if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
//if (!extension_loaded("gd")) die("Can not find gd extension.\n");
if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
@ -105,7 +134,8 @@ class FrameworkLoader
return true;
}
public function onWorkerStart(\Swoole\Server $server, $worker_id) {
public function onWorkerStart(\Swoole\Server $server, $worker_id)
{
self::$instance = $this;
self::$run_time = microtime(true);
EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);

View File

@ -1,10 +0,0 @@
<?php
namespace Framework;
class InfoLevel
{
const INFO = 0;
}

View File

@ -1,18 +0,0 @@
<?php
namespace Framework;
use Swoole\Coroutine\System;
class Logger
{
private static function getTimeFormat($type = "I") {
return "[" . date("Y-m-d H:i:s") . "]\t[" . $type . "]\t";
}
public static function writeSwooleLog($log) {
System::writeFile(CRASH_DIR . "swoole_error.log", "\n" . self::getTimeFormat() . $log, FILE_APPEND);
}
}

View File

@ -11,7 +11,6 @@ namespace Framework;
use Swoole\Atomic;
use swoole_atomic;
use ZM\connection\WSConnection;
use ZM\Utils\Scheduler;
use ZM\Utils\SQLPool;
class ZMBuf
@ -22,8 +21,7 @@ class ZMBuf
/** @var WSConnection[] */
static $connect = [];//储存连接实例的数组
//Scheduler计划任务连接实例只可以在单worker_num时使用
/** @var Scheduler|null */
static $scheduler = null;
static $scheduler = null; //This is stupid warning...
//Swoole SQL连接池多进程下每个进程一个连接池
/** @var SQLPool */
@ -52,6 +50,7 @@ class ZMBuf
public static $req_mapping = [];
public static $config = [];
public static $context = [];
public static $instance = [];
static function get($name, $default = null) {
return self::$cache[$name] ?? $default;
@ -106,6 +105,7 @@ class ZMBuf
self::$cache = [];
self::$connect = [];
self::$time_nlp = null;
self::$instance = [];
}
/**

View File

@ -176,6 +176,6 @@ function context() {
}
function debug($msg) {
if (ZMBuf::$atomics["info_level"]->get() == 1)
Console::log(date("[H:i:s ") . "DEBUG] " . $msg, 'gray');
if (ZMBuf::$atomics["info_level"]->get() >= 4)
Console::log(date("[H:i:s] ") . "[D] " . $msg, 'gray');
}

View File

@ -15,10 +15,12 @@ use ZM\ModBase;
/**
* Class Hello
* @package Module\Example
* @since 1.0
*/
class Hello extends ModBase
{
/**
* 在机器人连接后向终端输出信息
* @SwooleEventAt("open",rule="connectType:qq")
* @param $conn
*/
@ -26,6 +28,7 @@ class Hello extends ModBase
Console::info("机器人 ".$conn->getQQ()." 已连接!");
}
/**
* 向机器人发送"你好",即可回复这句话
* @CQCommand("你好")
*/
public function hello(){
@ -33,14 +36,16 @@ class Hello extends ModBase
}
/**
* @RequestMapping("/test/ping")
* 中间件测试的一个示例函数
* @RequestMapping("/httpTimer")
* @Middleware("timer")
*/
public function pong(){
return "pong";
public function timer(){
return "This page is used as testing TimerMiddleware! Do not use it in production.";
}
/**
* 框架会默认关闭未知的WebSocket链接因为这个绑定的事件你可以根据你自己的需求进行修改
* @SwooleEventAt(type="open",rule="connectType:unknown")
*/
public function closeUnknownConn(){

View File

@ -18,7 +18,7 @@ class CQ
if (is_numeric($qq) || $qq === "all") {
return "[CQ:at,qq=" . $qq . "]";
}
Console::error("传入的QQ号码($qq)错误!");
Console::warning("传入的QQ号码($qq)错误!");
return " ";
}
@ -31,7 +31,7 @@ class CQ
if (is_numeric($id)) {
return "[CQ:face,id=" . $id . "]";
}
Console::error("传入的face id($id)错误!");
Console::warning("传入的face id($id)错误!");
return " ";
}
@ -44,7 +44,7 @@ class CQ
if (is_numeric($id)) {
return "[CQ:emoji,id=" . $id . "]";
}
Console::error("传入的emoji id($id)错误!");
Console::warning("传入的emoji id($id)错误!");
return " ";
}
@ -66,7 +66,7 @@ class CQ
if (is_numeric($id)) {
return "[CQ:sface,id=" . $id . "]";
}
Console::error("传入的sface id($id)错误!");
Console::warning("传入的sface id($id)错误!");
return " ";
}
@ -151,7 +151,7 @@ class CQ
return "[CQ:music,type=$type,id=$id_or_url]";
case "custom":
if ($title === null || $audio === null) {
Console::error("传入CQ码实例的标题和音频链接不能为空");
Console::warning("传入CQ码实例的标题和音频链接不能为空");
return " ";
}
if ($content === null) $c = "";
@ -160,7 +160,7 @@ class CQ
else $i = ",image=" . $image;
return "[CQ:music,type=custom,url=" . $id_or_url . ",audio=" . $audio . ",title=" . $title . $c . $i . "]";
default:
Console::error("传入的music type($type)错误!");
Console::warning("传入的music type($type)错误!");
return " ";
}
}
@ -217,4 +217,4 @@ class CQ
}
return $msg;
}
}
}

View File

@ -93,12 +93,12 @@ class CQAPI
}
}
if ($find === null) {
Console::error("Unknown API " . $name);
Console::warning("Unknown API " . $name);
return false;
}
$reply = ["action" => $find];
if (!is_array($arg[1])) {
Console::error("Error when parsing params. Please make sure your params is an array.");
Console::warning("Error when parsing params. Please make sure your params is an array.");
return false;
}
if ($arg[1] != []) {
@ -107,7 +107,7 @@ class CQAPI
if (!($arg[0] instanceof CQConnection)) {
$robot = ConnectionManager::getByType("qq", ["self_id" => $arg[0]]);
if ($robot == []) {
Console::error("发送错误,机器人连接不存在!");
Console::warning("发送错误,机器人连接不存在!");
return false;
}
$arg[0] = $robot[0];
@ -195,7 +195,7 @@ class CQAPI
* @param |null $function
* @return bool
*/
private static function processAPI($connection, $reply, $function = null) {
public static function processAPI($connection, $reply, $function = null) {
$api_id = ZMBuf::$atomics["wait_msg_id"]->get();
$reply["echo"] = $api_id;
ZMBuf::$atomics["wait_msg_id"]->add(1);
@ -222,7 +222,7 @@ class CQAPI
]);
}
if ($connection->push(json_encode($reply))) {
Console::msg($reply, $connection->getQQ());
//Console::msg($reply, $connection->getQQ());
ZMBuf::$atomics["out_count"]->add(1);
if ($function === true) {
Co::suspend();
@ -232,6 +232,7 @@ class CQAPI
}
return true;
} else {
Console::warning("CQAPI send failed, websocket push error.");
$response = [
"status" => "failed",
"retcode" => 999,
@ -246,4 +247,4 @@ class CQAPI
return false;
}
}
}
}

View File

@ -10,14 +10,16 @@ use ReflectionException;
use ReflectionMethod;
use ZM\Annotation\CQ\{CQAfter, CQBefore, CQCommand, CQMessage, CQMetaEvent, CQNotice, CQRequest};
use ZM\Annotation\Http\{After, Before, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping};
use Swoole\Timer;
use ZM\Annotation\Interfaces\CustomAnnotation;
use ZM\Annotation\Interfaces\Level;
use ZM\Annotation\Module\{Closed, InitBuffer, SaveBuffer};
use ZM\Annotation\Swoole\{OnStart, SwooleEventAfter, SwooleEventAt};
use ZM\Annotation\Swoole\{OnStart, OnTick, SwooleEventAfter, SwooleEventAt};
use ZM\Annotation\Interfaces\Rule;
use ZM\Connection\WSConnection;
use ZM\Http\MiddlewareInterface;
use ZM\Utils\DataProvider;
use ZM\Utils\ZMUtil;
class AnnotationParser
{
@ -45,13 +47,15 @@ class AnnotationParser
if ($vs instanceof Closed) {
continue 2;
} elseif ($vs instanceof Controller) {
Console::debug("找到 Controller 中间件: ".$vs->class);
$class_prefix = $vs->prefix;
} elseif ($vs instanceof SaveBuffer) {
Console::debug("注册自动保存的缓存变量: ".$vs->buf_name." (Dir:".$vs->sub_folder.")");
DataProvider::addSaveBuffer($vs->buf_name, $vs->sub_folder);
} elseif ($vs instanceof InitBuffer) {
ZMBuf::set($vs->buf_name, []);
} elseif ($vs instanceof MiddlewareClass) {
Console::info("正在注册中间件 " . $vs->class);
Console::verbose("正在注册中间件 " . $vs->class);
$result = [
"class" => "\\" . $reflection_class->getName()
];
@ -95,13 +99,14 @@ class AnnotationParser
elseif ($vss instanceof CQCommand) ZMBuf::$events[CQCommand::class][] = $vss;
elseif ($vss instanceof RequestMapping) {
self::registerRequestMapping($vss, $vs, $reflection_class, $class_prefix);
if($middleware_addon !== null)
if ($middleware_addon !== null)
ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method] = $middleware_addon->middleware;
} elseif ($vss instanceof CustomAnnotation) ZMBuf::$events[get_class($vss)][] = $vss;
elseif ($vss instanceof CQBefore) ZMBuf::$events[CQBefore::class][$vss->cq_event][] = $vss;
elseif ($vss instanceof CQAfter) ZMBuf::$events[CQAfter::class][$vss->cq_event][] = $vss;
elseif ($vss instanceof OnStart) ZMBuf::$events[OnStart::class][] = $vss;
elseif ($vss instanceof Middleware) ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method] = $vss->middleware;
elseif ($vss instanceof OnTick) self::addTimerTick($vss);
}
}
}
@ -123,6 +128,10 @@ class AnnotationParser
}
}
}
if (ZMBuf::isset("timer_count")) {
Console::info("Added " . ZMBuf::get("timer_count") . " timer(s)!");
ZMBuf::unsetCache("timer_count");
}
}
private static function getRuleCallback($rule_str) {
@ -306,4 +315,9 @@ class AnnotationParser
$tree[] = &$items[$item['id']];
return $tree;
}
private static function addTimerTick(?OnTick $vss) {
ZMBuf::set("timer_count", ZMBuf::get("timer_count", 0) + 1);
Timer::tick($vss->tick_ms, [ZMUtil::getModInstance($vss->class), $vss->method]);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace ZM\Annotation\Swoole;
use Doctrine\Common\Annotations\Annotation\Required;
use Doctrine\Common\Annotations\Annotation\Target;
use ZM\Annotation\AnnotationBase;
/**
* Class OnTick
* @package ZM\Annotation\Swoole
* @Annotation
* @Target("METHOD")
* @since 1.2
*/
class OnTick extends AnnotationBase
{
/**
* @var int
* @Required()
*/
public $tick_ms;
}

View File

@ -11,7 +11,7 @@ class ConnectionManager
/**
* 通过server的fd获取WSConnection实例化对象
* @param int $fd
* @return WSConnection
* @return WSConnection|CQConnection|ProxyConnection
*/
public static function get(int $fd) {
foreach (ZMBuf::$connect as $v) {
@ -23,7 +23,7 @@ class ConnectionManager
/**
* @param string $type
* @param array $option
* @return WSConnection[]
* @return WSConnection[]|CQConnection[]
*/
public static function getByType(string $type, $option = []) {
$conn = [];
@ -70,4 +70,4 @@ class ConnectionManager
$classes = getAllClasses(WORKING_DIR . "/src/Custom/Connection/", "Custom\\Connection");
ZMBuf::$custom_connection_class = $classes;
}
}
}

View File

@ -5,7 +5,6 @@ namespace ZM\Connection;
use Framework\Console;
use Framework\Logger;
use swoole_websocket_server;
abstract class WSConnection
@ -43,10 +42,10 @@ abstract class WSConnection
}
if ($this->server->push($this->fd, $data) === false) {
$data = unicode_decode($data);
if ($push_error_record) Logger::writeSwooleLog("API push failed. Data: " . $data);
Console::error("websocket数据未成功推送长度" . strlen($data));
if ($push_error_record) Console::warning("API push failed. Data: " . $data);
Console::warning("websocket数据未成功推送长度" . strlen($data));
return false;
}
return true;
}
}
}

View File

@ -7,7 +7,10 @@ namespace ZM\Context;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use swoole_server;
use ZM\Connection\ConnectionManager;
use ZM\Connection\CQConnection;
use ZM\Http\Response;
use ZM\Utils\ZMRobot;
class Context implements ContextInterface
{
@ -68,4 +71,12 @@ class Context implements ContextInterface
public function getCid() {
return $this->cid;
}
/**
* @return ZMRobot|null
*/
public function getRobot() {
$conn = ConnectionManager::get($this->getFrame()->fd);
return $conn instanceof CQConnection ? new ZMRobot($conn) : null;
}
}

View File

@ -8,6 +8,7 @@ use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
use ZM\Http\Response;
use ZM\Utils\ZMRobot;
interface ContextInterface
{
@ -30,4 +31,7 @@ interface ContextInterface
/** @return Request */
public function getRequest();
/** @return ZMRobot */
public function getRobot();
}

View File

@ -14,6 +14,9 @@ class DB
{
private static $table_list = [];
/**
* @throws DbException
*/
public static function initTableList() {
$result = self::rawQuery("select TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='" . ZMBuf::globals("sql_config")["sql_database"] . "';", []);
foreach ($result as $v) {
@ -40,10 +43,19 @@ class DB
return Table::getTableInstance($table_name);
}
/**
* @param $line
* @throws DbException
*/
public static function statement($line) {
self::rawQuery($line, []);
}
/**
* @param $line
* @return bool
* @throws DbException
*/
public static function unprepared($line) {
if (ZMBuf::get("sql_log") === true) {
$starttime = microtime(true);
@ -64,11 +76,17 @@ class DB
"] " . $line . " (Error:" . $e->getMessage() . ")\n";
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
}
Console::error($e->getMessage());
return false;
Console::warning($e->getMessage());
throw $e;
}
}
/**
* @param string $line
* @param array $params
* @return mixed
* @throws DbException
*/
public static function rawQuery(string $line, $params = []) {
if (ZMBuf::get("sql_log") === true) {
$starttime = microtime(true);
@ -113,8 +131,8 @@ class DB
"] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . " (Error:" . $e->getMessage() . ")\n";
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
}
Console::error($e->getMessage());
return false;
Console::warning($e->getMessage());
throw $e;
}
}

View File

@ -4,6 +4,8 @@
namespace ZM\DB;
use ZM\Exception\DbException;
class DeleteBody
{
use WhereBody;
@ -21,8 +23,12 @@ class DeleteBody
$this->table = $table;
}
/**
* @return mixed
* @throws DbException
*/
public function save() {
list($sql, $param) = $this->getWhereSQL();
return DB::rawQuery("DELETE FROM " . $this->table->getTableName() . " WHERE " . $sql, $param);
}
}
}

View File

@ -4,6 +4,8 @@
namespace ZM\DB;
use ZM\Exception\DbException;
class InsertBody
{
/**
@ -22,6 +24,9 @@ class InsertBody
$this->row = $row;
}
/**
* @throws DbException
*/
public function save() {
DB::rawQuery('INSERT INTO ' . $this->table->getTableName() . ' VALUES ('.implode(',', array_fill(0, count($this->row), '?')).')', $this->row);
}

View File

@ -5,6 +5,7 @@ namespace ZM\DB;
use Framework\Console;
use ZM\Exception\DbException;
class SelectBody
{
@ -23,8 +24,16 @@ class SelectBody
$this->select_thing = $select_thing;
}
/**
* @return null
* @throws DbException
*/
public function get() { return $this->fetchAll(); }
/**
* @return null
* @throws DbException
*/
public function fetchAll() {
if ($this->table->isCacheEnabled()) {
$rr = md5(implode(",", $this->select_thing) . serialize($this->where_thing));
@ -40,10 +49,19 @@ class SelectBody
return $this->getResult();
}
/**
* @return mixed|null
* @throws DbException
*/
public function fetchFirst() {
return $this->fetchAll()[0] ?? null;
}
/**
* @param null $key
* @return mixed|null
* @throws DbException
*/
public function value($key = null) {
$r = $this->fetchFirst();
if ($r === null) return null;
@ -52,6 +70,9 @@ class SelectBody
else return $r[$key] ?? null;
}
/**
* @throws DbException
*/
public function execute() {
$str = $this->queryPrepare();
$this->result = DB::rawQuery($str[0], $str[1]);

View File

@ -11,6 +11,7 @@ use Framework\Console;
use Framework\ZMBuf;
use ZM\Event\Swoole\{MessageEvent, RequestEvent, WorkerStartEvent, WSCloseEvent, WSOpenEvent};
use ZM\Http\Response;
use ZM\Utils\DataProvider;
use ZM\Utils\ZMUtil;
class EventHandler
@ -21,15 +22,22 @@ class EventHandler
switch ($event_name) {
case "workerstart":
try {
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();
});
(new WorkerStartEvent($param0, $param1))->onActivate()->onAfter();
Console::log("\n=== Worker #" . $param0->worker_id . " 已启动 ===\n", "gold");
} catch (Exception $e) {
Console::error("Worker加载出错停止服务");
Console::error($e->getMessage() . "\n" . $e->getTraceAsString());
ZMUtil::stop();
return;
} catch(Error $e) {
} catch (Error $e) {
var_export($e);
ZMUtil::stop();
}
@ -43,8 +51,8 @@ class EventHandler
} catch (Exception $e) {
/** @var Response $param1 */
$param1->status(500);
Console::info($param0->server["remote_addr"].":".$param0->server["remote_port"].
" [".$param1->getStatusCode()."] ".$param0->server["request_uri"]
Console::info($param0->server["remote_addr"] . ":" . $param0->server["remote_port"] .
" [" . $param1->getStatusCode() . "] " . $param0->server["request_uri"]
);
if (!$param1->isEnd()) $param1->end("Internal server error: " . $e->getMessage());
Console::error("Internal server error (500), caused by uncaught exception.");

View File

@ -60,7 +60,7 @@ class MessageEvent implements SwooleEvent
}
}
} catch (Exception $e) {
Console::error("出现错误: " . $e->getMessage());
Console::warning("Websocket message event exception: " . $e->getMessage());
}
return $this;
}

View File

@ -9,6 +9,7 @@ use Doctrine\Common\Annotations\AnnotationException;
use Exception;
use ReflectionException;
use Swoole\Coroutine;
use Swoole\Process;
use Swoole\Timer;
use ZM\Annotation\AnnotationBase;
use ZM\Annotation\AnnotationParser;
@ -21,10 +22,12 @@ use Framework\Console;
use Framework\GlobalConfig;
use Framework\ZMBuf;
use Swoole\Server;
use ZM\Exception\DbException;
use ZM\ModBase;
use ZM\ModHandleType;
use ZM\Utils\DataProvider;
use ZM\Utils\SQLPool;
use ZM\Utils\ZMUtil;
class WorkerStartEvent implements SwooleEvent
{
@ -43,9 +46,14 @@ class WorkerStartEvent implements SwooleEvent
* @return WorkerStartEvent
* @throws AnnotationException
* @throws ReflectionException
* @throws DbException
*/
public function onActivate(): WorkerStartEvent {
Console::info("Worker启动中");
Process::signal(SIGINT, function (){
Console::warning("Server interrupted by keyboard.");
ZMUtil::stop(true);
});
ZMBuf::resetCache(); //清空变量缓存
ZMBuf::set("wait_start", []); //添加队列在workerStart运行完成前先让其他协程等待执行
$this->resetConnections();//释放所有与framework的连接

View File

@ -0,0 +1,20 @@
<?php
namespace ZM\Exception;
use Exception;
use Throwable;
/**
* Class RobotNotFoundException
* @package ZM\Exception
* @since 1.2
*/
class RobotNotFoundException extends Exception
{
public function __construct($message = "", $code = 0, Throwable $previous = null) {
parent::__construct($message, $code, $previous);
}
}

View File

@ -12,43 +12,53 @@ class DataProvider
{
public static $buffer_list = [];
public static function getResourceFolder() {
public static function getResourceFolder()
{
return WORKING_DIR . '/resources/';
}
public static function getDataConfig(){
public static function getDataConfig()
{
return CONFIG_DIR;
}
public static function addSaveBuffer($buf_name, $sub_folder = null) {
public static function addSaveBuffer($buf_name, $sub_folder = null)
{
$name = ($sub_folder ?? "") . "/" . $buf_name . ".json";
self::$buffer_list[$buf_name] = $name;
ZMBuf::set($buf_name, self::getJsonData($name));
}
public static function saveBuffer() {
$head = Console::setColor(date("[H:i:s ") . "INFO] Saving buffer......", "lightblue");
echo $head;
foreach(self::$buffer_list as $k => $v) {
public static function saveBuffer()
{
$head = Console::setColor(date("[H:i:s] ") . "[V] Saving buffer......", "blue");
if (ZMBuf::$atomics["info_level"]->get() >= 3)
echo $head;
foreach (self::$buffer_list as $k => $v) {
self::setJsonData($v, ZMBuf::get($k));
}
echo Console::setColor("saved", "lightblue").PHP_EOL;
if (ZMBuf::$atomics["info_level"]->get() >= 3)
echo Console::setColor("saved", "blue") . PHP_EOL;
}
public static function getFrameworkLink(){
public static function getFrameworkLink()
{
return ZMBuf::globals("http_reverse_link");
}
public static function getJsonData(string $string) {
if(!file_exists(self::getDataConfig().$string)) return [];
return json_decode(Co::readFile(self::getDataConfig().$string), true);
public static function getJsonData(string $string)
{
if (!file_exists(self::getDataConfig() . $string)) return [];
return json_decode(Co::readFile(self::getDataConfig() . $string), true);
}
private static function setJsonData($filename, array $args) {
private static function setJsonData($filename, array $args)
{
Co::writeFile(self::getDataConfig() . $filename, json_encode($args, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING));
}
public static function getDataFolder() {
public static function getDataFolder()
{
return ZM_DATA;
}
}

412
src/ZM/Utils/ZMRobot.php Normal file
View File

@ -0,0 +1,412 @@
<?php
namespace ZM\Utils;
use ZM\API\CQAPI;
use ZM\Connection\ConnectionManager;
use ZM\Connection\CQConnection;
use ZM\Exception\RobotNotFoundException;
/**
* Class ZMRobot
* @package ZM\Utils
* @since 1.2
*/
class ZMRobot
{
const API_ASYNC = 1;
const API_NORMAL = 0;
const API_RATE_LIMITED = 2;
private $connection;
private $callback = null;
private $prefix = 0;
/**
* @param $robot_id
* @return ZMRobot
* @throws RobotNotFoundException
*/
public static function get($robot_id) {
$r = ConnectionManager::getByType("qq", ["self_id" => $robot_id]);
if ($r == []) throw new RobotNotFoundException("机器人 " . $robot_id . " 未连接到框架!");
return new ZMRobot($r[0]);
}
/**
* @throws RobotNotFoundException
* @return ZMRobot
*/
public static function getRandom() {
$r = ConnectionManager::getByType("qq");
if($r == []) throw new RobotNotFoundException("没有任何机器人连接到框架!");
return new ZMRobot($r[array_rand($r)]);
}
public function __construct(CQConnection $connection) {
$this->connection = $connection;
}
public function setCallback($callback = true) {
$this->callback = $callback;
return $this;
}
public function setPrefix($prefix = self::API_NORMAL) {
$this->prefix = $prefix;
return $this;
}
public function sendPrivateMsg($user_id, $message, $auto_escape = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'user_id' => $user_id,
'message' => $message,
'auto_escape' => $auto_escape
]
], $this->callback);
}
public function sendGroupMsg($group_id, $message, $auto_escape = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'message' => $message,
'auto_escape' => $auto_escape
]
], $this->callback);
}
public function sendDiscussMsg($discuss_id, $message, $auto_escape = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'discuss_id' => $discuss_id,
'message' => $message,
'auto_escape' => $auto_escape
]
], $this->callback);
}
public function sendMsg($message_type, $target_id, $message, $auto_escape = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'message_type' => $message_type,
($message_type == 'private' ? 'user' : $message_type) . '_id' => $target_id,
'message' => $message,
'auto_escape' => $auto_escape
]
], $this->callback);
}
public function deleteMsg($message_id) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'message_id' => $message_id
]
], $this->callback);
}
public function sendLike($user_id, $times = 1) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'user_id' => $user_id,
'times' => $times
]
], $this->callback);
}
public function setGroupKick($group_id, $user_id, $reject_add_request = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'user_id' => $user_id,
'reject_add_request' => $reject_add_request
]
], $this->callback);
}
public function setGroupBan($group_id, $user_id, $duration = 1800) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'user_id' => $user_id,
'duration' => $duration
]
], $this->callback);
}
public function setGroupAnonymousBan($group_id, $anonymous_or_flag, $duration = 1800) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
(is_string($anonymous_or_flag) ? 'flag' : 'anonymous') => $anonymous_or_flag,
'duration' => $duration
]
], $this->callback);
}
public function setGroupWholeBan($group_id, $enable = true) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'enable' => $enable
]
], $this->callback);
}
public function setGroupAdmin($group_id, $user_id, $enable = true) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'user_id' => $user_id,
'enable' => $enable
]
], $this->callback);
}
public function setGroupAnonymous($group_id, $enable = true) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'enable' => $enable
]
], $this->callback);
}
public function setGroupCard($group_id, $user_id, $card = "") {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'user_id' => $user_id,
'card' => $card
]
], $this->callback);
}
public function setGroupLeave($group_id, $is_dismiss = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'is_dismiss' => $is_dismiss
]
], $this->callback);
}
public function setGroupSpecialTitle($group_id, $user_id, $special_title = "", $duration = -1) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'user_id' => $user_id,
'special_title' => $special_title,
'duration' => $duration
]
], $this->callback);
}
public function setDiscussLeave($discuss_id) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'discuss_id' => $discuss_id
]
], $this->callback);
}
public function setFriendAddRequest($flag, $approve = true, $remark = "") {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'flag' => $flag,
'approve' => $approve,
'remark' => $remark
]
], $this->callback);
}
public function setGroupAddRequest($flag, $sub_type, $approve = true, $reason = "") {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'flag' => $flag,
'sub_type' => $sub_type,
'approve' => $approve,
'reason' => $reason
]
], $this->callback);
}
public function getLoginInfo() {
return CQAPI::processAPI($this->connection, ['action' => $this->getActionName(__FUNCTION__)], $this->callback);
}
public function getStrangerInfo($user_id, $no_cache = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'user_id' => $user_id,
'no_cache' => $no_cache
]
], $this->callback);
}
public function getFriendList() {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getGroupList() {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getGroupInfo($group_id, $no_cache = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'no_cache' => $no_cache
]
], $this->callback);
}
public function getGroupMemberInfo($group_id, $user_id, $no_cache = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'user_id' => $user_id,
'no_cache' => $no_cache
]
], $this->callback);
}
public function getGroupMemberList($group_id) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id
]
]);
}
public function getCookie($domain = "") {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'domain' => $domain
]
]);
}
public function getCsrfToken() {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getCredentials($domain = "") {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'domain' => $domain
]
], $this->callback);
}
public function getRecord($file, $out_format, $full_path = false) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'file' => $file,
'out_format' => $out_format,
'full_path' => $full_path
]
], $this->callback);
}
public function getImage($file) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'file' => $file
]
], $this->callback);
}
public function canSendImage() {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function canSendRecord() {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getStatus() {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getVersionInfo() {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function setRestartPlugin($delay = 0) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'delay' => $delay
]
], $this->callback);
}
public function cleanDataDir($data_dir) {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__),
'params' => [
'data_dir' => $data_dir
]
], $this->callback);
}
public function cleanPluginLog() {
return CQAPI::processAPI($this->connection, [
'action' => $this->getActionName(__FUNCTION__)
], $this->callback);
}
public function getExperimentAPI() {
return new ZMRobotExperiment($this->connection, $this->callback, $this->prefix);
}
private function getActionName(string $method) {
$prefix = ($this->prefix == self::API_ASYNC ? '_async' : ($this->prefix == self::API_RATE_LIMITED ? '_rate_limited' : ''));
$func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
return $prefix . $func_name;
}
}

View File

@ -0,0 +1,104 @@
<?php
namespace ZM\Utils;
use ZM\API\CQAPI;
use ZM\Connection\CQConnection;
/**
* Class ZMRobotExperiment
* @package ZM\Utils
* @since 1.2
*/
class ZMRobotExperiment
{
/**
* @var CQConnection
*/
private $connection;
private $callback = null;
private $prefix = 0;
public function __construct(CQConnection $connection, $callback, $prefix) {
$this->connection = $connection;
$this->callback = $callback;
$this->prefix = $prefix;
}
public function setCallback($callback = true) {
$this->callback = $callback;
return $this;
}
public function setPrefix($prefix = ZMRobot::API_NORMAL) {
$this->prefix = $prefix;
return $this;
}
public function getFriendList($flat = false) {
return CQAPI::processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'flat' => $flat
]
], $this->callback);
}
public function getGroupInfo($group_id) {
return CQAPI::processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id
]
], $this->callback);
}
public function getVipInfo($user_id) {
return CQAPI::processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'user_id' => $user_id
]
], $this->callback);
}
public function getGroupNotice($group_id) {
return CQAPI::processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id
]
], $this->callback);
}
public function sendGroupNotice($group_id, $title, $content) {
return CQAPI::processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'group_id' => $group_id,
'title' => $title,
'content' => $content
]
], $this->callback);
}
public function setRestart($clean_log = false, $clean_cache = false, $clean_event = false) {
return CQAPI::processAPI($this->connection, [
'action' => '_' . $this->getActionName(__FUNCTION__),
'params' => [
'clean_log' => $clean_log,
'clean_cache' => $clean_cache,
'clean_event' => $clean_event
]
], $this->callback);
}
private function getActionName(string $method) {
$prefix = ($this->prefix == ZMRobot::API_ASYNC ? '_async' : ($this->prefix == ZMRobot::API_RATE_LIMITED ? '_rate_limited' : ''));
$func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method));
return $prefix . $func_name;
}
}

View File

@ -14,25 +14,26 @@ class ZMUtil
* 检查workerStart是否运行结束
*/
public static function checkWait() {
if(ZMBuf::isset("wait_start")) {
if (ZMBuf::isset("wait_start")) {
ZMBuf::append("wait_start", Co::getCid());
Co::suspend();
}
}
public static function stop() {
public static function stop($without_shutdown = false) {
Console::info(Console::setColor("Stopping server...", "red"));
foreach (ZMBuf::$server->connections as $v) {
ZMBuf::$server->close($v);
}
DataProvider::saveBuffer();
ZMBuf::$server->shutdown();
if (!$without_shutdown)
ZMBuf::$server->shutdown();
ZMBuf::$server->stop();
}
public static function getHttpCodePage(int $http_code) {
if(isset(ZMBuf::globals("http_default_code_page")[$http_code])) {
return Co::readFile(DataProvider::getResourceFolder()."html/".ZMBuf::globals("http_default_code_page")[$http_code]);
if (isset(ZMBuf::globals("http_default_code_page")[$http_code])) {
return Co::readFile(DataProvider::getResourceFolder() . "html/" . ZMBuf::globals("http_default_code_page")[$http_code]);
} else return null;
}
@ -68,4 +69,13 @@ class ZMUtil
}
return ["type" => $type, "params" => $array, "start" => $start, "end" => $end];
}
}
public static function getModInstance($class) {
if (!isset(ZMBuf::$instance[$class])) {
ZMBuf::$instance[$class] = new $class();
return ZMBuf::$instance[$class];
} else {
return ZMBuf::$instance[$class];
}
}
}