From 2de43376005f68d3c9ba295fa670b2aa7eedefbd Mon Sep 17 00:00:00 2001 From: jerry Date: Fri, 15 Jun 2018 09:18:53 +0800 Subject: [PATCH] =?UTF-8?q?Finished=20single=20websocket=20framework.=20It?= =?UTF-8?q?=20works.=20=E5=AE=8C=E6=88=90=E4=BA=86=E5=85=A8=E5=8F=8D?= =?UTF-8?q?=E5=90=91websocket=E6=A1=86=E6=9E=B6=EF=BC=8C=E5=85=A8=E9=9D=A2?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E6=9C=BA=E5=99=A8=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + src/cqbot/CQBot.php | 28 ++-- src/cqbot/Framework.php | 25 ++- src/cqbot/event/ApiMessageEvent.php | 15 -- src/cqbot/event/ApiUpgradeEvent.php | 55 ------- src/cqbot/event/WSCloseEvent.php | 7 +- src/cqbot/event/WSMessageEvent.php | 82 +++++++--- src/cqbot/event/WSOpenEvent.php | 5 +- src/cqbot/event/WorkerStartEvent.php | 9 -- src/cqbot/item/Group.php | 16 +- src/cqbot/item/GroupMember.php | 8 +- src/cqbot/item/WSConnection.php | 148 ++++++++++++++++++ src/cqbot/utils/APIHandler.php | 43 +++--- src/cqbot/utils/Buffer.php | 2 + src/cqbot/utils/CQUtil.php | 196 ++++++++++++++---------- start.php | 219 +++------------------------ tools.php | 183 ++++++++++++++++++++++ 17 files changed, 601 insertions(+), 441 deletions(-) delete mode 100644 src/cqbot/event/ApiMessageEvent.php delete mode 100644 src/cqbot/event/ApiUpgradeEvent.php create mode 100644 src/cqbot/item/WSConnection.php create mode 100644 tools.php diff --git a/README.md b/README.md index c18e7ee1..db90d67e 100755 --- a/README.md +++ b/README.md @@ -101,5 +101,6 @@ $ php start.php 获得过:
计算机应用能力大赛二等奖
溢达全国创意大赛一等奖
+未来可能会拿到全国计算机设计大赛的奖

