mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-03-18 05:04:51 +08:00
😘 重构完成!!!!!!
This commit is contained in:
parent
54b0fd41f0
commit
d629cf9b96
@ -1,6 +1,9 @@
|
||||
{
|
||||
"description": "high-performance intelligent assistant",
|
||||
"minimum-stability": "stable",
|
||||
"license": "proprietary",
|
||||
"require": {
|
||||
"php": ">=7.2.0",
|
||||
"php": ">=7.0.0",
|
||||
"eaglewu/swoole-ide-helper": "dev-master",
|
||||
"ext-mbstring": "^7.1",
|
||||
"ext-json": "*",
|
||||
|
||||
15
cqbot.json
15
cqbot.json
@ -1,15 +1,20 @@
|
||||
{
|
||||
"robot_name": "CQBot",
|
||||
"framework_version": "1.0.0",
|
||||
"cqbot_version": "1.0.0",
|
||||
"config_dir": "cq_data\/config\/",
|
||||
"crash_dir": "cq_data\/crash\/",
|
||||
"user_dir": "cq_data/users\/",
|
||||
"user_dir": "cq_data\/users\/",
|
||||
"cq_data": "cq_data\/",
|
||||
"admin_group": "",
|
||||
"host": "",
|
||||
"port": "",
|
||||
"access_token": "",
|
||||
"info_level": 0,
|
||||
"admin": []
|
||||
"admin": [],
|
||||
"save_user_data": true,
|
||||
"swoole_host": "",
|
||||
"swoole_port": "",
|
||||
"swoole_worker_num": 1,
|
||||
"swoole_dispatch_mode": 2,
|
||||
"swoole_log_file": "cq_data\/crash\/swoole.log",
|
||||
"swoole_use_tick": true,
|
||||
"swoole_tick_interval": 1000
|
||||
}
|
||||
@ -19,7 +19,7 @@ class CQBot
|
||||
|
||||
public $starttime;
|
||||
public $endtime;
|
||||
public $current_id;
|
||||
public $self_id;
|
||||
public $circle;
|
||||
|
||||
public function __construct(Framework $framework, $circle, $package) {
|
||||
@ -27,26 +27,26 @@ class CQBot
|
||||
$this->starttime = microtime(true);
|
||||
$this->framework = $framework;
|
||||
$this->data = $package;
|
||||
$this->current_id = $this->data["self_id"];
|
||||
$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"] == Buffer::get("admin_group")) {
|
||||
if ($this->getRobotId() != Buffer::get("admin_active")) {
|
||||
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 (Buffer::get("mods") as $v) {
|
||||
foreach (Cache::get("mods") as $v) {
|
||||
/** @var ModBase $r */
|
||||
$r = new $v($this, $this->data);
|
||||
if ($r->function_call === false) {
|
||||
if ($r->split_execute === true) {
|
||||
$msg = trim($this->data["message"]);
|
||||
$msg = explode(" ", $msg);
|
||||
$msg = explodeMsg($msg);
|
||||
$r->execute($msg);
|
||||
}
|
||||
}
|
||||
@ -54,51 +54,41 @@ class CQBot
|
||||
return $this->function_called;
|
||||
}
|
||||
|
||||
public function reply($msg){
|
||||
/**
|
||||
* 快速回复消息
|
||||
* @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->sendGroupMsg($this->data["group_id"], $msg);
|
||||
break;
|
||||
$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->sendPrivateMsg($this->data["user_id"], $msg);
|
||||
break;
|
||||
$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":
|
||||
$reply = json_encode(["action" => "send_discuss_msg", "params" => ["discuss_id" => $this->data["discuss_id"], "message" => $msg]]);
|
||||
$connect = CQUtil::getApiConnectionByQQ($this->current_id);
|
||||
if (CQUtil::sendAPI($connect->fd, $reply, ["send_discuss_msg"])) {
|
||||
$out_count = Buffer::$out_count->get();
|
||||
if (Buffer::$data["info_level"] == 2) {
|
||||
Console::put("************API PUSHED***********");
|
||||
}
|
||||
if (Buffer::$data["info_level"] >= 1) {
|
||||
Console::put(Console::setColor(date("H:i:s "), "lightpurple") . Console::setColor("[$out_count]REPLY", "blue") . Console::setColor(" > ", "gray") . json_decode($reply, true)['params']["message"]);
|
||||
}
|
||||
Buffer::$out_count->add(1);
|
||||
}
|
||||
break;
|
||||
$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 sendGroupMsg($groupId, $msg){
|
||||
$this->function_called = true;
|
||||
CQUtil::sendGroupMsg($groupId, $msg, $this->current_id);
|
||||
}
|
||||
|
||||
public function sendPrivateMsg($userId, $msg){
|
||||
$this->function_called = true;
|
||||
CQUtil::sendPrivateMsg($userId, $msg, $this->current_id);
|
||||
}
|
||||
|
||||
public function isAdmin($user){
|
||||
if (in_array($user, Buffer::get("admin"))) return true;
|
||||
public function isAdmin($user) {
|
||||
if (in_array($user, Cache::get("admin"))) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
public function replace($msg, $dat){
|
||||
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) {
|
||||
|
||||
@ -209,4 +209,23 @@ class CQ
|
||||
$str = str_replace("}}", "]", $str);
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
210
src/cqbot/api/CQAPI.php
Normal file
210
src/cqbot/api/CQAPI.php
Normal file
@ -0,0 +1,210 @@
|
||||
<?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) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
16
src/cqbot/connection/CustomWSConnection.php
Normal file
16
src/cqbot/connection/CustomWSConnection.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?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.
|
||||
}
|
||||
}
|
||||
37
src/cqbot/connection/NullConnection.php
Normal file
37
src/cqbot/connection/NullConnection.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
||||
134
src/cqbot/connection/RobotWSConnection.php
Normal file
134
src/cqbot/connection/RobotWSConnection.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?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()) {
|
||||
$this->getServer()->close($k);
|
||||
ConnectionManager::remove($k);
|
||||
}
|
||||
}
|
||||
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" => "[CrazyBot] 机器人 " . $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::unset("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::unset("sent_api", $data["echo"]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSubType() {
|
||||
return $this->sub_type;
|
||||
}
|
||||
}
|
||||
64
src/cqbot/connection/WSConnection.php
Normal file
64
src/cqbot/connection/WSConnection.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@ -11,10 +11,10 @@ class MessageEvent extends Event
|
||||
public function __construct($req) {
|
||||
if (CQUtil::isRobot($req["user_id"])) return;
|
||||
|
||||
$in_count = Buffer::$in_count->get();
|
||||
Buffer::$in_count->add(1);
|
||||
if (Buffer::$data["info_level"] >= 1) {
|
||||
$num = CQUtil::getRobotNum($req["self_id"]);
|
||||
$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"]);
|
||||
}
|
||||
|
||||
@ -9,6 +9,11 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,39 +9,11 @@
|
||||
class RequestEvent extends Event
|
||||
{
|
||||
public function __construct($req) {
|
||||
switch ($req["request_type"]) {
|
||||
case "friend":
|
||||
//好友添加请求处理
|
||||
$comment = $req["comment"];//验证信息
|
||||
$flag = $req["flag"];//添加好友用的flag,用于处理好友
|
||||
$user_id = $req["user_id"];
|
||||
|
||||
//如果不需要将好友添加信息发到群里,请注释下面这行
|
||||
CQUtil::sendDebugMsg("有用户请求添加好友啦!\n用户QQ:" . $user_id . "\n验证信息:" . $comment . "\n添加flag:" . $flag, $req["self_id"], 0);
|
||||
|
||||
//如果不需要要自动同意,请注释下面这几行
|
||||
$params = ["flag" => $flag, "approve" => true];
|
||||
CQUtil::sendAPI(CQUtil::getApiConnectionByQQ($req["self_id"])->fd, ["action" => "set_friend_add_request", "params" => $params], ["set_friend_add_request", $req["user_id"]]);
|
||||
CQUtil::sendGroupMsg(Buffer::get("admin_group"), "已自动同意 " . $req["user_id"], $req["self_id"]);
|
||||
break;
|
||||
case "group":
|
||||
switch ($req["sub_type"]) {
|
||||
case "invite":
|
||||
$comment = $req["comment"];//验证信息
|
||||
$user_id = $req["user_id"];//用户QQ
|
||||
$group_id = $req["group_id"];//被邀请进群的群号
|
||||
$flag = $req["flag"];//同意加群用的flag,用于处理是否加群
|
||||
//TODO: 邀请登录号入群事件处理
|
||||
break;
|
||||
case "add":
|
||||
$comment = $req["comment"];//验证信息
|
||||
$user_id = $req["user_id"];//用户QQ
|
||||
$group_id = $req["group_id"];//用户申请加群的群号
|
||||
$flag = $req["flag"];//同意加群用的flag,用于处理是否同意其入群
|
||||
//TODO: 机器人是管理员,处理入群请求事件
|
||||
break;
|
||||
|
||||
}
|
||||
foreach(Cache::get("mods") as $k => $v){
|
||||
if(in_array("onRequest", get_class_methods($v))){
|
||||
/** @var ModBase $v */
|
||||
$v::onRequest($req);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/10/13
|
||||
* Time: 3:53 PM
|
||||
*/
|
||||
|
||||
class APIConnectEvent extends Event
|
||||
{
|
||||
public function __construct(WSConnection $connection, swoole_websocket_server $server) {
|
||||
$alias = [
|
||||
"10001" => '小马哥'
|
||||
//这里放机器人连接的别名
|
||||
//"QQ" => "别名"
|
||||
];
|
||||
CQUtil::sendDebugMsg("[CQBot] 机器人 " . ($alias[$connection->getQQ()] ?? $connection->getQQ()) . " 已连接", strval($connection->getQQ()), false);
|
||||
Buffer::set("admin_active", $connection->getQQ());
|
||||
}
|
||||
}
|
||||
@ -6,7 +6,7 @@
|
||||
* Time: 下午2:05
|
||||
*/
|
||||
|
||||
class HTTPEvent extends Event
|
||||
class HTTPEvent extends ServerEvent
|
||||
{
|
||||
public $content;
|
||||
public $isValid = false;
|
||||
@ -17,12 +17,13 @@ class HTTPEvent extends Event
|
||||
* @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://www.swoole.com
|
||||
//此为HTTP请求的回复,更多设置回复头、传送文件、POST、GET请求解析等内容请查阅文档https://wiki.swoole.com
|
||||
}
|
||||
|
||||
/**
|
||||
* 此函数为炸毛机器人中的函数,为此预留
|
||||
* 此函数为一个示例的检测有效的函数,为此预留
|
||||
* 作用是判断传入的请求数据是合法的
|
||||
* @param $param
|
||||
* @return bool
|
||||
|
||||
16
src/cqbot/event/server/ServerEvent.php
Normal file
16
src/cqbot/event/server/ServerEvent.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@ -6,12 +6,16 @@
|
||||
* Time: 下午4:14
|
||||
*/
|
||||
|
||||
class WSCloseEvent extends Event
|
||||
class WSCloseEvent extends ServerEvent
|
||||
{
|
||||
public function __construct(swoole_server $server, int $fd) {
|
||||
$connect = CQUtil::getConnection($fd);
|
||||
if ($connect !== null)
|
||||
unset(Buffer::$connect[$fd]);
|
||||
Console::info("WebSocket Connection closed. fd: " . $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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,39 +6,58 @@
|
||||
* Time: 下午4:04
|
||||
*/
|
||||
|
||||
class WSMessageEvent extends Event
|
||||
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);
|
||||
$connect = CQUtil::getConnection($fd);
|
||||
if ($connect === null) {
|
||||
$conn = ConnectionManager::get($fd);
|
||||
if ($conn === null) {
|
||||
Console::info("收到一个未知链接发来的消息。" . $fd);
|
||||
return;
|
||||
}
|
||||
switch ($connect->getType()) {
|
||||
case 0:
|
||||
switch ($conn->getType()) {
|
||||
case "robot":
|
||||
//处理酷Q HTTP事件接口消息(Event)
|
||||
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;
|
||||
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);
|
||||
Cache::unset("sent_api", $req["echo"]);
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
Console::debug("收到来自API[" . $fd . "]连接的回复:" . json_encode($req, 128 | 256));
|
||||
if (isset($req["echo"])) CQAPIHandler::execute($req["echo"], $req);
|
||||
break;
|
||||
default:
|
||||
Console::info("收到未知链接的消息, 来自: " . $fd);
|
||||
|
||||
@ -6,9 +6,10 @@
|
||||
* Time: 下午4:10
|
||||
*/
|
||||
|
||||
class WSOpenEvent extends Event
|
||||
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;
|
||||
@ -21,15 +22,10 @@ class WSOpenEvent extends Event
|
||||
$server->close($fd);
|
||||
return;
|
||||
}
|
||||
if ($access_token == "") {
|
||||
Console::info("未指定连接token,关闭连接.");
|
||||
$server->close($fd);
|
||||
return;
|
||||
}
|
||||
//if (isset($request->header["authorization"])) {
|
||||
//$tokens = explode(" ", $request->header["authorization"]);
|
||||
//$tokens = trim($tokens[1]);
|
||||
if ($access_token !== Buffer::get("access_token")) {
|
||||
if ($access_token !== Cache::get("access_token") && Cache::get("access_token") != "") {
|
||||
Console::info("监测到WS连接,但是token不对,无法匹配。");
|
||||
$server->close($fd);
|
||||
return;
|
||||
@ -37,17 +33,24 @@ class WSOpenEvent extends Event
|
||||
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);
|
||||
CQUtil::getConnection($fd, $connect_type, $self_id);
|
||||
$robots = [];
|
||||
foreach (Buffer::get("robots") as $v) {
|
||||
$robots[] = $v["qq"];
|
||||
}
|
||||
if (!in_array($self_id, $robots)) {
|
||||
Buffer::append("robots", ["qq" => $self_id, "addr" => $request->server["remote_addr"]]);
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,19 +6,25 @@
|
||||
* Time: 下午3:15
|
||||
*/
|
||||
|
||||
class WorkerStartEvent extends Event
|
||||
class WorkerStartEvent extends ServerEvent
|
||||
{
|
||||
public function __construct(swoole_server $server, $worker_id){
|
||||
Buffer::$reload_time->add(1);
|
||||
Buffer::$connect = [];
|
||||
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)
|
||||
$this->getFramework()->scheduler = new Scheduler($this->getFramework());
|
||||
$server->tick(1000, [$this->getFramework(), "processTick"]);
|
||||
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("\n==========STARTUP DONE==========\n");
|
||||
Console::put("===== Worker " . Console::setColor("#" . $worker_id, "gold") . " 已启动 =====");
|
||||
}
|
||||
}
|
||||
@ -1,92 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/4/26
|
||||
* Time: 下午1:42
|
||||
*/
|
||||
|
||||
class CQAPIHandler
|
||||
{
|
||||
static function execute($cmd, $res = null) {
|
||||
if (!isset($cmd["type"])) return false;
|
||||
if ($res["status"] != "ok" && $res["status"] != "async") {
|
||||
$email_contact = [103, 201, -1, -2, -14];
|
||||
if (in_array($res["retcode"], $email_contact)) {
|
||||
CQUtil::errorLog("API推送失败, retcode = " . $res["retcode"], "API ERROR", 0);
|
||||
} else {
|
||||
CQUtil::errorLog("API推送失败, retcode = " . $res["retcode"] . "\n说明 = " . ErrorStatus::getMessage($res["retcode"]) . "\n" . json_encode($res["echo"], 128 | 256), "API ERROR");
|
||||
}
|
||||
echo("\n\n");
|
||||
}
|
||||
switch ($cmd["type"]) {
|
||||
case "get_group_list"://Done
|
||||
self::getGroupList($cmd["self_id"], $cmd["params"], $res["data"]);
|
||||
break;
|
||||
case "get_friend_list"://Done
|
||||
self::getFriendList($cmd["self_id"], $cmd["params"], $res["data"]["friends"]);
|
||||
break;
|
||||
case "set_friend_add_request":
|
||||
$id = $cmd["params"][0];
|
||||
$msg = "Hi~你好!";
|
||||
$msg .= "\n第一次见面请多关照!";
|
||||
CQUtil::sendPrivateMsg($id, $msg, $cmd["self_id"]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新群列表API
|
||||
* @param $self_id
|
||||
* @param $param
|
||||
* @param $data
|
||||
*/
|
||||
static function getGroupList($self_id, $param, $data) {
|
||||
switch ($param[0]) {
|
||||
case "step1":
|
||||
$list = Buffer::get("group_list");
|
||||
foreach ($data as $k => $v) {
|
||||
if (!isset($list[$v["group_id"]])) {
|
||||
$list[$v["group_id"]] = [
|
||||
"group_id" => $v["group_id"],
|
||||
"group_name" => $v["group_name"],
|
||||
"fetch_members" => false,
|
||||
"joiner" => [$self_id]
|
||||
];
|
||||
} elseif (!in_array($self_id, $list[$v["group_id"]]["joiner"])) {
|
||||
$list[$v["group_id"]]["joiner"][] = $self_id;
|
||||
}
|
||||
}
|
||||
Buffer::set("group_list", $list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新好友列表API
|
||||
* @param $self_id
|
||||
* @param $param
|
||||
* @param $data
|
||||
*/
|
||||
static function getFriendList($self_id, $param, $data) {
|
||||
switch ($param[0]) {
|
||||
case "step1":
|
||||
foreach ($data as $k => $v) {
|
||||
$user = CQUtil::getUser($v["user_id"]);
|
||||
$ls = $user->getFriend();
|
||||
if (!in_array($self_id, $ls)) {
|
||||
$ls[] = $self_id;
|
||||
}
|
||||
$user->setFriend($ls);
|
||||
$user->setNickname($v["nickname"]);
|
||||
}
|
||||
foreach (CQUtil::getAllUsers() as $k => $v) {
|
||||
$serial = serialize($v);
|
||||
file_put_contents(DataProvider::getUserFolder() . $k . ".dat", $serial);
|
||||
}
|
||||
Buffer::set("user", []);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/4/12
|
||||
* Time: 10:32
|
||||
*/
|
||||
|
||||
//加载需要优先加载的文件
|
||||
require_once(WORKING_DIR."src/cqbot/mods/ModBase.php");
|
||||
require_once(WORKING_DIR."src/cqbot/item/User.php");
|
||||
require_once(WORKING_DIR."src/cqbot/event/Event.php");
|
||||
|
||||
loadAllClass(WORKING_DIR."src/cqbot/");
|
||||
|
||||
//加载外部模块
|
||||
require_once(WORKING_DIR."src/extension/PHPMailer.phar");
|
||||
|
||||
/**
|
||||
* 下面是不能在loader里面加载的php文件,以下文件更新时必须停止脚本后再重启才能重新加载
|
||||
* start.php
|
||||
* src/cqbot/Framework.php
|
||||
* src/cqbot/Console.php
|
||||
* src/cqbot/utils/Buffer.php
|
||||
* src/cqbot/ErrorStatus.php
|
||||
*/
|
||||
@ -6,50 +6,67 @@
|
||||
* Time: 14:55
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class Admin
|
||||
* 框架管理模块,里面已经附带了一些查看状态、重载和停止的功能
|
||||
*/
|
||||
class Admin extends ModBase
|
||||
{
|
||||
protected $cmds;
|
||||
|
||||
public function __construct(CQBot $main, $data) {
|
||||
parent::__construct($main, $data);
|
||||
$this->split_execute = true;
|
||||
}
|
||||
|
||||
public static function initValues() {
|
||||
Buffer::set("msg_speed", []);//消息速度列表(存的是时间戳)
|
||||
Buffer::set("admin_active", "");
|
||||
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 ($tick % 21600 == 0) {//21600秒刷新一次好友列表
|
||||
GroupManager::updateGroupList();
|
||||
FriendManager::updateFriendList();
|
||||
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"://管理员重载代码
|
||||
case "reload": //管理员重载代码
|
||||
$this->reply("正在重新启动...");
|
||||
if (file_get_contents("/home/ubuntu/CrazyBotFramework/src/Framework.php") != Buffer::get("res_code"))
|
||||
if (file_get_contents("/home/ubuntu/CrazyBotFramework/src/Framework.php") != Cache::get("res_code"))
|
||||
$this->reply("检测到改变了Framework文件的内容!如需完全重载,请重启完整进程!");
|
||||
CQUtil::reload();
|
||||
return true;
|
||||
case "stop"://管理员停止server
|
||||
case "stop": //管理员停止server
|
||||
$this->reply("正在停止服务器...");
|
||||
CQUtil::stop();
|
||||
return true;
|
||||
case "op"://添加管理员
|
||||
$user = $it[1];
|
||||
Buffer::append("admin", $user);
|
||||
$this->reply("added operator $user");
|
||||
return true;
|
||||
case "deop"://删除管理员
|
||||
$user = $it[1];
|
||||
if (Buffer::in_array("admin", $user)) Buffer::unsetByValue("admin", $user);
|
||||
$this->reply("removed operator $user");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -5,24 +5,26 @@
|
||||
* 然后修改其class的名字(注意要和.php文件的文件名相同)
|
||||
* 例如,新建一个Mailer模块,则Mailer模块的文件名字为
|
||||
* Mailer.php
|
||||
* 然后更改class Entertain为class Mailer即可
|
||||
* 功能在execute中编写即可
|
||||
* 如果需要写判断所有文本的功能,则直接在__construct的parent::__construct下方编写自己的内容即可
|
||||
* 如果要开启框架的切割函数激活,请在__construct构造函数中
|
||||
* 添加一句:$this->split_execute = true;
|
||||
* 默认不会执行execute函数
|
||||
*/
|
||||
class Example extends ModBase
|
||||
{
|
||||
protected $cmds;
|
||||
|
||||
public function __construct(CQBot $main, $data) {
|
||||
parent::__construct($main, $data);
|
||||
$message = $data["message"];
|
||||
|
||||
//这里可以写一些匹配所有文本的功能,例如下面的一个简单的debug
|
||||
if (mb_substr($message, 0, 3) == "dbg") {
|
||||
$this->reply("你输入了:" . mb_substr($message, 3));
|
||||
}
|
||||
//$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":
|
||||
|
||||
@ -8,7 +8,10 @@
|
||||
|
||||
class Help extends ModBase
|
||||
{
|
||||
public function __construct(CQBot $main, $data) { parent::__construct($main, $data); }
|
||||
public function __construct(CQBot $main, $data) {
|
||||
parent::__construct($main, $data);
|
||||
$this->split_execute = true;
|
||||
}
|
||||
|
||||
public function execute($it) {
|
||||
switch ($it[0]) {
|
||||
@ -22,7 +25,6 @@ class Help extends ModBase
|
||||
$msg .= "\nCQBot采用关键词系统,你可以直接像现有源码一样添加case在switch里面,";
|
||||
$msg .= "\n也可以自己新建一个任意名称的Mod名称,例如Entertain.php,你可以在里面编写娱乐功能。";
|
||||
$msg .= "\n你可以直接复制框架中Example.php文件的内容进行编辑。";
|
||||
$msg .= "\n你也可以在tasks/Scheduler.php中tick函数里添加自己的定时执行的功能。";
|
||||
$msg .= "\n预先封装好的机器人函数均在CQUtil类中,只需直接使用静态方法调用即可!";
|
||||
$msg .= "\n更多示例功能会逐渐添加到框架中,记得更新哦~";
|
||||
$this->reply($msg);
|
||||
|
||||
@ -6,38 +6,36 @@
|
||||
* Time: 10:39
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ModBase
|
||||
* @method static onRequest($req)
|
||||
* @method static onNotice($req)
|
||||
*/
|
||||
abstract class ModBase
|
||||
{
|
||||
protected $main;
|
||||
protected $data;
|
||||
public $call_task = false;
|
||||
|
||||
/**
|
||||
* 控制模块是否调用execute函数的变量
|
||||
* 当function_call 为 TRUE时,表明CQBot主实例不需要调用execute函数
|
||||
* 当为FALSE时,CQBot在实例化模块对象后会执行execute函数
|
||||
* 控制模块是否调用分割函数的变量
|
||||
* 当split 为FALSE时,表明CQBot主实例不需要调用execute函数
|
||||
* 当为TRUE时,CQBot在实例化模块对象后会执行execute函数
|
||||
* @var bool
|
||||
*/
|
||||
public $function_call = false;
|
||||
public $split_execute = false;
|
||||
|
||||
public function __construct(CQBot $main, $data) {
|
||||
$this->main = $main;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getUser($data = null) {
|
||||
return CQUtil::getUser($data === null ? $this->data["user_id"] : $data["user_id"]);
|
||||
}
|
||||
public function execute($it) { }
|
||||
|
||||
public function getUser($data = null) { return CQUtil::getUser($data === null ? $this->data["user_id"] : $data["user_id"]); }
|
||||
|
||||
public function getUserId($data = null) { return $data === null ? strval($this->data["user_id"]) : strval($data["user_id"]); }
|
||||
|
||||
public function execute($it) { }
|
||||
|
||||
public function reply($msg) { $this->main->reply($msg); }
|
||||
|
||||
public function sendPrivateMsg($user, $msg) { $this->main->sendPrivateMsg($user, $msg); }
|
||||
|
||||
public function sendGroupMsg($user, $msg) { $this->main->sendGroupMsg($user, $msg); }
|
||||
public function reply($msg, callable $callback = null) { return $this->main->reply($msg, $callback); }
|
||||
|
||||
public function getMessageType() { return $this->data["message_type"]; }
|
||||
|
||||
|
||||
@ -10,17 +10,27 @@ use DataProvider as DP;
|
||||
|
||||
class CQUtil
|
||||
{
|
||||
public static function loadAllFiles() {
|
||||
Console::debug("loading configs...");
|
||||
Buffer::set("mods", self::getMods());//加载的模块列表
|
||||
Buffer::set("user", []);//清空用户列表
|
||||
Buffer::set("time_send", false);//发送Timing数据到管理群
|
||||
Buffer::set("res_code", file_get_contents(WORKING_DIR . "src/framework/Framework.php"));
|
||||
Buffer::set("group_list", DP::getJsonData("group_list.json"));//获取群组列表
|
||||
public static function initEmptyCaches() {
|
||||
$ls = [
|
||||
"user", // 储存用户对象的数组
|
||||
"sent_api", // 储存每条API请求的会调函数Closure等原始内容
|
||||
"msg_speed", // 储存记录消息速度的数组
|
||||
"bug_msg_list" // 储存当前状态下所有未发出去的消息列表
|
||||
];
|
||||
foreach ($ls as $v) {
|
||||
Cache::set($v, []);
|
||||
}
|
||||
}
|
||||
|
||||
public static function loadAllFiles() {
|
||||
Cache::set("info_level", settings()["info_level"]);
|
||||
Console::debug("loading configs...");
|
||||
Cache::set("mods", self::getMods());//加载的模块列表
|
||||
Cache::set("group_list", DP::getJsonData("group_list.json"));//获取群组列表
|
||||
Cache::set("admin_group", settings()["admin_group"]);
|
||||
|
||||
//加载全局屏蔽的机器人列表
|
||||
Buffer::set("bots", DP::getJsonData("bots.json"));
|
||||
Cache::set("bots", DP::getJsonData("bots.json"));
|
||||
|
||||
//调用各个模块单独的Buffer数据
|
||||
foreach (self::getMods() as $v) {
|
||||
@ -31,18 +41,20 @@ class CQUtil
|
||||
}
|
||||
|
||||
public static function saveAllFiles() {
|
||||
Console::info("Saving files...");
|
||||
|
||||
DP::setJsonData("bots.json", Buffer::get("bots"));
|
||||
DP::setJsonData("group_list.json", Buffer::get("group_list"));
|
||||
Console::info("Saving files... ", null, "");
|
||||
|
||||
DP::setJsonDataAsync("bots.json", Cache::get("bots"));
|
||||
DP::setJsonDataAsync("group_list.json", Cache::get("group_list"));
|
||||
|
||||
//保存用户数据
|
||||
foreach (self::getAllUsers() as $k => $v) {
|
||||
$serial = serialize($v);
|
||||
file_put_contents(DP::getUserFolder() . $k . ".dat", $serial);
|
||||
if (settings()["save_user_data"]) {
|
||||
foreach (self::getAllUsers() as $k => $v) {
|
||||
$serial = serialize($v);
|
||||
file_put_contents(DP::getUserFolder() . $k . ".dat", $serial);
|
||||
}
|
||||
}
|
||||
Console::info("Saved files");
|
||||
|
||||
Console::put("Saved files.");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,243 +68,16 @@ class CQUtil
|
||||
$time = date("Y-m-d H:i:s");
|
||||
$msg = "[$head @ $time]: $log\n";
|
||||
file_put_contents(DP::getDataFolder() . "log_error.txt", $msg, FILE_APPEND);
|
||||
if ($send_debug_message)
|
||||
self::sendDebugMsg($msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送调试信息到管理群(需先设置管理群号)
|
||||
* @param $msg
|
||||
* @param $self_id
|
||||
* @param int $need_head
|
||||
* @return null
|
||||
*/
|
||||
static function sendDebugMsg($msg, $self_id = null, $need_head = 1) {
|
||||
if ($self_id === null) $self_id = self::findRobot();
|
||||
if ($self_id === null) return null;
|
||||
|
||||
if ((Buffer::get("admin_group") ?? "") == "") return null;
|
||||
if ($need_head)
|
||||
$data = CQMsg("[DEBUG] " . date("H:i:s") . ": " . $msg, "group", Buffer::get("admin_group"));
|
||||
else
|
||||
$data = CQMsg($msg, "group", Buffer::get("admin_group"));
|
||||
$connect = CQUtil::getApiConnectionByQQ($self_id);
|
||||
return self::sendAPI($connect->fd, $data, ["send_debug_msg"]);
|
||||
if ($send_debug_message) CQAPI::debug($msg);
|
||||
}
|
||||
|
||||
static function findRobot() {
|
||||
foreach (self::getConnections("api") as $v) {
|
||||
foreach (ConnectionManager::getAll("robot") as $v) {
|
||||
return $v->getQQ();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送API,给API端口
|
||||
* @param $fd
|
||||
* @param $data
|
||||
* @return bool
|
||||
*/
|
||||
static function APIPush($fd, $data) {
|
||||
if ($data == null || $data == "") {
|
||||
Console::error("EMPTY DATA PUSH");
|
||||
return false;
|
||||
}
|
||||
/*if (self::checkAPIConnection() === -1) {
|
||||
//忽略掉framework链接API之前的消息
|
||||
self::errorlog("API推送失败,未发送的消息: \n" . $data, "API ERROR", 0);
|
||||
return false;
|
||||
}
|
||||
if (self::checkAPIConnection() === 0) {
|
||||
self::APIPushAfterConnected($data);
|
||||
return true;
|
||||
}*/
|
||||
if (Buffer::$event->push($fd, $data) === false) {
|
||||
$data = self::unicodeDecode($data);
|
||||
self::errorlog("API推送失败,未发送的消息: \n" . $data, "API ERROR", 0);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟推送在API连接断开后收到的消息函数//待定
|
||||
*/
|
||||
/*static function APIPushDelayMsg() {
|
||||
$delay_push_list = Buffer::get("delay_push");
|
||||
$cur_time = time();
|
||||
foreach ($delay_push_list as $item) {
|
||||
$data = $item["data"];
|
||||
$time = $item["time"];
|
||||
if ($cur_time - $time <= 10) {
|
||||
self::APIPush($data);
|
||||
}
|
||||
}
|
||||
Buffer::set("delay_push", []);
|
||||
}*/
|
||||
|
||||
/**
|
||||
* 推迟推送API,用于酷Q重启后的重新连接API//待定
|
||||
* @param $data
|
||||
*/
|
||||
static function APIPushAfterConnected($data) {
|
||||
$delay_push_list = Buffer::get("delay_push");
|
||||
$delay_push_list[] = ["data" => $data, "time" => time()];
|
||||
Buffer::set("delay_push", $delay_push_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码unicode中文编码
|
||||
* @param $str
|
||||
* @return null|string|string[]
|
||||
*/
|
||||
static function unicodeDecode($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟发送一个HTML-get请求
|
||||
* @param $url
|
||||
* @return mixed
|
||||
*/
|
||||
static function getHTML($url) {
|
||||
$ch = curl_init();
|
||||
$timeout = 5;
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_HEADER, 1);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0');
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
|
||||
$r = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串的反转结果
|
||||
* @param $str
|
||||
* @param string $encoding
|
||||
* @return string
|
||||
*/
|
||||
static public function getRev($str, $encoding = 'utf-8') {
|
||||
$result = '';
|
||||
$len = mb_strlen($str);
|
||||
for ($i = $len - 1; $i >= 0; $i--) {
|
||||
$result .= mb_substr($str, $i, 1, $encoding);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮件功能,基于PHPMailer模块,需先安装phpmailer。默认此工程demo里已包含有phpmailer了。
|
||||
* 请根据实际自己的邮箱更新下面的用户名、密码、smtp服务器地址、端口等。
|
||||
* 此功能非基于本作者编写的代码,如有问题请在github上找PHPMailer项目进行反馈
|
||||
* @param $address
|
||||
* @param $title
|
||||
* @param $content
|
||||
* @param $self_id
|
||||
* @param string $name
|
||||
* @param int $send_debug_message
|
||||
* @return bool|string
|
||||
*/
|
||||
static function sendEmail($address, $title, $content, $self_id, $name = "CQ开发团队", $send_debug_message = 1) {
|
||||
$mail = new \PHPMailer(true);
|
||||
try {
|
||||
$mail->isSMTP();
|
||||
$mail->Host = 'here your smtp host';
|
||||
$mail->SMTPAuth = true;
|
||||
$mail->Username = 'here your mailbox address';
|
||||
$mail->Password = 'here your password';
|
||||
$mail->SMTPSecure = 'ssl';
|
||||
$mail->Port = 465;
|
||||
$mail->setFrom('here your mailbox address', $name);
|
||||
if (is_array($address)) {
|
||||
foreach ($address as $item)
|
||||
$mail->addAddress($item);
|
||||
} else {
|
||||
$mail->addAddress($address);
|
||||
}
|
||||
//Content
|
||||
$mail->isHTML(true);
|
||||
$mail->Subject = $title;
|
||||
$mail->CharSet = "UTF-8";
|
||||
$mail->Body = $content;
|
||||
$mail->send();
|
||||
if (is_array($address))
|
||||
$address = implode(",", $address);
|
||||
Console::info("向 $address 发送的邮件完成");
|
||||
unset($mail);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
self::errorLog("发送邮件错误!错误信息:" . $info = $mail->ErrorInfo, "ERROR", 0);
|
||||
unset($mail);
|
||||
return $info;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回所有api、event连接
|
||||
* @param string $type
|
||||
* @return WSConnection[]
|
||||
*/
|
||||
static function getConnections($type = "all") {
|
||||
switch ($type) {
|
||||
case "all":
|
||||
return Buffer::$connect;
|
||||
case "event":
|
||||
$ls = [];
|
||||
foreach (Buffer::$connect as $fd => $connection) {
|
||||
if ($connection->getType() === 0) {
|
||||
$ls[$fd] = $connection;
|
||||
}
|
||||
}
|
||||
return $ls;
|
||||
case "api":
|
||||
$ls = [];
|
||||
foreach (Buffer::$connect as $fd => $connection) {
|
||||
if ($connection->getType() === 1) {
|
||||
$ls[$fd] = $connection;
|
||||
}
|
||||
}
|
||||
return $ls;
|
||||
default:
|
||||
Console::error("寻找连接时链接类型传入错误!");
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $fd
|
||||
* @param null $type
|
||||
* @param null $qq
|
||||
* @return WSConnection
|
||||
*/
|
||||
static function getConnection($fd, $type = null, $qq = null) {
|
||||
//var_dump(Buffer::$connect);
|
||||
if (!isset(Buffer::$connect[$fd]) && $type !== null && $qq !== null) {
|
||||
Console::info("创建连接 " . $fd . " 中...");
|
||||
$s = new WSConnection(Buffer::$event, $fd, $type, $qq);
|
||||
if ($s->create_success) {
|
||||
Buffer::$connect[$fd] = $s;
|
||||
Console::debug("创建成功!");
|
||||
$s->initConnection();
|
||||
} else return null;
|
||||
}
|
||||
return Buffer::$connect[$fd] ?? null;
|
||||
}
|
||||
|
||||
static function getApiConnectionByQQ($qq) {
|
||||
foreach (self::getConnections() as $fd => $c) {
|
||||
if ($c->getType() === 1 && $c->getQQ() == $qq) {
|
||||
return $c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取运行时间
|
||||
* @param $time
|
||||
@ -347,38 +132,6 @@ class CQUtil
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为群组管理员或群主功能,此功能需要先获取群组列表,否则会产生一个warning
|
||||
* @param $group
|
||||
* @param $user_id
|
||||
* @return bool
|
||||
*/
|
||||
static function isGroupAdmin($group, $user_id) {
|
||||
$ls = Buffer::get("group_list")[$group]["member"];
|
||||
$is_admin = false;
|
||||
foreach ($ls as $k => $v) {
|
||||
if ($v["user_id"] == $user_id) {
|
||||
if ($v["role"] == "admin" || $v["role"] == "owner") {
|
||||
$is_admin = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $is_admin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于发送错误日志邮件的功能,请根据实际情况填写邮箱。
|
||||
* 此功能基于sendMail,请看上方sendMail函数的介绍
|
||||
* @param $title
|
||||
* @param $content
|
||||
* @param $self_id
|
||||
* @param string $name
|
||||
*/
|
||||
static function sendErrorEmail($title, $content, $self_id, $name = "机器人错误提示") {
|
||||
self::sendEmail(["here your receive email address"], $title, $content, $self_id, $name, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已经加载到内存的用户。
|
||||
* read_all为true时,会加载所有User.dat到内存中,false时仅会读取已经加载到内存的用户
|
||||
@ -393,25 +146,27 @@ class CQUtil
|
||||
$vs = explode(".", $v);
|
||||
if (array_pop($vs) == "dat") {
|
||||
$class = unserialize(file_get_contents(DP::getUserFolder() . $v));
|
||||
if (!Buffer::array_key_exists("user", $vs[0])) {
|
||||
Buffer::appendKey("user", $vs[0], $class);
|
||||
if (!Cache::array_key_exists("user", $vs[0])) {
|
||||
Cache::appendKey("user", $vs[0], $class);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Buffer::get("user");
|
||||
return Cache::get("user");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户实例
|
||||
* @param $id
|
||||
* @param bool $enable_init
|
||||
* @return User
|
||||
*/
|
||||
static function getUser($id) {
|
||||
$d = Buffer::get("user");
|
||||
static function getUser($id, $enable_init = true) {
|
||||
$d = Cache::get("user");
|
||||
if (!isset($d[$id])) {
|
||||
self::initUser($id);
|
||||
$d = Buffer::get("user");
|
||||
$r = self::initUser($id, $enable_init);
|
||||
if (!$r) return null;
|
||||
$d = Cache::get("user");
|
||||
}
|
||||
/** @var User $class */
|
||||
$class = $d[$id];
|
||||
@ -421,103 +176,19 @@ class CQUtil
|
||||
/**
|
||||
* 初始化用户实例。如果没有此用户的实例数据,会创建
|
||||
* @param $id
|
||||
* @param bool $enable_init
|
||||
* @return bool
|
||||
*/
|
||||
static function initUser($id) {
|
||||
static function initUser($id, $enable_init = true) {
|
||||
if (file_exists(DP::getUserFolder() . $id . ".dat")) $class = unserialize(file_get_contents(DP::getUserFolder() . $id . ".dat"));
|
||||
else {
|
||||
Console::info("无法找到用户 " . $id . " 的数据,正在创建...");
|
||||
$class = new User($id);
|
||||
if ($enable_init) {
|
||||
Console::info("无法找到用户 " . $id . " 的数据,正在创建...");
|
||||
$class = new User($id);
|
||||
} else return false;
|
||||
}
|
||||
Buffer::appendKey("user", $id, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送群组消息,含控制台推出
|
||||
* @param $groupId
|
||||
* @param $msg
|
||||
* @param string $self_id
|
||||
* @return bool
|
||||
*/
|
||||
static function sendGroupMsg($groupId, $msg, $self_id) {
|
||||
$reply = ["action" => "send_group_msg", "params" => ["group_id" => $groupId, "message" => $msg]];
|
||||
$reply["echo"] = $reply;
|
||||
$reply["echo"]["time"] = time();
|
||||
$connections = CQUtil::getApiConnectionByQQ($self_id);
|
||||
if ($connections === null) {
|
||||
Console::error("未找到qq号:" . $self_id . "的API连接");
|
||||
return false;
|
||||
} else {
|
||||
$api_fd = $connections->fd;
|
||||
}
|
||||
if (self::sendAPI($api_fd, $reply, ["send_group_msg"])) {
|
||||
if (Buffer::$data["info_level"] == 1) {
|
||||
$out_count = Buffer::$out_count->get();
|
||||
Console::put(Console::setColor(date("H:i:s "), "lightpurple") . Console::setColor("[{$out_count}]GROUP", "blue") . Console::setColor(" " . $groupId, "yellow") . Console::setColor(" > ", "gray") . $msg);
|
||||
Buffer::$out_count->add(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送私聊消息
|
||||
* @param $userId
|
||||
* @param $msg
|
||||
* @param $self_id
|
||||
* @return bool
|
||||
*/
|
||||
static function sendPrivateMsg($userId, $msg, $self_id) {
|
||||
$reply = ["action" => "send_private_msg", "params" => ["user_id" => $userId, "message" => $msg]];
|
||||
$reply["echo"] = $reply;
|
||||
$reply["echo"]["time"] = time();
|
||||
$connections = CQUtil::getApiConnectionByQQ($self_id);
|
||||
if ($connections === null) {
|
||||
Console::error("未找到qq号:" . $self_id . "的API连接");
|
||||
return false;
|
||||
} else {
|
||||
$api_fd = $connections->fd;
|
||||
}
|
||||
if (self::sendAPI($api_fd, $reply, ["send_private_msg"])) {
|
||||
if (Buffer::$data["info_level"] == 1) {
|
||||
$out_count = Buffer::$out_count->get();
|
||||
Console::put(Console::setColor(date("H:i:s "), "lightpurple") . Console::setColor("[{$out_count}]PRIVATE", "blue") . Console::setColor(" " . $userId, "yellow") . Console::setColor(" > ", "gray") . $msg);
|
||||
Buffer::$out_count->add(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static function getFriendName($qq) { return Buffer::get("friend_list")[$qq]["nickname"] ?? "unknown"; }
|
||||
|
||||
static function getGroupName($group) { return Buffer::get("group_list")[$group]["group_name"] ?? "unknown"; }
|
||||
|
||||
/**
|
||||
* 发送其他API,HTTP插件支持的其他API都可以发送。
|
||||
* echo是返回内容,可以在APIHandler.php里面解析
|
||||
* @param $fd
|
||||
* @param $data
|
||||
* @param $echo
|
||||
* @return bool
|
||||
*/
|
||||
static function sendAPI($fd, $data, $echo) {
|
||||
if (!is_array($data)) {
|
||||
$api = [];
|
||||
$api["action"] = $data;
|
||||
} else {
|
||||
$api = $data;
|
||||
}
|
||||
$rw = $echo;
|
||||
$echo = [
|
||||
"self_id" => self::getConnection($fd)->getQQ(),
|
||||
"type" => array_shift($rw),
|
||||
"params" => $rw
|
||||
];
|
||||
$api["echo"] = $echo;
|
||||
Console::info("将要发送的API包:" . json_encode($api, 128 | 256));
|
||||
return self::APIPush($fd, json_encode($api));
|
||||
Cache::appendKey("user", $id, $class);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -536,26 +207,33 @@ class CQUtil
|
||||
Console::debug("loading mod: " . $name);
|
||||
}
|
||||
}
|
||||
/** @var ModBase[] $ls */
|
||||
for ($i = 0; $i < count($ls) - 1; $i++) {
|
||||
for ($j = 0; $j < count($ls) - $i - 1; $j++) {
|
||||
$s = defined($ls[$j] . "::mod_level") ? $ls[$j]::mod_level : 10;
|
||||
$s1 = defined($ls[$j + 1] . "::mod_level") ? $ls[$j + 1]::mod_level : 10;
|
||||
//Console::info("Comparing mod " . $ls[$j] . " with " . $ls[$j + 1] . ", level are " . $s . ", " . $s1);
|
||||
if ($s < $s1) {
|
||||
$t = $ls[$j + 1];
|
||||
$ls[$j + 1] = $ls[$j];
|
||||
$ls[$j] = $t;
|
||||
}
|
||||
}
|
||||
}
|
||||
for ($i = count($ls) - 1; $i >= 0; $i--) {
|
||||
$s = defined($ls[$i] . "::mod_level") ? $ls[$i]::mod_level : 10;
|
||||
if ($s === 0) unset($ls[$i]);
|
||||
}
|
||||
return $ls;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断模块是否存在
|
||||
* @param $mod_name
|
||||
* @return bool
|
||||
*/
|
||||
static function isModExists($mod_name) {
|
||||
$ls = self::getMods();
|
||||
return in_array($mod_name, $ls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重启框架,此服务重启为全自动的
|
||||
*/
|
||||
static function reload() {
|
||||
Console::info("Reloading server");
|
||||
self::saveAllFiles();
|
||||
Buffer::$event->reload();
|
||||
Cache::$server->reload();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -564,8 +242,7 @@ class CQUtil
|
||||
static function stop() {
|
||||
Console::info("Stopping server...");
|
||||
self::saveAllFiles();
|
||||
Buffer::$api->close();
|
||||
Buffer::$event->shutdown();
|
||||
Cache::$server->shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -656,41 +333,20 @@ class CQUtil
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
static function isRobot($user_id) {
|
||||
$robots = [];
|
||||
foreach (Buffer::get("robots") as $v) {
|
||||
$robots[] = $v["qq"];
|
||||
foreach (ConnectionManager::getAll("robot") as $v) {
|
||||
if (!in_array($v->getQQ(), $robots))
|
||||
$robots[] = $v->getQQ();
|
||||
}
|
||||
foreach (Buffer::get("bots") as $v) {
|
||||
foreach (Cache::get("bots") as $v) {
|
||||
$robots[] = $v;
|
||||
}
|
||||
return in_array($user_id, $robots);
|
||||
}
|
||||
|
||||
static function getRobotNum($qq) {
|
||||
$ls = Buffer::get("robots");
|
||||
foreach ($ls as $k => $v) {
|
||||
if ($v["qq"] == $qq)
|
||||
return $k;
|
||||
}
|
||||
return null;
|
||||
static function getRobotAlias($qq) {
|
||||
return RobotWSConnection::ALIAS_LIST[$qq] ?? "机器人";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -699,7 +355,7 @@ class CQUtil
|
||||
* @param bool $insert
|
||||
*/
|
||||
static function updateMsg($insert = true) {
|
||||
$ls = Buffer::get("msg_speed");
|
||||
$ls = Cache::get("msg_speed");
|
||||
if ($insert === true)
|
||||
$ls [] = time();
|
||||
foreach ($ls as $k => $v) {
|
||||
@ -707,6 +363,6 @@ class CQUtil
|
||||
array_splice($ls, $k, 1);
|
||||
}
|
||||
}
|
||||
Buffer::set("msg_speed", $ls);
|
||||
Cache::set("msg_speed", $ls);
|
||||
}
|
||||
}
|
||||
45
src/cqbot/utils/ConnectionManager.php
Normal file
45
src/cqbot/utils/ConnectionManager.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/11/26
|
||||
* Time: 10:20 AM
|
||||
*/
|
||||
|
||||
class ConnectionManager
|
||||
{
|
||||
static function getAll($type = null){
|
||||
if ($type === null) return Cache::$connect;
|
||||
$ls = [];
|
||||
foreach (Cache::$connect as $k => $v) {
|
||||
switch ($type) {
|
||||
case "robot":
|
||||
/** @var RobotWSConnection[] $ls */
|
||||
if ($v instanceof RobotWSConnection) $ls[] = $v;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $ls;
|
||||
}
|
||||
|
||||
public static function get($fd) { return Cache::$connect[$fd] ?? null; }
|
||||
|
||||
public static function set($fd, WSConnection $connection) { Cache::$connect[$fd] = $connection; }
|
||||
|
||||
public static function remove($fd) { unset(Cache::$connect[$fd]); }
|
||||
|
||||
public static function isConnectExists($fd) { return array_key_exists($fd, Cache::$connect); }
|
||||
|
||||
/**
|
||||
* @param $qq
|
||||
* @return null|RobotWSConnection|NullConnection
|
||||
*/
|
||||
public static function getRobotConnection($qq) {
|
||||
foreach (Cache::$connect as $v) {
|
||||
if ($v instanceof RobotWSConnection && $v->getQQ() == $qq && $v->getSubType() != "event") return $v;
|
||||
}
|
||||
return new NullConnection(Cache::$server, -1, "0.0.0.0", $qq);
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,10 @@
|
||||
* Time: 12:54
|
||||
*/
|
||||
|
||||
/**
|
||||
* 此类中使用的读取和写入文件等IO有区分同步IO和异步IO,请(尽量)不要在事件循环中使用过多阻塞IO的方法。
|
||||
* 如使用同步逻辑,推荐将数据写入内存缓存类Cache中后进行读写,使用定时器或关闭服务时储存。
|
||||
*/
|
||||
class DataProvider
|
||||
{
|
||||
/**
|
||||
@ -35,11 +39,26 @@ class DataProvider
|
||||
}
|
||||
|
||||
/**
|
||||
* 储存PHP数组为json文件,文件不存在则会创建文件
|
||||
* 储存PHP数组为json文件,文件不存在则会创建文件。
|
||||
* 此方式为同步阻塞执行,可能会阻塞worker进程。
|
||||
* @param $filename
|
||||
* @param array $args
|
||||
*/
|
||||
static function setJsonData($filename, array $args){
|
||||
file_put_contents(self::getDataFolder() . $filename, json_encode($args, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING));
|
||||
}
|
||||
|
||||
/**
|
||||
* 储存PHP数组为json文件,文件不存在会创建文件
|
||||
* 此方式为异步非阻塞执行,不会对worker造成阻塞。
|
||||
* @param $filename
|
||||
* @param array $args
|
||||
* @param callable|null $function
|
||||
*/
|
||||
static function setJsonDataAsync($filename, array $args, callable $function = null) {
|
||||
$data = json_encode($args, 128 | 256);
|
||||
$filename = self::getDataFolder() . $filename;
|
||||
if ($function === null) swoole_async_writefile($filename, $data, function () { });
|
||||
else swoole_async_writefile($filename, $data, $function);
|
||||
}
|
||||
}
|
||||
@ -8,17 +8,35 @@
|
||||
|
||||
class FriendManager
|
||||
{
|
||||
|
||||
/**
|
||||
* 更新所有机器人的好友列表
|
||||
*/
|
||||
public static function updateFriendList() {
|
||||
$list = CQUtil::getAllUsers(true);
|
||||
foreach ($list as $k => $v) {
|
||||
$v->setFriend([]);
|
||||
}
|
||||
foreach (CQUtil::getConnections("api") as $k => $v) {
|
||||
$fd = $v->fd;
|
||||
$robot_id = $v->getQQ();
|
||||
Console::put("正在获取机器人 " . $robot_id . " 的好友列表...");
|
||||
CQUtil::sendAPI($fd, ["action" => "_get_friend_list", "params" => ["flat" => "true"]], ["get_friend_list", "step1"]);
|
||||
foreach (ConnectionManager::getAll("robot") as $k => $v) {
|
||||
if ($v->getSubType() != "event") {
|
||||
$robot_id = $v->getQQ();
|
||||
Console::put("正在获取机器人 " . $robot_id . " 的好友列表...");
|
||||
$v->sendAPI("_get_friend_list", ["flat" => "true"], function ($response) use ($robot_id) {
|
||||
foreach ($response["data"]["friends"] as $k => $v) {
|
||||
$user = CQUtil::getUser($v["user_id"]);
|
||||
$ls = $user->getFriend();
|
||||
if (!in_array($robot_id, $ls)) {
|
||||
$ls[] = $robot_id;
|
||||
}
|
||||
$user->setFriend($ls);
|
||||
$user->setNickname($v["nickname"]);
|
||||
}
|
||||
foreach (CQUtil::getAllUsers() as $k => $v) {
|
||||
$serial = serialize($v);
|
||||
file_put_contents(DataProvider::getUserFolder() . $k . ".dat", $serial);
|
||||
}
|
||||
Cache::set("user", []);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,15 +10,30 @@ class GroupManager
|
||||
{
|
||||
/**
|
||||
* 更新群组列表
|
||||
* @param null $qq
|
||||
*/
|
||||
public static function updateGroupList($qq = null) {
|
||||
Buffer::set("group_list", []);
|
||||
foreach (CQUtil::getConnections("api") as $k => $v) {
|
||||
$fd = $v->fd;
|
||||
$robot_id = $v->getQQ();
|
||||
Console::put("正在获取机器人 " . $robot_id . " 的群组列表...");
|
||||
CQUtil::sendAPI($fd, "get_group_list", ["get_group_list", "step1"]);
|
||||
public static function updateGroupList() {
|
||||
Cache::set("group_list", []);
|
||||
foreach (ConnectionManager::getAll("robot") as $k => $v) {
|
||||
if ($v->getSubType() != "event") {
|
||||
$robot_id = $v->getQQ();
|
||||
Console::put("正在获取机器人 " . $robot_id . " 的群组列表...");
|
||||
$v->sendAPI("get_group_list", [], function ($response) use ($robot_id) {
|
||||
$list = Cache::get("group_list");
|
||||
foreach ($response["data"] as $k => $v) {
|
||||
if (!isset($list[$v["group_id"]])) {
|
||||
$list[$v["group_id"]] = [
|
||||
"group_id" => $v["group_id"],
|
||||
"group_name" => $v["group_name"],
|
||||
"fetch_members" => false,
|
||||
"joiner" => [$robot_id]
|
||||
];
|
||||
} elseif (!in_array($robot_id, $list[$v["group_id"]]["joiner"])) {
|
||||
$list[$v["group_id"]]["joiner"][] = $robot_id;
|
||||
}
|
||||
}
|
||||
Cache::set("group_list", $list);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,25 +13,30 @@ class Scheduler
|
||||
|
||||
private $framework;
|
||||
|
||||
private $start_time;
|
||||
|
||||
/**
|
||||
* ScheduleTask constructor.
|
||||
* @param Framework $framework
|
||||
* @param $start_time
|
||||
*/
|
||||
public function __construct(Framework $framework) {
|
||||
public function __construct(Framework $framework, $start_time) {
|
||||
$this->framework = $framework;
|
||||
self::$obj = $this;
|
||||
$this->start_time = $start_time;
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
return self::$obj;
|
||||
}
|
||||
|
||||
public function tick($id, $tick_time) {
|
||||
public function tick($id) {
|
||||
//Console::info("Timer ".Console::setColor("#".$id, "gold").":".Cache::$server->worker_id." ticking at ".time());
|
||||
/** @var array $ls */
|
||||
$ls = Buffer::get("mods");
|
||||
$ls = Cache::get("mods");
|
||||
foreach ($ls as $v) {
|
||||
if (in_array("onTick", get_class_methods($v))) {
|
||||
$v::onTick($tick_time - $this->framework->run_time);
|
||||
$v::onTick(time() - $this->start_time, $id);
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/cqbot/utils/StatusParser.php
Normal file
29
src/cqbot/utils/StatusParser.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/11/28
|
||||
* Time: 12:24 PM
|
||||
*/
|
||||
|
||||
class StatusParser
|
||||
{
|
||||
public static function parse($response, $origin) {
|
||||
switch ($response["retcode"]) {
|
||||
case 103:
|
||||
case 201:
|
||||
case -1:
|
||||
case -2:
|
||||
case -14:
|
||||
case 999:
|
||||
case 998:
|
||||
CQUtil::errorLog("API推送失败, retcode = " . $response["retcode"], "API ERROR", 0);
|
||||
break;
|
||||
case 200:
|
||||
break;
|
||||
default:
|
||||
CQUtil::errorLog("API推送失败, retcode = " . $response["retcode"] . "\n说明 = " . ErrorStatus::getMessage($response["retcode"]) . "\n" . json_encode($origin, 128 | 256), "API ERROR");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,70 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/6/13
|
||||
* Time: 8:10 PM
|
||||
*/
|
||||
|
||||
class WSConnection
|
||||
{
|
||||
public $fd;
|
||||
|
||||
/**
|
||||
* 0 = event连接
|
||||
* 1 = api连接
|
||||
* 默认为event连接,如果可以收到返回的get_status则标记为1
|
||||
* @var int
|
||||
*/
|
||||
protected $type = 0;
|
||||
|
||||
protected $server;
|
||||
|
||||
protected $qq = "";
|
||||
|
||||
public $create_success = false;
|
||||
|
||||
public function __construct(swoole_websocket_server $server, $fd, $type, $qq) {
|
||||
$this->server = $server;
|
||||
$this->fd = $fd;
|
||||
if ($type = null || $qq === null) return;
|
||||
$this->type = ($type == "event" ? 0 : 1);
|
||||
$this->qq = $qq;
|
||||
$this->create_success = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回swoole server
|
||||
* @return swoole_websocket_server
|
||||
*/
|
||||
public function getServer() {
|
||||
return $this->server;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回本连接是什么类型的
|
||||
* @return int
|
||||
*/
|
||||
public function getType() {
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function initConnection() {
|
||||
foreach (CQUtil::getConnections(($this->getType() == 0 ? "event" : "api")) as $k => $v) {
|
||||
if ($v->getQQ() == $this->getQQ() && $k != $this->fd) {
|
||||
$this->server->close($k);
|
||||
unset(Buffer::$connect[$k]);
|
||||
}
|
||||
}
|
||||
if ($this->type === 1) {
|
||||
new APIConnectEvent($this, $this->server);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getQQ() {
|
||||
return $this->qq;
|
||||
}
|
||||
}
|
||||
@ -7,26 +7,31 @@
|
||||
*/
|
||||
|
||||
|
||||
class Buffer
|
||||
class Cache
|
||||
{
|
||||
/** @var int */
|
||||
static $worker_id = -1;
|
||||
/** @var array Cache data */
|
||||
static $data = [];
|
||||
static $api_session = [];
|
||||
/** @var \swoole_http_client $api */
|
||||
static $api;
|
||||
/** @var \swoole_websocket_server $event */
|
||||
static $event;
|
||||
/** @var string $log_file */
|
||||
static $log_file = "";
|
||||
/** @var \swoole_server $comm */
|
||||
static $comm = null;
|
||||
/** @var \swoole_websocket_server $server */
|
||||
static $server;
|
||||
/** @var swoole_http_client $http_client */
|
||||
static $http_client;
|
||||
/** @var Scheduler $scheduler */
|
||||
static $scheduler;
|
||||
|
||||
//共享内存的原子操作数据部分
|
||||
/** @var \swoole_atomic $in_count */
|
||||
static $in_count;//接收消息
|
||||
/** @var \swoole_atomic $out_count */
|
||||
static $out_count;//发送消息数量
|
||||
/** @var WSConnection[] */
|
||||
static $connect = [];
|
||||
/** @var swoole_atomic $reload_time */
|
||||
static $reload_time;
|
||||
/** @var \swoole_atomic $api_id */
|
||||
static $api_id;
|
||||
|
||||
/** @var WSConnection[] $connect */
|
||||
static $connect = [];
|
||||
|
||||
static function get($name){ return self::$data[$name] ?? null; }
|
||||
|
||||
@ -51,30 +56,4 @@ class Buffer
|
||||
if(!is_array((self::$data[$name] ?? 1))) return false;
|
||||
return in_array($value, self::$data[$name]);
|
||||
}
|
||||
|
||||
////////////预留部分,为redis更新作准备/////////////
|
||||
|
||||
/** @var string[] 为未来支持redis数据库做准备 */
|
||||
static $vars = [];
|
||||
static $ls = [];
|
||||
|
||||
static function _get(string $name){
|
||||
|
||||
}
|
||||
|
||||
static function _setString(string $key, string $value){
|
||||
|
||||
}
|
||||
|
||||
static function _setList(string $key, array $value){
|
||||
|
||||
}
|
||||
|
||||
static function _appendList(string $key, string $value){
|
||||
|
||||
}
|
||||
|
||||
static function _ping(){
|
||||
|
||||
}
|
||||
}
|
||||
@ -38,7 +38,7 @@ class Console
|
||||
|
||||
static function debug($obj, $head = null) {
|
||||
if ($head === null) $head = "[" . date("H:i:s") . " DEBUG]";
|
||||
if ((Buffer::get("info_level") ?? 0) < 2) return;
|
||||
if ((Cache::get("info_level") ?? 0) < 2) return;
|
||||
if (!is_string($obj)) var_dump($obj);
|
||||
else echo(self::setColor($head . $obj, "green") . "\n");
|
||||
}
|
||||
@ -49,18 +49,36 @@ class Console
|
||||
else echo(self::setColor($head . $obj, "red") . "\n");
|
||||
}
|
||||
|
||||
static function info($obj, $head = null) {
|
||||
static function info($obj, $head = null, $tail = "\n") {
|
||||
if ($head === null) $head = "[" . date("H:i:s") . " INFO] ";
|
||||
if (!is_string($obj)) var_dump($obj);
|
||||
else echo(self::setColor($head . $obj, "blue") . "\n");
|
||||
}
|
||||
|
||||
static function chatLog($head, $msg) {
|
||||
|
||||
else echo(self::setColor($head . $obj, "blue") . $tail);
|
||||
}
|
||||
|
||||
static function put($obj, $color = "") {
|
||||
if (!is_string($obj)) var_dump($obj);
|
||||
else echo(self::setColor($obj, $color) . "\n");
|
||||
}
|
||||
|
||||
public static function msg($obj) {
|
||||
if (Cache::get("info_level") >= 1) {
|
||||
switch ($obj["action"]) {
|
||||
case "send_private_msg":
|
||||
Console::put(Console::setColor("[".date("H:i:s")." PRIVATE] ", "blue").Console::setColor($obj["params"]["user_id"] ?? "", "yellow") . Console::setColor(" > ", "gray").($obj["params"]["message"] ?? ""));
|
||||
break;
|
||||
case "send_group_msg":
|
||||
Console::put(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::put(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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,66 +8,55 @@
|
||||
|
||||
class Framework
|
||||
{
|
||||
public static $super_user;
|
||||
public $host = "127.0.0.1";
|
||||
public $event_port = 20000;
|
||||
public $host;
|
||||
public $port;
|
||||
|
||||
const VERSION = "1.0.0";
|
||||
|
||||
/** @var \swoole_websocket_server $event */
|
||||
public $event;
|
||||
|
||||
public static $obj = null;
|
||||
|
||||
public $run_time;
|
||||
public $info_level = 1;
|
||||
|
||||
/** @var \swoole_http_client $api */
|
||||
public $api;
|
||||
private $log_file;
|
||||
public $tick_time;
|
||||
|
||||
/** @var Scheduler */
|
||||
public $scheduler = null;
|
||||
|
||||
public function __construct($config) {
|
||||
$this->host = $config["host"];
|
||||
$this->event_port = $config["port"];
|
||||
Buffer::set("access_token", $config["access_token"] ?? "");
|
||||
Buffer::set("info_level", $config["info_level"]);
|
||||
Buffer::set("admin_group", $config["admin_group"]);
|
||||
$this->host = $config["swoole_host"];
|
||||
$this->port = $config["swoole_port"];
|
||||
//Buffer::set("access_token", $config["access_token"] ?? "");
|
||||
//Buffer::set("info_level", $config["info_level"]);
|
||||
//Buffer::set("admin_group", $config["admin_group"]);
|
||||
|
||||
$this->selfCheck();
|
||||
|
||||
Console::info("CQBot Framework starting...");
|
||||
$this->event = new swoole_websocket_server($this->host, $this->event_port);
|
||||
Console::info("CQBot Framework starting on " . $this->host . ":" . $this->port);
|
||||
$this->event = new swoole_websocket_server($this->host, $this->port);
|
||||
|
||||
Buffer::$log_file = CRASH_DIR . "swoole.log";
|
||||
|
||||
//设置swoole基本参数
|
||||
$worker_num = 1;//进程数调整,默认为1,如果调整worker为多个,则需要自行修改Buffer类的储存方式到redis、或其他数据库等数据结构
|
||||
$dispatch_mode = 2;//解析模式,详见wiki.swoole.com
|
||||
$this->event->set([
|
||||
"log_file" => Buffer::$log_file,
|
||||
"worker_num" => $worker_num,
|
||||
"dispatch_mode" => $dispatch_mode
|
||||
"log_file" => $config["swoole_log_file"],
|
||||
"worker_num" => $config["swoole_worker_num"],
|
||||
"dispatch_mode" => $config["swoole_dispatch_mode"]
|
||||
]);
|
||||
|
||||
//swoole服务器启动时运行的函数
|
||||
$this->event->on('WorkerStart', [$this, 'onWorkerStart']);
|
||||
|
||||
//swoole服务端收到WebSocket信息时运行的函数
|
||||
$this->event->on('message', [$this, 'onEventMessage']);
|
||||
$this->event->on('message', function ($server, $frame) { new WSMessageEvent($server, $frame); });
|
||||
|
||||
//收到ws连接和断开连接回调的函数
|
||||
$this->event->on('open', function (\swoole_websocket_server $server, \swoole_http_request $request) { new WSOpenEvent($server, $request); });
|
||||
$this->event->on('close', function (\swoole_server $server, int $fd) { new WSCloseEvent($server, $fd); });
|
||||
|
||||
//设置接收HTTP接口接收的内容,兼容微信公众号和其他服务用
|
||||
$this->event->on("request", [$this, "onRequest"]);
|
||||
$this->event->on("request", function (\swoole_http_request $request, \swoole_http_response $response) { new HTTPEvent($request, $response); });
|
||||
|
||||
//设置原子计数器
|
||||
Buffer::$in_count = new \swoole_atomic(1);
|
||||
Buffer::$out_count = new \swoole_atomic(1);
|
||||
Buffer::$reload_time = new \swoole_atomic(0);
|
||||
Cache::$in_count = new \swoole_atomic(0);
|
||||
Cache::$out_count = new \swoole_atomic(0);
|
||||
Cache::$reload_time = new \swoole_atomic(0);
|
||||
Cache::$api_id = new \swoole_atomic(0);
|
||||
}
|
||||
|
||||
public function start() { $this->event->start(); }
|
||||
@ -84,35 +73,9 @@ class Framework
|
||||
*/
|
||||
public function onWorkerStart(\swoole_server $server, $worker_id) {
|
||||
self::$obj = $this;
|
||||
$this->run_time = time();
|
||||
Buffer::set("info_level", $this->info_level);//设置info等级
|
||||
Buffer::$event = $server;
|
||||
require_once(WORKING_DIR . "src/cqbot/loader.php");
|
||||
new WorkerStartEvent($server, $worker_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调函数:当HTTP插件发来json包后激活此函数
|
||||
* @param swoole_websocket_server $server
|
||||
* @param $frame
|
||||
*/
|
||||
public function onEventMessage($server, $frame) { new WSMessageEvent($server, $frame); }
|
||||
|
||||
/**
|
||||
* 回调函数:当IP:event端口收到相关HTTP请求时候调用
|
||||
* 你可在此编写HTTP请求回复的内容(比如做一个web界面?)
|
||||
* 也可以在这里处理微信公众号的请求(可能需要端口转发)
|
||||
* @param swoole_http_request $request
|
||||
* @param swoole_http_response $response
|
||||
*/
|
||||
public function onRequest($request, $response) { new HTTPEvent($request, $response); }
|
||||
|
||||
/**
|
||||
* 回调函数:异步计时器,一秒执行一次。请勿在此使用阻塞IO方法
|
||||
* @param $id
|
||||
*/
|
||||
public function processTick($id) { $this->scheduler->tick($id, ($this->tick_time = time())); }
|
||||
|
||||
/**
|
||||
* 开启时候的自检模块
|
||||
* 检测项目在下面列举
|
||||
@ -123,7 +86,7 @@ class Framework
|
||||
if (substr(PHP_VERSION, 0, 1) != "7") die("PHP >=7 required.\n");
|
||||
if (!function_exists("curl_exec")) die("无法找到curl扩展,请先安装.\n");
|
||||
//if (!class_exists("ZipArchive")) die("无法找到zip扩展,请先安装.(如果不需要zip功能可以删除此条自检)\n");
|
||||
if (!is_file(CRASH_DIR . "swoole.log")) file_put_contents(CRASH_DIR . "swoole.log", "");
|
||||
if (!is_file(settings()["swoole_log_file"])) file_put_contents(settings()["swoole_log_file"], "");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2,10 +2,19 @@
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/6/14
|
||||
* Time: 11:04 AM
|
||||
* Date: 2018/11/22
|
||||
* Time: 2:42 PM
|
||||
*/
|
||||
|
||||
/**
|
||||
* 获取全局配置
|
||||
* 此函数使用同步阻塞IO,请不要在功能逻辑代码中使用此函数。
|
||||
* @return mixed
|
||||
*/
|
||||
function settings() {
|
||||
return json_decode(file_get_contents(WORKING_DIR . "/cqbot.json"), true);
|
||||
}
|
||||
|
||||
register_shutdown_function(function () {
|
||||
$error = error_get_last();
|
||||
if (isset($error['type'])) {
|
||||
@ -68,6 +77,38 @@ function CQMsg($msg, $type, $id) {
|
||||
return $reply;
|
||||
}
|
||||
|
||||
function getClassPath($dir, $class_name) {
|
||||
$list = scandir($dir);
|
||||
unset($list[0], $list[1]);
|
||||
foreach ($list as $v) {
|
||||
$taskFileName = explode(".", $v);
|
||||
if (is_dir($dir . $v)) {
|
||||
if (($find = getClassPath($dir . $v . "/", $class_name)) !== null) return $find;
|
||||
else continue;
|
||||
} else {
|
||||
if (array_pop($taskFileName) == "php" && $taskFileName[0] == $class_name) return $dir . $v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function class_loader($p) {
|
||||
$dir = WORKING_DIR . "src/";
|
||||
$filepath = getClassPath($dir, $p);
|
||||
require_once $filepath;
|
||||
}
|
||||
|
||||
function load_extensions() {
|
||||
$dir = WORKING_DIR . "src/extension/";
|
||||
$ls = scandir($dir);
|
||||
unset($ls[0], $ls[1]);
|
||||
foreach ($ls as $k => $v) {
|
||||
if (mb_substr($v, -4) == "phar") {
|
||||
require_once $dir . $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function color($str, $end = "\n") {
|
||||
$str = str_replace("{red}", "\e[38;5;203m", $str);
|
||||
$str = str_replace("{green}", "\e[38;5;83m", $str);
|
||||
@ -81,4 +122,36 @@ function color($str, $end = "\n") {
|
||||
$str = str_replace("{r}", "\e[m", $str);
|
||||
$str .= "\e[m" . $end;
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用自己定义的万(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unicode解析
|
||||
* @param $str
|
||||
* @return null|string|string[]
|
||||
*/
|
||||
function unicodeDecode($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);
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: jerry
|
||||
* Date: 2018/10/28
|
||||
* Time: 4:41 PM
|
||||
*/
|
||||
|
||||
loadAllClass(WORKING_DIR . "src/framework/");
|
||||
45
start.php
45
start.php
@ -1,29 +1,8 @@
|
||||
<?php
|
||||
|
||||
function loadAllClass($dir) {
|
||||
$dir_obj = scandir($dir);
|
||||
unset($dir_obj[0], $dir_obj[1]);
|
||||
foreach ($dir_obj as $m) {
|
||||
$taskFileName = explode(".", $m);
|
||||
if (is_dir($dir . $m . "/")) loadAllClass($dir . $m . "/");
|
||||
else {
|
||||
if (count($taskFileName) < 2 || ($taskFileName[1] != "php" && $taskFileName[1] != "phar")) continue;
|
||||
require_once($dir . $m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//工作目录设置
|
||||
//initialize settings.
|
||||
define("WORKING_DIR", __DIR__ . "/");
|
||||
|
||||
function settings() { return json_decode(file_get_contents(WORKING_DIR . "/cqbot.json"), true); }
|
||||
|
||||
if (settings() === null) die("Start failed. Please check cqbot.json file.\n");
|
||||
|
||||
//启动时间
|
||||
define("START_TIME", time());
|
||||
|
||||
//设置dir常量
|
||||
include(WORKING_DIR."src/framework/global_functions.php");
|
||||
define("CQ_DATA", WORKING_DIR . settings()["cq_data"]);
|
||||
define("CONFIG_DIR", WORKING_DIR . settings()["config_dir"]);
|
||||
define("USER_DIR", WORKING_DIR . settings()["user_dir"]);
|
||||
@ -33,19 +12,19 @@ define("CRASH_DIR", WORKING_DIR . settings()["crash_dir"]);
|
||||
@mkdir(USER_DIR, 0777, true);
|
||||
@mkdir(CRASH_DIR, 0777, true);
|
||||
|
||||
spl_autoload_register("class_loader");
|
||||
|
||||
if (settings() === null) die("Start failed. Please check cqbot.json file.\n");
|
||||
|
||||
//启动时间
|
||||
define("START_TIME", time());
|
||||
|
||||
//设置时区(如果非国内用户请自行注释或更改)
|
||||
date_default_timezone_set("Asia/Shanghai");
|
||||
|
||||
//加载框架
|
||||
require(WORKING_DIR . "src/framework/loader.php");
|
||||
|
||||
//加载全局函数
|
||||
require("tools.php");
|
||||
|
||||
|
||||
//如果是第一次启动或监听地址为空,则启动配置向导
|
||||
if (settings()["host"] == "" || settings()["port"] == "") include("wizard.php");
|
||||
if (settings()["swoole_host"] == "" || settings()["swoole_port"] == "") include("wizard.php");
|
||||
|
||||
//initializing framework
|
||||
$cqbot = new Framework(settings());
|
||||
$cqbot->start();
|
||||
(new Framework(settings()))->start();
|
||||
|
||||
|
||||
40
wizard.php
40
wizard.php
@ -10,46 +10,45 @@ function printHelp() {
|
||||
echo color("{gold}=======CQBot-swoole=======");
|
||||
echo color("{gold}* 首次使用设置 *");
|
||||
echo color("{red}红色{r}为未设置,{green}绿色{r}为已设置,其他颜色为可选设置");
|
||||
echo color("[{green}1{r}] {" . (settings()["host"] == "" ? "red" : "green") . "}设置监听地址");
|
||||
echo color("[{green}2{r}] {" . (settings()["port"] == "" ? "red" : "green") . "}设置监听端口");
|
||||
echo color("[{green}1{r}] {" . (settings()["swoole_host"] == "" ? "red" : "green") . "}设置监听地址");
|
||||
echo color("[{green}2{r}] {" . (settings()["swoole_port"] == "" ? "red" : "green") . "}设置监听端口");
|
||||
echo color("[{green}3{r}] {" . (settings()["admin_group"] == "" ? "red" : "green") . "}设置管理群");
|
||||
echo color("[{green}4{r}] {yellow}设置管理员");
|
||||
echo color("[{green}5{r}] {yellow}设置连接token");
|
||||
echo color("[{green}6{r}] {lightlightblue}开始运行");
|
||||
}
|
||||
|
||||
$id = "1";
|
||||
while (true) {
|
||||
printHelp();
|
||||
echo color("> ", "");
|
||||
$id = trim(fgets(STDIN));
|
||||
switch ($id) {
|
||||
case "1":
|
||||
echo color("请输入监听地址(默认0.0.0.0):", "");
|
||||
$host = trim(fgets(STDIN));
|
||||
if ($host == "") {
|
||||
$properties["host"] = "0.0.0.0";
|
||||
$properties["swoole_host"] = "0.0.0.0";
|
||||
echo color("{gray}已设置地址:0.0.0.0(默认)");
|
||||
} else {
|
||||
$properties["host"] = $host;
|
||||
$properties["swoole_host"] = $host;
|
||||
echo color("{gray}已设置地址:" . $host);
|
||||
}
|
||||
$ls = settings();
|
||||
$ls["host"] = $properties["host"];
|
||||
$ls["swoole_host"] = $properties["swoole_host"];
|
||||
file_put_contents("cqbot.json", json_encode($ls, 128 | 256));
|
||||
$id = 2;
|
||||
break;
|
||||
case "2":
|
||||
echo color("请输入监听端口(默认20000):", "");
|
||||
$host = trim(fgets(STDIN));
|
||||
if ($host == "") {
|
||||
$properties["port"] = 20000;
|
||||
$properties["swoole_port"] = 20000;
|
||||
echo color("{gray}已设置端口:20000(默认)");
|
||||
} else {
|
||||
$properties["port"] = $host;
|
||||
$properties["swoole_port"] = $host;
|
||||
echo color("{gray}已设置端口:" . $host);
|
||||
}
|
||||
$ls = settings();
|
||||
$ls["port"] = $properties["port"];
|
||||
$ls["swoole_port"] = $properties["swoole_port"];
|
||||
file_put_contents("cqbot.json", json_encode($ls, 128 | 256));
|
||||
$id = 3;
|
||||
break;
|
||||
case "3":
|
||||
echo color("请输入机器人QQ的管理群(机器人必须已经在群内):", "");
|
||||
@ -63,22 +62,25 @@ while (true) {
|
||||
$ls = settings();
|
||||
$ls["admin_group"] = $properties["admin_group"];
|
||||
file_put_contents("cqbot.json", json_encode($ls, 128 | 256));
|
||||
$id = 4;
|
||||
break;
|
||||
case "4":
|
||||
echo color("请输入机器人管理员QQ:", "");
|
||||
echo color("请输入机器人管理员QQ(多个用空格分割):", "");
|
||||
$group = trim(fgets(STDIN));
|
||||
if ($group == "") {
|
||||
echo color("{red}请勿输入空数据!");
|
||||
break;
|
||||
}
|
||||
$properties["admin"][] = $group;
|
||||
echo color("{gray}已设置机器人的管理员:" . $group);
|
||||
$s = explodeMsg($group, true);
|
||||
$properties["admin"] = $s;
|
||||
echo color("{gray}已设置机器人的管理员:" . implode(", ",$s));
|
||||
$ls = settings();
|
||||
$ls["admin"] = $properties["admin"];
|
||||
file_put_contents("cqbot.json", json_encode($ls, 128 | 256));
|
||||
$id = 5;
|
||||
break;
|
||||
case "5":
|
||||
echo color("请输入连接框架的access_token:", "");
|
||||
echo color("请输入连接框架的access_token(如果不用token则直接回车):", "");
|
||||
$group = trim(fgets(STDIN));
|
||||
$properties["access_token"] = $group;
|
||||
if ($group == "") echo color("{gray}token为空,将不检测验证token!");
|
||||
@ -86,7 +88,7 @@ while (true) {
|
||||
$ls = settings();
|
||||
$ls["access_token"] = $properties["access_token"];
|
||||
file_put_contents("cqbot.json", json_encode($ls, 128 | 256));
|
||||
break;
|
||||
break 2;
|
||||
case "6":
|
||||
break 2;
|
||||
case "?":
|
||||
@ -99,5 +101,5 @@ while (true) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (settings()["host"] != "") return true;
|
||||
else return false;
|
||||
echo color("{green}设置就绪!3秒后启动服务器。");
|
||||
sleep(3);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user