😘 重构完成!!!!!!

This commit is contained in:
jerry 2018-11-28 13:26:18 +08:00
parent 54b0fd41f0
commit d629cf9b96
41 changed files with 1103 additions and 997 deletions

View File

@ -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": "*",

View File

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

View File

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

View File

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

View 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.
}
}

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@ -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") . " 已启动 =====");
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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"; }
/**
* 发送其他APIHTTP插件支持的其他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);
}
}

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

View File

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

View File

@ -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", []);
});
}
}
}
}

View File

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

View File

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

View 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");
}
}
}

View File

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

View File

@ -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(){
}
}

View File

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

View File

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

View File

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

View File

@ -1,9 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: jerry
* Date: 2018/10/28
* Time: 4:41 PM
*/
loadAllClass(WORKING_DIR . "src/framework/");

View File

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

1
tmp.md
View File

@ -1 +0,0 @@
#CQBot-swoole

View File

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