如有任何问题可以随时戳死作者哦 diff --git a/src/cqbot/CQBot.php b/src/cqbot/CQBot.php index b8079710..1829e733 100755 --- a/src/cqbot/CQBot.php +++ b/src/cqbot/CQBot.php @@ -19,24 +19,27 @@ class CQBot public $starttime; public $endtime; + public $current_id; - public function __construct(Framework $framework){ + public function __construct(Framework $framework, $package) { $this->starttime = microtime(true); $this->framework = $framework; - } - - public function execute($it){ - $this->data = $it; - if ($it["post_type"] == "message") { + $this->data = $package; + $this->current_id = $this->data["self_id"]; + if ($package["post_type"] == "message") { try { - $this->callTask($it); + $this->callTask($package); } catch (\Exception $e) { - CQUtil::errorLog("请求执行任务时异常\n" . $e->getMessage()); - CQUtil::sendDebugMsg("引起异常的消息:\n" . $it["message"]); + CQUtil::errorLog("请求执行任务时异常\n" . $e->getMessage(), $this->current_id); + CQUtil::sendDebugMsg("引起异常的消息:\n" . $package["message"], $this->current_id); } } } + public function execute($it) { + + } + public function callTask($it){ if ($this->data["post_type"] == "message") { foreach(Buffer::get("mods") as $v){ @@ -69,7 +72,8 @@ class CQBot break; case "discuss": $reply = json_encode(["action" => "send_discuss_msg", "params" => ["discuss_id" => $this->data["discuss_id"], "message" => $msg]]); - if (CQUtil::APIPush($reply)) { + $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***********"); @@ -88,12 +92,12 @@ class CQBot public function sendGroupMsg($groupId, $msg){ $this->function_called = true; - CQUtil::sendGroupMsg($groupId, $msg); + CQUtil::sendGroupMsg($groupId, $msg, $this->current_id); } public function sendPrivateMsg($userId, $msg){ $this->function_called = true; - CQUtil::sendPrivateMsg($userId, $msg); + CQUtil::sendPrivateMsg($userId, $msg, $this->current_id); } public function isAdmin($user){ diff --git a/src/cqbot/Framework.php b/src/cqbot/Framework.php index d4c473bb..3c3952be 100755 --- a/src/cqbot/Framework.php +++ b/src/cqbot/Framework.php @@ -42,14 +42,15 @@ class Framework public function setInfoLevel($level){ $this->info_level = $level; } + public function setSuperUser($option) { self::$super_user = $option; } + public function eventServerStart(){ $this->event->start(); } public static function getInstance(){ return self::$obj; } - public function init($option = []){ + public function init() { $this->selfCheck(); $this->checkFiles(); - self::$super_user = $option; Console::info("CQBot Framework starting..."); $this->event = new \swoole_websocket_server($this->host, $this->event_port); @@ -96,16 +97,11 @@ class Framework 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); } - /** - * 回调函数:API连接升级为WebSocket时候调用,可用于成功和酷Qhttp建立连接的检测依据 - * @param $cli - */ - public function onUpgrade($cli){ new ApiUpgradeEvent($cli); } - /** * 回调函数:有客户端或HTTP插件反向客户端连接时调用 * @param swoole_websocket_server $server @@ -113,7 +109,13 @@ class Framework */ public function onEventOpen(\swoole_websocket_server $server, \swoole_http_request $request){ new WSOpenEvent($server, $request); } + /** + * 回调函数:断开连接时候回调的函数 + * @param swoole_server $server + * @param int $fd + */ public function onEventClose(\swoole_server $server, int $fd) { new WSCloseEvent($server, $fd); } + /** * 回调函数:当HTTP插件发来json包后激活此函数 * @param swoole_websocket_server $server @@ -130,13 +132,6 @@ class Framework */ public function onRequest($request, $response){ new HTTPEvent($request, $response); } - /** - * 回调函数:API响应函数,用于发送api请求后返回的状态包的检查,比如rescode = 200 - * @param swoole_http_client $client - * @param $frame - */ - public function onApiMessage($client, $frame){ new ApiMessageEvent($client, $frame); } - /** * 回调函数:异步计时器,一秒执行一次。请勿在此使用过多的阻塞方法 * @param $id diff --git a/src/cqbot/event/ApiMessageEvent.php b/src/cqbot/event/ApiMessageEvent.php deleted file mode 100644 index abb78cf5..00000000 --- a/src/cqbot/event/ApiMessageEvent.php +++ /dev/null @@ -1,15 +0,0 @@ -data, true); - if (isset($res["echo"])) APIHandler::execute($res["echo"], $res); - } -} \ No newline at end of file diff --git a/src/cqbot/event/ApiUpgradeEvent.php b/src/cqbot/event/ApiUpgradeEvent.php deleted file mode 100644 index efcc06bd..00000000 --- a/src/cqbot/event/ApiUpgradeEvent.php +++ /dev/null @@ -1,55 +0,0 @@ -getFramework()->api->isConnected()) { - echo "API connection lost.\nI will try next time after 30 second.\n"; - - //这里本来该用异步计时器的,但是我太懒了,直接睡30秒先。 - //需要改用异步计时器的话,告诉我我会改的233333。 - sleep(30); - $this->getFramework()->api = new \swoole_http_client($this->getFramework()->host, $this->getFramework()->api_port); - $this->getFramework()->api->set(['websocket_mask' => true]); - $this->getFramework()->api->on('message', [$this->getFramework(), "onApiMessage"]); - $this->getFramework()->api->on("close", function ($cli){ - Console::info(Console::setColor("API connection closed", "red")); - }); - $this->getFramework()->api->upgrade('/api/', [$this->getFramework(), "onUpgrade"]); - return; - } - Buffer::$api = $this->getFramework()->api; - Buffer::$event = $this->getFramework()->event; - if ($data = file(CONFIG_DIR . "log/last_error.log")) { - $last_time = file_get_contents(CONFIG_DIR . "log/error_flag"); - if (time() - $last_time < 2) { - CQUtil::sendDebugMsg("检测到重复引起异常,停止服务器", 0); - file_put_contents(CONFIG_DIR."log/last_error.log", ""); - $this->getFramework()->event->shutdown(); - return; - } - CQUtil::sendDebugMsg("检测到异常", 0); - $msg = ""; - foreach ($data as $e) { - $msg = $msg . $e . "\n"; - } - CQUtil::sendDebugMsg($msg, 0); - CQUtil::sendDebugMsg("[CQBot] 成功开启!", 0); - file_put_contents(CONFIG_DIR . "error_flag", time()); - file_put_contents(CONFIG_DIR . "last_error.log", ""); - } - else { - CQUtil::sendDebugMsg("[CQBot] 成功开启!", 0); - } - CQUtil::sendAPI("_get_friend_list", ["get_friend_list"]); - CQUtil::sendAPI("get_group_list", ["get_group_list"]); - CQUtil::sendAPI("get_version_info", ["get_version_info"]); - } -} \ No newline at end of file diff --git a/src/cqbot/event/WSCloseEvent.php b/src/cqbot/event/WSCloseEvent.php index ae43f54a..537a9470 100644 --- a/src/cqbot/event/WSCloseEvent.php +++ b/src/cqbot/event/WSCloseEvent.php @@ -9,6 +9,11 @@ class WSCloseEvent extends Event { public function __construct(swoole_server $server, int $fd) { - + $connect = CQUtil::getConnection($fd); + if ($connect->getPair() !== null) { + $connect->getPair()->setPair(null); + $connect->setPair(null); + } + unset(Buffer::$connect[$fd]); } } \ No newline at end of file diff --git a/src/cqbot/event/WSMessageEvent.php b/src/cqbot/event/WSMessageEvent.php index 4e0368ca..b17ac8e9 100644 --- a/src/cqbot/event/WSMessageEvent.php +++ b/src/cqbot/event/WSMessageEvent.php @@ -9,30 +9,68 @@ class WSMessageEvent extends Event { public function __construct(swoole_websocket_server $server, swoole_websocket_frame $frame) { - $in_count = Buffer::$in_count->get(); - Buffer::$in_count->add(1); $req = json_decode($frame->data, true); - if (Buffer::$data["info_level"] == 2) { - Console::put("************EVENT RECEIVED***********"); - Console::put("msg_id = " . $in_count); - Console::put("worker_id = " . $server->worker_id); + if (isset($req["echo"])) if (APIHandler::execute($req["echo"], $req)) return; + if (isset($req["echo"]["type"]) && $req["echo"]["type"] === "handshake") { + $fd_id = $frame->fd; + $connect = CQUtil::getConnection($fd_id); + $connect->setQQ($req["user_id"]); + $connect->setType(1); + $connect->findSub(); + if ($data = file(CONFIG_DIR . "log/last_error.log")) { + $last_time = file_get_contents(CONFIG_DIR . "log/error_flag"); + if (time() - $last_time < 2) { + CQUtil::sendDebugMsg("检测到重复引起异常,停止服务器", $req["user_id"], 0); + file_put_contents(CONFIG_DIR . "log/last_error.log", ""); + $this->getFramework()->event->shutdown(); + return; + } + CQUtil::sendDebugMsg("检测到异常", $req["user_id"], 0); + $msg = ""; + foreach ($data as $e) { + $msg = $msg . $e . "\n"; + } + CQUtil::sendDebugMsg($msg, $req["user_id"], 0); + CQUtil::sendDebugMsg("[CQBot] 成功开启!", $req["user_id"], 0); + file_put_contents(CONFIG_DIR . "error_flag", time()); + file_put_contents(CONFIG_DIR . "last_error.log", ""); + } else { + CQUtil::sendDebugMsg("[CQBot] 成功开启!", $req["user_id"], 0); + } + CQUtil::sendAPI($frame->fd, "_get_friend_list", ["get_friend_list"]); + CQUtil::sendAPI($frame->fd, "get_group_list", ["get_group_list"]); + CQUtil::sendAPI($frame->fd, "get_version_info", ["get_version_info"]); + return; } - if (Buffer::$data["info_level"] >= 1) { - $type = $req["post_type"] == "message" ? ($req["message_type"] == "group" ? "GROUP_MSG:" . $req["group_id"] : ($req["message_type"] == "private" ? "PRIVATE_MSG" : "DISCUSS_MSG:" . $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["post_type"] == "message" ? $req["message"] : Console::setColor(CQUtil::executeType($req), "gold"))); - } - //传入业务逻辑:CQBot - try { - $c = new CQBot($this->getFramework()); - $c->execute($req); - $c->endtime = microtime(true); - $value = $c->endtime - $c->starttime; - Console::debug("Using time: ".$value); - if(Buffer::get("time_send") === true) - CQUtil::sendDebugMsg("Using time: ".$value); - } catch (Exception $e) { - CQUtil::errorlog("处理消息时异常,消息处理中断\n" . $e->getMessage() . "\n" . $e->getTraceAsString()); - CQUtil::sendDebugMsg("引起异常的消息:\n" . var_export($req, true)); + $connect = CQUtil::getConnection($frame->fd); + switch ($connect->getType()) { + case 0: + $connect->setQQ($req["self_id"]); + $connect->findSub(); + $in_count = Buffer::$in_count->get(); + Buffer::$in_count->add(1); + if (Buffer::$data["info_level"] == 2) { + Console::put("************EVENT RECEIVED***********"); + Console::put("msg_id = " . $in_count); + Console::put("worker_id = " . $server->worker_id); + } + if (Buffer::$data["info_level"] >= 1) { + $type = $req["post_type"] == "message" ? ($req["message_type"] == "group" ? "GROUP_MSG:" . $req["group_id"] : ($req["message_type"] == "private" ? "PRIVATE_MSG" : "DISCUSS_MSG:" . $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["post_type"] == "message" ? $req["message"] : Console::setColor(CQUtil::executeType($req), "gold"))); + } + //传入业务逻辑:CQBot + try { + $c = new CQBot($this->getFramework(), $req); + $c->endtime = microtime(true); + $value = $c->endtime - $c->starttime; + Console::debug("Using time: " . $value); + if (Buffer::get("time_send") === true) + CQUtil::sendDebugMsg("Using time: " . $value, $req["self_id"]); + } catch (Exception $e) { + CQUtil::errorlog("处理消息时异常,消息处理中断\n" . $e->getMessage() . "\n" . $e->getTraceAsString(), $req["self_id"]); + CQUtil::sendDebugMsg("引起异常的消息:\n" . var_export($req, true), $req['self_id']); + } } + } } \ No newline at end of file diff --git a/src/cqbot/event/WSOpenEvent.php b/src/cqbot/event/WSOpenEvent.php index c439155f..3c40eba4 100644 --- a/src/cqbot/event/WSOpenEvent.php +++ b/src/cqbot/event/WSOpenEvent.php @@ -8,5 +8,8 @@ class WSOpenEvent extends Event { - public function __construct(swoole_websocket_server $server, swoole_http_request $request) { } + public function __construct(swoole_websocket_server $server, swoole_http_request $request) { + $fd = $request->fd; + CQUtil::getConnection($fd); + } } \ No newline at end of file diff --git a/src/cqbot/event/WorkerStartEvent.php b/src/cqbot/event/WorkerStartEvent.php index 38dda563..7e11f5bb 100644 --- a/src/cqbot/event/WorkerStartEvent.php +++ b/src/cqbot/event/WorkerStartEvent.php @@ -20,15 +20,6 @@ class WorkerStartEvent extends Event $this->getFramework()->scheduler = new Scheduler($this->getFramework()); $server->tick(1000, [$this->getFramework(), "processTick"]); - //API连接部分 - $this->getFramework()->api = new \swoole_http_client($this->getFramework()->host, $this->getFramework()->api_port); - $this->getFramework()->api->set(['websocket_mask' => true]); - $this->getFramework()->api->on('message', [$this->getFramework(), "onApiMessage"]); - $this->getFramework()->api->on("close", function ($cli){ - Console::info(Console::setColor("API connection closed", "red")); - }); - $this->getFramework()->api->upgrade('/api/', [$this->getFramework(), "onUpgrade"]); - Console::debug("master_pid = " . $server->master_pid); Console::debug("worker_id = " . $worker_id); Console::put("\n==========STARTUP DONE==========\n"); diff --git a/src/cqbot/item/Group.php b/src/cqbot/item/Group.php index a98f8ed9..8464658a 100644 --- a/src/cqbot/item/Group.php +++ b/src/cqbot/item/Group.php @@ -10,10 +10,12 @@ class Group { private $group_id; private $group_name; + private $self_id; //private $prefix; private $members = []; - public function __construct($group_id, $info) { + public function __construct($group_id, $info, $self_id) { + $this->self_id = $self_id; $this->group_id = $group_id; $this->group_name = $info["group_name"]; //$this->prefix = $info["prefix"]; @@ -82,9 +84,17 @@ class Group * @param bool $with_members */ public function updateData($with_members = false) { - CQUtil::sendAPI(["action" => "get_group_list"], ["update_group_info", $this->getGroupId()]); + $connection = CQUtil::getApiConnectionByQQ($this->getSelfId()); + CQUtil::sendAPI($connection->fd, ["action" => "get_group_list"], ["update_group_info", $this->getGroupId()]); if ($with_members) { - CQUtil::sendAPI(["action" => "get_group_member_list", "params" => ["group_id" => $this->getGroupId()]], ["update_group_member_list", strval($this->getGroupId())]); + CQUtil::sendAPI($connection->fd, ["action" => "get_group_member_list", "params" => ["group_id" => $this->getGroupId()]], ["update_group_member_list", strval($this->getGroupId())]); } } + + /** + * @return mixed + */ + public function getSelfId() { + return $this->self_id; + } } \ No newline at end of file diff --git a/src/cqbot/item/GroupMember.php b/src/cqbot/item/GroupMember.php index c107b6f6..5900f016 100644 --- a/src/cqbot/item/GroupMember.php +++ b/src/cqbot/item/GroupMember.php @@ -65,7 +65,7 @@ class GroupMember extends User * 返回用户是不是群管理员 * @return bool */ - public function isAdmin(){ + public function isAdmin() { return in_array($this->getRole(), ["owner", "admin"]); } @@ -82,7 +82,7 @@ class GroupMember extends User "card" => $card ] ]; - CQUtil::sendAPI($data, []); + CQUtil::sendAPI(CQUtil::getApiConnectionByQQ($this->getGroup()->getSelfId())->fd, $data, []); } /** @@ -123,9 +123,9 @@ class GroupMember extends User /** * 更新群组成员信息 */ - public function updateData(){ + public function updateData() { $user_id = $this->getId(); - CQUtil::sendAPI([ + CQUtil::sendAPI(CQUtil::getApiConnectionByQQ($this->getGroup()->getSelfId())->fd, [ "action" => "get_group_member_info", "params" => [ "group_id" => $this->getGroup()->getGroupId(), diff --git a/src/cqbot/item/WSConnection.php b/src/cqbot/item/WSConnection.php new file mode 100644 index 00000000..9037493c --- /dev/null +++ b/src/cqbot/item/WSConnection.php @@ -0,0 +1,148 @@ +server = $server; + $this->fd = $fd; + $this->manageType(); + } + + /** + * 返回swoole server + * @return swoole_websocket_server + */ + public function getServer() { + return $this->server; + } + + /** + * 返回本连接是什么类型的 + * @return int + */ + public function getType() { + return $this->type; + } + + /** + * 用来确认此连接是API还是event + * 如果此fd连接是event,则不会返回任何信息,关于QQ的匹配,则会在接收入第一条消息后设置 + * 如果此连接是api,则此操作后,HTTP API会返回登录号的号码,如果返回了则标记此连接为api并记录这个api连接属于的QQ号 + */ + private function manageType() { + $this->server->push($this->fd, '{"action":"get_login_info","echo":{"type":"handshake"}}'); + } + + /** + * 返回此连接相关联的event连接,使用前需初始化完成 + * @return $this|null|WSConnection + */ + public function getEventConnection() { + switch ($this->type) { + case 0: + return $this; + case 1: + return $this->pair; + default: + return null; + } + } + + /** + * 返回此链接相关联的api连接,使用前需初始化完成 + * @return $this|null|WSConnection + */ + public function getApiConnection() { + switch ($this->type) { + case 0: + return $this->pair; + case 1: + return $this; + default: + return null; + } + } + + /** + * 检查此连接对应的QQ,此部分较为复杂,先留着 + * @param $qq + */ + public function manageQQ($qq) { + //TODO + } + + /** + * 返回此连接属于的QQ号 + * @return string + */ + public function getQQ() { + return $this->qq; + } + + /** + * 返回关联连接(experiment) + * @return WSConnection + */ + public function getPair() { + return $this->pair; + } + + /** + * 设置关联连接 + * @param WSConnection $pair + */ + public function setPair(WSConnection $pair) { + $this->pair = $pair; + } + + /** + * @param string $qq + */ + public function setQQ($qq) { + $this->qq = $qq; + } + + /** + * @param int $type + */ + public function setType(int $type) { + $this->type = $type; + } + + /** + * + */ + public function findSub() { + if ($this->qq != "") { + foreach (CQUtil::getConnections() as $fd => $cn) { + if ($cn->getQQ() == $this->qq && $cn->getType() != $this->getType()) { + $this->setPair($cn); + $cn->setPair($this); + } + } + } + } +} \ No newline at end of file diff --git a/src/cqbot/utils/APIHandler.php b/src/cqbot/utils/APIHandler.php index 90da5e08..2a9f0ac4 100644 --- a/src/cqbot/utils/APIHandler.php +++ b/src/cqbot/utils/APIHandler.php @@ -9,14 +9,14 @@ class APIHandler { static function execute($cmd, $res = null) { - if (!isset($cmd[0])) return; - switch ($cmd[0]) { + if (!isset($cmd["type"])) return false; + switch ($cmd["type"]) { case "set_friend_add_request": - $id = $cmd[1]; + $id = $cmd["params"][0]; $msg = "Hi~你好!"; $msg .= "\n第一次见面请多关照!"; - CQUtil::sendPrivateMsg($id, $msg); - break; + CQUtil::sendPrivateMsg($id, $msg, $cmd["self_id"]); + return true; case "get_friend_list": $friend = $res["data"][0]["friends"]; $list = []; @@ -25,9 +25,9 @@ class APIHandler } Buffer::set("friend_list", $list); Console::put(Console::setColor("已读取" . count(Buffer::get("friend_list")) . "个好友", "blue")); - break; + return true; case "update_group_member_list": - $group_id = $cmd[1]; + $group_id = $cmd["params"][0]; $info_data = $res["data"]; Console::info(Console::setColor("Updating group $group_id members, it will take several minutes.", "yellow")); foreach ($info_data as $k => $v) { @@ -35,11 +35,11 @@ class APIHandler CQUtil::getGroup($group_id)->setMember($v["user_id"], $s); $s->updateData(); } - break; + return true; case "update_group_member_info": $info_data = $res["data"]; - $group = $cmd[1]; - $user = $cmd[2]; + $group = $cmd["params"][0]; + $user = $cmd["params"][1]; $g = CQUtil::getGroup($group); $member = $g->getMember($user); $member->setAttribute($info_data); @@ -48,43 +48,44 @@ class APIHandler $member->setLastSentTime($info_data["last_sent_time"]); $member->setRole($info_data["role"]); Console::info("Updated group member information: " . $group . ":" . $user); - break; + return true; case "update_group_info": $group = $res["data"]; - $current = $cmd[1]; + $current = $cmd["params"][0]; $list = []; foreach ($group as $k => $v) { $list[$v["group_id"]] = $group[$k]; } if (!isset($list[$current]) && Buffer::array_key_exists("groups", $current)) { Buffer::unset("groups", $current); - break; + return true; } $g = CQUtil::getGroup($current); $g->setGroupName($list[$current]["group_name"]); - $g->setPrefix($list[$current]["prefix"]); - break; + return true; case "get_group_member_list": $group_data = $res["data"]; $ls = Buffer::get("group_list"); - $group_id = $cmd[1]; + $group_id = $cmd["params"][0]; $ls[$group_id]["member"] = $group_data; - $group = new Group($group_id, $ls[$group_id]); + $group = new Group($group_id, $ls[$group_id], $cmd["self_id"]); + //TODO: 添加获取API时多账号对群的实例化的支持 Buffer::appendKey("groups", $group_id, $group); - break; + return true; case "get_group_list": $group = $res["data"]; $list = []; foreach ($group as $k => $v) { $list[$v["group_id"]] = $group[$k]; - CQUtil::sendAPI(["action" => "get_group_member_list", "params" => ["group_id" => $v["group_id"]]], ["get_group_member_list", $v["group_id"]]); + CQUtil::sendAPI(CQUtil::getApiConnectionByQQ($cmd["self_id"])->fd, ["action" => "get_group_member_list", "params" => ["group_id" => $v["group_id"]]], ["get_group_member_list", $v["group_id"]]); } Buffer::set("group_list", $list); Console::put(Console::setColor("已读取" . count(Buffer::get("group_list")) . "个群", "blue")); - break; + return true; case "get_version_info": Buffer::set("version_info", $res["data"]); - break; + return true; } + return false; } } \ No newline at end of file diff --git a/src/cqbot/utils/Buffer.php b/src/cqbot/utils/Buffer.php index 830d0688..f10120c9 100755 --- a/src/cqbot/utils/Buffer.php +++ b/src/cqbot/utils/Buffer.php @@ -25,6 +25,8 @@ class Buffer static $out_count;//发送消息数量 /** @var \swoole_atomic $out_count */ static $api_id;//API调用ID + /** @var WSConnection[] */ + static $connect = []; static function get($name){ return self::$data[$name] ?? null; } diff --git a/src/cqbot/utils/CQUtil.php b/src/cqbot/utils/CQUtil.php index 6ed7176e..d10cc899 100755 --- a/src/cqbot/utils/CQUtil.php +++ b/src/cqbot/utils/CQUtil.php @@ -18,7 +18,7 @@ class CQUtil Buffer::set("user", []);//清空用户列表 Buffer::set("time_send", false);//发送Timing数据到管理群 Buffer::set("cmd_prefix", DP::getJsonData("config.json")["cmd_prefix"] ?? "");//设置指令的前缀符号 - Buffer::set("res_code", file_get_contents(WORKING_DIR."src/cqbot/Framework.php")); + Buffer::set("res_code", file_get_contents(WORKING_DIR . "src/cqbot/Framework.php")); } public static function saveAllFiles() { @@ -41,65 +41,48 @@ class CQUtil /** * 生成报错日志 * @param $log + * @param $self_id * @param string $head * @param int $send_debug_message */ - public static function errorLog($log, $head = "ERROR", $send_debug_message = 1) { + public static function errorLog($log, $self_id, $head = "ERROR", $send_debug_message = 1) { Console::error($log, ($head === "ERROR") ? null : "[" . $head . "] "); $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 (self::checkAPIConnection() === -1) { - file_put_contents(DP::getDataFolder() . "last_error.log", $msg, FILE_APPEND); - } else { - if ($send_debug_message) - self::sendDebugMsg($msg, 0); - } + if ($send_debug_message) + self::sendDebugMsg($msg, $self_id); } /** * 发送调试信息到管理群(需先设置管理群号) * @param $msg + * @param $self_id * @param int $need_head * @return null */ - static function sendDebugMsg($msg, $need_head = 1) { - if (Framework::$admin_group == "") return null; + static function sendDebugMsg($msg, $self_id, $need_head = 1) { + if (Framework::$admin_group[$self_id] == "") return null; if ($need_head) $data = CQMsg("[DEBUG] " . date("H:i:s") . ": " . $msg, "group", Framework::$admin_group); else $data = CQMsg($msg, "group", Framework::$admin_group); - return self::APIPush($data); - } - - /** - * 检查API端口连接情况 - * @return int - */ - static function checkAPIConnection() { - if (Buffer::$api === null) return -1;//在framework链接API之前 - if (Buffer::$api->isConnected() === false) { - //链接被断开 - Buffer::$api->upgrade('/api/', function ($cli) { - self::sendDebugMsg("API重新链接成功"); - self::APIPushDelayMsg(); - }); - return 0; - } - return 1; + $connect = CQUtil::getApiConnectionByQQ($self_id); + return self::sendAPI($connect->fd, $data, ["send_debug_msg"]); } /** * 推送API,给API端口 + * @param $fd * @param $data * @return bool */ - static function APIPush($data) { + static function APIPush($fd, $data) { if ($data == null || $data == "") { Console::error("EMPTY DATA PUSH"); return false; } - if (self::checkAPIConnection() === -1) { + /*if (self::checkAPIConnection() === -1) { //忽略掉framework链接API之前的消息 self::errorlog("API推送失败,未发送的消息: \n" . $data, "API ERROR", 0); return false; @@ -107,20 +90,21 @@ class CQUtil if (self::checkAPIConnection() === 0) { self::APIPushAfterConnected($data); return true; - } - if (Buffer::$api->push($data) === false) { + }*/ + if (Buffer::$event->push($fd, $data) === false) { $data = self::unicodeDecode($data); - self::errorlog("API推送失败,未发送的消息: \n" . $data, "API ERROR", 0); - self::sendErrorEmail("API推送失败", "未成功推送的消息:
$data
请检查酷q是否开启及网络链接情况
在此期间,机器人会中断所有消息处理
请及时处理"); + $connect = self::getConnection($fd); + self::errorlog("API推送失败,未发送的消息: \n" . $data, $connect->getQQ(), "API ERROR", 0); + self::sendErrorEmail("API推送失败", "未成功推送的消息:
$data
请检查酷q是否开启及网络链接情况
在此期间,机器人会中断所有消息处理
请及时处理", $connect->getQQ()); return false; } return true; } /** - * 延迟推送在API连接断开后收到的消息函数 + * 延迟推送在API连接断开后收到的消息函数//待定 */ - static function APIPushDelayMsg() { + /*static function APIPushDelayMsg() { $delay_push_list = Buffer::get("delay_push"); $cur_time = time(); foreach ($delay_push_list as $item) { @@ -131,10 +115,10 @@ class CQUtil } } Buffer::set("delay_push", []); - } + }*/ /** - * 推迟推送API,用于酷Q重启后的重新连接API + * 推迟推送API,用于酷Q重启后的重新连接API//待定 * @param $data */ static function APIPushAfterConnected($data) { @@ -195,11 +179,12 @@ class CQUtil * @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, $name = "CQ开发团队", $send_debug_message = 1) { + static function sendEmail($address, $title, $content, $self_id, $name = "CQ开发团队", $send_debug_message = 1) { $mail = new \PHPMailer(true); try { $mail->isSMTP(); @@ -228,12 +213,64 @@ class CQUtil unset($mail); return true; } catch (\Exception $e) { - self::errorLog("发送邮件错误!错误信息:" . $info = $mail->ErrorInfo, "ERROR", $send_debug_message); + self::errorLog("发送邮件错误!错误信息:" . $info = $mail->ErrorInfo, $self_id, "ERROR", $send_debug_message); 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 + * @return WSConnection + */ + static function getConnection($fd) { + if (!isset(Buffer::$connect[$fd])) { + $s = new WSConnection(Buffer::$event, $fd); + Buffer::$connect[$fd] = $s; + } + return Buffer::$connect[$fd]; + } + + static function getApiConnectionByQQ($qq) { + foreach (self::getConnections() as $fd => $c) { + if ($c->getType() === 1 && $c->getQQ() == $qq) { + return $c; + } + } + return null; + } + /** * 获取运行时间 * @param $time @@ -313,10 +350,11 @@ class CQUtil * 此功能基于sendMail,请看上方sendMail函数的介绍 * @param $title * @param $content + * @param $self_id * @param string $name */ - static function sendErrorEmail($title, $content, $name = "机器人错误提示") { - self::sendEmail(["here your receive email address"], $title, $content, $name, 0); + static function sendErrorEmail($title, $content, $self_id, $name = "机器人错误提示") { + self::sendEmail(["here your receive email address"], $title, $content, $self_id, $name, 0); } /** @@ -375,14 +413,22 @@ class CQUtil * 发送群组消息,含控制台推出 * @param $groupId * @param $msg + * @param string $self_id * @return bool */ - static function sendGroupMsg($groupId, $msg) { + 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(); $reply = json_encode($reply); - if (self::APIPush($reply)) { + $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); @@ -397,14 +443,22 @@ class CQUtil * 发送私聊消息 * @param $userId * @param $msg + * @param $self_id * @return bool */ - static function sendPrivateMsg($userId, $msg) { + 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(); $reply = json_encode($reply); - if (self::APIPush($reply)) { + $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); @@ -423,50 +477,26 @@ class CQUtil /** * 发送其他API,HTTP插件支持的其他API都可以发送。 * echo是返回内容,可以在APIHandler.php里面解析 + * @param $fd * @param $data * @param $echo + * @return bool */ - static function sendAPI($data, $echo) { + 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; - self::APIPush(json_encode($api)); - } - - /** - * 删除一个和模块相关联的指令 - * @param $name - * @return bool - */ - static function removeCommand($name) { - $list = Buffer::get("commands"); - if (!isset($list[$name])) return false; - unset($list[$name]); - Buffer::set("commands", $list); - DP::setJsonData("commands.json", $list); - return true; - } - - /** - * 添加一个指令给非callTask方式激活的模块。 - * 注意:如果给callTask方式激活的模块添加指令,则在使用对应功能时会回复多次同样的内容 - * @param $name - * @param $class - * @return bool - */ - static function addCommand($name, $class) { - if (!is_file(WORKING_DIR . 'src/cqbot/mods/' . $class . '.php')) { - return false; - } - $list = Buffer::get("commands"); - $list[$name] = $class; - DP::setJsonData("commands.json", $list); - Buffer::set("commands", $list); - return true; + return self::APIPush($fd, json_encode($api)); } /** @@ -501,7 +531,7 @@ class CQUtil /** * 重启框架,此服务重启为全自动的 */ - static function reload(){ + static function reload() { Console::info("Reloading server"); self::saveAllFiles(); Buffer::$event->reload(); @@ -510,7 +540,7 @@ class CQUtil /** * 停止运行框架,需要用shell再次开启才能启动 */ - static function stop(){ + static function stop() { Console::info("Stopping server..."); self::saveAllFiles(); Buffer::$api->close(); @@ -610,7 +640,7 @@ class CQUtil * @param $group_id * @return Group|null */ - static function getGroup($group_id){ + static function getGroup($group_id) { $d = Buffer::get("groups"); return $d[$group_id] ?? null; } diff --git a/start.php b/start.php index 57dedb51..4a1e95a4 100755 --- a/start.php +++ b/start.php @@ -1,204 +1,11 @@ $t) { - if (!isset($t['file'])) { - $t['file'] = 'unknown'; - } - if (!isset($t['line'])) { - $t['line'] = 0; - } - if (!isset($t['function'])) { - $t['function'] = 'unknown'; - } - $log .= "#$i {$t['file']}({$t['line']}): "; - if (isset($t['object']) and is_object($t['object'])) { - $log .= get_class($t['object']) . '->'; - } - $log .= "{$t['function']}()\n"; - } - - file_put_contents(CONFIG_DIR . "last_error.log", $log); - break; - default: - break; - } - } -} - -function CQMsg($msg, $type, $id) { - if ($type === "group") { - $reply = ["action" => "send_group_msg", "params" => ["group_id" => $id, "message" => $msg]]; - $reply["echo"] = $reply; - $reply["echo"]["time"] = time(); - $reply = json_encode($reply); - } else if ($type === "private") { - $reply = ["action" => "send_private_msg", "params" => ["user_id" => $id, "message" => $msg]]; - $reply["echo"] = $reply; - $reply["echo"]["time"] = time(); - $reply = json_encode($reply); - } else if ($type === "discuss") { - $reply = ["action" => "send_discuss_msg", "params" => ["discuss_id" => $id, "message" => $msg]]; - $reply["echo"] = $reply; - $reply["echo"]["time"] = time(); - $reply = json_encode($reply); - } else { - $reply = false; - } - return $reply; -} - - - -$host = "0.0.0.0"; -$api_host = "127.0.0.1"; -$api_port = 10000; -$event_port = 20000; -$admin_group = ""; -$info_level = 1; -$super_user = []; - -if (!file_exists(CONFIG_DIR . "config.json")) { - file_put_contents(CONFIG_DIR . "config.json", json_encode([])); -} -$json = json_decode(file_get_contents(CONFIG_DIR . "config.json"), true); -if (!isset($json["host"])) { - echo "请输入你要监听的Event IP(默认0.0.0.0) :"; - $r = strtolower(trim(fgets(STDIN))); - if ($r == "") { - echo "监听地址:0.0.0.0(默认)\n"; - $json["host"] = $host; - } else { - $host = $r; - echo "监听地址:" . $r . "\n"; - $json["host"] = $host; - } -} else { - $host = $json["host"]; -} -if (!isset($json["event_port"])) { - a3: - echo "请输入你要监听的Event端口(默认20000) :"; - $r = strtolower(trim(fgets(STDIN))); - if ($r == "") { - echo "监听地址:20000(默认)\n"; - $json["event_port"] = $event_port; - } else { - if (!is_numeric($r)) { - echo "输入错误!请输入数字!(1-65535)\n"; - goto a3; - } - $event_port = $r; - echo "监听地址:" . $r . "\n"; - $json["event_port"] = $event_port; - } -} else { - $event_port = $json["event_port"]; -} -if (!isset($json["api_host"])) { - echo "请输入你要连接的api server IP(默认127.0.0.1) :"; - $r = strtolower(trim(fgets(STDIN))); - if ($r == "") { - echo "API地址:127.0.0.1(默认)\n"; - $json["api_host"] = $api_host; - } else { - $api_host = $r; - echo "监听地址:" . $r . "\n"; - $json["api_host"] = $api_host; - } -} else { - $api_host = $json["api_host"]; -} -if (!isset($json["api_port"])) { - a2: - echo "请输入你要监听的API端口(默认10000) :"; - $r = strtolower(trim(fgets(STDIN))); - if ($r == "") { - echo "监听地址:10000(默认)\n"; - $json["api_port"] = $api_port; - } else { - if (!is_numeric($r)) { - echo "输入错误!请输入数字!(1-65535)\n"; - goto a2; - } - $api_port = $r; - echo "监听地址:" . $r . "\n"; - $json["api_port"] = $api_port; - } -} else { - $api_port = $json["api_port"]; -} -if (!isset($json["admin_group"])) { - a4: - echo "请输入你要设置的管理员群:"; - $r = strtolower(trim(fgets(STDIN))); - if ($r == "") { - echo "检测到你没有设置管理员群,本次跳过\n"; - } else { - if (!is_numeric($r)) { - echo "输入错误!请输入数字群号!\n"; - goto a4; - } - $admin_group = $r; - echo "管理群:" . $r . "\n"; - $json["admin_group"] = $admin_group; - } -} else { - $admin_group = $json["admin_group"]; -} - -if (!isset($json["super_user"])) { - a5: - echo "请输入你要设置的高级管理员:"; - $r = strtolower(trim(fgets(STDIN))); - if ($r == "") { - echo "检测到你没有设置高级管理员,本次跳过\n"; - } else { - if (!is_numeric($r)) { - echo "输入错误!请输入数字QQ号!\n"; - goto a5; - } - $super_user[] = $r; - echo "管理员:" . $r . "\n"; - $json["super_user"][] = $r; - } -} else { - $super_user = $json["super_user"]; -} - -file_put_contents(CONFIG_DIR."config.json", json_encode($json, 128 | 256)); +require("tools.php"); //loading projects require(WORKING_DIR . "src/cqbot/Framework.php"); @@ -206,11 +13,23 @@ require(WORKING_DIR . "src/cqbot/utils/Buffer.php"); require(WORKING_DIR . "src/cqbot/utils/ErrorStatus.php"); require(WORKING_DIR . "src/cqbot/utils/Console.php"); +//初始参数设置:host、端口、多个机器人号对应的admin_group、事件等级、多个机器人号对应的超级管理员 +$properties["host"] = "0.0.0.0"; +$properties["port"] = 20000; +$properties["admin_group"] = []; +$properties["info_level"] = 1; +$properties["super_user"] = []; + +$json = json_decode(file_get_contents(CONFIG_DIR . "config.json"), true); + +if (!isset($json["host"]) || !isset($json["port"])) setupWizard($json, $properties); + +//initializing framework $cqbot = new Framework(); -$cqbot->setHost($host); -$cqbot->setApiPort($api_port); -$cqbot->setEventPort($event_port); -$cqbot->setAdminGroup($admin_group); -$cqbot->setInfoLevel($info_level); -$cqbot->init($super_user); +$cqbot->setHost($properties["host"]); +$cqbot->setEventPort($properties["port"]); +$cqbot->setAdminGroup($properties["admin_group"]); +$cqbot->setInfoLevel($properties["info_level"]); +$cqbot->setSuperUser($properties["super_user"]); +$cqbot->init(); $cqbot->eventServerStart(); \ No newline at end of file diff --git a/tools.php b/tools.php new file mode 100644 index 00000000..79560475 --- /dev/null +++ b/tools.php @@ -0,0 +1,183 @@ + $t) { + if (!isset($t['file'])) { + $t['file'] = 'unknown'; + } + if (!isset($t['line'])) { + $t['line'] = 0; + } + if (!isset($t['function'])) { + $t['function'] = 'unknown'; + } + $log .= "#$i {$t['file']}({$t['line']}): "; + if (isset($t['object']) and is_object($t['object'])) { + $log .= get_class($t['object']) . '->'; + } + $log .= "{$t['function']}()\n"; + } + + file_put_contents(CONFIG_DIR . "last_error.log", $log); + break; + default: + break; + } + } +} + +function CQMsg($msg, $type, $id) { + if ($type === "group") { + $reply = ["action" => "send_group_msg", "params" => ["group_id" => $id, "message" => $msg]]; + $reply["echo"] = $reply; + $reply["echo"]["time"] = time(); + $reply = json_encode($reply); + } else if ($type === "private") { + $reply = ["action" => "send_private_msg", "params" => ["user_id" => $id, "message" => $msg]]; + $reply["echo"] = $reply; + $reply["echo"]["time"] = time(); + $reply = json_encode($reply); + } else if ($type === "discuss") { + $reply = ["action" => "send_discuss_msg", "params" => ["discuss_id" => $id, "message" => $msg]]; + $reply["echo"] = $reply; + $reply["echo"]["time"] = time(); + $reply = json_encode($reply); + } else { + $reply = false; + } + return $reply; +} + +function printHelp() { + echo color("{gold}=====CQBot-swoole====="); + echo color("{gold}* 首次使用设置 *"); + echo color("[{green}?{r}] {lightlightblue}查看此列表"); + echo color("[{green}1{r}] {yellow}设置监听地址"); + echo color("[{green}2{r}] {yellow}设置监听端口"); + echo color("[{green}3{r}] {yellow}设置管理群"); + echo color("[{green}4{r}] {yellow}设置管理员"); + echo color("[{green}5{r}] {lightlightblue}开始运行"); +} + +function setupWizard(&$json, &$properties) { + printHelp(); + while (true) { + 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"; + echo color("{gray}已设置地址:0.0.0.0(默认)"); + } else { + $properties["host"] = $host; + echo color("{gray}已设置地址:" . $host); + } + break; + case "2": + echo color("请输入监听端口(默认20000):", ""); + $host = trim(fgets(STDIN)); + if ($host == "") { + $properties["port"] = 20000; + echo color("{gray}已设置端口:20000(默认)"); + } else { + $properties["port"] = $host; + echo color("{gray}已设置端口:" . $host); + } + break; + case "3": + echo color("请输入机器人QQ号:", ""); + $self_id = trim(fgets(STDIN)); + if ($self_id == "") { + echo color("{red}请勿输入空数据!"); + break; + } + echo color("请输入本机器人QQ的管理群(机器人必须已经在群内):", ""); + $group = trim(fgets(STDIN)); + if ($group == "") { + echo color("{red}请勿输入空数据!"); + break; + } + $properties["admin_group"][$self_id][] = $group; + echo color("{gray}已设置机器人" . $self_id . "的管理群:" . $group); + break; + case "4": + echo color("请输入机器人QQ号:", ""); + $self_id = trim(fgets(STDIN)); + if ($self_id == "") { + echo color("{red}请勿输入空数据!"); + break; + } + echo color("请输入本机器人QQ的管理员QQ:", ""); + $group = trim(fgets(STDIN)); + if ($group == "") { + echo color("{red}请勿输入空数据!"); + break; + } + $properties["super_user"][$self_id][] = $group; + echo color("{gray}已设置机器人" . $self_id . "的管理员:" . $group); + break; + case "5": + break 2; + case "?": + case "?": + printHelp(); + break; + default: + echo color("{red}请输入正确的编号进行操作!\n在设置监听端口和监听地址后可开始运行服务器"); + break; + } + } + $json["host"] = $properties["host"]; + $json["port"] = $properties["port"]; + $json["admin_group"] = $properties["admin_group"]; + $json["super_user"] = $properties["super_user"]; + $json["info_level"] = $properties["info_level"]; + file_put_contents(CONFIG_DIR . "config.json", json_encode($json, 128 | 256)); +} + +function color($str, $end = "\n") { + $str = str_replace("{red}", "\e[38;5;203m", $str); + $str = str_replace("{green}", "\e[38;5;83m", $str); + $str = str_replace("{yellow}", "\e[38;5;227m", $str); + $str = str_replace("{lightpurple}", "\e[38;5;207m", $str); + $str = str_replace("{lightblue}", "\e[38;5;87m", $str); + $str = str_replace("{gold}", "\e[38;5;214m", $str); + $str = str_replace("{gray}", "\e[38;5;59m", $str); + $str = str_replace("{pink}", "\e[38;5;207m", $str); + $str = str_replace("{lightlightblue}", "\e[38;5;63m", $str); + $str = str_replace("{r}", "\e[m", $str); + $str .= "\e[m" . $end; + return $str; +} \ No newline at end of file