mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-07-21 07:35:34 +08:00
initial commit
This commit is contained in:
19
src/Custom/Annotation/Example.php
Normal file
19
src/Custom/Annotation/Example.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Custom\Annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\Interfaces\CustomAnnotation;
|
||||
|
||||
/**
|
||||
* Class CustomAnnotation
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @package Custom\Annotation
|
||||
*/
|
||||
class Example implements CustomAnnotation
|
||||
{
|
||||
/** @var string */
|
||||
public $str;
|
||||
}
|
||||
14
src/Custom/Connection/CustomConnection.php
Normal file
14
src/Custom/Connection/CustomConnection.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Custom\Connection;
|
||||
|
||||
|
||||
use ZM\Connection\WSConnection;
|
||||
|
||||
class CustomConnection extends WSConnection
|
||||
{
|
||||
public function getType() {
|
||||
return "custom";
|
||||
}
|
||||
}
|
||||
212
src/Framework/Console.php
Executable file
212
src/Framework/Console.php
Executable file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/2/10
|
||||
* Time: 下午6:13
|
||||
*/
|
||||
|
||||
namespace Framework;
|
||||
|
||||
use co;
|
||||
use ZM\Utils\ZMUtil;
|
||||
use Exception;
|
||||
|
||||
class Console
|
||||
{
|
||||
static function setColor($string, $color = "") {
|
||||
switch ($color) {
|
||||
case "red":
|
||||
return "\x1b[38;5;203m" . $string . "\x1b[m";
|
||||
case "green":
|
||||
return "\x1b[38;5;83m" . $string . "\x1b[m";
|
||||
case "yellow":
|
||||
return "\x1b[38;5;227m" . $string . "\x1b[m";
|
||||
case "blue":
|
||||
return "\033[34m" . $string . "\033[0m";
|
||||
case "lightpurple":
|
||||
return "\x1b[38;5;207m" . $string . "\x1b[m";
|
||||
case "lightblue":
|
||||
return "\x1b[38;5;87m" . $string . "\x1b[m";
|
||||
case "gold":
|
||||
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:
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
static function error($obj, $head = null) {
|
||||
if ($head === null) $head = date("[H:i:s ") . "ERROR] ";
|
||||
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}";
|
||||
}
|
||||
echo(self::setColor($head . ($trace ?? "") . $obj, "red") . "\n");
|
||||
}
|
||||
|
||||
static function warning($obj, $head = null) {
|
||||
if ($head === null) $head = date("[H:i:s") . " WARN] ";
|
||||
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}";
|
||||
}
|
||||
echo(self::setColor($head . ($trace ?? "") . $obj, "yellow") . "\n");
|
||||
}
|
||||
|
||||
static function info($obj, $head = null) {
|
||||
if ($head === null) $head = date("[H:i:s ") . "INFO] ";
|
||||
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}";
|
||||
}
|
||||
echo(self::setColor($head . ($trace ?? "") . $obj, "lightblue") . "\n");
|
||||
}
|
||||
|
||||
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(){
|
||||
$log = "Stack trace:\n";
|
||||
$trace = debug_backtrace();
|
||||
//array_shift($trace);
|
||||
foreach ($trace as $i => $t) {
|
||||
if (!isset($t['file'])) {
|
||||
$t['file'] = 'unknown';
|
||||
}
|
||||
if (!isset($t['line'])) {
|
||||
$t['line'] = 0;
|
||||
}
|
||||
if (!isset($t['function'])) {
|
||||
$t['function'] = 'unknown';
|
||||
}
|
||||
$log .= "#$i {$t['file']}({$t['line']}): ";
|
||||
if (isset($t['object']) and is_object($t['object'])) {
|
||||
$log .= get_class($t['object']) . '->';
|
||||
}
|
||||
$log .= "{$t['function']}()\n";
|
||||
}
|
||||
$log = Console::setColor($log, "gray");
|
||||
echo $log;
|
||||
}
|
||||
|
||||
static function listenConsole(){
|
||||
go(function () {
|
||||
while (true) {
|
||||
$cmd = trim(co::fread(STDIN));
|
||||
if (self::executeCommand($cmd) === false) break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cmd
|
||||
* @return bool
|
||||
*/
|
||||
private static function executeCommand(string $cmd) {
|
||||
$it = explodeMsg($cmd);
|
||||
switch ($it[0] ?? '') {
|
||||
case 'call':
|
||||
$class_name = $it[1];
|
||||
$function_name = $it[2];
|
||||
$class = new $class_name([]);
|
||||
call_user_func_array([$class, $function_name],[]);
|
||||
return true;
|
||||
case 'bc':
|
||||
$code = base64_decode($it[1] ?? '', true);
|
||||
try {
|
||||
eval($code);
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
return true;
|
||||
case 'echo':
|
||||
Console::info($it[1]);
|
||||
return true;
|
||||
case 'stop':
|
||||
ZMUtil::stop();
|
||||
return false;
|
||||
case 'reload':
|
||||
case 'r':
|
||||
ZMUtil::reload();
|
||||
return false;
|
||||
case '':
|
||||
return true;
|
||||
default:
|
||||
Console::info("Command not found: " . $it[0]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static function withSleep(string $string, int $int) {
|
||||
self::info($string);
|
||||
sleep($int);
|
||||
}
|
||||
}
|
||||
106
src/Framework/FrameworkLoader.php
Normal file
106
src/Framework/FrameworkLoader.php
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Framework;
|
||||
|
||||
use ZM\Event\EventHandler;
|
||||
use Exception;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\Http\Response;
|
||||
|
||||
/**
|
||||
* Class FrameworkLoader
|
||||
* Everything is beginning from here
|
||||
* @package Framework
|
||||
*/
|
||||
class FrameworkLoader
|
||||
{
|
||||
/** @var GlobalConfig */
|
||||
public static $settings;
|
||||
|
||||
/** @var FrameworkLoader|null */
|
||||
public static $instance = null;
|
||||
|
||||
/** @var float|string */
|
||||
public static $run_time;
|
||||
|
||||
/** @var Server */
|
||||
private $server;
|
||||
|
||||
public function __construct($args = []) {
|
||||
if (self::$instance !== null) die("Cannot run two FrameworkLoader in one process!");
|
||||
self::$instance = $this;
|
||||
|
||||
chdir(__DIR__ . '/../..');
|
||||
define('WORKING_DIR', getcwd());
|
||||
$this->requireGlobalFunctions();
|
||||
$this->registerAutoloader('classLoader');
|
||||
self::$settings = new GlobalConfig();
|
||||
ZMBuf::$globals = self::$settings;
|
||||
if (!self::$settings->success) die("Failed to load global config. Please check config/global.php file");
|
||||
$this->defineProperties();
|
||||
|
||||
//start swoole Framework
|
||||
$this->selfCheck();
|
||||
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"));
|
||||
$this->server->on("WorkerStart", [$this, "onWorkerStart"]);
|
||||
$this->server->on("message", function ($server, $frame) { EventHandler::callSwooleEvent("message", $server, $frame); });
|
||||
$this->server->on("request", function ($request, $response) {
|
||||
$response = new Response($response);
|
||||
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); });
|
||||
ZMBuf::initAtomic();
|
||||
Console::info("host: ".self::$settings->get("host").", port: ".self::$settings->get("port"));
|
||||
$this->server->start();
|
||||
} catch (Exception $e) {
|
||||
Console::error("Framework初始化出现错误,请检查!");
|
||||
Console::error($e->getMessage());
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
private function requireGlobalFunctions() {
|
||||
require __DIR__ . '/global_functions.php';
|
||||
}
|
||||
|
||||
private function registerAutoloader(string $string) {
|
||||
if (!spl_autoload_register($string)) die("Failed to register autoloader named \"$string\" !");
|
||||
}
|
||||
|
||||
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"));
|
||||
define("CRASH_DIR", self::$settings->get("crash_dir"));
|
||||
@mkdir(ZM_DATA);
|
||||
@mkdir(CONFIG_DIR);
|
||||
@mkdir(CRASH_DIR);
|
||||
define("ZM_MATCH_ALL", 0);
|
||||
define("ZM_MATCH_FIRST", 1);
|
||||
define("ZM_MATCH_NUMBER", 2);
|
||||
define("ZM_MATCH_SECOND", 3);
|
||||
}
|
||||
|
||||
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");
|
||||
if (!function_exists("mb_substr")) die("Can not find mbstring extension.\n");
|
||||
if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
|
||||
//if (!function_exists("curl_exec")) die("Can not find curl extension.\n");
|
||||
//if (!class_exists("ZipArchive")) die("Can not find Zip extension.\n");
|
||||
//if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onWorkerStart(\Swoole\Server $server, $worker_id) {
|
||||
self::$instance = $this;
|
||||
self::$run_time = microtime(true);
|
||||
EventHandler::callSwooleEvent("WorkerStart", $server, $worker_id);
|
||||
}
|
||||
}
|
||||
33
src/Framework/GlobalConfig.php
Normal file
33
src/Framework/GlobalConfig.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2019-03-16
|
||||
* Time: 13:58
|
||||
*/
|
||||
|
||||
namespace Framework;
|
||||
|
||||
/**
|
||||
* 请不要diss此class的语法。可能写的很糟糕。
|
||||
* Class GlobalConfig
|
||||
*/
|
||||
class GlobalConfig
|
||||
{
|
||||
private $config = null;
|
||||
public $success = false;
|
||||
|
||||
public function __construct() {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
include_once WORKING_DIR.'/config/global.php';
|
||||
global $config;
|
||||
$this->success = true;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function get($key) {
|
||||
$r = $this->config[$key] ?? null;
|
||||
if ($r === null) return null;
|
||||
return $r;
|
||||
}
|
||||
}
|
||||
10
src/Framework/InfoLevel.php
Normal file
10
src/Framework/InfoLevel.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Framework;
|
||||
|
||||
|
||||
class InfoLevel
|
||||
{
|
||||
const INFO = 0;
|
||||
}
|
||||
18
src/Framework/Logger.php
Normal file
18
src/Framework/Logger.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
264
src/Framework/RemoteShell.php
Normal file
264
src/Framework/RemoteShell.php
Normal file
@@ -0,0 +1,264 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Framework;
|
||||
|
||||
|
||||
use Co;
|
||||
use Exception;
|
||||
use Swoole\Coroutine;
|
||||
use swoole\server;
|
||||
|
||||
class RemoteShell
|
||||
{
|
||||
const STX = "DEBUG";
|
||||
private static $contexts = array();
|
||||
static $oriPipeMessageCallback = null;
|
||||
/**
|
||||
* @var server
|
||||
*/
|
||||
static $serv;
|
||||
static $menu = array(
|
||||
"p|print [variant]\t打印一个PHP变量的值",
|
||||
"e|exec [code]\t执行一段PHP代码",
|
||||
"w|worker [id]\t切换Worker进程",
|
||||
"l|list\t打印服务器所有连接的fd",
|
||||
"s|stats\t打印服务器状态",
|
||||
"c|coros\t打印协程列表",
|
||||
"b|bt\t打印协程调用栈",
|
||||
"i|info [fd]\t显示某个连接的信息",
|
||||
"h|help\t显示帮助界面",
|
||||
"q|quit\t退出终端",
|
||||
);
|
||||
const PAGESIZE = 20;
|
||||
|
||||
/**
|
||||
* @param $serv server
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @throws Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
static function listen($serv, $host = "127.0.0.1", $port = 9599) {
|
||||
Console::warning("正在监听".$host.":".strval($port)."的调试接口,请注意安全");
|
||||
$port = $serv->listen($host, $port, SWOOLE_SOCK_TCP);
|
||||
if (!$port) {
|
||||
throw new Exception("listen fail.");
|
||||
}
|
||||
$port->set(array(
|
||||
"open_eof_split" => true,
|
||||
'package_eof' => "\r\n",
|
||||
));
|
||||
$port->on("Connect", array(__CLASS__, 'onConnect'));
|
||||
$port->on("Close", array(__CLASS__, 'onClose'));
|
||||
$port->on("Receive", array(__CLASS__, 'onReceive'));
|
||||
if (method_exists($serv, 'getCallback')) {
|
||||
self::$oriPipeMessageCallback = $serv->getCallback('PipeMessage');
|
||||
}
|
||||
$serv->on("PipeMessage", array(__CLASS__, 'onPipeMessage'));
|
||||
self::$serv = $serv;
|
||||
}
|
||||
|
||||
static function onConnect($serv, $fd, $reactor_id) {
|
||||
self::$contexts[$fd]['worker_id'] = $serv->worker_id;
|
||||
self::output($fd, implode("\r\n", self::$menu));
|
||||
}
|
||||
|
||||
static function output($fd, $msg) {
|
||||
if (!isset(self::$contexts[$fd]['worker_id'])) {
|
||||
$msg .= "\r\nworker#" . self::$serv->worker_id . "$ ";
|
||||
} else {
|
||||
$msg .= "\r\nworker#" . self::$contexts[$fd]['worker_id'] . "$ ";
|
||||
}
|
||||
self::$serv->send($fd, $msg);
|
||||
}
|
||||
|
||||
static function onClose($serv, $fd, $reactor_id) {
|
||||
unset(self::$contexts[$fd]);
|
||||
}
|
||||
|
||||
static function onPipeMessage($serv, $src_worker_id, $message) {
|
||||
//不是 debug 消息
|
||||
if (!is_string($message) or substr($message, 0, strlen(self::STX)) != self::STX) {
|
||||
if (self::$oriPipeMessageCallback == null) {
|
||||
trigger_error("require swoole-4.3.0 or later.", E_USER_WARNING);
|
||||
return true;
|
||||
}
|
||||
return call_user_func(self::$oriPipeMessageCallback, $serv, $src_worker_id, $message);
|
||||
} else {
|
||||
$request = unserialize(substr($message, strlen(self::STX)));
|
||||
self::call($request['fd'], $request['func'], $request['args']);
|
||||
}
|
||||
return true ;
|
||||
}
|
||||
|
||||
static protected function call($fd, $func, $args) {
|
||||
ob_start();
|
||||
call_user_func_array($func, $args);
|
||||
self::output($fd, ob_get_clean());
|
||||
}
|
||||
|
||||
static protected function exec($fd, $func, $args) {
|
||||
//不在当前Worker进程
|
||||
if (self::$contexts[$fd]['worker_id'] != self::$serv->worker_id) {
|
||||
self::$serv->sendMessage(self::STX . serialize(['fd' => $fd, 'func' => $func, 'args' => $args]), self::$contexts[$fd]['worker_id']);
|
||||
} else {
|
||||
self::call($fd, $func, $args);
|
||||
}
|
||||
}
|
||||
|
||||
static function getCoros() {
|
||||
var_export(iterator_to_array(Coroutine::listCoroutines()));
|
||||
}
|
||||
|
||||
static function getBackTrace($_cid) {
|
||||
$info = Co::getBackTrace($_cid);
|
||||
if (!$info) {
|
||||
echo "coroutine $_cid not found.";
|
||||
} else {
|
||||
echo get_debug_print_backtrace($info);
|
||||
}
|
||||
}
|
||||
|
||||
static function printVariant($var) {
|
||||
$var = ltrim($var, '$ ');
|
||||
var_dump($var);
|
||||
var_dump($$var);
|
||||
}
|
||||
|
||||
static function evalCode($code) {
|
||||
eval($code . ';');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $serv server
|
||||
* @param $fd
|
||||
* @param $reactor_id
|
||||
* @param $data
|
||||
*/
|
||||
static function onReceive($serv, $fd, $reactor_id, $data) {
|
||||
$args = explode(" ", $data, 2);
|
||||
$cmd = trim($args[0]);
|
||||
unset($args[0]);
|
||||
switch ($cmd) {
|
||||
case 'w':
|
||||
case 'worker':
|
||||
if (!isset($args[1])) {
|
||||
self::output($fd, "invalid command.");
|
||||
break;
|
||||
}
|
||||
$dstWorkerId = intval($args[1]);
|
||||
self::$contexts[$fd]['worker_id'] = $dstWorkerId;
|
||||
self::output($fd, "[switching to worker " . self::$contexts[$fd]['worker_id'] . "]");
|
||||
break;
|
||||
case 'e':
|
||||
case 'exec':
|
||||
if (!isset($args[1])) {
|
||||
self::output($fd, "invalid command.");
|
||||
break;
|
||||
}
|
||||
$var = trim($args[1]);
|
||||
self::exec($fd, 'self::evalCode', [$var]);
|
||||
break;
|
||||
case 'p':
|
||||
case 'print':
|
||||
$var = trim($args[1]);
|
||||
self::exec($fd, 'self::printVariant', [$var]);
|
||||
break;
|
||||
case 'h':
|
||||
case 'help':
|
||||
self::output($fd, implode("\r\n", self::$menu));
|
||||
break;
|
||||
case 's':
|
||||
case 'stats':
|
||||
$stats = $serv->stats();
|
||||
self::output($fd, var_export($stats, true));
|
||||
break;
|
||||
case 'c':
|
||||
case 'coros':
|
||||
self::exec($fd, 'self::getCoros', []);
|
||||
break;
|
||||
/**
|
||||
* 查看协程堆栈
|
||||
*/
|
||||
case 'bt':
|
||||
case 'b':
|
||||
case 'backtrace':
|
||||
if (empty($args[1])) {
|
||||
self::output($fd, "invalid command [" . trim($args[1]) . "].");
|
||||
break;
|
||||
}
|
||||
$_cid = intval($args[1]);
|
||||
self::exec($fd, 'self::getBackTrace', [$_cid]);
|
||||
break;
|
||||
case 'i':
|
||||
case 'info':
|
||||
if (empty($args[1])) {
|
||||
self::output($fd, "invalid command [" . trim($args[1]) . "].");
|
||||
break;
|
||||
}
|
||||
$_fd = intval($args[1]);
|
||||
$info = $serv->getClientInfo($_fd);
|
||||
if (!$info) {
|
||||
self::output($fd, "connection $_fd not found.");
|
||||
} else {
|
||||
self::output($fd, var_export($info, true));
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
case 'list':
|
||||
$tmp = array();
|
||||
foreach ($serv->connections as $fd) {
|
||||
$tmp[] = $fd;
|
||||
if (count($tmp) > self::PAGESIZE) {
|
||||
self::output($fd, json_encode($tmp));
|
||||
$tmp = array();
|
||||
}
|
||||
}
|
||||
if (count($tmp) > 0) {
|
||||
self::output($fd, json_encode($tmp));
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
case 'quit':
|
||||
$serv->close($fd);
|
||||
break;
|
||||
default:
|
||||
self::output($fd, "unknow command[$cmd]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function get_debug_print_backtrace($traces) {
|
||||
$ret = array();
|
||||
foreach ($traces as $i => $call) {
|
||||
$object = '';
|
||||
if (isset($call['class'])) {
|
||||
$object = $call['class'] . $call['type'];
|
||||
if (is_array($call['args'])) {
|
||||
foreach ($call['args'] as &$arg) {
|
||||
get_arg($arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
$ret[] = '#' . str_pad($i, 3, ' ')
|
||||
. $object . $call['function'] . '(' . implode(', ', $call['args'])
|
||||
. ') called at [' . $call['file'] . ':' . $call['line'] . ']';
|
||||
}
|
||||
return implode("\n", $ret);
|
||||
}
|
||||
|
||||
function get_arg(&$arg) {
|
||||
if (is_object($arg)) {
|
||||
$arr = (array)$arg;
|
||||
$args = array();
|
||||
foreach ($arr as $key => $value) {
|
||||
if (strpos($key, chr(0)) !== false) {
|
||||
$key = ''; // Private variable found
|
||||
}
|
||||
$args[] = '[' . $key . '] => ' . get_arg($value);
|
||||
}
|
||||
$arg = get_class($arg) . ' Object (' . implode(',', $args) . ')';
|
||||
}
|
||||
}
|
||||
95
src/Framework/ZMBuf.php
Executable file
95
src/Framework/ZMBuf.php
Executable file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/2/25
|
||||
* Time: 下午11:11
|
||||
*/
|
||||
|
||||
namespace Framework;
|
||||
|
||||
use Swoole\Atomic;
|
||||
use swoole_atomic;
|
||||
use ZM\Annotation\MappingNode;
|
||||
use ZM\connection\WSConnection;
|
||||
use ZM\Utils\Scheduler;
|
||||
use ZM\Utils\SQLPool;
|
||||
|
||||
class ZMBuf
|
||||
{
|
||||
//读写的缓存数据,需要在worker_num = 1下才能正常使用
|
||||
/** @var mixed[] ZMBuf的data */
|
||||
private static $cache = [];
|
||||
/** @var WSConnection[] */
|
||||
static $connect = [];//储存连接实例的数组
|
||||
//Scheduler计划任务连接实例,只可以在单worker_num时使用
|
||||
/** @var Scheduler|null */
|
||||
static $scheduler = null;
|
||||
|
||||
//Swoole SQL连接池,多进程下每个进程一个连接池
|
||||
/** @var SQLPool */
|
||||
static $sql_pool = null;//保存sql连接池的类
|
||||
|
||||
//只读的数据,可以在多worker_num下使用
|
||||
/** @var null|\Framework\GlobalConfig */
|
||||
static $globals = null;
|
||||
|
||||
// swoole server操作对象,每个进程均分配
|
||||
/** @var swoole_websocket_server $server */
|
||||
static $server;
|
||||
/** @var MappingNode Http请求uri路径根节点 */
|
||||
public static $req_mapping_node;
|
||||
/** @var mixed TimeNLP初始化后的对象,每个进程均可初始化 */
|
||||
public static $time_nlp;
|
||||
/** @var string[] $custom_connection_class */
|
||||
public static $custom_connection_class = [];//保存自定义的ws connection连接类型的
|
||||
|
||||
// Atomic:可跨进程读写的原子计数,任何地方均可使用
|
||||
/** @var null|swoole_atomic */
|
||||
static $info_level = null;//保存log等级的原子计数
|
||||
/** @var swoole_atomic $reload_time */
|
||||
|
||||
public static $events = [];
|
||||
/** @var Atomic[] */
|
||||
public static $atomics;
|
||||
|
||||
static function get($name, $default = null) { return self::$cache[$name] ?? $default; }
|
||||
|
||||
static function set($name, $value) { self::$cache[$name] = $value; }
|
||||
|
||||
static function append($name, $value) { self::$cache[$name][] = $value; }
|
||||
|
||||
static function appendKey($name, $key, $value) { self::$cache[$name][$key] = $value; }
|
||||
|
||||
static function appendKeyInKey($name, $key, $value) { self::$cache[$name][$key][] = $value; }
|
||||
|
||||
static function unsetCache($name) { unset(self::$cache[$name]); }
|
||||
|
||||
static function unsetByValue($name, $vale) {
|
||||
$key = array_search($vale, self::$cache[$name]);
|
||||
array_splice(self::$cache[$name], $key, 1);
|
||||
}
|
||||
|
||||
static function isset($name) { return isset(self::$cache[$name]); }
|
||||
|
||||
static function array_key_exists($name, $key) { return isset(self::$cache[$name][$key]); }
|
||||
|
||||
static function in_array($name, $val) { return in_array($val, self::$cache[$name]); }
|
||||
|
||||
static function globals($key) { return self::$globals->get($key); }
|
||||
|
||||
public static function resetCache() {
|
||||
self::$cache = [];
|
||||
self::$connect = [];
|
||||
self::$time_nlp = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化atomic计数器
|
||||
*/
|
||||
public static function initAtomic() {
|
||||
foreach(ZMBuf::globals("init_atomics") as $k => $v) {
|
||||
self::$atomics[$k] = new Atomic($v);
|
||||
}
|
||||
}
|
||||
}
|
||||
146
src/Framework/global_functions.php
Normal file
146
src/Framework/global_functions.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
function classLoader($p) {
|
||||
$filepath = getClassPath($p);
|
||||
if ($filepath === null)
|
||||
echo "F:Warning: get class path wrongs.$p\n";
|
||||
//else echo "F:DBG: Found " . $p . "\n";
|
||||
try {
|
||||
require_once $filepath;
|
||||
} catch (Exception $e) {
|
||||
echo "Error when finding class: ".$p.PHP_EOL;
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
function getClassPath($class_name) {
|
||||
$dir = str_replace("\\", "/", $class_name);
|
||||
$dir = WORKING_DIR . "/src/" . $dir . ".php";
|
||||
//echo "@@@".$dir.PHP_EOL;
|
||||
$dir = str_replace("\\", "/", $dir);
|
||||
if (file_exists($dir)) return $dir;
|
||||
else return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己定义的万(san)能分割函数
|
||||
* @param $msg
|
||||
* @param bool $ban_comma
|
||||
* @return array
|
||||
*/
|
||||
function explodeMsg($msg, $ban_comma = false) {
|
||||
$msg = str_replace(" ", "\n", trim($msg));
|
||||
if (!$ban_comma) {
|
||||
//$msg = str_replace(",", "\n", $msg);
|
||||
$msg = str_replace("\t", "\n", $msg);
|
||||
}
|
||||
$msgs = explode("\n", $msg);
|
||||
$ls = [];
|
||||
foreach ($msgs as $k => $v) {
|
||||
if (trim($v) == "") continue;
|
||||
$ls[] = trim($v);
|
||||
}
|
||||
return $ls;
|
||||
}
|
||||
|
||||
function unicode_decode($str) {
|
||||
return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', function ($matches) {
|
||||
return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE");
|
||||
}, $str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块文件夹下的每个类文件的类名称
|
||||
* @param $dir
|
||||
* @param string $indoor_name
|
||||
* @return array
|
||||
*/
|
||||
function getAllClasses($dir, $indoor_name) {
|
||||
$list = scandir($dir);
|
||||
$classes = [];
|
||||
unset($list[0], $list[1]);
|
||||
foreach ($list as $v) {
|
||||
//echo "Finding " . $dir . $v . PHP_EOL;
|
||||
//echo "At " . $indoor_name . PHP_EOL;
|
||||
if (is_dir($dir . $v)) $classes = array_merge($classes, getAllClasses($dir . $v . "/", $indoor_name . "\\" . $v));
|
||||
elseif (mb_substr($v, -4) == ".php") {
|
||||
$class_name = $indoor_name . "\\" . mb_substr($v, 0, -4);
|
||||
$classes [] = $class_name;
|
||||
}
|
||||
}
|
||||
return $classes;
|
||||
}
|
||||
|
||||
function matchPattern($pattern, $context) {
|
||||
if (mb_substr($pattern, 0, 1) == "" && mb_substr($context, 0, 1) == "")
|
||||
return true;
|
||||
if ('*' == mb_substr($pattern, 0, 1) && "" != mb_substr($pattern, 1, 1) && "" == mb_substr($context, 0, 1))
|
||||
return false;
|
||||
if (mb_substr($pattern, 0, 1) == mb_substr($context, 0, 1))
|
||||
return matchPattern(mb_substr($pattern, 1), mb_substr($context, 1));
|
||||
if (mb_substr($pattern, 0, 1) == "*")
|
||||
return matchPattern(mb_substr($pattern, 1), $context) || matchPattern($pattern, mb_substr($context, 1));
|
||||
return false;
|
||||
}
|
||||
|
||||
function split_explode($del, $str, $divide_en = false) {
|
||||
$str = explode($del, $str);
|
||||
for ($i = 0; $i < mb_strlen($str[0]); $i++) {
|
||||
if (
|
||||
is_numeric(mb_substr($str[0], $i, 1)) &&
|
||||
(
|
||||
!is_numeric(mb_substr($str[0], $i - 1, 1)) &&
|
||||
mb_substr($str[0], $i - 1, 1) != ' ' &&
|
||||
ctype_alpha(mb_substr($str[0], $i - 1, 1)) === false
|
||||
)
|
||||
) {
|
||||
$str[0] = mb_substr($str[0], 0, $i) . " " . mb_substr($str[0], $i);
|
||||
} elseif (
|
||||
$divide_en &&
|
||||
ctype_alnum(mb_substr($str[0], $i, 1)) &&
|
||||
!ctype_alnum(mb_substr($str[0], $i - 1, 1)) &&
|
||||
mb_substr($str[0], $i - 1, 1) != ' '
|
||||
) {
|
||||
$str[0] = mb_substr($str[0], 0, $i) . ' ' . mb_substr($str[0], $i);
|
||||
}
|
||||
}
|
||||
$str = implode($del, $str);
|
||||
//echo $str."\n";
|
||||
$ls = [];
|
||||
foreach (explode($del, $str) as $k => $v) {
|
||||
if (trim($v) == "") continue;
|
||||
$ls[] = $v;
|
||||
}
|
||||
//var_dump($ls);
|
||||
return $ls == [] ? [""] : $ls;
|
||||
}
|
||||
|
||||
function matchArgs($pattern, $context) {
|
||||
$result = [];
|
||||
if (matchPattern($pattern, $context)) {
|
||||
if (mb_strpos($pattern, "*") === false) return [];
|
||||
$exp = explode("*", $pattern);
|
||||
$i = 0;
|
||||
foreach ($exp as $k => $v) {
|
||||
//echo "[MATCH$k] " . $v . PHP_EOL;
|
||||
if ($v == "" && $k == 0) continue;
|
||||
elseif ($v == "" && $k == count($exp) - 1) {
|
||||
$context = $context . "^EOL";
|
||||
$v = "^EOL";
|
||||
}
|
||||
$cur_var = "";
|
||||
//echo mb_substr($context, $i) . "|" . $v . PHP_EOL;
|
||||
$ori = $i;
|
||||
while (($a = mb_substr($context, $i, mb_strlen($v))) != $v && $a != "") {
|
||||
$cur_var .= mb_substr($context, $i, 1);
|
||||
++$i;
|
||||
}
|
||||
if ($i != $ori || $k == 1 || $k == count($exp) - 1) {
|
||||
//echo $cur_var . PHP_EOL;
|
||||
$result[] = $cur_var;
|
||||
}
|
||||
$i += mb_strlen($v);
|
||||
}
|
||||
return $result;
|
||||
} else return false;
|
||||
}
|
||||
34
src/Module/Example/Hello.php
Normal file
34
src/Module/Example/Hello.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Module\Example;
|
||||
|
||||
|
||||
use Framework\Console;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\Module\Closed;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\ModBase;
|
||||
|
||||
/**
|
||||
* Class Hello
|
||||
* @package Module\Example
|
||||
* @Closed()
|
||||
*/
|
||||
class Hello extends ModBase
|
||||
{
|
||||
/**
|
||||
* @SwooleEventAt("open",rule="connectType:qq")
|
||||
* @param $conn
|
||||
*/
|
||||
public function onConnect(CQConnection $conn){
|
||||
Console::info("机器人 ".$conn->getQQ()." 已连接!");
|
||||
}
|
||||
/**
|
||||
* @CQCommand("你好")
|
||||
*/
|
||||
public function hello(){
|
||||
return "你好啊,我是由炸毛框架构建的机器人!";
|
||||
}
|
||||
}
|
||||
45
src/Module/TestMod/CQTest.php
Normal file
45
src/Module/TestMod/CQTest.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Module\TestMod;
|
||||
|
||||
|
||||
use Framework\Console;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\CQ\CQNotice;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\ModBase;
|
||||
|
||||
/**
|
||||
* Class CQTest
|
||||
* @package Module\TestMod
|
||||
*/
|
||||
class CQTest extends ModBase
|
||||
{
|
||||
/**
|
||||
* @SwooleEventAt(type="open",rule="connectType:qq")
|
||||
* @param CQConnection $conn
|
||||
*/
|
||||
public function onRobotConnect($conn){
|
||||
Console::info("QQ robot: ".$conn->getQQ()." connected.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @CQCommand("多命令a")
|
||||
* @CQCommand(regexMatch="*是什么")
|
||||
* @param $arg
|
||||
* @return string
|
||||
* @throws \ZM\Exception\DbException
|
||||
*/
|
||||
public function hello($arg) {
|
||||
return "我也不知道".$arg[0]."是什么呀";
|
||||
}
|
||||
|
||||
/**
|
||||
* @CQNotice(notice_type="group_admin")
|
||||
*/
|
||||
public function onNotice(){
|
||||
|
||||
}
|
||||
}
|
||||
28
src/Module/TestMod/Hola.php
Normal file
28
src/Module/TestMod/Hola.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Module\TestMod;
|
||||
|
||||
use Framework\Console;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\ModBase;
|
||||
|
||||
/**
|
||||
* Class Hola
|
||||
* @package Module\TestMod
|
||||
* @noinspection PhpUnused
|
||||
*/
|
||||
class Hola extends ModBase
|
||||
{
|
||||
|
||||
/**
|
||||
* @SwooleEventAt(type="open",rule="connectType:unknown")
|
||||
* @param WSConnection $conn
|
||||
*/
|
||||
public function onUnknownConnect() {
|
||||
Console::warning("Unknown websocket has been shutdown.");
|
||||
Console::stackTrace();
|
||||
$this->connection->close();
|
||||
}
|
||||
}
|
||||
29
src/Scheduler/MessageEvent.php
Normal file
29
src/Scheduler/MessageEvent.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Scheduler;
|
||||
|
||||
|
||||
use Swoole\Coroutine\Http\Client;
|
||||
use Swoole\WebSocket\Frame;
|
||||
|
||||
class MessageEvent
|
||||
{
|
||||
/**
|
||||
* @var Frame
|
||||
*/
|
||||
private $frame;
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
public function __construct(Client $client, Frame $frame) {
|
||||
$this->client = $client;
|
||||
$this->frame = $frame;
|
||||
}
|
||||
|
||||
public function onActivate() {
|
||||
//TODO: 写Scheduler计时器内的处理逻辑
|
||||
}
|
||||
}
|
||||
140
src/Scheduler/Scheduler.php
Normal file
140
src/Scheduler/Scheduler.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Scheduler;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Framework\Console;
|
||||
use Framework\GlobalConfig;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Coroutine\Http\Client;
|
||||
use Swoole\Process;
|
||||
use Swoole\WebSocket\Frame;
|
||||
|
||||
class Scheduler
|
||||
{
|
||||
const PROCESS = 1;
|
||||
const REMOTE = 2;
|
||||
/**
|
||||
* @var Process
|
||||
*/
|
||||
private $process = null;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $m_pid;
|
||||
private $pid;
|
||||
/**
|
||||
* @var Scheduler
|
||||
*/
|
||||
private static $instance;
|
||||
/**
|
||||
* @var GlobalConfig
|
||||
*/
|
||||
private $settings;
|
||||
/**
|
||||
* @var Client
|
||||
*/
|
||||
private $client;
|
||||
|
||||
public function __construct($method = self::PROCESS, $option = []) {
|
||||
if (self::$instance !== null) die("Cannot run two scheduler in on process!");
|
||||
self::$instance = $this;
|
||||
if ($method == self::PROCESS) $this->initProcess();
|
||||
elseif ($method == self::REMOTE) $this->initRemote();
|
||||
}
|
||||
|
||||
private function initProcess() { //TODO: 完成Process模式的代码
|
||||
$m_pid = posix_getpid();
|
||||
$this->process = new Process(function (Process $worker) use ($m_pid) { self::onWork($worker, $m_pid); }, false, 2, true);
|
||||
$this->pid = $this->process->start();
|
||||
while (1) {
|
||||
$ret = Process::wait();
|
||||
if ($ret) {
|
||||
$this->process = new Process(function (Process $worker) use ($m_pid) { self::onWork($worker, $m_pid); }, false, 2, true);
|
||||
$this->pid = $this->process->start();
|
||||
echo "Reboot done.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function initRemote() {
|
||||
define('WORKING_DIR', __DIR__ . '../..');
|
||||
$this->requireGlobalFunctions();
|
||||
$this->registerAutoloader('classLoader');
|
||||
$this->settings = new GlobalConfig();
|
||||
if (!$this->settings->success) die("Failed to load global config. Please check config/global.php file");
|
||||
$this->defineProperties();
|
||||
|
||||
//start swoole Framework
|
||||
$this->selfCheck();
|
||||
try {
|
||||
$host = $this->settings->get("scheduler")["host"];
|
||||
$port = $this->settings->get("scheduler")["port"];
|
||||
$token = $this->settings->get("scheduler")["token"];
|
||||
$this->client = new Client($host, $port);
|
||||
$path = "/" . ($token != "" ? ("?token=" . urlencode($token)) : "");
|
||||
while (true) {
|
||||
if ($this->client->upgrade($path)) {
|
||||
while (true) {
|
||||
$recv = $this->client->recv();
|
||||
if ($recv instanceof Frame) {
|
||||
(new MessageEvent($this->client, $recv))->onActivate();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Console::warning("无法连接Framework,将在5秒后重连...");
|
||||
Coroutine::sleep(5);
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Console::error($e);
|
||||
}
|
||||
}
|
||||
|
||||
private function requireGlobalFunctions() {
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require WORKING_DIR . '/src/Framework/global_functions.php';
|
||||
}
|
||||
|
||||
private function registerAutoloader(string $string) {
|
||||
if (!spl_autoload_register($string)) die("Failed to register autoloader named \"$string\" !");
|
||||
}
|
||||
|
||||
private function defineProperties() {
|
||||
define("ZM_START_TIME", microtime(true));
|
||||
define("ZM_DATA", $this->settings->get("zm_data"));
|
||||
//define("CONFIG_DIR", $this->settings->get("config_dir"));
|
||||
define("CRASH_DIR", $this->settings->get("crash_dir"));
|
||||
}
|
||||
|
||||
private function selfCheck() {
|
||||
if (!extension_loaded("swoole")) die("Can not find swoole extension.\n");
|
||||
if (!extension_loaded("sockets")) die("Can not find sockets extension.\n");
|
||||
if (!function_exists("mb_substr")) die("Can not find mbstring extension.\n");
|
||||
if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
|
||||
//if (!class_exists("ZipArchive")) die("Can not find Zip extension.\n");
|
||||
if (!file_exists(CRASH_DIR . "last_error.log")) die("Can not find log file.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
private static function onWork(Process $worker, $m_pid) {
|
||||
swoole_set_process_name('php-scheduler');
|
||||
for ($j = 0; $j < 16000; $j++) {
|
||||
self::checkMpid($worker, $m_pid);
|
||||
echo "msg: {$j}\n";
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static function checkMpid(Process $worker, $m_pid) {
|
||||
if (!Process::kill($m_pid, 0)) {
|
||||
$worker->exit(); //主进程死了我也死
|
||||
// 这句提示,实际是看不到的.需要写到日志中
|
||||
echo "Master process exited, I [{$worker['pid']}] also quit\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/8/27
|
||||
* Time: 7:17 PM
|
||||
*/
|
||||
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
|
||||
use Framework\Console;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class CQ
|
||||
{
|
||||
@@ -147,7 +148,7 @@ class CQ
|
||||
case "qq":
|
||||
case "163":
|
||||
case "xiami":
|
||||
return "[CQ:music,id=" . $id_or_url . "]";
|
||||
return "[CQ:music,type=$type,id=$id_or_url]";
|
||||
case "custom":
|
||||
if ($title === null || $audio === null) {
|
||||
Console::error("传入CQ码实例的标题和音频链接不能为空!");
|
||||
@@ -180,18 +181,6 @@ class CQ
|
||||
return "[CQ:share,url=" . $url . ",title=" . $title . $c . $i . "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串中的CQ码敏感符号进行转义
|
||||
* @param $str
|
||||
* @return mixed
|
||||
*/
|
||||
public static function encode($str) {
|
||||
$str = str_replace("&", "&", $str);
|
||||
$str = str_replace("[", "[", $str);
|
||||
$str = str_replace("]", "]", $str);
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反转义字符串中的CQ码敏感符号
|
||||
* @param $str
|
||||
@@ -210,22 +199,22 @@ class CQ
|
||||
return $str;
|
||||
}
|
||||
|
||||
|
||||
static function getCQ($msg) {
|
||||
if (($start = mb_strpos($msg, '[')) === false) return null;
|
||||
if (($end = mb_strpos($msg, ']')) === false) return null;
|
||||
$msg = mb_substr($msg, $start + 1, $end - $start - 1);
|
||||
if (mb_substr($msg, 0, 3) != "CQ:") return null;
|
||||
$msg = mb_substr($msg, 3);
|
||||
$msg2 = explode(",", $msg);
|
||||
$type = array_shift($msg2);
|
||||
$array = [];
|
||||
foreach ($msg2 as $k => $v) {
|
||||
$ss = explode("=", $v);
|
||||
$sk = array_shift($ss);
|
||||
$array[$sk] = implode("=", $ss);
|
||||
}
|
||||
return ["type" => $type, "params" => $array, "start" => $start, "end" => $end];
|
||||
/**
|
||||
* 转义CQ码
|
||||
* @param $msg
|
||||
* @return mixed
|
||||
*/
|
||||
public static function escape($msg) {
|
||||
$msg = str_replace("&", "&", $msg);
|
||||
$msg = str_replace("[", "[", $msg);
|
||||
$msg = str_replace("]", "]", $msg);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
public static function removeCQ($msg) {
|
||||
while (($cq = ZMUtil::getCQ($msg)) !== null) {
|
||||
$msg = str_replace(mb_substr($msg, $cq["start"], $cq["end"] - $cq["start"] + 1), "", $msg);
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
}
|
||||
249
src/ZM/API/CQAPI.php
Normal file
249
src/ZM/API/CQAPI.php
Normal file
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\API;
|
||||
|
||||
|
||||
use Co;
|
||||
use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\Connection\WSConnection;
|
||||
|
||||
/**
|
||||
* @method static send_private_msg($self_id, $params, $function = null)
|
||||
* @method static send_group_msg($self_id, $params, $function = null)
|
||||
* @method static send_discuss_msg($self_id, $params, $function = null)
|
||||
* @method static send_msg($self_id, $params, $function = null)
|
||||
* @method static delete_msg($self_id, $params, $function = null)
|
||||
* @method static send_like($self_id, $params, $function = null)
|
||||
* @method static set_group_kick($self_id, $params, $function = null)
|
||||
* @method static set_group_ban($self_id, $params, $function = null)
|
||||
* @method static set_group_anonymous_ban($self_id, $params, $function = null)
|
||||
* @method static set_group_whole_ban($self_id, $params, $function = null)
|
||||
* @method static set_group_admin($self_id, $params, $function = null)
|
||||
* @method static set_group_anonymous($self_id, $params, $function = null)
|
||||
* @method static set_group_card($self_id, $params, $function = null)
|
||||
* @method static set_group_leave($self_id, $params, $function = null)
|
||||
* @method static set_group_special_title($self_id, $params, $function = null)
|
||||
* @method static set_discuss_leave($self_id, $params, $function = null)
|
||||
* @method static set_friend_add_request($self_id, $params, $function = null)
|
||||
* @method static set_group_add_request($self_id, $params, $function = null)
|
||||
* @method static get_login_info($self_id, $params, $function = null)
|
||||
* @method static get_stranger_info($self_id, $params, $function = null)
|
||||
* @method static get_group_list($self_id, $params, $function = null)
|
||||
* @method static get_group_member_info($self_id, $params, $function = null)
|
||||
* @method static get_group_member_list($self_id, $params, $function = null)
|
||||
* @method static get_cookies($self_id, $params, $function = null)
|
||||
* @method static get_csrf_token($self_id, $params, $function = null)
|
||||
* @method static get_credentials($self_id, $params, $function = null)
|
||||
* @method static get_record($self_id, $params, $function = null)
|
||||
* @method static get_status($self_id, $params, $function = null)
|
||||
* @method static get_version_info($self_id, $params, $function = null)
|
||||
* @method static set_restart($self_id, $params, $function = null)
|
||||
* @method static set_restart_plugin($self_id, $params, $function = null)
|
||||
* @method static clean_data_dir($self_id, $params, $function = null)
|
||||
* @method static clean_plugin_log($self_id, $params, $function = null)
|
||||
* @method static _get_friend_list($self_id, $params, $function = null)
|
||||
* @method static _get_group_info($self_id, $params, $function = null)
|
||||
* @method static _get_vip_info($self_id, $params, $function = null)
|
||||
* @method static send_private_msg_async($self_id, $params, $function = null)
|
||||
* @method static send_group_msg_async($self_id, $params, $function = null)
|
||||
* @method static send_discuss_msg_async($self_id, $params, $function = null)
|
||||
* @method static send_msg_async($self_id, $params, $function = null)
|
||||
* @method static delete_msg_async($self_id, $params, $function = null)
|
||||
* @method static set_group_kick_async($self_id, $params, $function = null)
|
||||
* @method static set_group_ban_async($self_id, $params, $function = null)
|
||||
* @method static set_group_anonymous_ban_async($self_id, $params, $function = null)
|
||||
* @method static set_group_whole_ban_async($self_id, $params, $function = null)
|
||||
* @method static set_group_admin_async($self_id, $params, $function = null)
|
||||
* @method static set_group_anonymous_async($self_id, $params, $function = null)
|
||||
* @method static set_group_card_async($self_id, $params, $function = null)
|
||||
* @method static set_group_leave_async($self_id, $params, $function = null)
|
||||
* @method static set_group_special_title_async($self_id, $params, $function = null)
|
||||
* @method static set_discuss_leave_async($self_id, $params, $function = null)
|
||||
* @method static set_friend_add_request_async($self_id, $params, $function = null)
|
||||
* @method static set_group_add_request_async($self_id, $params, $function = null)
|
||||
*/
|
||||
class CQAPI
|
||||
{
|
||||
public static function quick_reply(WSConnection $conn, $data, $msg, $yield = null) {
|
||||
switch ($data["message_type"]) {
|
||||
case "group":
|
||||
return self::send_group_msg($conn, ["group_id" => $data["group_id"], "message" => $msg], $yield);
|
||||
case "private":
|
||||
return self::send_private_msg($conn, ["user_id" => $data["user_id"], "message" => $msg], $yield);
|
||||
case "discuss":
|
||||
return self::send_discuss_msg($conn, ["discuss_id" => $data["discuss_id"], "message" => $msg], $yield);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function __callStatic($name, $arg) {
|
||||
$all = self::getSupportedAPIs();
|
||||
$find = null;
|
||||
if (in_array($name, $all)) $find = $name;
|
||||
else {
|
||||
foreach ($all as $v) {
|
||||
if (strtolower($name) == strtolower(str_replace("_", "", $v))) {
|
||||
$find = $v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($find === null) {
|
||||
Console::error("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.");
|
||||
return false;
|
||||
}
|
||||
if ($arg[1] != []) {
|
||||
$reply["params"] = $arg[1];
|
||||
}
|
||||
if (!($arg[0] instanceof CQConnection)) {
|
||||
$robot = ConnectionManager::getByType("qq", ["self_id" => $arg[0]]);
|
||||
if ($robot == []) {
|
||||
Console::error("发送错误,机器人连接不存在!");
|
||||
return false;
|
||||
}
|
||||
$arg[0] = $robot[0];
|
||||
}
|
||||
return self::processAPI($arg[0], $reply, $arg[2] ?? null);
|
||||
}
|
||||
|
||||
/********************** non-API Part **********************/
|
||||
|
||||
private static function getSupportedAPIs() {
|
||||
return [
|
||||
"send_private_msg",
|
||||
"send_group_msg",
|
||||
"send_discuss_msg",
|
||||
"send_msg",
|
||||
"delete_msg",
|
||||
"send_like",
|
||||
"set_group_kick",
|
||||
"set_group_ban",
|
||||
"set_group_anonymous_ban",
|
||||
"set_group_whole_ban",
|
||||
"set_group_admin",
|
||||
"set_group_anonymous",
|
||||
"set_group_card",
|
||||
"set_group_leave",
|
||||
"set_group_special_title",
|
||||
"set_discuss_leave",
|
||||
"set_friend_add_request",
|
||||
"set_group_add_request",
|
||||
"get_login_info",
|
||||
"get_stranger_info",
|
||||
"get_group_list",
|
||||
"get_group_member_info",
|
||||
"get_group_member_list",
|
||||
"get_cookies",
|
||||
"get_csrf_token",
|
||||
"get_credentials",
|
||||
"get_record",
|
||||
"get_status",
|
||||
"get_version_info",
|
||||
"set_restart",
|
||||
"set_restart_plugin",
|
||||
"clean_data_dir",
|
||||
"clean_plugin_log",
|
||||
"_get_friend_list",
|
||||
"_get_group_info",
|
||||
"_get_vip_info",
|
||||
//异步API
|
||||
"send_private_msg_async",
|
||||
"send_group_msg_async",
|
||||
"send_discuss_msg_async",
|
||||
"send_msg_async",
|
||||
"delete_msg_async",
|
||||
"set_group_kick_async",
|
||||
"set_group_ban_async",
|
||||
"set_group_anonymous_ban_async",
|
||||
"set_group_whole_ban_async",
|
||||
"set_group_admin_async",
|
||||
"set_group_anonymous_async",
|
||||
"set_group_card_async",
|
||||
"set_group_leave_async",
|
||||
"set_group_special_title_async",
|
||||
"set_discuss_leave_async",
|
||||
"set_friend_add_request_async",
|
||||
"set_group_add_request_async"
|
||||
];
|
||||
}
|
||||
|
||||
public static function getLoggedAPIs() {
|
||||
return [
|
||||
"send_private_msg",
|
||||
"send_group_msg",
|
||||
"send_discuss_msg",
|
||||
"send_msg",
|
||||
"send_private_msg_async",
|
||||
"send_group_msg_async",
|
||||
"send_discuss_msg_async",
|
||||
"send_msg_async"
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WSConnection $connection
|
||||
* @param $reply
|
||||
* @param |null $function
|
||||
* @return bool
|
||||
*/
|
||||
private 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);
|
||||
|
||||
if (is_callable($function)) {
|
||||
ZMBuf::appendKey("sent_api", $api_id, [
|
||||
"data" => $reply,
|
||||
"time" => microtime(true),
|
||||
"func" => $function,
|
||||
"self_id" => $connection->getQQ()
|
||||
]);
|
||||
} elseif ($function === true) {
|
||||
ZMBuf::appendKey("sent_api", $api_id, [
|
||||
"data" => $reply,
|
||||
"time" => microtime(true),
|
||||
"coroutine" => Co::getuid(),
|
||||
"self_id" => $connection->getQQ()
|
||||
]);
|
||||
} else {
|
||||
ZMBuf::appendKey("sent_api", $api_id, [
|
||||
"data" => $reply,
|
||||
"time" => microtime(true),
|
||||
"self_id" => $connection->getQQ()
|
||||
]);
|
||||
}
|
||||
if ($connection->push(json_encode($reply))) {
|
||||
Console::msg($reply, $connection->getQQ());
|
||||
ZMBuf::$atomics["out_count"]->add(1);
|
||||
if ($function === true) {
|
||||
Co::suspend();
|
||||
$data = ZMBuf::get("sent_api")[$api_id];
|
||||
ZMBuf::unsetByValue("sent_api", $reply["echo"]);
|
||||
return isset($data['result']) ? $data['result'] : null;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
$response = [
|
||||
"status" => "failed",
|
||||
"retcode" => 999,
|
||||
"data" => null,
|
||||
"self_id" => $connection->getQQ()
|
||||
];
|
||||
$s = ZMBuf::get("sent_api")[$reply["echo"]];
|
||||
if (($s["func"] ?? null) !== null)
|
||||
call_user_func($s["func"], $response, $reply);
|
||||
ZMBuf::unsetByValue("sent_api", $reply["echo"]);
|
||||
if ($function === true) return null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/ZM/Annotation/AnnotationBase.php
Normal file
29
src/ZM/Annotation/AnnotationBase.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation;
|
||||
|
||||
|
||||
use Closure;
|
||||
|
||||
abstract class AnnotationBase
|
||||
{
|
||||
public $method;
|
||||
|
||||
public $class;
|
||||
|
||||
public function __toString() {
|
||||
$str = __CLASS__ . ": ";
|
||||
foreach ($this as $k => $v) {
|
||||
$str .= "\n\t" . $k . " => ";
|
||||
if (is_string($v)) $str .= "\"$v\"";
|
||||
elseif (is_numeric($v)) $str .= $v;
|
||||
elseif (is_bool($v)) $str .= ($v ? "TRUE" : "FALSE");
|
||||
elseif (is_array($v)) $str .= json_encode($v, JSON_UNESCAPED_UNICODE);
|
||||
elseif ($v instanceof Closure) $str .= "@AnonymousFunction";
|
||||
elseif (is_null($v)) $str .= "NULL";
|
||||
else $str .= "@Unknown";
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
223
src/ZM/Annotation/AnnotationParser.php
Normal file
223
src/ZM/Annotation/AnnotationParser.php
Normal file
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Framework\ZMBuf;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionMethod;
|
||||
use ZM\Annotation\CQ\{CQAfter, CQBefore, CQCommand, CQMessage, CQMetaEvent, CQNotice, CQRequest};
|
||||
use ZM\Annotation\Http\Controller;
|
||||
use ZM\Annotation\Http\RequestMapping;
|
||||
use ZM\Annotation\Interfaces\CustomAnnotation;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
use ZM\Annotation\Module\Closed;
|
||||
use ZM\Annotation\Module\SaveBuffer;
|
||||
use ZM\Annotation\Swoole\SwooleEventAfter;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Annotation\Interfaces\Rule;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\Utils\DataProvider;
|
||||
|
||||
class AnnotationParser
|
||||
{
|
||||
/**
|
||||
* 注册各个模块类的注解和模块level的排序
|
||||
* @throws ReflectionException
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public static function registerMods() {
|
||||
self::loadAnnotationClasses();
|
||||
$all_class = getAllClasses(WORKING_DIR . "/src/Module/", "Module");
|
||||
$reader = new AnnotationReader();
|
||||
foreach ($all_class as $v) {
|
||||
$reflection_class = new ReflectionClass($v);
|
||||
$class_prefix = '';
|
||||
$methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC);
|
||||
$class_annotations = $reader->getClassAnnotations($reflection_class);
|
||||
foreach ($class_annotations as $vs) {
|
||||
if ($vs instanceof Closed) {
|
||||
continue 2;
|
||||
} elseif ($vs instanceof Controller) {
|
||||
$class_prefix = $vs->prefix;
|
||||
} elseif ($vs instanceof SaveBuffer) {
|
||||
DataProvider::addSaveBuffer($vs->buf_name, $vs->sub_folder);
|
||||
}
|
||||
}
|
||||
foreach ($methods as $vs) {
|
||||
$method_annotations = $reader->getMethodAnnotations($vs);
|
||||
foreach ($method_annotations as $vss) {
|
||||
if ($vss instanceof Rule) $vss = self::registerRuleEvent($vss, $vs, $reflection_class);
|
||||
else $vss = self::registerMethod($vss, $vs, $reflection_class);
|
||||
|
||||
if ($vss instanceof SwooleEventAt) ZMBuf::$events[SwooleEventAt::class][] = $vss;
|
||||
elseif ($vss instanceof SwooleEventAfter) ZMBuf::$events[SwooleEventAfter::class][] = $vss;
|
||||
elseif ($vss instanceof CQMessage) ZMBuf::$events[CQMessage::class][] = $vss;
|
||||
elseif ($vss instanceof CQNotice) ZMBuf::$events[CQNotice::class][] = $vss;
|
||||
elseif ($vss instanceof CQRequest) ZMBuf::$events[CQRequest::class][] = $vss;
|
||||
elseif ($vss instanceof CQMetaEvent) ZMBuf::$events[CQMetaEvent::class][] = $vss;
|
||||
elseif ($vss instanceof CQCommand) ZMBuf::$events[CQCommand::class][] = $vss;
|
||||
elseif ($vss instanceof RequestMapping) self::registerRequestMapping($vss, $vs, $reflection_class, $class_prefix);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//给支持level的排个序
|
||||
foreach (ZMBuf::$events as $class_name => $v) {
|
||||
if ((new $class_name()) instanceof Level) {
|
||||
for ($i = 0; $i < count(ZMBuf::$events[$class_name]) - 1; ++$i) {
|
||||
for ($j = 0; $j < count(ZMBuf::$events[$class_name]) - $i - 1; ++$j) {
|
||||
$l1 = ZMBuf::$events[$class_name][$j]->level;
|
||||
$l2 = ZMBuf::$events[$class_name][$j + 1]->level;
|
||||
if ($l1 < $l2) {
|
||||
$t = ZMBuf::$events[$class_name][$j + 1];
|
||||
ZMBuf::$events[$class_name][$j + 1] = ZMBuf::$events[$class_name][$j];
|
||||
ZMBuf::$events[$class_name][$j] = $t;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function getRuleCallback($rule_str) {
|
||||
$func = null;
|
||||
$rule = $rule_str;
|
||||
if ($rule != "") {
|
||||
$asp = explode(":", $rule);
|
||||
$asp_name = array_shift($asp);
|
||||
$rest = implode(":", $asp);
|
||||
//Swoole 事件时走此switch
|
||||
switch ($asp_name) {
|
||||
case "connectType": //websocket连接类型
|
||||
$func = function (WSConnection $connection) use ($rest) {
|
||||
return $connection->getType() == $rest ? true : false;
|
||||
};
|
||||
break;
|
||||
case "containsGet": //handle http request事件时才能用
|
||||
case "containsPost":
|
||||
$get_list = explode(",", $rest);
|
||||
if ($asp_name == "containsGet")
|
||||
$func = function ($request) use ($get_list) {
|
||||
foreach ($get_list as $v) if (!isset($request->get[$v])) return false;
|
||||
return true;
|
||||
};
|
||||
else
|
||||
$func = function ($request) use ($get_list) {
|
||||
foreach ($get_list as $v) if (!isset($request->post[$v])) return false;
|
||||
return true;
|
||||
};
|
||||
/*
|
||||
if ($controller_prefix != '') {
|
||||
$p = ZMBuf::$req_mapping_node;
|
||||
$prefix_exp = explode("/", $controller_prefix);
|
||||
foreach ($prefix_exp as $k => $v) {
|
||||
if ($v == "" || $v == ".." || $v == ".") {
|
||||
unset($prefix_exp[$k]);
|
||||
}
|
||||
}
|
||||
while (($shift = array_shift($prefix_exp)) !== null) {
|
||||
$p->addRoute($shift, new MappingNode($shift));
|
||||
$p = $p->getRoute($shift);
|
||||
}
|
||||
if ($p->getNodeName() != "/") {
|
||||
$p->setMethod($method->getName());
|
||||
$p->setClass($class->getName());
|
||||
$p->setRule($func);
|
||||
return "mapped";
|
||||
}
|
||||
}*/
|
||||
break;
|
||||
case "containsJson": //handle http request事件时才能用
|
||||
$json_list = explode(",", $rest);
|
||||
$func = function ($json) use ($json_list) {
|
||||
foreach ($json_list as $v) if (!isset($json[$v])) return false;
|
||||
return true;
|
||||
};
|
||||
break;
|
||||
case "dataEqual": //handle websocket message事件时才能用
|
||||
$func = function ($data) use ($rest) { return $data == $rest; };
|
||||
break;
|
||||
}
|
||||
switch ($asp_name) {
|
||||
case "msgMatch": //handle cq message事件时才能用
|
||||
$func = function ($msg) use ($rest) { return matchPattern($rest, $msg); };
|
||||
break;
|
||||
case "msgEqual": //handle cq message事件时才能用
|
||||
$func = function ($msg) use ($rest) { return trim($msg) == $rest; };
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
return $func;
|
||||
}
|
||||
|
||||
private static function registerRuleEvent(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
|
||||
$vss->callback = self::getRuleCallback($vss->getRule());
|
||||
$vss->method = $method->getName();
|
||||
$vss->class = $class->getName();
|
||||
return $vss;
|
||||
}
|
||||
|
||||
private static function registerMethod(?AnnotationBase $vss, ReflectionMethod $method, ReflectionClass $class) {
|
||||
$vss->method = $method->getName();
|
||||
$vss->class = $class->getName();
|
||||
return $vss;
|
||||
}
|
||||
|
||||
private static function registerRequestMapping(RequestMapping $vss, ReflectionMethod $method, ReflectionClass $class, string $prefix) {
|
||||
$prefix_exp = explode("/", $prefix);
|
||||
$route_exp = explode("/", $vss->route);
|
||||
foreach ($prefix_exp as $k => $v) {
|
||||
if ($v == "" || $v == ".." || $v == ".") {
|
||||
unset($prefix_exp[$k]);
|
||||
}
|
||||
}
|
||||
foreach ($route_exp as $k => $v) {
|
||||
if ($v == "" || $v == ".." || $v == ".") {
|
||||
unset($route_exp[$k]);
|
||||
}
|
||||
}
|
||||
$a = ZMBuf::$req_mapping_node;
|
||||
$p = $a;
|
||||
if ($prefix_exp == [] && $route_exp == []) {
|
||||
$p->setMethod($method->getName());
|
||||
$p->setClass($class->getName());
|
||||
$p->setRequestMethod($vss->request_method);
|
||||
return;
|
||||
}
|
||||
while (($shift = array_shift($prefix_exp)) !== null) {
|
||||
$p->addRoute($shift, new MappingNode($shift));
|
||||
$p = $p->getRoute($shift);
|
||||
}
|
||||
while (($shift = array_shift($route_exp)) !== null) {
|
||||
if (mb_substr($shift, 0, 1) == "{" && mb_substr($shift, -1, 1) == "}") {
|
||||
$p->removeAllRoute();
|
||||
}
|
||||
$p->addRoute($shift, new MappingNode($shift));
|
||||
$p = $p->getRoute($shift);
|
||||
}
|
||||
$p->setMethod($method->getName());
|
||||
$p->setClass($class->getName());
|
||||
$p->setRequestMethod($vss->request_method);
|
||||
}
|
||||
|
||||
private static function loadAnnotationClasses() {
|
||||
$class = getAllClasses(WORKING_DIR . "/src/ZM/Annotation/", "ZM\\Annotation");
|
||||
foreach ($class as $v) {
|
||||
$s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
|
||||
require_once $s;
|
||||
}
|
||||
$class = getAllClasses(WORKING_DIR . "/src/Custom/Annotation/", "Custom\\Annotation");
|
||||
foreach ($class as $v) {
|
||||
$s = WORKING_DIR . '/src/' . str_replace("\\", "/", $v) . ".php";
|
||||
require_once $s;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/ZM/Annotation/CQ/CQAfter.php
Normal file
22
src/ZM/Annotation/CQ/CQAfter.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
|
||||
/**
|
||||
* Class CQAfter
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
* @package ZM\Annotation\CQ
|
||||
*/
|
||||
class CQAfter extends AnnotationBase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required()
|
||||
*/
|
||||
public $cq_event;
|
||||
}
|
||||
42
src/ZM/Annotation/CQ/CQBefore.php
Normal file
42
src/ZM/Annotation/CQ/CQBefore.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
|
||||
/**
|
||||
* Class CQBefore
|
||||
* @Annotation
|
||||
* @Target("METHOD")
|
||||
* @package ZM\Annotation\CQ
|
||||
*/
|
||||
class CQBefore extends AnnotationBase implements Level
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required()
|
||||
*/
|
||||
public $cq_event;
|
||||
|
||||
public $level = 20;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLevel() {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $level
|
||||
*/
|
||||
public function setLevel($level): void {
|
||||
$this->level = $level;
|
||||
}
|
||||
|
||||
}
|
||||
35
src/ZM/Annotation/CQ/CQCommand.php
Normal file
35
src/ZM/Annotation/CQ/CQCommand.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
|
||||
/**
|
||||
* Class CQCommand
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @package ZM\Annotation\CQ
|
||||
*/
|
||||
class CQCommand extends AnnotationBase implements Level
|
||||
{
|
||||
/** @var string */
|
||||
public $match = "";
|
||||
/** @var string */
|
||||
public $regexMatch = "";
|
||||
/** @var int */
|
||||
public $level = 20;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLevel(): int { return $this->level; }
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
*/
|
||||
public function setLevel(int $level) { $this->level = $level; }
|
||||
|
||||
}
|
||||
41
src/ZM/Annotation/CQ/CQMessage.php
Normal file
41
src/ZM/Annotation/CQ/CQMessage.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
|
||||
/**
|
||||
* Class CQMessage
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @package ZM\Annotation\CQ
|
||||
*/
|
||||
class CQMessage extends AnnotationBase implements Level
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $message_type = "";
|
||||
/** @var int */
|
||||
public $user_id = 0;
|
||||
/** @var int */
|
||||
public $group_id = 0;
|
||||
/** @var int */
|
||||
public $discuss_id = 0;
|
||||
/** @var string */
|
||||
public $message = "";
|
||||
/** @var string */
|
||||
public $raw_message = "";
|
||||
/** @var int */
|
||||
public $level = 20;
|
||||
|
||||
public function getLevel() { return $this->level; }
|
||||
|
||||
public function setLevel(int $level) {
|
||||
$this->level = $level;
|
||||
}
|
||||
}
|
||||
40
src/ZM/Annotation/CQ/CQMetaEvent.php
Normal file
40
src/ZM/Annotation/CQ/CQMetaEvent.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
|
||||
/**
|
||||
* Class CQMetaEvent
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @package ZM\Annotation\CQ
|
||||
*/
|
||||
class CQMetaEvent extends AnnotationBase implements Level
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required()
|
||||
*/
|
||||
public $meta_event_type = '';
|
||||
/** @var string */
|
||||
public $sub_type = '';
|
||||
/** @var int */
|
||||
public $level;
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLevel() { return $this->level; }
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
*/
|
||||
public function setLevel(int $level): void {
|
||||
$this->level = $level;
|
||||
}
|
||||
}
|
||||
42
src/ZM/Annotation/CQ/CQNotice.php
Normal file
42
src/ZM/Annotation/CQ/CQNotice.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
|
||||
/**
|
||||
* Class CQNotice
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @package ZM\Annotation\CQ
|
||||
*/
|
||||
class CQNotice extends AnnotationBase implements Level
|
||||
{
|
||||
/** @var string */
|
||||
public $notice_type = "";
|
||||
/** @var string */
|
||||
public $sub_type = "";
|
||||
/** @var int */
|
||||
public $group_id = 0;
|
||||
/** @var int */
|
||||
public $operator_id = 0;
|
||||
/** @var int */
|
||||
public $level = 20;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLevel(): int {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
*/
|
||||
public function setLevel(int $level): void {
|
||||
$this->level = $level;
|
||||
}
|
||||
}
|
||||
42
src/ZM/Annotation/CQ/CQRequest.php
Normal file
42
src/ZM/Annotation/CQ/CQRequest.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\CQ;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\Interfaces\Level;
|
||||
|
||||
/**
|
||||
* Class CQRequest
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @package ZM\Annotation\CQ
|
||||
*/
|
||||
class CQRequest extends AnnotationBase implements Level
|
||||
{
|
||||
/** @var string */
|
||||
public $request_type = "";
|
||||
/** @var string */
|
||||
public $sub_type = "";
|
||||
/** @var int */
|
||||
public $user_id = 0;
|
||||
/** @var string */
|
||||
public $comment = "";
|
||||
/** @var int */
|
||||
public $level = 20;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLevel(): int {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
*/
|
||||
public function setLevel(int $level): void {
|
||||
$this->level = $level;
|
||||
}
|
||||
}
|
||||
23
src/ZM/Annotation/Http/Controller.php
Normal file
23
src/ZM/Annotation/Http/Controller.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Http;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
|
||||
/**
|
||||
* Class Controller
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
* @package ZM\Annotation\Http
|
||||
*/
|
||||
class Controller extends AnnotationBase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required()
|
||||
*/
|
||||
public $prefix = '';
|
||||
}
|
||||
39
src/ZM/Annotation/Http/RequestMapping.php
Normal file
39
src/ZM/Annotation/Http/RequestMapping.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Http;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
|
||||
/**
|
||||
* Class RequestMapping
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @package ZM\Annotation\Http
|
||||
*/
|
||||
class RequestMapping extends AnnotationBase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required()
|
||||
*/
|
||||
public $route = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $request_method = [RequestMethod::GET, RequestMethod::POST];
|
||||
|
||||
/**
|
||||
* Routing path params binding. eg. {"id"="\d+"}
|
||||
* @var array
|
||||
*/
|
||||
public $params = [];
|
||||
}
|
||||
30
src/ZM/Annotation/Http/RequestMethod.php
Normal file
30
src/ZM/Annotation/Http/RequestMethod.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Http;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
|
||||
/**
|
||||
* Class RequestMethod
|
||||
* @Annotation
|
||||
*
|
||||
* @package ZM\Annotation\Http
|
||||
*/
|
||||
class RequestMethod extends AnnotationBase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required()
|
||||
*/
|
||||
public $method = self::GET;
|
||||
|
||||
public const GET = 'GET';
|
||||
public const POST = 'POST';
|
||||
public const PUT = 'PUT';
|
||||
public const PATCH = 'PATCH';
|
||||
public const DELETE = 'DELETE';
|
||||
public const OPTIONS = 'OPTIONS';
|
||||
public const HEAD = 'HEAD';
|
||||
}
|
||||
10
src/ZM/Annotation/Interfaces/CustomAnnotation.php
Normal file
10
src/ZM/Annotation/Interfaces/CustomAnnotation.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Interfaces;
|
||||
|
||||
|
||||
interface CustomAnnotation
|
||||
{
|
||||
|
||||
}
|
||||
12
src/ZM/Annotation/Interfaces/Level.php
Normal file
12
src/ZM/Annotation/Interfaces/Level.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Interfaces;
|
||||
|
||||
|
||||
interface Level
|
||||
{
|
||||
public function getLevel();
|
||||
|
||||
public function setLevel(int $level);
|
||||
}
|
||||
10
src/ZM/Annotation/Interfaces/Rule.php
Normal file
10
src/ZM/Annotation/Interfaces/Rule.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Interfaces;
|
||||
|
||||
|
||||
interface Rule
|
||||
{
|
||||
public function getRule();
|
||||
}
|
||||
103
src/ZM/Annotation/MappingNode.php
Normal file
103
src/ZM/Annotation/MappingNode.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation;
|
||||
|
||||
|
||||
use Closure;
|
||||
|
||||
class MappingNode
|
||||
{
|
||||
private $node;
|
||||
|
||||
/** @var MappingNode[] */
|
||||
private $route = [];
|
||||
private $method = null;
|
||||
private $class = null;
|
||||
private $request_method = [];
|
||||
/** @var Closure|null */
|
||||
private $rule = null;
|
||||
|
||||
public function __construct(string $node_name) { $this->node = $node_name; }
|
||||
|
||||
public function addRoute(string $route_name, MappingNode $route_node) { $this->route[$route_name] = $route_node; }
|
||||
|
||||
/**
|
||||
* @param string $shift
|
||||
* @return MappingNode|null
|
||||
*/
|
||||
public function getRoute(string $shift) {
|
||||
return $this->route[$shift] ?? null;
|
||||
}
|
||||
|
||||
public function getRealRoute(string $shift, array &$bind_params) {
|
||||
if (mb_substr(key($this->route), 0, 1) == "{" && mb_substr(key($this->route), -1, 1) == "}") {
|
||||
$param_name = mb_substr(current($this->route)->getNodeName(), 1, -1);
|
||||
$bind_params[$param_name] = $shift;
|
||||
return current($this->route);
|
||||
} else return $this->route[$shift] ?? null;
|
||||
}
|
||||
|
||||
public function setMethod($method) { $this->method = $method; }
|
||||
|
||||
public function setClass($class) { $this->class = $class; }
|
||||
|
||||
public function setRequestMethod($method) {
|
||||
if (is_string($method)) $this->request_method = [$method];
|
||||
else $this->request_method = $method;
|
||||
}
|
||||
|
||||
public function getNodeName() { return $this->node; }
|
||||
|
||||
public function getRule() { return $this->rule; }
|
||||
|
||||
public function setRule(Closure $rule): void { $this->rule = $rule; }
|
||||
|
||||
public function removeAllRoute() { $this->route = []; }
|
||||
|
||||
/**
|
||||
* @return null
|
||||
*/
|
||||
public function getMethod() {
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRequestMethod(): array {
|
||||
return $this->request_method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null
|
||||
*/
|
||||
public function getClass() {
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNode(): string {
|
||||
return $this->node;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
$str = "[" . $this->node . "] => ";
|
||||
if ($this->class != "" && $this->class != null)
|
||||
$str .= "\n\t" . $this->class . "->" . $this->method . ": " . implode(", ", $this->request_method);
|
||||
$str .= "\n\t[Route] => [";
|
||||
foreach ($this->route as $k => $v) {
|
||||
$r = $v;
|
||||
$r = explode("\n", $r);
|
||||
foreach ($r as $ks => $vs) {
|
||||
$r[$ks] = "\t" . $r[$ks];
|
||||
}
|
||||
$r = implode("\n", $r);
|
||||
$str .= "\n\t" . $r;
|
||||
}
|
||||
$str .= "\n]";
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
18
src/ZM/Annotation/Module/Closed.php
Normal file
18
src/ZM/Annotation/Module/Closed.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Module;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
|
||||
/**
|
||||
* Class Closed
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
* @package ZM\Annotation\Module
|
||||
*/
|
||||
class Closed extends AnnotationBase
|
||||
{
|
||||
|
||||
}
|
||||
25
src/ZM/Annotation/Module/SaveBuffer.php
Normal file
25
src/ZM/Annotation/Module/SaveBuffer.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Annotation\Module;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\Required;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
|
||||
/**
|
||||
* Class SaveBuffer
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
* @package ZM\Annotation\Module
|
||||
*/
|
||||
class SaveBuffer
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*@Required()
|
||||
*/
|
||||
public $buf_name;
|
||||
/** @var string|null $sub_folder */
|
||||
public $sub_folder = null;
|
||||
}
|
||||
76
src/ZM/Annotation/Swoole/SwooleEventAfter.php
Normal file
76
src/ZM/Annotation/Swoole/SwooleEventAfter.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?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\Level;
|
||||
use ZM\Annotation\Interfaces\Rule;
|
||||
|
||||
/**
|
||||
* Class SwooleEventAfter
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @package ZM\Annotation\Swoole
|
||||
*/
|
||||
class SwooleEventAfter extends AnnotationBase implements Rule, Level
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/** @var string */
|
||||
public $rule = "";
|
||||
|
||||
/** @var int */
|
||||
public $level = 20;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*/
|
||||
public function setType(string $type): void {
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRule(): string {
|
||||
return $this->rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rule
|
||||
*/
|
||||
public function setRule(string $rule): void {
|
||||
$this->rule = $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLevel(): int {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
*/
|
||||
public function setLevel(int $level): void {
|
||||
$this->level = $level;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
76
src/ZM/Annotation/Swoole/SwooleEventAt.php
Normal file
76
src/ZM/Annotation/Swoole/SwooleEventAt.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?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\Level;
|
||||
use ZM\Annotation\Interfaces\Rule;
|
||||
|
||||
/**
|
||||
* Class SwooleEventAt
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
* @package ZM\Annotation\Swoole
|
||||
*/
|
||||
class SwooleEventAt extends AnnotationBase implements Rule, Level
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Required
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/** @var string */
|
||||
public $rule = "";
|
||||
|
||||
/** @var int */
|
||||
public $level = 20;
|
||||
|
||||
public $callback = null;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*/
|
||||
public function setType(string $type): void {
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getRule(): string {
|
||||
return $this->rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rule
|
||||
*/
|
||||
public function setRule(string $rule): void {
|
||||
$this->rule = $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLevel(): int {
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $level
|
||||
*/
|
||||
public function setLevel(int $level): void {
|
||||
$this->level = $level;
|
||||
}
|
||||
|
||||
}
|
||||
23
src/ZM/Connection/CQConnection.php
Normal file
23
src/ZM/Connection/CQConnection.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Connection;
|
||||
|
||||
|
||||
class CQConnection extends WSConnection
|
||||
{
|
||||
public $self_id = null;
|
||||
|
||||
public function __construct($server, $fd, $self_id) {
|
||||
parent::__construct($server, $fd);
|
||||
$this->self_id = $self_id;
|
||||
}
|
||||
|
||||
public function getQQ(){
|
||||
return $this->self_id;
|
||||
}
|
||||
|
||||
public function getType() {
|
||||
return "qq";
|
||||
}
|
||||
}
|
||||
73
src/ZM/Connection/ConnectionManager.php
Normal file
73
src/ZM/Connection/ConnectionManager.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Connection;
|
||||
|
||||
|
||||
use Framework\ZMBuf;
|
||||
|
||||
class ConnectionManager
|
||||
{
|
||||
/**
|
||||
* 通过server的fd获取WSConnection实例化对象
|
||||
* @param int $fd
|
||||
* @return WSConnection
|
||||
*/
|
||||
public static function get(int $fd) {
|
||||
foreach (ZMBuf::$connect as $v) {
|
||||
if ($v->fd == $fd) return $v;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param array $option
|
||||
* @return WSConnection[]
|
||||
*/
|
||||
public static function getByType(string $type, $option = []) {
|
||||
$conn = [];
|
||||
foreach (ZMBuf::$connect as $v) {
|
||||
foreach ($option as $ks => $vs) {
|
||||
if (($v->$ks ?? "") == $vs) continue;
|
||||
else continue 2;
|
||||
}
|
||||
if ($v->getType() == $type) $conn[] = $v;
|
||||
}
|
||||
return $conn;
|
||||
}
|
||||
|
||||
public static function getTypeClassName(string $type) {
|
||||
switch (strtolower($type)) {
|
||||
case "qq":
|
||||
case "universal":
|
||||
return CQConnection::class;
|
||||
case "webconsole":
|
||||
return WCConnection::class;
|
||||
case "proxy":
|
||||
return ProxyConnection::class;
|
||||
default:
|
||||
foreach (ZMBuf::$custom_connection_class as $v) {
|
||||
/** @var WSConnection $r */
|
||||
$r = new $v(ZMBuf::$server, -1);
|
||||
if ($r->getType() == strtolower($type)) return $v;
|
||||
}
|
||||
return UnknownConnection::class;
|
||||
}
|
||||
}
|
||||
|
||||
public static function close($fd) {
|
||||
foreach (ZMBuf::$connect as $k => $v) {
|
||||
if ($v->fd == $fd) {
|
||||
ZMBuf::$server->close($fd);
|
||||
unset(ZMBuf::$connect[$k]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function registerCustomClass() {
|
||||
$classes = getAllClasses(WORKING_DIR . "/src/Custom/Connection/", "Custom\\Connection");
|
||||
ZMBuf::$custom_connection_class = $classes;
|
||||
}
|
||||
}
|
||||
13
src/ZM/Connection/ProxyConnection.php
Normal file
13
src/ZM/Connection/ProxyConnection.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Connection;
|
||||
|
||||
|
||||
class ProxyConnection extends WSConnection
|
||||
{
|
||||
|
||||
public function getType() {
|
||||
return "proxy";
|
||||
}
|
||||
}
|
||||
13
src/ZM/Connection/UnknownConnection.php
Normal file
13
src/ZM/Connection/UnknownConnection.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Connection;
|
||||
|
||||
|
||||
class UnknownConnection extends WSConnection
|
||||
{
|
||||
|
||||
public function getType() {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
10
src/ZM/Connection/WCConnection.php
Normal file
10
src/ZM/Connection/WCConnection.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Connection;
|
||||
|
||||
|
||||
class WCConnection extends WSConnection
|
||||
{
|
||||
public function getType() { return "wc"; }
|
||||
}
|
||||
52
src/ZM/Connection/WSConnection.php
Normal file
52
src/ZM/Connection/WSConnection.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Connection;
|
||||
|
||||
|
||||
use Framework\Console;
|
||||
use Framework\Logger;
|
||||
use swoole_websocket_server;
|
||||
|
||||
abstract class WSConnection
|
||||
{
|
||||
public $fd;
|
||||
|
||||
/** @var swoole_websocket_server */
|
||||
protected $server;
|
||||
|
||||
public $available = false;
|
||||
|
||||
public function __construct($server, $fd) {
|
||||
$this->server = $server;
|
||||
$this->fd = $fd;
|
||||
}
|
||||
|
||||
public abstract function getType();
|
||||
|
||||
public function exists() {
|
||||
return $this->available = $this->server->exist($this->fd);
|
||||
}
|
||||
|
||||
public function close() {
|
||||
ConnectionManager::close($this->fd);
|
||||
}
|
||||
|
||||
public function push($data, $push_error_record = true) {
|
||||
if ($data === null || $data == "") {
|
||||
Console::warning("推送了空消息");
|
||||
return false;
|
||||
}
|
||||
if (!$this->server->exist($this->fd)) {
|
||||
Console::warning("Swoole 原生 websocket连接池中无此连接");
|
||||
return false;
|
||||
}
|
||||
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));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
120
src/ZM/DB/DB.php
Normal file
120
src/ZM/DB/DB.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
use framework\Console;
|
||||
use framework\ZMBuf;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Coroutine\MySQL\Statement;
|
||||
use ZM\Exception\DbException;
|
||||
|
||||
class DB
|
||||
{
|
||||
private static $table_list = [];
|
||||
|
||||
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) {
|
||||
self::$table_list[] = $v['TABLE_NAME'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $table_name
|
||||
* @param bool $enable_cache
|
||||
* @return Table
|
||||
* @throws DbException
|
||||
*/
|
||||
public static function table($table_name, $enable_cache = null) {
|
||||
if (Table::getTableInstance($table_name) === null) {
|
||||
if (in_array($table_name, self::$table_list))
|
||||
return new Table($table_name, $enable_cache ?? ZMBuf::globals("sql_config")["sql_enable_cache"]);
|
||||
elseif(ZMBuf::$sql_pool !== null){
|
||||
throw new DbException("Table " . $table_name . " not exist in database.");
|
||||
} else {
|
||||
throw new DbException("Database connection not exist or connect failed. Please check sql configuration");
|
||||
}
|
||||
}
|
||||
return Table::getTableInstance($table_name);
|
||||
}
|
||||
|
||||
public static function statement($line) {
|
||||
self::rawQuery($line, []);
|
||||
}
|
||||
|
||||
public static function unprepared($line) {
|
||||
if (ZMBuf::get("sql_log") === true) {
|
||||
$starttime = microtime(true);
|
||||
}
|
||||
try {
|
||||
$conn = ZMBuf::$sql_pool->get();
|
||||
if ($conn === false) {
|
||||
throw new DbException("无法连接SQL!" . $line);
|
||||
}
|
||||
$result = $conn->query($line) === false ? false : ($conn->errno != 0 ? false : true);
|
||||
ZMBuf::$sql_pool->put($conn);
|
||||
return $result;
|
||||
} catch (DBException $e) {
|
||||
if (ZMBuf::get("sql_log") === true) {
|
||||
$log =
|
||||
"[" . date("Y-m-d H:i:s") .
|
||||
" " . round(microtime(true) - $starttime, 5) .
|
||||
"] " . $line . " (Error:" . $e->getMessage() . ")\n";
|
||||
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
|
||||
}
|
||||
Console::error($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function rawQuery(string $line, $params) {
|
||||
if (ZMBuf::get("sql_log") === true) {
|
||||
$starttime = microtime(true);
|
||||
}
|
||||
try {
|
||||
$conn = ZMBuf::$sql_pool->get();
|
||||
if ($conn === false) {
|
||||
throw new DbException("无法连接SQL!" . $line);
|
||||
}
|
||||
$ps = $conn->prepare($line);
|
||||
if ($ps === false) {
|
||||
$conn->close();
|
||||
ZMBuf::$sql_pool->connect_cnt -= 1;
|
||||
throw new DbException("SQL语句查询错误," . $line . ",错误信息:" . $conn->error);
|
||||
} else {
|
||||
if (!($ps instanceof Statement)) {
|
||||
throw new DbException("语句查询错误!" . $line);
|
||||
}
|
||||
if ($params == []) $result = $ps->execute();
|
||||
elseif (!is_array($params)) {
|
||||
$result = $ps->execute([$params]);
|
||||
} else $result = $ps->execute($params);
|
||||
ZMBuf::$sql_pool->put($conn);
|
||||
if ($ps->errno != 0) {
|
||||
throw new DBException("语句[$line]错误!" . $ps->error);
|
||||
//echo json_encode(debug_backtrace(), 128 | 256);
|
||||
}
|
||||
if (ZMBuf::get("sql_log") === true) {
|
||||
$log =
|
||||
"[" . date("Y-m-d H:i:s") .
|
||||
" " . round(microtime(true) - $starttime, 4) .
|
||||
"] " . $line . " " . json_encode($params, JSON_UNESCAPED_UNICODE) . "\n";
|
||||
Coroutine::writeFile(CRASH_DIR . "sql.log", $log, FILE_APPEND);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
} catch (DBException $e) {
|
||||
if (ZMBuf::get("sql_log") === true) {
|
||||
$log =
|
||||
"[" . date("Y-m-d H:i:s") .
|
||||
" " . round(microtime(true) - $starttime, 4) .
|
||||
"] " . $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;
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/ZM/DB/DeleteBody.php
Normal file
28
src/ZM/DB/DeleteBody.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
class DeleteBody
|
||||
{
|
||||
use WhereBody;
|
||||
|
||||
/**
|
||||
* @var Table
|
||||
*/
|
||||
private $table;
|
||||
|
||||
/**
|
||||
* DeleteBody constructor.
|
||||
* @param Table $table
|
||||
*/
|
||||
public function __construct(Table $table) {
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
public function save() {
|
||||
list($sql, $param) = $this->getWhereSQL();
|
||||
return DB::rawQuery("DELETE FROM " . $this->table->getTableName() . " WHERE " . $sql, $param);
|
||||
}
|
||||
}
|
||||
28
src/ZM/DB/InsertBody.php
Normal file
28
src/ZM/DB/InsertBody.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
class InsertBody
|
||||
{
|
||||
/**
|
||||
* @var Table
|
||||
*/
|
||||
private $table;
|
||||
private $row;
|
||||
|
||||
/**
|
||||
* InsertBody constructor.
|
||||
* @param Table $table
|
||||
* @param $row
|
||||
*/
|
||||
public function __construct(Table $table, $row) {
|
||||
$this->table = $table;
|
||||
$this->row = $row;
|
||||
}
|
||||
|
||||
public function save() {
|
||||
DB::rawQuery('INSERT INTO ' . $this->table->getTableName() . ' VALUES ('.implode(',', array_fill(0, 5, '?')).')', $this->row);
|
||||
}
|
||||
}
|
||||
88
src/ZM/DB/SelectBody.php
Normal file
88
src/ZM/DB/SelectBody.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
use Framework\Console;
|
||||
|
||||
class SelectBody
|
||||
{
|
||||
use WhereBody;
|
||||
|
||||
/** @var Table */
|
||||
private $table;
|
||||
|
||||
private $select_thing;
|
||||
|
||||
|
||||
private $result = null;
|
||||
|
||||
public function __construct($table, $select_thing) {
|
||||
$this->table = $table;
|
||||
$this->select_thing = $select_thing;
|
||||
}
|
||||
|
||||
public function get() { return $this->fetchAll(); }
|
||||
|
||||
public function fetchAll() {
|
||||
if ($this->table->isCacheEnabled()) {
|
||||
$rr = md5(implode(",", $this->select_thing) . serialize($this->where_thing));
|
||||
if (array_key_exists($rr, $this->table->cache)) {
|
||||
Console::info('SQL query cached: ' . $rr, date("[H:i:s ") . 'DB] ');
|
||||
return $this->table->cache[$rr]->getResult();
|
||||
}
|
||||
}
|
||||
$this->execute();
|
||||
if ($this->table->isCacheEnabled() && !in_array($rr, $this->table->cache)) {
|
||||
$this->table->cache[$rr] = $this;
|
||||
}
|
||||
return $this->getResult();
|
||||
}
|
||||
|
||||
public function fetchFirst() {
|
||||
return $this->fetchAll()[0] ?? null;
|
||||
}
|
||||
|
||||
public function value() {
|
||||
$r = $this->fetchFirst();
|
||||
if ($r === null) return null;
|
||||
return current($r);
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$str = $this->queryPrepare();
|
||||
$this->result = DB::rawQuery($str[0], $str[1]);
|
||||
}
|
||||
|
||||
public function getResult() { return $this->result; }
|
||||
|
||||
public function equals(SelectBody $body) {
|
||||
if ($this->select_thing != $body->getSelectThing()) return false;
|
||||
elseif ($this->where_thing == $body->getWhereThing()) return false;
|
||||
else return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSelectThing() { return $this->select_thing; }
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getWhereThing() { return $this->where_thing; }
|
||||
|
||||
private function queryPrepare() {
|
||||
$msg = "SELECT " . implode(", ", $this->select_thing) . " FROM " . $this->table->getTableName();
|
||||
$sql = $this->table->paintWhereSQL($this->where_thing['='] ?? [], '=');
|
||||
if ($sql[0] != '') {
|
||||
$msg .= " WHERE " . $sql[0];
|
||||
$array = $sql[1];
|
||||
$sql = $this->table->paintWhereSQL($this->where_thing['!='] ?? [], '!=');
|
||||
if ($sql[0] != '') $msg .= " AND " . $sql[0];
|
||||
$array = array_merge($array, $sql[1]);
|
||||
}
|
||||
return [$msg, $array ?? []];
|
||||
}
|
||||
}
|
||||
79
src/ZM/DB/Table.php
Normal file
79
src/ZM/DB/Table.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
|
||||
class Table
|
||||
{
|
||||
private $table_name;
|
||||
|
||||
/** @var SelectBody[] */
|
||||
public $cache = [];
|
||||
|
||||
private static $table_instance = [];
|
||||
|
||||
private $enable_cache;
|
||||
|
||||
public function __construct($table_name, $enable_cache) {
|
||||
$this->enable_cache = $enable_cache;
|
||||
$this->table_name = $table_name;
|
||||
self::$table_instance[$table_name] = $this;
|
||||
}
|
||||
|
||||
public static function getTableInstance($table_name) {
|
||||
if (isset(self::$table_instance[$table_name])) return self::$table_instance[$table_name];
|
||||
else return null;
|
||||
}
|
||||
|
||||
public function select($what = []) {
|
||||
return new SelectBody($this, $what == [] ? ["*"] : $what);
|
||||
}
|
||||
|
||||
public function where($column, $operation_or_value, $value = null){
|
||||
return (new SelectBody($this, ["*"]))->where($column, $operation_or_value, $value);
|
||||
}
|
||||
|
||||
public function insert($row) {
|
||||
$this->cache = [];
|
||||
return new InsertBody($this, $row);
|
||||
}
|
||||
|
||||
public function update(array $set_value) {
|
||||
$this->cache = [];
|
||||
return new UpdateBody($this, $set_value);
|
||||
}
|
||||
|
||||
public function delete() {
|
||||
$this->cache = [];
|
||||
return new DeleteBody($this);
|
||||
}
|
||||
|
||||
public function statement($line){
|
||||
$this->cache = [];
|
||||
//TODO: 无返回的statement语句
|
||||
}
|
||||
|
||||
public function paintWhereSQL($rule, $operator) {
|
||||
if ($rule == []) return ["", []];
|
||||
$msg = "";
|
||||
$param = [];
|
||||
foreach ($rule as $k => $v) {
|
||||
if ($msg == "") {
|
||||
$msg .= $k . " $operator ? ";
|
||||
} else {
|
||||
$msg .= "AND " . $k . " $operator ?";
|
||||
}
|
||||
$param[] = $v;
|
||||
}
|
||||
return [$msg, $param];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTableName() { return $this->table_name; }
|
||||
|
||||
public function isCacheEnabled() { return $this->enable_cache; }
|
||||
}
|
||||
50
src/ZM/DB/UpdateBody.php
Normal file
50
src/ZM/DB/UpdateBody.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
use ZM\Exception\DbException;
|
||||
|
||||
class UpdateBody
|
||||
{
|
||||
use WhereBody;
|
||||
|
||||
/**
|
||||
* @var Table
|
||||
*/
|
||||
private $table;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $set_value;
|
||||
/**
|
||||
* UpdateBody constructor.
|
||||
* @param Table $table
|
||||
* @param array $set_value
|
||||
*/
|
||||
public function __construct(Table $table, array $set_value) {
|
||||
$this->table = $table;
|
||||
$this->set_value = $set_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DbException
|
||||
*/
|
||||
public function save(){
|
||||
$arr = [];
|
||||
$msg = [];
|
||||
foreach($this->set_value as $k => $v) {
|
||||
$msg []= $k .' = ?';
|
||||
$arr[]=$v;
|
||||
}
|
||||
if(($msg ?? []) == []) throw new DbException('update value sets can not be empty!');
|
||||
$line = 'UPDATE '.$this->table->getTableName().' SET '.implode(', ', $msg);
|
||||
if($this->where_thing != []) {
|
||||
list($sql, $param) = $this->getWhereSQL();
|
||||
$arr = array_merge($arr, $param);
|
||||
$line .= ' WHERE '.$sql;
|
||||
}
|
||||
return DB::rawQuery($line, $arr);
|
||||
}
|
||||
}
|
||||
33
src/ZM/DB/WhereBody.php
Normal file
33
src/ZM/DB/WhereBody.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DB;
|
||||
|
||||
|
||||
trait WhereBody
|
||||
{
|
||||
protected $where_thing = [];
|
||||
|
||||
public function where($column, $operation_or_value, $value = null) {
|
||||
if (!in_array($operation_or_value, ['=', '!='])) $this->where_thing['='][$column] = $operation_or_value;
|
||||
elseif ($value !== null) $this->where_thing[$operation_or_value][$column] = $value;
|
||||
else $this->where_thing['='][$column] = $operation_or_value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getWhereSQL(){
|
||||
$param = [];
|
||||
$msg = '';
|
||||
foreach($this->where_thing as $k => $v) {
|
||||
foreach($v as $ks => $vs) {
|
||||
if($param != []) {
|
||||
$msg .= ' AND '.$ks ." $k ?";
|
||||
} else {
|
||||
$msg .= "$ks $k ?";
|
||||
}
|
||||
$param []=$vs;
|
||||
}
|
||||
}
|
||||
return [$msg, $param];
|
||||
}
|
||||
}
|
||||
18
src/ZM/DBCache/CourseCache.php
Normal file
18
src/ZM/DBCache/CourseCache.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DBCache;
|
||||
|
||||
|
||||
class CourseCache implements DBCache
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $data;
|
||||
|
||||
public static function reset() {
|
||||
self::$data = [];
|
||||
}
|
||||
}
|
||||
10
src/ZM/DBCache/DBCache.php
Normal file
10
src/ZM/DBCache/DBCache.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DBCache;
|
||||
|
||||
|
||||
interface DBCache
|
||||
{
|
||||
public static function reset();
|
||||
}
|
||||
15
src/ZM/DBCache/DBCacheManager.php
Normal file
15
src/ZM/DBCache/DBCacheManager.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DBCache;
|
||||
|
||||
|
||||
class DBCacheManager
|
||||
{
|
||||
public static function freeAllCache(){
|
||||
DHUerCache::reset();
|
||||
UserCache::reset();
|
||||
GroupCache::reset();
|
||||
CourseCache::reset();
|
||||
}
|
||||
}
|
||||
17
src/ZM/DBCache/DHUerCache.php
Normal file
17
src/ZM/DBCache/DHUerCache.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DBCache;
|
||||
|
||||
|
||||
class DHUerCache implements DBCache
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $data;
|
||||
|
||||
public static function reset() {
|
||||
self::$data = [];
|
||||
}
|
||||
}
|
||||
18
src/ZM/DBCache/GroupCache.php
Normal file
18
src/ZM/DBCache/GroupCache.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DBCache;
|
||||
|
||||
|
||||
class GroupCache implements DBCache
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $data;
|
||||
|
||||
public static function reset() {
|
||||
self::$data = [];
|
||||
}
|
||||
}
|
||||
18
src/ZM/DBCache/UserCache.php
Normal file
18
src/ZM/DBCache/UserCache.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\DBCache;
|
||||
|
||||
|
||||
class UserCache implements DBCache
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $data;
|
||||
|
||||
public static function reset() {
|
||||
self::$data = [];
|
||||
}
|
||||
}
|
||||
149
src/ZM/Event/CQ/MessageEvent.php
Normal file
149
src/ZM/Event/CQ/MessageEvent.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\CQ;
|
||||
|
||||
|
||||
use Co;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Annotation\CQ\CQAfter;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQCommand;
|
||||
use ZM\Annotation\CQ\CQMessage;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
|
||||
class MessageEvent
|
||||
{
|
||||
private $function_call = false;
|
||||
private $data;
|
||||
private $circle;
|
||||
/**
|
||||
* @var \ZM\Event\Swoole\MessageEvent
|
||||
*/
|
||||
private $swoole_event;
|
||||
|
||||
public function __construct($data, \ZM\Event\Swoole\MessageEvent $event, $circle = 0) {
|
||||
$this->data = $data;
|
||||
$this->swoole_event = $event;
|
||||
$this->circle = $circle;
|
||||
}
|
||||
|
||||
public function onBefore() {
|
||||
foreach (ZMBuf::$events[CQBefore::class][CQMessage::class] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_MESSAGE);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
}
|
||||
foreach (ZMBuf::get("wait_api", []) as $k => $v) {
|
||||
if($this->data["user_id"] == $v["user_id"] &&
|
||||
$this->data["self_id"] == $v["self_id"] &&
|
||||
$this->data["message_type"] == $v["message_type"] &&
|
||||
($this->data[$this->data["message_type"]."_id"] ?? $this->data["user_id"]) ==
|
||||
($v[$v["message_type"]."_id"] ?? $v["user_id"])){
|
||||
$v["result"] = $this->data["message"];
|
||||
ZMBuf::appendKey("wait_api", $k, $v);
|
||||
Co::resume($v["coroutine"]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @noinspection PhpRedundantCatchClauseInspection */
|
||||
public function onActivate() {
|
||||
try {
|
||||
$word = split_explode(" ", str_replace("\r", "", $this->data["message"]));
|
||||
if (count(explode("\n", $word[0])) >= 2) {
|
||||
$enter = explode("\n", $this->data["message"]);
|
||||
$first = split_explode(" ", array_shift($enter));
|
||||
$word = array_merge($first, $enter);
|
||||
foreach ($word as $k => $v) {
|
||||
$word[$k] = trim($word[$k]);
|
||||
}
|
||||
}
|
||||
/** @var ModBase[] $obj */
|
||||
$obj = [];
|
||||
foreach (ZMBuf::$events[CQCommand::class] ?? [] as $v) {
|
||||
/** @var CQCommand $v */
|
||||
if ($v->match == "" && $v->regexMatch == "") continue;
|
||||
else {
|
||||
$c = $v->class;
|
||||
if (!isset($obj[$c]))
|
||||
$obj[$c] = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_MESSAGE);
|
||||
if ($word[0] != "" && $v->match == $word[0]) {
|
||||
$r = call_user_func([$obj[$c], $v->method], $word);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
$this->function_call = true;
|
||||
return;
|
||||
} elseif (($args = matchArgs($v->regexMatch, $this->data["message"])) !== false) {
|
||||
$r = call_user_func([$obj[$c], $v->method], $args);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
$this->function_call = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (ZMBuf::$events[CQMessage::class] ?? [] as $v) {
|
||||
/** @var CQMessage $v */
|
||||
if (
|
||||
($v->message == '' || ($v->message != '' && $v->message == $this->data["message"])) &&
|
||||
($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == $this->data["user_id"])) &&
|
||||
($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == ($this->data["group_id"] ?? 0))) &&
|
||||
($v->discuss_id == 0 || ($v->discuss_id != 0 && $v->discuss_id == ($this->data["discuss_id"] ?? 0))) &&
|
||||
($v->message_type == '' || ($v->message_type != '' && $v->message_type == $this->data["message_type"])) &&
|
||||
($v->raw_message == '' || ($v->raw_message != '' && $v->raw_message == $this->data["raw_message"]))) {
|
||||
$c = $v->class;
|
||||
if (!isset($obj[$c]))
|
||||
$obj[$c] = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_MESSAGE);
|
||||
$r = call_user_func([$obj[$c], $v->method], $this->data["message"]);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
if ($obj[$c]->block_continue) return;
|
||||
}
|
||||
}
|
||||
} catch (WaitTimeoutException $e) {
|
||||
|
||||
$e->module->finalReply($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在调用完事件后执行的
|
||||
*/
|
||||
public function onAfter() {
|
||||
foreach (ZMBuf::$events[CQAfter::class][CQMessage::class] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_MESSAGE);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function hasReply() {
|
||||
return $this->function_call;
|
||||
}
|
||||
}
|
||||
71
src/ZM/Event/CQ/MetaEvent.php
Normal file
71
src/ZM/Event/CQ/MetaEvent.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\CQ;
|
||||
|
||||
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQMetaEvent;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
|
||||
class MetaEvent
|
||||
{
|
||||
private $data;
|
||||
/** @var \ZM\Event\Swoole\MessageEvent */
|
||||
private $swoole_event;
|
||||
private $circle;
|
||||
|
||||
public function __construct($data, \ZM\Event\Swoole\MessageEvent $event, $circle = 0) {
|
||||
$this->data = $data;
|
||||
$this->swoole_event = $event;
|
||||
$this->circle = $circle;
|
||||
}
|
||||
|
||||
public function onBefore() {
|
||||
foreach (ZMBuf::$events[CQBefore::class][CQMetaEvent::class] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
/** @var CQMetaEvent $v */
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_META_EVENT);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @noinspection PhpRedundantCatchClauseInspection */
|
||||
public function onActivate() {
|
||||
try {
|
||||
/** @var ModBase[] $obj */
|
||||
$obj = [];
|
||||
foreach (ZMBuf::$events[CQMetaEvent::class] ?? [] as $v) {
|
||||
/** @var CQMetaEvent $v */
|
||||
if (
|
||||
($v->meta_event_type == '' || ($v->meta_event_type != '' && $v->meta_event_type == $this->data["meta_event_type"])) &&
|
||||
($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"]))) {
|
||||
$c = $v->class;
|
||||
if (!isset($obj[$c]))
|
||||
$obj[$c] = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_META_EVENT);
|
||||
$r = call_user_func([$obj[$c], $v->method]);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
if ($obj[$c]->block_continue) return;
|
||||
}
|
||||
}
|
||||
} catch (WaitTimeoutException $e) {
|
||||
$e->module->finalReply($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
88
src/ZM/Event/CQ/NoticeEvent.php
Normal file
88
src/ZM/Event/CQ/NoticeEvent.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\CQ;
|
||||
|
||||
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Annotation\CQ\CQAfter;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQNotice;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
|
||||
class NoticeEvent
|
||||
{
|
||||
private $data;
|
||||
/** @var \ZM\Event\Swoole\MessageEvent */
|
||||
private $swoole_event;
|
||||
private $circle;
|
||||
|
||||
public function __construct($data, \ZM\Event\Swoole\MessageEvent $event, $circle = 0) {
|
||||
$this->data = $data;
|
||||
$this->swoole_event = $event;
|
||||
$this->circle = $circle;
|
||||
}
|
||||
|
||||
public function onBefore() {
|
||||
foreach (ZMBuf::$events[CQBefore::class][CQNotice::class] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
/** @var CQNotice $v */
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_NOTICE);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onActivate() {
|
||||
try {
|
||||
/** @var ModBase[] $obj */
|
||||
$obj = [];
|
||||
foreach (ZMBuf::$events[CQNotice::class] ?? [] as $v) {
|
||||
/** @var CQNotice $v */
|
||||
if (
|
||||
($v->notice_type == '' || ($v->notice_type != '' && $v->notice_type == $this->data["notice_type"])) &&
|
||||
($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"])) &&
|
||||
($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == ($this->data["group_id"] ?? 0))) &&
|
||||
($v->operator_id == 0 || ($v->operator_id != 0 && $v->operator_id == ($this->data["operator_id"] ?? 0)))) {
|
||||
$c = $v->class;
|
||||
if (!isset($obj[$c]))
|
||||
$obj[$c] = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_NOTICE);
|
||||
$r = call_user_func([$obj[$c], $v->method]);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
if ($obj[$c]->block_continue) return;
|
||||
}
|
||||
}
|
||||
} /** @noinspection PhpRedundantCatchClauseInspection */ catch (WaitTimeoutException $e) {
|
||||
$e->module->finalReply($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function onAfter() {
|
||||
foreach (ZMBuf::$events[CQAfter::class][CQNotice::class] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_NOTICE);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
89
src/ZM/Event/CQ/RequestEvent.php
Normal file
89
src/ZM/Event/CQ/RequestEvent.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\CQ;
|
||||
|
||||
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Annotation\CQ\CQAfter;
|
||||
use ZM\Annotation\CQ\CQBefore;
|
||||
use ZM\Annotation\CQ\CQRequest;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
|
||||
class RequestEvent
|
||||
{
|
||||
private $data;
|
||||
/** @var \ZM\Event\Swoole\MessageEvent */
|
||||
private $swoole_event;
|
||||
private $circle;
|
||||
|
||||
public function __construct($data, \ZM\Event\Swoole\MessageEvent $event, $circle = 0) {
|
||||
$this->data = $data;
|
||||
$this->swoole_event = $event;
|
||||
$this->circle = $circle;
|
||||
}
|
||||
|
||||
public function onBefore() {
|
||||
foreach (ZMBuf::$events[CQBefore::class][CQRequest::class] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
/** @var CQRequest $v */
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_REQUEST);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @noinspection PhpRedundantCatchClauseInspection */
|
||||
public function onActivate() {
|
||||
try {
|
||||
/** @var ModBase[] $obj */
|
||||
$obj = [];
|
||||
foreach (ZMBuf::$events[CQRequest::class] ?? [] as $v) {
|
||||
/** @var CQRequest $v */
|
||||
if (
|
||||
($v->request_type == '' || ($v->request_type != '' && $v->request_type == $this->data["request_type"])) &&
|
||||
($v->sub_type == 0 || ($v->sub_type != 0 && $v->sub_type == $this->data["sub_type"])) &&
|
||||
($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == ($this->data["user_id"] ?? 0))) &&
|
||||
($v->comment == 0 || ($v->comment != 0 && $v->comment == ($this->data["comment"] ?? 0)))) {
|
||||
$c = $v->class;
|
||||
if (!isset($obj[$c]))
|
||||
$obj[$c] = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_REQUEST);
|
||||
$r = call_user_func([$obj[$c], $v->method]);
|
||||
if (is_string($r)) $obj[$c]->reply($r);
|
||||
if ($obj[$c]->block_continue) return;
|
||||
}
|
||||
}
|
||||
} catch (WaitTimeoutException $e) {
|
||||
$e->module->finalReply($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function onAfter() {
|
||||
foreach (ZMBuf::$events[CQAfter::class][CQRequest::class] ?? [] as $v) {
|
||||
$c = $v->class;
|
||||
$class = new $c([
|
||||
"data" => $this->data,
|
||||
"frame" => $this->swoole_event->frame,
|
||||
"server" => $this->swoole_event->server,
|
||||
"connection" => ConnectionManager::get($this->swoole_event->frame->fd)
|
||||
], ModHandleType::CQ_REQUEST);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if (!$r || $class->block_continue) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
11
src/ZM/Event/Event.php
Normal file
11
src/ZM/Event/Event.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event;
|
||||
|
||||
|
||||
interface Event
|
||||
{
|
||||
const SWOOLE = 1;
|
||||
const CQ = 2;
|
||||
}
|
||||
115
src/ZM/Event/EventHandler.php
Normal file
115
src/ZM/Event/EventHandler.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event;
|
||||
|
||||
|
||||
use Co;
|
||||
use Exception;
|
||||
use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
use ZM\Event\Swoole\{MessageEvent, RequestEvent, WorkerStartEvent, WSCloseEvent, WSOpenEvent};
|
||||
use ZM\Http\Response;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class EventHandler
|
||||
{
|
||||
public static function callSwooleEvent($event_name, $param0, $param1 = null) {
|
||||
$starttime = microtime(true);
|
||||
$event_name = strtolower($event_name);
|
||||
switch ($event_name) {
|
||||
case "workerstart":
|
||||
try {
|
||||
(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;
|
||||
}
|
||||
break;
|
||||
case "message":
|
||||
(new MessageEvent($param0, $param1))->onActivate()->onAfter();
|
||||
break;
|
||||
case "request":
|
||||
try {
|
||||
(new RequestEvent($param0, $param1))->onActivate()->onAfter();
|
||||
} catch (Exception $e) {
|
||||
/** @var Response $param1 */
|
||||
$param1->status(500);
|
||||
if (!$param1->isEnd()) $param1->end("Internal server error: " . $e->getMessage());
|
||||
Console::error("Internal server error (500), caused by uncaught exception.");
|
||||
Console::log($e->getTraceAsString(), "gray");
|
||||
}
|
||||
break;
|
||||
case "open":
|
||||
(new WSOpenEvent($param0, $param1))->onActivate()->onAfter();
|
||||
break;
|
||||
case "close":
|
||||
(new WSCloseEvent($param0, $param1))->onActivate()->onAfter();
|
||||
break;
|
||||
}
|
||||
Console::info(Console::setColor("Event: " . $event_name . " 运行了 " . round(microtime(true) - $starttime, 5) . " 秒", "gold"));
|
||||
}
|
||||
|
||||
public static function callCQEvent($event_data, MessageEvent $event, $level = 0) {
|
||||
if ($level >= 5) {
|
||||
Console::warning("Recursive call reached " . $level . " times");
|
||||
Console::stackTrace();
|
||||
return false;
|
||||
}
|
||||
$starttime = microtime(true);
|
||||
switch ($event_data["post_type"]) {
|
||||
case "message":
|
||||
$event = new CQ\MessageEvent($event_data, $event, $level);
|
||||
if ($event->onBefore()) $event->onActivate();
|
||||
$event->onAfter();
|
||||
return $event->hasReply();
|
||||
break;
|
||||
case "notice":
|
||||
$event = new CQ\NoticeEvent($event_data, $event, $level);
|
||||
if($event->onBefore()) $event->onActivate();
|
||||
$event->onAfter();
|
||||
return true;
|
||||
case "request":
|
||||
$event = new CQ\RequestEvent($event_data, $event, $level);
|
||||
if($event->onBefore()) $event->onActivate();
|
||||
$event->onAfter();
|
||||
return true;
|
||||
case "meta_event":
|
||||
$event = new CQ\MetaEvent($event_data, $event, $level);
|
||||
if($event->onBefore()) $event->onActivate();
|
||||
return true;
|
||||
}
|
||||
unset($starttime);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function callCQResponse($req) {
|
||||
//Console::info("收到来自API连接的回复:".json_encode($req, 128|256));
|
||||
if(isset($req["echo"]) && ZMBuf::array_key_exists("sent_api", $req["echo"])) {
|
||||
$status = $req["status"];
|
||||
$retcode = $req["retcode"];
|
||||
$data = $req["data"];
|
||||
$origin = ZMBuf::get("sent_api")[$req["echo"]];
|
||||
$self_id = $origin["self_id"];
|
||||
$response = [
|
||||
"status" => $status,
|
||||
"retcode" => $retcode,
|
||||
"data" => $data,
|
||||
"self_id" => $self_id
|
||||
];
|
||||
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"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
94
src/ZM/Event/Swoole/MessageEvent.php
Normal file
94
src/ZM/Event/Swoole/MessageEvent.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\Swoole;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\Annotation\Swoole\SwooleEventAfter;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use Exception;
|
||||
use ZM\Event\EventHandler;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class MessageEvent implements SwooleEvent
|
||||
{
|
||||
/**
|
||||
* @var Server
|
||||
*/
|
||||
public $server;
|
||||
/**
|
||||
* @var Frame
|
||||
*/
|
||||
public $frame;
|
||||
|
||||
public function __construct(Server $server, Frame $frame) {
|
||||
$this->server = $server;
|
||||
$this->frame = $frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function onActivate() {
|
||||
ZMUtil::checkWait();
|
||||
try {
|
||||
if (ConnectionManager::get($this->frame->fd)->getType() == "qq") {
|
||||
$data = json_decode($this->frame->data, true);
|
||||
if (isset($data["post_type"]))
|
||||
EventHandler::callCQEvent($data, $this, 0);
|
||||
else
|
||||
EventHandler::callCQResponse($data);
|
||||
}
|
||||
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "message" && $this->parseSwooleRule($v)) {
|
||||
$conn = ConnectionManager::get($this->frame->fd);
|
||||
$c = $v->class;
|
||||
/** @var ModBase $class */
|
||||
$class = new $c(["server" => $this->server, "frame" => $this->frame, "connection" => $conn], ModHandleType::SWOOLE_MESSAGE);
|
||||
call_user_func_array([$class, $v->method], [$conn]);
|
||||
if ($class->block_continue) break;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Console::error("出现错误: " . $e->getMessage());
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function onAfter() {
|
||||
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "message" && $this->parseSwooleRule($v) === true) {
|
||||
$conn = ConnectionManager::get($this->frame->fd);
|
||||
$c = $v->class;
|
||||
/** @var ModBase $class */
|
||||
$class = new $c(["server" => $this->server, "frame" => $this->frame, "connection" => $conn], ModHandleType::SWOOLE_MESSAGE);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
if ($class->block_continue) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function parseSwooleRule($v) {
|
||||
switch (explode(":", $v->rule)[0]) {
|
||||
case "connectType": //websocket连接类型
|
||||
if ($v->callback instanceof Closure) return call_user_func($v->callback, ConnectionManager::get($this->frame->fd));
|
||||
break;
|
||||
case "dataEqual": //handle websocket message事件时才能用
|
||||
if ($v->callback instanceof Closure) return call_user_func($v->callback, $this->frame->data);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
132
src/ZM/Event/Swoole/RequestEvent.php
Normal file
132
src/ZM/Event/Swoole/RequestEvent.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\Swoole;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Http\Request;
|
||||
use ZM\Annotation\Swoole\SwooleEventAfter;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Http\Response;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class RequestEvent implements SwooleEvent
|
||||
{
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
private $request;
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
private $response;
|
||||
|
||||
public function __construct(Request $request, Response $response) {
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function onActivate() {
|
||||
ZMUtil::checkWait();
|
||||
foreach (ZMBuf::globals("http_header") as $k => $v) {
|
||||
$this->response->setHeader($k, $v);
|
||||
}
|
||||
$uri = $this->request->server["request_uri"];
|
||||
if ($uri != "/") {
|
||||
$uri = explode("/", $uri);
|
||||
$uri = array_diff($uri, ["..", "", "."]);
|
||||
$node = ZMBuf::$req_mapping_node;
|
||||
$params = [];
|
||||
while (true) {
|
||||
$r = array_shift($uri);
|
||||
if ($r === null) break;
|
||||
if (($node2 = $node->getRealRoute($r, $params)) !== null) {
|
||||
$node = $node2;
|
||||
continue;
|
||||
} else {
|
||||
$this->responseStatus(404);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
if ($node->getRule() === null || call_user_func($node->getRule(), $this->request) === true) { //判断规则是否存在,如果有规则则走一遍规则
|
||||
if (in_array(strtoupper($this->request->server["request_method"]), $node->getRequestMethod())) { //判断目标方法在不在里面
|
||||
$c_name = $node->getClass();
|
||||
/** @var ModBase $class */
|
||||
$class = new $c_name(["request" => $this->request, "response" => $this->response, "params" => $params], ModHandleType::SWOOLE_REQUEST);
|
||||
$r = call_user_func_array([$class, $node->getMethod()], [$params]);
|
||||
if (is_string($r) && !$this->response->isEnd()) $this->response->end($r);
|
||||
if ($class->block_continue) return $this;
|
||||
if ($this->response->isEnd()) return $this;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (($node = ZMBuf::$req_mapping_node)->getMethod() !== null) {
|
||||
if (in_array(strtoupper($this->request->server["request_method"]), $node->getRequestMethod())) { //判断目标方法在不在里面
|
||||
$c_name = $node->getClass();
|
||||
/** @var ModBase $class */
|
||||
$class = new $c_name(["request" => $this->request, "response" => $this->response], ModHandleType::SWOOLE_REQUEST);
|
||||
$r = call_user_func_array([$class, $node->getMethod()], []);
|
||||
if (is_string($r) && !$this->response->isEnd()) $this->response->end($r);
|
||||
if ($class->block_continue) return $this;
|
||||
if ($this->response->isEnd()) return $this;
|
||||
}
|
||||
}
|
||||
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) {
|
||||
$c = $v->class;
|
||||
$class = new $c(["request" => $this->request, "response" => $this->response]);
|
||||
$r = call_user_func_array([$class, $v->method], []);
|
||||
if ($class->block_continue) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$this->response->isEnd()) {
|
||||
$this->response->status(404);
|
||||
$this->response->end(ZMUtil::getHttpCodePage(404));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function onAfter() {
|
||||
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) {
|
||||
$c = $v->class;
|
||||
$class = new $c(["request" => $this->request, "response" => $this->response]);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
if ($class->block_continue) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function responseStatus(int $int) {
|
||||
$this->response->status($int);
|
||||
$this->response->end();
|
||||
}
|
||||
|
||||
private function parseSwooleRule($v) {
|
||||
switch ($v->rule) {
|
||||
case "containsGet":
|
||||
case "containsPost":
|
||||
if ($v->callback instanceof Closure) return call_user_func($v->callback, $this->request);
|
||||
break;
|
||||
case "containsJson":
|
||||
$content = $this->request->rawContent();
|
||||
$content = json_decode($content, true);
|
||||
if ($content === null) return false;
|
||||
if ($v->callback instanceof Closure) return call_user_func($v->callback, $content);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
20
src/ZM/Event/Swoole/SwooleEvent.php
Normal file
20
src/ZM/Event/Swoole/SwooleEvent.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\Swoole;
|
||||
|
||||
|
||||
use ZM\Event\Event;
|
||||
|
||||
interface SwooleEvent extends Event
|
||||
{
|
||||
/**
|
||||
* @return SwooleEvent
|
||||
*/
|
||||
public function onActivate();
|
||||
|
||||
/**
|
||||
* @return SwooleEvent
|
||||
*/
|
||||
public function onAfter();
|
||||
}
|
||||
61
src/ZM/Event/Swoole/WSCloseEvent.php
Normal file
61
src/ZM/Event/Swoole/WSCloseEvent.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\Swoole;
|
||||
|
||||
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Server;
|
||||
use ZM\Annotation\Swoole\SwooleEventAfter;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class WSCloseEvent implements SwooleEvent
|
||||
{
|
||||
public $server;
|
||||
|
||||
public $fd;
|
||||
|
||||
public function __construct(Server $server, int $fd) {
|
||||
$this->server = $server;
|
||||
$this->fd = $fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function onActivate() {
|
||||
ZMUtil::checkWait();
|
||||
foreach(ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||
if(strtolower($v->type) == "close" && $this->parseSwooleRule($v)) {
|
||||
$c = $v->class;
|
||||
$class = new $c(["server" => $this->server, "fd" => $this->fd], ModHandleType::SWOOLE_CLOSE);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
if($class->block_continue) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function onAfter() {
|
||||
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "close" && $this->parseSwooleRule($v) === true) {
|
||||
$c = $v->class;
|
||||
/** @var ModBase $class */
|
||||
$class = new $c(["server" => $this->server, "fd" => $this->fd], ModHandleType::SWOOLE_CLOSE);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
if($class->block_continue) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function parseSwooleRule($v) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
93
src/ZM/Event/Swoole/WSOpenEvent.php
Normal file
93
src/ZM/Event/Swoole/WSOpenEvent.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\Swoole;
|
||||
|
||||
|
||||
use Closure;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Http\Request;
|
||||
use Swoole\WebSocket\Server;
|
||||
use ZM\Annotation\Swoole\SwooleEventAfter;
|
||||
use ZM\Annotation\Swoole\SwooleEventAt;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\Connection\CQConnection;
|
||||
use ZM\Connection\UnknownConnection;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
use ZM\Utils\ZMUtil;
|
||||
|
||||
class WSOpenEvent implements SwooleEvent
|
||||
{
|
||||
/**
|
||||
* @var Server
|
||||
*/
|
||||
private $server;
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
private $request;
|
||||
/**
|
||||
* @var WSConnection
|
||||
*/
|
||||
private $conn;
|
||||
|
||||
public function __construct(Server $server, Request $request) {
|
||||
$this->server = $server;
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function onActivate() {
|
||||
ZMUtil::checkWait();
|
||||
$type = strtolower($this->request->get["type"] ?? $this->request->header["x-client-role"] ?? "");
|
||||
$type_conn = ConnectionManager::getTypeClassName($type);
|
||||
if ($type_conn == CQConnection::class) {
|
||||
$qq = $this->request->get["qq"] ?? $this->request->header["x-self-id"] ?? "";
|
||||
$self_token = ZMBuf::globals("access_token") ?? "";
|
||||
$remote_token = $this->request->get["token"] ?? (isset($header["authorization"]) ? explode(" ", $this->request->header["authorization"])[1] : "");
|
||||
if ($qq != "" && ($self_token == $remote_token)) $this->conn = new CQConnection($this->server, $this->request->fd, $qq);
|
||||
else $this->conn = new UnknownConnection($this->server, $this->request->fd);
|
||||
} else {
|
||||
$this->conn = new $type_conn($this->server, $this->request->fd);
|
||||
}
|
||||
ZMBuf::$connect[$this->request->fd] = $this->conn;
|
||||
foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) {
|
||||
$c = $v->class;
|
||||
$class = new $c(["server" => $this->server, "request" => $this->request, "connection" => $this->conn], ModHandleType::SWOOLE_OPEN);
|
||||
call_user_func_array([$class, $v->method], [$this->conn]);
|
||||
if ($class->block_continue) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function onAfter() {
|
||||
if (!$this->conn->exists()) return $this;
|
||||
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
||||
if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) {
|
||||
/** @var ModBase $class */
|
||||
$class = new $v["class"](["server" => $this->server, "request" => $this->request, "connection" => $this->conn], ModHandleType::SWOOLE_OPEN);
|
||||
call_user_func_array([$class, $v["method"]], [$this->conn]);
|
||||
if ($class->block_continue) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function parseSwooleRule($v) {
|
||||
switch (explode(":", $v->rule)[0]) {
|
||||
case "connectType": //websocket连接类型
|
||||
if ($v->callback instanceof Closure) return call_user_func($v->callback, $this->conn);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
137
src/ZM/Event/Swoole/WorkerStartEvent.php
Normal file
137
src/ZM/Event/Swoole/WorkerStartEvent.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Event\Swoole;
|
||||
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
use ReflectionException;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Timer;
|
||||
use ZM\Annotation\AnnotationBase;
|
||||
use ZM\Annotation\AnnotationParser;
|
||||
use ZM\Annotation\MappingNode;
|
||||
use ZM\Annotation\Swoole\SwooleEventAfter;
|
||||
use ZM\Connection\ConnectionManager;
|
||||
use ZM\DB\DB;
|
||||
use ZM\DBCache\DBCacheManager;
|
||||
use Framework\Console;
|
||||
use Framework\GlobalConfig;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Server;
|
||||
use ZM\ModBase;
|
||||
use ZM\ModHandleType;
|
||||
use ZM\Utils\DataProvider;
|
||||
use ZM\Utils\SQLPool;
|
||||
|
||||
class WorkerStartEvent implements SwooleEvent
|
||||
{
|
||||
private $worker_id;
|
||||
/**
|
||||
* @var Server
|
||||
*/
|
||||
private $server;
|
||||
|
||||
public function __construct(Server $server, $worker_id) {
|
||||
$this->server = $server;
|
||||
$this->worker_id = $worker_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WorkerStartEvent
|
||||
* @throws AnnotationException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function onActivate(): WorkerStartEvent {
|
||||
Console::info("Worker启动中");
|
||||
ZMBuf::resetCache(); //清空变量缓存
|
||||
ZMBuf::set("wait_start", []); //添加队列,在workerStart运行完成前先让其他协程等待执行
|
||||
DBCacheManager::freeAllCache(); // 清空数据库缓存
|
||||
$this->resetConnections();//释放所有与framework的连接
|
||||
|
||||
//设置炸毛buf中储存的对象
|
||||
ZMBuf::$globals = new GlobalConfig();
|
||||
if (ZMBuf::globals("sql_config")["sql_host"] != "") {
|
||||
Console::info("新建SQL连接池中");
|
||||
ZMBuf::$sql_pool = new SQLPool();
|
||||
DB::initTableList();
|
||||
}
|
||||
ZMBuf::$server = $this->server;
|
||||
ZMBuf::$atomics['reload_time']->add(1);
|
||||
ZMBuf::$req_mapping_node = new MappingNode("/");
|
||||
|
||||
Console::info("监听console输入");
|
||||
Console::listenConsole(); //这个方法只能在这里调用,且如果worker_num不为1的话,此功能不可用
|
||||
|
||||
$this->loadAllClass(); //加载composer资源、phar外置包、注解解析注册等
|
||||
|
||||
$this->setAutosaveTimer(ZMBuf::globals("auto_save_interval"));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onAfter(): WorkerStartEvent {
|
||||
foreach (ZMBuf::get("wait_start") as $v) {
|
||||
Coroutine::resume($v);
|
||||
}
|
||||
ZMBuf::unsetCache("wait_start");
|
||||
foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) {
|
||||
/** @var AnnotationBase $v */
|
||||
if (strtolower($v->type) == "workerstart") {
|
||||
$class_name = $v->class;
|
||||
/** @var ModBase $class */
|
||||
$class = new $class_name(["server" => $this->server, "worker_id" => $this->worker_id], ModHandleType::SWOOLE_WORKER_START);
|
||||
call_user_func_array([$class, $v->method], []);
|
||||
if ($class->block_continue) break;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function resetConnections() {
|
||||
foreach ($this->server->connections as $v) {
|
||||
$this->server->close($v);
|
||||
}
|
||||
if (ZMBuf::$sql_pool instanceof SqlPool) {
|
||||
ZMBuf::$sql_pool->destruct();
|
||||
ZMBuf::$sql_pool = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AnnotationException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
private function loadAllClass() {
|
||||
//加载phar包
|
||||
Console::info("加载外部phar包中");
|
||||
$dir = WORKING_DIR . "/resources/package/";
|
||||
if (is_dir($dir)) {
|
||||
$list = scandir($dir);
|
||||
unset($list[0], $list[1]);
|
||||
foreach ($list as $v) {
|
||||
if (is_dir($dir . $v)) continue;
|
||||
if (pathinfo($dir . $v, 4) == "phar") require_once($dir . $v);
|
||||
}
|
||||
}
|
||||
|
||||
//加载composer类
|
||||
Console::info("加载composer资源中");
|
||||
/** @noinspection PhpIncludeInspection */
|
||||
require_once WORKING_DIR . "/vendor/autoload.php";
|
||||
|
||||
//加载各个模块的注解类,以及反射
|
||||
Console::info("检索Module中");
|
||||
AnnotationParser::registerMods();
|
||||
|
||||
//加载Custom目录下的自定义的内部类
|
||||
ConnectionManager::registerCustomClass();
|
||||
}
|
||||
|
||||
private function setAutosaveTimer($globals) {
|
||||
DataProvider::$buffer_list = [];
|
||||
Timer::tick($globals * 1000, function() {
|
||||
DataProvider::saveBuffer();
|
||||
});
|
||||
}
|
||||
}
|
||||
12
src/ZM/Exception/DbException.php
Normal file
12
src/ZM/Exception/DbException.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
use Exception;
|
||||
|
||||
class DbException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
12
src/ZM/Exception/InvalidArgumentException.php
Normal file
12
src/ZM/Exception/InvalidArgumentException.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidArgumentException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
20
src/ZM/Exception/WaitTimeoutException.php
Normal file
20
src/ZM/Exception/WaitTimeoutException.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Exception;
|
||||
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
use ZM\ModBase;
|
||||
|
||||
class WaitTimeoutException extends Exception
|
||||
{
|
||||
/** @var ModBase */
|
||||
public $module;
|
||||
|
||||
public function __construct($module, $message = "", $code = 0, Throwable $previous = null) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->module = $module;
|
||||
}
|
||||
}
|
||||
229
src/ZM/Http/Response.php
Normal file
229
src/ZM/Http/Response.php
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Http;
|
||||
|
||||
|
||||
class Response
|
||||
{
|
||||
|
||||
public $fd = 0;
|
||||
|
||||
public $socket = null;
|
||||
|
||||
public $header = null;
|
||||
|
||||
public $cookie = null;
|
||||
|
||||
public $trailer = null;
|
||||
/**
|
||||
* @var \Swoole\Http\Response
|
||||
*/
|
||||
private $response;
|
||||
private $is_end = false;
|
||||
|
||||
public function __construct(\Swoole\Http\Response $response) {
|
||||
$this->response = $response;
|
||||
$this->fd = $response->fd;
|
||||
$this->socket = $response->socket;
|
||||
$this->header = $response->header;
|
||||
$this->cookie = $response->cookie;
|
||||
if (isset($response->trailer))
|
||||
$this->trailer = $response->trailer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function initHeader() {
|
||||
return $this->response->initHeader();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $value
|
||||
* @param $expires
|
||||
* @param $path
|
||||
* @param $domain
|
||||
* @param $secure
|
||||
* @param $httponly
|
||||
* @param $samesite
|
||||
* @return mixed
|
||||
*/
|
||||
public function cookie($name, $value = null, $expires = null, $path = null, $domain = null, $secure = null, $httponly = null, $samesite = null) {
|
||||
return $this->response->rawcookie($name, $value, $expires, $path, $domain, $secure, $httponly, $samesite);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $value
|
||||
* @param $expires
|
||||
* @param $path
|
||||
* @param $domain
|
||||
* @param $secure
|
||||
* @param $httponly
|
||||
* @param $samesite
|
||||
* @return mixed
|
||||
*/
|
||||
public function setCookie($name, $value = null, $expires = null, $path = null, $domain = null, $secure = null, $httponly = null, $samesite = null) {
|
||||
return $this->response->setCookie($name, $value, $expires, $path, $domain, $secure, $httponly, $samesite);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param $value
|
||||
* @param $expires
|
||||
* @param $path
|
||||
* @param $domain
|
||||
* @param $secure
|
||||
* @param $httponly
|
||||
* @param $samesite
|
||||
* @return mixed
|
||||
*/
|
||||
public function rawcookie($name, $value = null, $expires = null, $path = null, $domain = null, $secure = null, $httponly = null, $samesite = null) {
|
||||
return $this->response->rawcookie($name, $value, $expires, $path, $domain, $secure, $httponly, $samesite);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $http_code
|
||||
* @param $reason
|
||||
* @return mixed
|
||||
*/
|
||||
public function status($http_code, $reason = null) {
|
||||
return $this->response->status($http_code, $reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $http_code
|
||||
* @param $reason
|
||||
* @return mixed
|
||||
*/
|
||||
public function setStatusCode($http_code, $reason = null) {
|
||||
return $this->response->setStatusCode($http_code, $reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @param $ucwords
|
||||
* @return mixed
|
||||
*/
|
||||
public function header($key, $value, $ucwords = null) {
|
||||
return $this->response->header($key, $value, $ucwords);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @param $ucwords
|
||||
* @return mixed
|
||||
*/
|
||||
public function setHeader($key, $value, $ucwords = null) {
|
||||
return $this->response->setHeader($key, $value, $ucwords);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @param $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function trailer($key, $value) {
|
||||
return $this->response->trailer($key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function ping() {
|
||||
return $this->response->ping();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $content
|
||||
* @return mixed
|
||||
*/
|
||||
public function write($content) {
|
||||
return $this->response->write($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $content
|
||||
* @return mixed
|
||||
*/
|
||||
public function end($content = null) {
|
||||
$this->is_end = true;
|
||||
return $this->response->end($content);
|
||||
}
|
||||
|
||||
public function isEnd() { return $this->is_end; }
|
||||
|
||||
/**
|
||||
* @param $filename
|
||||
* @param $offset
|
||||
* @param $length
|
||||
* @return mixed
|
||||
*/
|
||||
public function sendfile($filename, $offset = null, $length = null) {
|
||||
return $this->response->sendfile($filename, $offset, $length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $location
|
||||
* @param $http_code
|
||||
* @return mixed
|
||||
*/
|
||||
public function redirect($location, $http_code = null) {
|
||||
return $this->redirect($location, $http_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function detach() {
|
||||
return $this->response->detach();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $fd
|
||||
* @return mixed
|
||||
*/
|
||||
public static function create($fd) {
|
||||
return \Swoole\Http\Response::create($fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function upgrade() {
|
||||
return $this->response->upgrade();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @param null $opcode
|
||||
* @param null $flags
|
||||
* @return mixed
|
||||
*/
|
||||
public function push($data, $opcode = null, $flags = null) {
|
||||
return $this->response->push($data, $opcode, $flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function recv() {
|
||||
return $this->response->recv();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function close() {
|
||||
return $this->response->close();
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
165
src/ZM/ModBase.php
Normal file
165
src/ZM/ModBase.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM;
|
||||
|
||||
|
||||
use Co;
|
||||
use Framework\ZMBuf;
|
||||
use Swoole\Http\Request;
|
||||
use ZM\API\CQAPI;
|
||||
use ZM\Connection\WSConnection;
|
||||
use ZM\Exception\InvalidArgumentException;
|
||||
use ZM\Exception\WaitTimeoutException;
|
||||
use ZM\Http\Response;
|
||||
use Swoole\WebSocket\Frame;
|
||||
use Swoole\WebSocket\Server;
|
||||
|
||||
abstract class ModBase
|
||||
{
|
||||
/** @var Server */
|
||||
protected $server;
|
||||
/** @var Frame */
|
||||
protected $frame;
|
||||
/** @var array */
|
||||
protected $data;
|
||||
/** @var Request */
|
||||
protected $request;
|
||||
/** @var Response */
|
||||
protected $response;
|
||||
/** @var int */
|
||||
protected $fd;
|
||||
/** @var int */
|
||||
protected $worker_id;
|
||||
/** @var WSConnection */
|
||||
protected $connection;
|
||||
|
||||
protected $handle_type = ModHandleType::CQ_MESSAGE;
|
||||
|
||||
public $block_continue = false;
|
||||
|
||||
public function __construct($param0 = [], $handle_type = 0) {
|
||||
if (isset($param0["server"])) $this->server = $param0["server"];
|
||||
if (isset($param0["frame"])) $this->frame = $param0["frame"];
|
||||
if (isset($param0["data"])) $this->data = $param0["data"];
|
||||
if (isset($param0["request"])) $this->request = $param0["request"];
|
||||
if (isset($param0["response"])) $this->response = $param0["response"];
|
||||
if (isset($param0["fd"])) $this->fd = $param0["fd"];
|
||||
if (isset($param0["worker_id"])) $this->worker_id = $param0["worker_id"];
|
||||
if (isset($param0["connection"])) $this->connection = $param0["connection"];
|
||||
$this->handle_type = $handle_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* only can used by cq->message event function
|
||||
* @param $msg
|
||||
* @param bool $yield
|
||||
* @return mixed
|
||||
*/
|
||||
public function reply($msg, $yield = false) {
|
||||
switch ($this->data["message_type"]) {
|
||||
case "group":
|
||||
case "private":
|
||||
case "discuss":
|
||||
return CQAPI::quick_reply($this->connection, $this->data, $msg, $yield);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function finalReply($msg, $yield = false) {
|
||||
$this->block_continue = true;
|
||||
if ($msg == "") return true;
|
||||
return $this->reply($msg, $yield);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $prompt
|
||||
* @param int $timeout
|
||||
* @param string $timeout_prompt
|
||||
* @return string
|
||||
* @throws InvalidArgumentException
|
||||
* @throws WaitTimeoutException
|
||||
*/
|
||||
public function waitMessage($prompt = "", $timeout = 600, $timeout_prompt = "") {
|
||||
if ($prompt != "") $this->reply($prompt);
|
||||
if (!isset($this->data["user_id"], $this->data["message"], $this->data["self_id"]))
|
||||
throw new InvalidArgumentException("协程等待参数缺失");
|
||||
$cid = Co::getuid();
|
||||
$api_id = ZMBuf::$atomics["wait_msg_id"]->get();
|
||||
ZMBuf::$atomics["wait_msg_id"]->add(1);
|
||||
$hang = [
|
||||
"coroutine" => $cid,
|
||||
"user_id" => $this->data["user_id"],
|
||||
"message" => $this->data["message"],
|
||||
"self_id" => $this->data["self_id"],
|
||||
"message_type" => $this->data["message_type"],
|
||||
"result" => null
|
||||
];
|
||||
if ($hang["message_type"] == "group" || $hang["message_type"] == "discuss") {
|
||||
$hang[$hang["message_type"] . "_id"] = $this->data[$this->data["message_type"] . "_id"];
|
||||
}
|
||||
ZMBuf::appendKey("wait_api", $api_id, $hang);
|
||||
$id = swoole_timer_after($timeout * 1000, function () use ($api_id, $timeout_prompt) {
|
||||
$r = ZMBuf::get("wait_api")[$api_id] ?? null;
|
||||
if ($r !== null) {
|
||||
Co::resume($r["coroutine"]);
|
||||
}
|
||||
});
|
||||
|
||||
Co::suspend();
|
||||
$sess = ZMBuf::get("wait_api")[$api_id];
|
||||
ZMBuf::unsetByValue("wait_api", $api_id);
|
||||
$result = $sess["result"];
|
||||
if (isset($id)) swoole_timer_clear($id);
|
||||
if ($result === null) throw new WaitTimeoutException($this, $timeout_prompt);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $arg
|
||||
* @param $mode
|
||||
* @param $prompt_msg
|
||||
* @return mixed|string
|
||||
* @throws InvalidArgumentException
|
||||
* @throws WaitTimeoutException
|
||||
*/
|
||||
public function getArgs(&$arg, $mode, $prompt_msg) {
|
||||
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) {
|
||||
if (is_numeric($v)) {
|
||||
array_splice($arg, $k, 1);
|
||||
return $v;
|
||||
}
|
||||
}
|
||||
return $this->waitMessage($prompt_msg);
|
||||
case ZM_MATCH_FIRST:
|
||||
if (isset($arg[1])) {
|
||||
$a = $arg[1];
|
||||
array_splice($arg, 1, 1);
|
||||
return $a;
|
||||
} else {
|
||||
return $this->waitMessage($prompt_msg);
|
||||
}
|
||||
}
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
|
||||
public function getMessage() { return $this->data["message"] ?? null; }
|
||||
|
||||
public function getUserId() { return $this->data["user_id"] ?? null; }
|
||||
|
||||
public function getGroupId() { return $this->data["group_id"] ?? null; }
|
||||
|
||||
public function getMessageType() { return $this->data["message_type"] ?? null; }
|
||||
|
||||
public function getRobotId() { return $this->data["self_id"]; }
|
||||
|
||||
public function getConnection() { return $this->connection; }
|
||||
|
||||
public function setBlock($result = true) { $this->block_continue = $result; }
|
||||
}
|
||||
20
src/ZM/ModHandleType.php
Normal file
20
src/ZM/ModHandleType.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM;
|
||||
|
||||
|
||||
class ModHandleType
|
||||
{
|
||||
const CQ_MESSAGE = 0;
|
||||
const CQ_REQUEST = 1;
|
||||
const CQ_NOTICE = 2;
|
||||
const CQ_META_EVENT = 3;
|
||||
|
||||
const SWOOLE_OPEN = 4;
|
||||
const SWOOLE_CLOSE = 5;
|
||||
const SWOOLE_MESSAGE = 6;
|
||||
const SWOOLE_REQUEST = 7;
|
||||
const SWOOLE_WORKER_START = 8;
|
||||
const SWOOLE_WORKER_STOP = 9;
|
||||
}
|
||||
46
src/ZM/Utils/DataProvider.php
Normal file
46
src/ZM/Utils/DataProvider.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use Framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
|
||||
class DataProvider
|
||||
{
|
||||
public static $buffer_list = [];
|
||||
|
||||
public static function getResourceFolder() {
|
||||
return WORKING_DIR . '/resources/';
|
||||
}
|
||||
|
||||
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) {
|
||||
self::setJsonData($v, ZMBuf::get($k));
|
||||
}
|
||||
echo Console::setColor("saved", "lightblue").PHP_EOL;
|
||||
}
|
||||
|
||||
private static function getJsonData(string $string) {
|
||||
if(!file_exists(self::getDataFolder().$string)) return [];
|
||||
return json_decode(Co::readFile(self::getDataFolder().$string), true);
|
||||
}
|
||||
|
||||
private static function setJsonData($filename, array $args) {
|
||||
Co::writeFile(self::getDataFolder() . $filename, json_encode($args, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING));
|
||||
}
|
||||
|
||||
private static function getDataFolder() {
|
||||
return CONFIG_DIR;
|
||||
}
|
||||
}
|
||||
101
src/ZM/Utils/SQLPool.php
Normal file
101
src/ZM/Utils/SQLPool.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2019/1/5
|
||||
* Time: 4:48 PM
|
||||
*/
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
use framework\Console;
|
||||
use framework\ZMBuf;
|
||||
use SplQueue;
|
||||
use Swoole\Coroutine;
|
||||
use Swoole\Coroutine\Mysql;
|
||||
|
||||
class SQLPool
|
||||
{
|
||||
protected $available = true;
|
||||
protected $pool;
|
||||
private $info;
|
||||
|
||||
public $co_list = [];
|
||||
public $connect_cnt = 0;
|
||||
|
||||
public function __construct() {
|
||||
$this->pool = new SplQueue;
|
||||
$this->info = [
|
||||
"host" => ZMBuf::globals("sql_config")["sql_host"],
|
||||
"port" => ZMBuf::globals("sql_config")["sql_port"],
|
||||
"user" => ZMBuf::globals("sql_config")["sql_username"],
|
||||
"password" => ZMBuf::globals("sql_config")["sql_password"],
|
||||
"database" => ZMBuf::globals("sql_config")["sql_database"]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 将利用过的连接入队
|
||||
* @param $mysql
|
||||
*/
|
||||
public function put($mysql) {
|
||||
$this->pool->push($mysql);
|
||||
if (($a = array_shift($this->co_list)) !== null) {
|
||||
Coroutine::resume($a);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队中的连接,如果不存在则创建新的
|
||||
* @param bool $no_new_conn
|
||||
* @return bool|mixed|Mysql
|
||||
*/
|
||||
public function get($no_new_conn = false) {
|
||||
if (count($this->pool) == 0 && $this->connect_cnt <= 70) {
|
||||
if($no_new_conn) return false;
|
||||
$this->connect_cnt += 1;
|
||||
$r = $this->newConnect();
|
||||
if ($r !== false) {
|
||||
return $r;
|
||||
} else {
|
||||
$this->connect_cnt -= 1;
|
||||
return false;
|
||||
}
|
||||
} elseif (count($this->pool) > 0) {
|
||||
$con = $this->pool->pop();
|
||||
if ($con->connected !== false) return $con;
|
||||
} elseif ($this->connect_cnt > 70) {
|
||||
$this->co_list[]=Coroutine::getuid();
|
||||
Console::warning("数据库连接过多,协程等待重复利用中...当前协程数 ".Coroutine::stats()["coroutine_num"]);
|
||||
Coroutine::suspend();
|
||||
return $this->get($no_new_conn);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getCount() {
|
||||
return $this->pool->count();
|
||||
}
|
||||
|
||||
public function destruct() {
|
||||
// 连接池销毁, 置不可用状态, 防止新的客户端进入常驻连接池, 导致服务器无法平滑退出
|
||||
$this->available = false;
|
||||
while (!$this->pool->isEmpty()) {
|
||||
$this->pool->pop();
|
||||
}
|
||||
}
|
||||
|
||||
private function newConnect() {
|
||||
//无空闲连接,创建新连接
|
||||
$mysql = new Mysql();
|
||||
|
||||
Console::info("创建SQL连接中,当前有" . $this->connect_cnt . "个连接");
|
||||
$res = $mysql->connect($this->info);
|
||||
if ($res == false) {
|
||||
echo $mysql->error . PHP_EOL;
|
||||
return false;
|
||||
} else {
|
||||
return $mysql;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/ZM/Utils/ScheduleManager.php
Normal file
10
src/ZM/Utils/ScheduleManager.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
class ScheduleManager
|
||||
{
|
||||
//TODO: 写framework部分的schedule控制代码
|
||||
}
|
||||
71
src/ZM/Utils/ZMUtil.php
Normal file
71
src/ZM/Utils/ZMUtil.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace ZM\Utils;
|
||||
|
||||
|
||||
use Co;
|
||||
use framework\Console;
|
||||
use Framework\ZMBuf;
|
||||
|
||||
class ZMUtil
|
||||
{
|
||||
/**
|
||||
* 检查workerStart是否运行结束
|
||||
*/
|
||||
public static function checkWait() {
|
||||
if(ZMBuf::isset("wait_start")) {
|
||||
ZMBuf::append("wait_start", Co::getCid());
|
||||
Co::suspend();
|
||||
}
|
||||
}
|
||||
|
||||
public static function stop() {
|
||||
Console::info(Console::setColor("Stopping server...", "red"));
|
||||
foreach (ZMBuf::$server->connections as $v) {
|
||||
ZMBuf::$server->close($v);
|
||||
}
|
||||
DataProvider::saveBuffer();
|
||||
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]);
|
||||
} else return null;
|
||||
}
|
||||
|
||||
public static function reload() {
|
||||
Console::info(Console::setColor("Reloading server...", "gold"));
|
||||
foreach (ZMBuf::$server->connections as $v) {
|
||||
ZMBuf::$server->close($v);
|
||||
}
|
||||
DataProvider::saveBuffer();
|
||||
ZMBuf::$server->reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析CQ码
|
||||
* @param $msg
|
||||
* @return array|null
|
||||
* 0123456
|
||||
* [CQ:at]
|
||||
*/
|
||||
static function getCQ($msg) {
|
||||
if (($start = mb_strpos($msg, '[')) === false) return null;
|
||||
if (($end = mb_strpos($msg, ']')) === false) return null;
|
||||
$msg = mb_substr($msg, $start + 1, $end - $start - 1);
|
||||
if (mb_substr($msg, 0, 3) != "CQ:") return null;
|
||||
$msg = mb_substr($msg, 3);
|
||||
$msg2 = explode(",", $msg);
|
||||
$type = array_shift($msg2);
|
||||
$array = [];
|
||||
foreach ($msg2 as $k => $v) {
|
||||
$ss = explode("=", $v);
|
||||
$sk = array_shift($ss);
|
||||
$array[$sk] = implode("=", $ss);
|
||||
}
|
||||
return ["type" => $type, "params" => $array, "start" => $start, "end" => $end];
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/4/12
|
||||
* Time: 10:43
|
||||
*/
|
||||
|
||||
class CQBot
|
||||
{
|
||||
/** @var Framework */
|
||||
public $framework;
|
||||
|
||||
//传入数据
|
||||
public $data = null;
|
||||
|
||||
//检测有没有回复过消息
|
||||
private $function_called = false;
|
||||
|
||||
public $starttime;
|
||||
public $endtime;
|
||||
public $self_id;
|
||||
public $circle;
|
||||
|
||||
public function __construct(Framework $framework, $circle, $package) {
|
||||
$this->circle = $circle;
|
||||
$this->starttime = microtime(true);
|
||||
$this->framework = $framework;
|
||||
$this->data = $package;
|
||||
$this->self_id = $this->data["self_id"];
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
if ($this->circle >= 5) return false;
|
||||
if ($this->data === null) return false;
|
||||
if (isset($it["user_id"]) && CQUtil::isRobot($this->data["user_id"])) return false;
|
||||
if (isset($it["group_id"]) && $this->data["group_id"] == Cache::get("admin_group")) {
|
||||
if ($this->getRobotId() != Cache::get("admin_active")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($this->data["message"] == "")
|
||||
return false;
|
||||
foreach (Cache::get("mods") as $v) {
|
||||
/** @var ModBase $r */
|
||||
$r = new $v($this, $this->data);
|
||||
if ($r->split_execute === true) {
|
||||
$msg = trim($this->data["message"]);
|
||||
$msg = explodeMsg($msg);
|
||||
$r->execute($msg);
|
||||
}
|
||||
}
|
||||
$this->endtime = microtime(true);
|
||||
return $this->function_called;
|
||||
}
|
||||
|
||||
/**
|
||||
* 快速回复消息
|
||||
* @param $msg
|
||||
* @param callable|null $callback
|
||||
* @param bool $async
|
||||
* @return bool
|
||||
*/
|
||||
public function reply($msg, callable $callback = null, $async = false) {
|
||||
$this->function_called = true;
|
||||
switch ($this->data["message_type"]) {
|
||||
case "group":
|
||||
$this->function_called = true;
|
||||
if (!$async) return CQAPI::send_group_msg($this->getRobotId(), ["group_id" => $this->data["group_id"], "message" => $msg], $callback);
|
||||
else return CQAPI::send_group_msg_async($this->getRobotId(), ["group_id" => $this->data["group_id"], "message" => $msg], $callback);
|
||||
case "private":
|
||||
$this->function_called = true;
|
||||
if (!$async) return CQAPI::send_private_msg($this->getRobotId(), ["user_id" => $this->data["user_id"], "message" => $msg], $callback);
|
||||
else return CQAPI::send_private_msg_async($this->getRobotId(), ["user_id" => $this->data["user_id"], "message" => $msg], $callback);
|
||||
case "discuss":
|
||||
$this->function_called = true;
|
||||
if (!$async) return CQAPI::send_discuss_msg($this->getRobotId(), ["discuss_id" => $this->data["discuss_id"], "message" => $msg], $callback);
|
||||
else return CQAPI::send_discuss_msg_async($this->getRobotId(), ["discuss_id" => $this->data["discuss_id"], "message" => $msg], $callback);
|
||||
case "wechat":
|
||||
//TODO: add wechat account support in the future
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isAdmin($user) {
|
||||
if (in_array($user, Cache::get("admin"))) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
public function replace($msg, $dat) {
|
||||
$msg = str_replace("{at}", '[CQ:at,qq=' . $dat["user_id"] . ']', $msg);
|
||||
$msg = str_replace("{and}", '&', $msg);
|
||||
while (strpos($msg, '{') !== false && strpos($msg, '}') !== false) {
|
||||
if (strpos($msg, '{') > strpos($msg, '}')) return $msg;
|
||||
$start = strpos($msg, '{');
|
||||
$end = strpos($msg, '}');
|
||||
$sub = explode("=", substr($msg, $start + 1, $end - $start - 1));
|
||||
switch ($sub[0]) {
|
||||
case "at":
|
||||
$qq = $sub[1];
|
||||
$msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:at,qq=' . $qq . ']', $msg);
|
||||
break;
|
||||
case "image":
|
||||
case "record":
|
||||
$pictFile = $sub[1];
|
||||
$msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:' . $sub[0] . ',file=' . $pictFile . ']', $msg);
|
||||
break;
|
||||
case "dice":
|
||||
$file = $sub[1];
|
||||
$msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:dice,type=' . $file . ']', $msg);
|
||||
break;
|
||||
case "shake":
|
||||
$msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:shake]', $msg);
|
||||
break;
|
||||
case "music":
|
||||
$id = $sub[1];
|
||||
$msg = str_replace(substr($msg, $start, $end - $start + 1), '[CQ:music,type=163,id=' . $id . ']', $msg);
|
||||
break;
|
||||
case "internet":
|
||||
array_shift($sub);
|
||||
$id = implode("=", $sub);
|
||||
if (substr($id, 0, 7) != "http://") $id = "http://" . $id;
|
||||
$is = file_get_contents($id, false, NULL, 0, 1024);
|
||||
if ($is == false) $is = "[请求时发生了错误] 如有疑问,请联系管理员";
|
||||
$msg = str_replace(substr($msg, $start, $end - $start + 1), $is, $msg);
|
||||
break 2;
|
||||
default:
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回当前机器人的id
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRobotId() {
|
||||
return $this->data["self_id"] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/11/26
|
||||
* Time: 9:23 AM
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class CQAPI
|
||||
* @method static send_private_msg($self_id, $params, callable $function = null)
|
||||
* @method static send_group_msg($self_id, $params, callable $function = null)
|
||||
* @method static send_discuss_msg($self_id, $params, callable $function = null)
|
||||
* @method static send_msg($self_id, $params, callable $function = null)
|
||||
* @method static delete_msg($self_id, $params, callable $function = null)
|
||||
* @method static send_like($self_id, $params, callable $function = null)
|
||||
* @method static set_group_kick($self_id, $params, callable $function = null)
|
||||
* @method static set_group_ban($self_id, $params, callable $function = null)
|
||||
* @method static set_group_anonymous_ban($self_id, $params, callable $function = null)
|
||||
* @method static set_group_whole_ban($self_id, $params, callable $function = null)
|
||||
* @method static set_group_admin($self_id, $params, callable $function = null)
|
||||
* @method static set_group_anonymous($self_id, $params, callable $function = null)
|
||||
* @method static set_group_card($self_id, $params, callable $function = null)
|
||||
* @method static set_group_leave($self_id, $params, callable $function = null)
|
||||
* @method static set_group_special_title($self_id, $params, callable $function = null)
|
||||
* @method static set_discuss_leave($self_id, $params, callable $function = null)
|
||||
* @method static set_friend_add_request($self_id, $params, callable $function = null)
|
||||
* @method static set_group_add_request($self_id, $params, callable $function = null)
|
||||
* @method static get_login_info($self_id, $params, callable $function = null)
|
||||
* @method static get_stranger_info($self_id, $params, callable $function = null)
|
||||
* @method static get_group_list($self_id, $params, callable $function = null)
|
||||
* @method static get_group_member_info($self_id, $params, callable $function = null)
|
||||
* @method static get_group_member_list($self_id, $params, callable $function = null)
|
||||
* @method static get_cookies($self_id, $params, callable $function = null)
|
||||
* @method static get_csrf_token($self_id, $params, callable $function = null)
|
||||
* @method static get_credentials($self_id, $params, callable $function = null)
|
||||
* @method static get_record($self_id, $params, callable $function = null)
|
||||
* @method static get_status($self_id, $params, callable $function = null)
|
||||
* @method static get_version_info($self_id, $params, callable $function = null)
|
||||
* @method static set_restart($self_id, $params, callable $function = null)
|
||||
* @method static set_restart_plugin($self_id, $params, callable $function = null)
|
||||
* @method static clean_data_dir($self_id, $params, callable $function = null)
|
||||
* @method static clean_plugin_log($self_id, $params, callable $function = null)
|
||||
* @method static _get_friend_list($self_id, $params, callable $function = null)
|
||||
* @method static _get_group_info($self_id, $params, callable $function = null)
|
||||
* @method static _get_vip_info($self_id, $params, callable $function = null)
|
||||
* @method static send_private_msg_async($self_id, $params, callable $function = null)
|
||||
* @method static send_group_msg_async($self_id, $params, callable $function = null)
|
||||
* @method static send_discuss_msg_async($self_id, $params, callable $function = null)
|
||||
* @method static send_msg_async($self_id, $params, callable $function = null)
|
||||
* @method static delete_msg_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_kick_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_ban_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_anonymous_ban_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_whole_ban_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_admin_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_anonymous_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_card_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_leave_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_special_title_async($self_id, $params, callable $function = null)
|
||||
* @method static set_discuss_leave_async($self_id, $params, callable $function = null)
|
||||
* @method static set_friend_add_request_async($self_id, $params, callable $function = null)
|
||||
* @method static set_group_add_request_async($self_id, $params, callable $function = null)
|
||||
*/
|
||||
class CQAPI
|
||||
{
|
||||
public static function debug($msg, $head = null, $self_id = null) {
|
||||
if($head === null) $msg = date("[H:i:s") . " DEBUG] ".$msg;
|
||||
if($self_id === null) $self_id = CQUtil::findRobot();
|
||||
else $msg = $head.$msg;
|
||||
if($self_id !== null){
|
||||
return self::send_group_msg($self_id, ["message" => $msg, "group_id" => Cache::get("admin_group")]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function __callStatic($name, $arg) {
|
||||
if(mb_substr($name, -6) == "_after"){
|
||||
$all = self::getSupportedAPIs();
|
||||
$find = null;
|
||||
$true_name = mb_substr($name, 0, -6);
|
||||
if(!in_array($true_name, $all)){
|
||||
Console::error("Unknown API " . $name);
|
||||
return false;
|
||||
}
|
||||
$ms = array_shift($arg);
|
||||
Scheduler::after($ms, function() use ($true_name, $arg){
|
||||
$reply = ["action" => $true_name];
|
||||
if (!is_array($arg[1])) {
|
||||
Console::error("Error when parsing params. Please make sure your params is an array.");
|
||||
return false;
|
||||
}
|
||||
if ($arg[1] != []) {
|
||||
$reply["params"] = $arg[1];
|
||||
}
|
||||
return self::processAPI($arg[0], $reply, $arg[2]);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
$all = self::getSupportedAPIs();
|
||||
$find = null;
|
||||
if (in_array($name, $all)) $find = $name;
|
||||
else {
|
||||
foreach ($all as $v) {
|
||||
if (strtolower($name) == strtolower(str_replace("_", "", $v))) {
|
||||
$find = $v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($find === null) {
|
||||
Console::error("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.");
|
||||
return false;
|
||||
}
|
||||
if ($arg[1] != []) {
|
||||
$reply["params"] = $arg[1];
|
||||
}
|
||||
return self::processAPI($arg[0], $reply, $arg[2]);
|
||||
}
|
||||
|
||||
/********************** non-API Part **********************/
|
||||
|
||||
private static function getSupportedAPIs() {
|
||||
return [
|
||||
"send_private_msg",
|
||||
"send_group_msg",
|
||||
"send_discuss_msg",
|
||||
"send_msg",
|
||||
"delete_msg",
|
||||
"send_like",
|
||||
"set_group_kick",
|
||||
"set_group_ban",
|
||||
"set_group_anonymous_ban",
|
||||
"set_group_whole_ban",
|
||||
"set_group_admin",
|
||||
"set_group_anonymous",
|
||||
"set_group_card",
|
||||
"set_group_leave",
|
||||
"set_group_special_title",
|
||||
"set_discuss_leave",
|
||||
"set_friend_add_request",
|
||||
"set_group_add_request",
|
||||
"get_login_info",
|
||||
"get_stranger_info",
|
||||
"get_group_list",
|
||||
"get_group_member_info",
|
||||
"get_group_member_list",
|
||||
"get_cookies",
|
||||
"get_csrf_token",
|
||||
"get_credentials",
|
||||
"get_record",
|
||||
"get_status",
|
||||
"get_version_info",
|
||||
"set_restart",
|
||||
"set_restart_plugin",
|
||||
"clean_data_dir",
|
||||
"clean_plugin_log",
|
||||
"_get_friend_list",
|
||||
"_get_group_info",
|
||||
"_get_vip_info",
|
||||
//异步API
|
||||
"send_private_msg_async",
|
||||
"send_group_msg_async",
|
||||
"send_discuss_msg_async",
|
||||
"send_msg_async",
|
||||
"delete_msg_async",
|
||||
"set_group_kick_async",
|
||||
"set_group_ban_async",
|
||||
"set_group_anonymous_ban_async",
|
||||
"set_group_whole_ban_async",
|
||||
"set_group_admin_async",
|
||||
"set_group_anonymous_async",
|
||||
"set_group_card_async",
|
||||
"set_group_leave_async",
|
||||
"set_group_special_title_async",
|
||||
"set_discuss_leave_async",
|
||||
"set_friend_add_request_async",
|
||||
"set_group_add_request_async"
|
||||
];
|
||||
}
|
||||
|
||||
public static function getLoggedAPIs() {
|
||||
return [
|
||||
"send_private_msg",
|
||||
"send_group_msg",
|
||||
"send_discuss_msg",
|
||||
"send_msg",
|
||||
"send_private_msg_async",
|
||||
"send_group_msg_async",
|
||||
"send_discuss_msg_async",
|
||||
"send_msg_async"
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $self_id
|
||||
* @param $reply
|
||||
* @param callable|null $function
|
||||
* @return bool
|
||||
*/
|
||||
private static function processAPI($self_id, $reply, callable $function = null) {
|
||||
$api_id = Cache::$api_id->get();
|
||||
$reply["echo"] = $api_id;
|
||||
Cache::$api_id->add(1);
|
||||
if ($self_id instanceof RobotWSConnection) {
|
||||
$connection = $self_id;
|
||||
$self_id = $connection->getQQ();
|
||||
} else $connection = ConnectionManager::getRobotConnection($self_id);
|
||||
if ($connection instanceof NullConnection) {
|
||||
Console::error("未找到qq号:" . $self_id . "的API连接");
|
||||
return false;
|
||||
}
|
||||
if ($connection->push(json_encode($reply))) {
|
||||
Cache::appendKey("sent_api", $api_id, [
|
||||
"data" => $reply,
|
||||
"time" => microtime(true),
|
||||
"func" => $function,
|
||||
"self_id" => $self_id
|
||||
]);
|
||||
if (in_array($reply["action"], self::getLoggedAPIs())) {
|
||||
Console::msg($reply);
|
||||
Cache::$out_count->add(1);
|
||||
}
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/11/26
|
||||
* Time: 10:01 PM
|
||||
*/
|
||||
|
||||
class CustomWSConnection extends WSConnection
|
||||
{
|
||||
public function __construct(swoole_websocket_server $server, $fd, $request) {
|
||||
parent::__construct($server, $fd, $request->server["remote_addr"]);
|
||||
$this->create_success = true;
|
||||
// Here to put your custom other websocket connection to manage.
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/11/18
|
||||
* Time: 11:11 AM
|
||||
*/
|
||||
|
||||
class NullConnection extends WSConnection
|
||||
{
|
||||
private $qq = 0;
|
||||
|
||||
public function __construct(swoole_websocket_server $server, $fd, $remote, $qq = 0) {
|
||||
parent::__construct($server, $fd, $remote);
|
||||
$this->qq = $qq;
|
||||
}
|
||||
|
||||
public function push($data, $push_error_record = true) {
|
||||
$data = unicodeDecode($data);
|
||||
CQUtil::errorlog("API推送失败,未发送的消息: \n" . $data, "API ERROR", false);
|
||||
if ($push_error_record) Cache::append("bug_msg_list", json_decode($data, true));
|
||||
return false;
|
||||
}
|
||||
|
||||
public function sendAPI($api, $params = [], $echo = []){
|
||||
$data["action"] = $api;
|
||||
if($params != []) $data["params"] = $params;
|
||||
$echo_result["self_id"] = $this->qq;
|
||||
if($echo != []){
|
||||
if(count($echo) >= 1) $echo_result['type'] = array_shift($echo);
|
||||
if(!empty($echo)) $echo_result["params"] = $echo;
|
||||
}
|
||||
$data["echo"] = $echo_result;
|
||||
Console::debug("将要发送的API包:" . json_encode($data, 128 | 256));
|
||||
return $this->push(json_encode($data));
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/11/14
|
||||
* Time: 11:59 PM
|
||||
*/
|
||||
|
||||
class RobotWSConnection extends WSConnection
|
||||
{
|
||||
const ALIAS_LIST = [
|
||||
"10001" => "小马哥",
|
||||
"838714432" => "鲸鱼的test机器人"
|
||||
];
|
||||
|
||||
private $qq;
|
||||
private $alias;//别名
|
||||
private $sub_type;
|
||||
|
||||
public function __construct(swoole_websocket_server $server, $fd, $qq, swoole_http_request $request, $sub_type) {
|
||||
parent::__construct($server, $fd, $request->server["remote_addr"]);
|
||||
$this->qq = $qq;
|
||||
$this->alias = self::ALIAS_LIST[$qq] ?? "机器人" . $qq;
|
||||
$this->sub_type = $sub_type;
|
||||
foreach (ConnectionManager::getAll("robot") as $k => $v) {
|
||||
if ($v->getQQ() == $this->getQQ() && $k != $this->fd && $v->getSubType() == $this->getSubType()) {
|
||||
$v->close();
|
||||
}
|
||||
}
|
||||
if ($sub_type != "event") {
|
||||
$obj = $this;
|
||||
$r = $this->sendAPI("get_version_info", [], function ($response) use ($obj) {
|
||||
Cache::set("version_info", $response["data"]);
|
||||
});
|
||||
if ($r)
|
||||
$this->create_success = $this->sendAPI("send_group_msg", ["message" => "[CQBot] 机器人 " . $this->getAlias() . " 已连接,链接fd:" . $this->fd, "group_id" => Cache::get("admin_group")]);
|
||||
} else $this->create_success = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回连接的QQ
|
||||
* @return mixed
|
||||
*/
|
||||
public function getQQ() {
|
||||
return $this->qq;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function getAlias() {
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
public function sendAPI($api, $params = [], callable $callback = null) {
|
||||
$data["action"] = $api;
|
||||
if ($params != []) $data["params"] = $params;
|
||||
|
||||
if ($this->sub_type == "event") {
|
||||
$conns = ConnectionManager::getAll("robot");
|
||||
foreach ($conns as $k => $v) {
|
||||
if ($v->getSubType() == "api" && $v->getQQ() == $this->getQQ()) {
|
||||
$api_id = Cache::$api_id->get();
|
||||
$data["echo"] = $api_id;
|
||||
Cache::$api_id->add(1);
|
||||
Cache::appendKey("sent_api", $api_id, [
|
||||
"data" => $data,
|
||||
"time" => microtime(true),
|
||||
"func" => $callback,
|
||||
"self_id" => $this->getQQ()
|
||||
]);
|
||||
if ($v->push(json_encode($data))) {
|
||||
if (in_array($data["action"], CQAPI::getLoggedAPIs())) {
|
||||
Console::msg($data);
|
||||
Cache::$out_count->add(1);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
$response = [
|
||||
"status" => "failed",
|
||||
"retcode" => 998,
|
||||
"data" => null,
|
||||
"self_id" => $this->getQQ()
|
||||
];
|
||||
$s = Cache::get("sent_api")[$data["echo"]];
|
||||
StatusParser::parse($response, $data);
|
||||
if ($s["func"] !== null)
|
||||
call_user_func($s["func"], $response, $data);
|
||||
Cache::removeKey("sent_api", $data["echo"]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$api_id = Cache::$api_id->get();
|
||||
$data["echo"] = $api_id;
|
||||
Cache::$api_id->add(1);
|
||||
Cache::appendKey("sent_api", $api_id, [
|
||||
"data" => $data,
|
||||
"time" => microtime(true),
|
||||
"func" => $callback,
|
||||
"self_id" => $this->getQQ()
|
||||
]);
|
||||
if ($this->push(json_encode($data))) {
|
||||
if (in_array($data["action"], CQAPI::getLoggedAPIs())) {
|
||||
Console::msg($data);
|
||||
Cache::$out_count->add(1);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
$response = [
|
||||
"status" => "failed",
|
||||
"retcode" => 999,
|
||||
"data" => null,
|
||||
"self_id" => $this->getQQ()
|
||||
];
|
||||
$s = Cache::get("sent_api")[$data["echo"]];
|
||||
StatusParser::parse($response, $data);
|
||||
if ($s["func"] !== null)
|
||||
call_user_func($s["func"], $response, $data);
|
||||
Cache::removeKey("sent_api", $data["echo"]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSubType() {
|
||||
return $this->sub_type;
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/7/27
|
||||
* Time: 12:31 PM
|
||||
*/
|
||||
|
||||
abstract class WSConnection
|
||||
{
|
||||
public $fd;
|
||||
|
||||
protected $server;
|
||||
protected $remote_address;
|
||||
|
||||
public $create_success = false;
|
||||
|
||||
public function __construct(swoole_websocket_server $server, $fd, $remote) {
|
||||
$this->server = $server;
|
||||
$this->fd = $fd;
|
||||
$this->remote_address = $remote;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回swoole server
|
||||
* @return swoole_websocket_server
|
||||
*/
|
||||
public function getServer() {
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
public function getType(){
|
||||
if($this instanceof RobotWSConnection) return "robot";
|
||||
elseif($this instanceof CustomWSConnection) return "custom";
|
||||
else return "unknown";
|
||||
}
|
||||
|
||||
public function push($data, $push_error_record = true) {
|
||||
if ($data === null || $data == "") {
|
||||
Console::error("Empty data pushed.");
|
||||
return false;
|
||||
}
|
||||
if ($this->server->push($this->fd, $data) === false) {
|
||||
$data = unicodeDecode($data);
|
||||
CQUtil::errorlog("API推送失败,未发送的消息: \n" . $data, "API ERROR", false);
|
||||
if ($push_error_record) Cache::append("bug_msg_list", json_decode($data, true));
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function close() {
|
||||
$this->server->close($this->fd);
|
||||
ConnectionManager::remove($this->fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getRemoteAddress() {
|
||||
return $this->remote_address;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/5/26
|
||||
* Time: 下午3:10
|
||||
*/
|
||||
|
||||
abstract class Event
|
||||
{
|
||||
/**
|
||||
* @return Framework
|
||||
*/
|
||||
public function getFramework(){
|
||||
return Framework::getInstance();
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/10/15
|
||||
* Time: 10:35 AM
|
||||
*/
|
||||
|
||||
class MessageEvent extends Event
|
||||
{
|
||||
public function __construct($req) {
|
||||
if (CQUtil::isRobot($req["user_id"])) return;
|
||||
|
||||
$in_count = Cache::$in_count->get();
|
||||
Cache::$in_count->add(1);
|
||||
if (Cache::$data["info_level"] >= 1) {
|
||||
$num = CQUtil::getRobotAlias($req["self_id"]);
|
||||
$type = $req["post_type"] == "message" ? ($req["message_type"] == "group" ? "GROUP_MSG" . $num . ":" . $req["group_id"] : ($req["message_type"] == "private" ? "PRIVATE_MSG" . $num : "DISCUSS_MSG" . $num . ":" . $req["discuss_id"])) : strtoupper($req["post_type"]);
|
||||
Console::put(Console::setColor(date("H:i:s"), "green") . Console::setColor(" [$in_count]" . $type, "lightlightblue") . Console::setColor(" " . $req["user_id"], "yellow") . Console::setColor(" > ", "gray") . $req["message"]);
|
||||
}
|
||||
|
||||
CQUtil::updateMsg();//更新消息速度
|
||||
|
||||
//传入消息处理的逻辑
|
||||
$c = new CQBot($this->getFramework(), 0, $req);
|
||||
$c->execute();
|
||||
$value = $c->endtime - $c->starttime;
|
||||
Console::debug("Using time: " . $value);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/10/15
|
||||
* Time: 11:06 AM
|
||||
*/
|
||||
|
||||
class MetaEvent extends Event
|
||||
{
|
||||
public function __construct($req) {
|
||||
switch ($req["meta_event_type"]) {
|
||||
case "lifecycle":
|
||||
//插件生命周期事件
|
||||
switch ($req["sub_type"]) {
|
||||
case "enable":
|
||||
Console::info("机器人" . $req["self_id"] . "的HTTP插件" . Console::setColor("启动", "green") . "运行");
|
||||
break;
|
||||
case "disable":
|
||||
Console::info("机器人" . $req["self_id"] . "的HTTP插件" . Console::setColor("停止", "red") . "运行");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "heartbeat":
|
||||
//处理心跳包中的status事件目前炸毛不需要。
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/10/15
|
||||
* Time: 10:36 AM
|
||||
*/
|
||||
|
||||
class NoticeEvent extends Event
|
||||
{
|
||||
public function __construct($req) {
|
||||
foreach(Cache::get("mods") as $k => $v){
|
||||
if(in_array("onNotice", get_class_methods($v))){
|
||||
/** @var ModBase $v */
|
||||
$v::onNotice($req);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/10/15
|
||||
* Time: 10:36 AM
|
||||
*/
|
||||
|
||||
class RequestEvent extends Event
|
||||
{
|
||||
public function __construct($req) {
|
||||
foreach(Cache::get("mods") as $k => $v){
|
||||
if(in_array("onRequest", get_class_methods($v))){
|
||||
/** @var ModBase $v */
|
||||
$v::onRequest($req);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/10/15
|
||||
* Time: 10:40 AM
|
||||
*/
|
||||
|
||||
class UnknownEvent extends Event
|
||||
{
|
||||
public function __construct($req) {
|
||||
Console::info("Unknown event.");
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/5/16
|
||||
* Time: 下午2:05
|
||||
*/
|
||||
|
||||
class HTTPEvent extends ServerEvent
|
||||
{
|
||||
public $content;
|
||||
public $isValid = false;
|
||||
|
||||
/**
|
||||
* HTTPEvent constructor.
|
||||
* @param swoole_http_request $request
|
||||
* @param swoole_http_response $response
|
||||
*/
|
||||
public function __construct(swoole_http_request $request, swoole_http_response $response) {
|
||||
parent::__construct(Cache::$server);
|
||||
$response->end("Hello world!");
|
||||
//此为HTTP请求的回复,更多设置回复头、传送文件、POST、GET请求解析等内容请查阅文档https://wiki.swoole.com
|
||||
}
|
||||
|
||||
/**
|
||||
* 此函数为一个示例的检测有效的函数,为此预留
|
||||
* 作用是判断传入的请求数据是合法的
|
||||
* @param $param
|
||||
* @return bool
|
||||
*/
|
||||
public function isValidParam($param) {
|
||||
if ($param === null) return false;
|
||||
if (!isset($param["event"])) return false;
|
||||
if (!isset($param["timestamp"])) return false;
|
||||
if (!isset($param[$param["event"]])) return false;
|
||||
if(($param["timestamp"] > (time() + 10)) || ($param["timestamp"] < (time() - 10))) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/11/26
|
||||
* Time: 9:30 AM
|
||||
*/
|
||||
|
||||
abstract class ServerEvent extends Event
|
||||
{
|
||||
public $server;
|
||||
|
||||
public function __construct(\swoole_server $server) {
|
||||
$this->server = $server;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/5/26
|
||||
* Time: 下午4:14
|
||||
*/
|
||||
|
||||
class WSCloseEvent extends ServerEvent
|
||||
{
|
||||
public function __construct(swoole_server $server, int $fd) {
|
||||
parent::__construct($server);
|
||||
$connect = ConnectionManager::get($fd);
|
||||
if ($connect !== null) {
|
||||
ConnectionManager::remove($fd);
|
||||
Console::info("WebSocket Connection closed. fd: " . $fd);
|
||||
} else {
|
||||
Console::info("Unknown WS or HTTP Connection closed. fd: ".$fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/5/26
|
||||
* Time: 下午4:04
|
||||
*/
|
||||
|
||||
class WSMessageEvent extends ServerEvent
|
||||
{
|
||||
public function __construct(swoole_websocket_server $server, swoole_websocket_frame $frame) {
|
||||
parent::__construct($server);
|
||||
$fd = $frame->fd;
|
||||
$req = json_decode($frame->data, true);
|
||||
$conn = ConnectionManager::get($fd);
|
||||
if ($conn === null) {
|
||||
Console::info("收到一个未知链接发来的消息。" . $fd);
|
||||
return;
|
||||
}
|
||||
switch ($conn->getType()) {
|
||||
case "robot":
|
||||
//处理酷Q HTTP事件接口消息(Event)
|
||||
if (isset($req["post_type"])) {
|
||||
switch ($req["post_type"]) {
|
||||
case "message":
|
||||
new MessageEvent($req);
|
||||
break 2;
|
||||
case "notice":
|
||||
new NoticeEvent($req);
|
||||
break 2;
|
||||
case "request":
|
||||
new RequestEvent($req);
|
||||
break 2;
|
||||
case "meta_event":
|
||||
new MetaEvent($req);
|
||||
break 2;
|
||||
default:
|
||||
new UnknownEvent($req);
|
||||
break 2;
|
||||
}
|
||||
} else {
|
||||
Console::debug("收到来自API[" . $fd . "]连接的回复:" . json_encode($req, 128 | 256));
|
||||
if (isset($req["echo"]) && Cache::array_key_exists("sent_api", $req["echo"])) {
|
||||
$status = $req["status"];
|
||||
$retcode = $req["retcode"];
|
||||
$data = $req["data"];
|
||||
$origin = Cache::get("sent_api")[$req["echo"]];
|
||||
$self_id = $origin["self_id"];
|
||||
$response = [
|
||||
"status" => $status,
|
||||
"retcode" => $retcode,
|
||||
"data" => $data,
|
||||
"self_id" => $self_id
|
||||
];
|
||||
StatusParser::parse($response, $origin);
|
||||
if ($origin["func"] !== null)
|
||||
call_user_func($origin["func"], $response, $origin, $conn);
|
||||
Cache::removeKey("sent_api", $req["echo"]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Console::info("收到未知链接的消息, 来自: " . $fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/5/26
|
||||
* Time: 下午4:10
|
||||
*/
|
||||
|
||||
class WSOpenEvent extends ServerEvent
|
||||
{
|
||||
public function __construct(swoole_websocket_server $server, swoole_http_request $request) {
|
||||
parent::__construct($server);
|
||||
$fd = $request->fd;
|
||||
$get = $request->get;
|
||||
$header = $request->header;
|
||||
//echo json_encode($header, 128|256);
|
||||
$connect_type = $get["type"] ?? (isset($header["x-client-role"]) ? strtolower($header["x-client-role"]) : "");
|
||||
$access_token = $get["token"] ?? (isset($header["authorization"]) ? explode(" ", $header["authorization"])[1] : "");
|
||||
//Console::info("链接类型:".$connect_type."\n链接token:".$access_token);
|
||||
if ($connect_type == "") {
|
||||
Console::info("未指定连接类型,关闭连接.");
|
||||
$server->close($fd);
|
||||
return;
|
||||
}
|
||||
//if (isset($request->header["authorization"])) {
|
||||
//$tokens = explode(" ", $request->header["authorization"]);
|
||||
//$tokens = trim($tokens[1]);
|
||||
if ($access_token !== Cache::get("access_token") && Cache::get("access_token") != "") {
|
||||
Console::info("监测到WS连接,但是token不对,无法匹配。");
|
||||
$server->close($fd);
|
||||
return;
|
||||
}
|
||||
switch ($connect_type) {
|
||||
case "event":
|
||||
case "api":
|
||||
case "universal":
|
||||
$self_id = $get["qq"] ?? ($header["x-self-id"] ?? "");
|
||||
Console::info("收到 " . $connect_type . " 连接,来自机器人:" . $self_id . ",fd:" . $fd);
|
||||
$conn = new RobotWSConnection($server, $fd, $self_id, $request, $connect_type);
|
||||
if($conn->create_success) ConnectionManager::set($fd, $conn);
|
||||
else {
|
||||
Console::error("初始化WS连接失败!fd:".$fd.",QQ:".$self_id);
|
||||
$server->close($fd);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "custom":
|
||||
$conn = new CustomWSConnection($server, $fd, $request);
|
||||
if($conn->create_success) ConnectionManager::set($fd, $conn);
|
||||
break;
|
||||
default:
|
||||
Console::info("Unknown WS Connection connected. I will close it.");
|
||||
$server->close($fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/5/26
|
||||
* Time: 下午3:15
|
||||
*/
|
||||
|
||||
class WorkerStartEvent extends ServerEvent
|
||||
{
|
||||
public function __construct(swoole_server $server, $worker_id) {
|
||||
parent::__construct($server);
|
||||
Cache::$server = $server;
|
||||
load_extensions();
|
||||
Cache::$reload_time->add(1);
|
||||
CQUtil::loadAllFiles();
|
||||
$set = settings();
|
||||
foreach (get_included_files() as $file)
|
||||
Console::debug("Loaded " . $file);
|
||||
//计时器(ms)
|
||||
if ($set["swoole_use_tick"] === true) {
|
||||
Cache::$scheduler = new Scheduler($this->getFramework(), time());
|
||||
//$timer_id = $server->tick($set["swoole_tick_interval"], [Cache::$scheduler, "tick"]);
|
||||
Console::info("已在worker #" . $worker_id . " 中成功启动了计时器!计时器间隔:" . $set["swoole_tick_interval"]);
|
||||
}
|
||||
Console::debug("master_pid = " . $server->master_pid);
|
||||
Console::debug("worker_id = " . $worker_id);
|
||||
Console::put("===== Worker " . Console::setColor("#" . $worker_id, "gold") . " 已启动 =====");
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/4/14
|
||||
* Time: 13:29
|
||||
*/
|
||||
|
||||
use DataProvider as DP;
|
||||
|
||||
class User
|
||||
{
|
||||
private $id;
|
||||
|
||||
protected $nickname = "";
|
||||
protected $permission = 0;
|
||||
protected $friend = [];
|
||||
protected $lexicon = [];
|
||||
protected $user_type = 0;//预留位置,0为QQ用户,1为为微信公众号用户
|
||||
|
||||
protected $function = [];
|
||||
|
||||
public $buffer = null;
|
||||
|
||||
public function __construct($qid) {
|
||||
$this->id = $qid;
|
||||
if (strlen($qid) >= 15) $this->user_type = 1;
|
||||
$this->permission = DP::getJsonData("permissions.json")[$qid] ?? 0;
|
||||
$this->friend = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户QQ号
|
||||
* @return mixed
|
||||
*/
|
||||
public function getId() { return $this->id; }
|
||||
|
||||
/**
|
||||
* 获取用户权限值
|
||||
* @return int
|
||||
*/
|
||||
public function getPermission() { return $this->permission; }
|
||||
|
||||
/**
|
||||
* @param int $permission
|
||||
* @return User
|
||||
*/
|
||||
public function setPermission(int $permission) {
|
||||
$this->permission = $permission;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBuffer() {
|
||||
return $this->buffer;
|
||||
}
|
||||
|
||||
public function setBuffer($buffer) {
|
||||
$this->buffer = $buffer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNickname() {
|
||||
return $this->nickname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFriend() {
|
||||
return $this->friend;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $friend
|
||||
*/
|
||||
public function setFriend(array $friend) {
|
||||
$this->friend = $friend;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $nickname
|
||||
*/
|
||||
public function setNickname(string $nickname) {
|
||||
$this->nickname = $nickname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getLexicon() {
|
||||
return $this->lexicon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $lexicon
|
||||
*/
|
||||
public function setLexicon(array $lexicon) {
|
||||
$this->lexicon = $lexicon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getFunction() {
|
||||
return $this->function;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $function
|
||||
*/
|
||||
public function setFunction(array $function) {
|
||||
$this->function = $function;
|
||||
}
|
||||
|
||||
public function closeFunction($name) {
|
||||
unset($this->function[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getUserType() {
|
||||
return $this->user_type;
|
||||
}
|
||||
|
||||
public function toJson() {
|
||||
$ls = [];
|
||||
foreach ($this as $k => $v) {
|
||||
$ls[$k] = $v;
|
||||
}
|
||||
return json_encode($ls, 128 | 256);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/4/19
|
||||
* Time: 14:55
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class Admin
|
||||
* 框架管理模块,里面已经附带了一些查看状态、重载和停止的功能
|
||||
*/
|
||||
class Admin extends ModBase
|
||||
{
|
||||
public function __construct(CQBot $main, $data) {
|
||||
parent::__construct($main, $data);
|
||||
$this->split_execute = true;
|
||||
}
|
||||
|
||||
public static function initValues() {
|
||||
Cache::set("msg_speed", []);//消息速度列表(存的是时间戳)
|
||||
Cache::set("admin_active", "");
|
||||
Cache::set("admin", settings()["admin"]);
|
||||
}
|
||||
|
||||
public static function onTick($tick) {
|
||||
if ($tick % 900 == 0) CQUtil::saveAllFiles();//900秒储存一次数据
|
||||
if (settings()["save_user_data"]) {
|
||||
if ($tick % 21600 == 0) { //21600秒刷新一次好友列表
|
||||
GroupManager::updateGroupList();
|
||||
FriendManager::updateFriendList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function onRequest($req) {
|
||||
switch ($req["request_type"]) {
|
||||
case "friend":
|
||||
//好友添加请求处理
|
||||
$comment = $req["comment"];//验证信息
|
||||
$flag = $req["flag"];//添加好友用的flag,用于处理好友
|
||||
$user_id = $req["user_id"];
|
||||
|
||||
//如果不需要将好友添加信息发到群里,请注释下面这行
|
||||
CQAPI::debug("有用户请求添加好友啦!\n用户QQ:" . $user_id . "\n验证信息:" . $comment . "\n添加flag:" . $flag, "");
|
||||
|
||||
//如果不需要要自动同意,请注释下面这几行
|
||||
$params = ["flag" => $flag, "approve" => true];
|
||||
CQAPI::set_friend_add_request($req["self_id"], $params, function ($response) use ($user_id) {
|
||||
CQAPI::debug("已自动同意 " . $user_id, "", $response["self_id"]);
|
||||
CQAPI::send_private_msg($user_id, ["message" => "你好,第一次见面请多关照!"]);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function execute($it) {
|
||||
if (!$this->main->isAdmin($this->getUserId())) return false;
|
||||
switch ($it[0]) {
|
||||
case "reload": //管理员重载代码
|
||||
$this->reply("正在重新启动...");
|
||||
CQUtil::reload();
|
||||
return true;
|
||||
case "stop": //管理员停止server
|
||||
$this->reply("正在停止服务器...");
|
||||
CQUtil::stop();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 这是一个示例模块php文件,你可以直接复制此文件中的代码
|
||||
* 然后修改其class的名字(注意要和.php文件的文件名相同)
|
||||
* 例如,新建一个Mailer模块,则Mailer模块的文件名字为
|
||||
* Mailer.php
|
||||
* 如果要开启框架的切割函数激活,请在__construct构造函数中
|
||||
* 添加一句:$this->split_execute = true;
|
||||
* 默认不会执行execute函数
|
||||
*/
|
||||
class Example extends ModBase
|
||||
{
|
||||
public function __construct(CQBot $main, $data) {
|
||||
parent::__construct($main, $data);
|
||||
//$data为CQHTTP插件上报的消息事件数组
|
||||
//这里编写你的内容
|
||||
$this->split_execute = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分词函数,如果开启分词模式的话将调用此数组。
|
||||
* 如果将一句话使用空格、换行和Tab进行分割,用来处理多项参数的功能指令
|
||||
* 例如:"随机数 1 100" 将被分割成数组$it ["随机数","1","100"]
|
||||
* @param $it
|
||||
* @return bool
|
||||
*/
|
||||
public function execute($it) {
|
||||
switch ($it[0]) {
|
||||
case "ping":
|
||||
$this->reply("pong");
|
||||
return true;
|
||||
case "你好":
|
||||
$this->reply("你好,我是CQBot!");
|
||||
return true;
|
||||
case "随机数":
|
||||
if (!isset($it[1]) || !isset($it[2])) {
|
||||
$this->reply("用法: 随机数 开始整数 结束整数");
|
||||
return true;
|
||||
}
|
||||
$c1 = intval($it[1]);
|
||||
$c2 = intval($it[2]);
|
||||
if ($c1 > $c2) {
|
||||
$this->reply("随机数范围错误!应该从小的一方到大的一方!例如:\n随机数 1 99");
|
||||
return true;
|
||||
}
|
||||
$this->reply("生成的随机数是 " . mt_rand($c1, $c2));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/7/17
|
||||
* Time: 2:21 PM
|
||||
*/
|
||||
|
||||
class Help extends ModBase
|
||||
{
|
||||
public function __construct(CQBot $main, $data) {
|
||||
parent::__construct($main, $data);
|
||||
$this->split_execute = true;
|
||||
}
|
||||
|
||||
public function execute($it) {
|
||||
switch ($it[0]) {
|
||||
case "帮助":
|
||||
$msg = "「机器人帮助」";
|
||||
$msg .= "\n随机数:生成一个随机数";
|
||||
$this->reply($msg);
|
||||
return true;
|
||||
case "如何增加机器人功能":
|
||||
$msg = "机器人功能是在框架中src/cqbot/mods/xxx.php文件中编写的。";
|
||||
$msg .= "\nCQBot采用关键词系统,你可以直接像现有源码一样添加case在switch里面,";
|
||||
$msg .= "\n也可以自己新建一个任意名称的Mod名称,例如Entertain.php,你可以在里面编写娱乐功能。";
|
||||
$msg .= "\n你可以直接复制框架中Example.php文件的内容进行编辑。";
|
||||
$msg .= "\n预先封装好的机器人函数均在CQUtil类中,只需直接使用静态方法调用即可!";
|
||||
$msg .= "\n更多示例功能会逐渐添加到框架中,记得更新哦~";
|
||||
$this->reply($msg);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user