// This file is generated automatically by buildScript; // When contributing to this repository, please DO NOT edit this file. // Copyright (c) 2020 - 2022. Eritque arcus and contributors. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or any later version(in your opinion). // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // #include //from include/Bot.cpp namespace MiraiCP { Group Bot::getGroup(QQID groupid) const { return {groupid, this->id}; } Friend Bot::getFriend(QQID i) const { return Friend(i, this->id); } bool Bot::operator==(const Contact &c) const { return this->id == c.id(); } void Bot::refreshInfo() { if (this->id == 0) return; nlohmann::json j; j["source"] = Contact(4, 0, 0, "", this->id).toString(); LowLevelAPI::info tmp = LowLevelAPI::info0(KtOperation::ktOperation(KtOperation::RefreshInfo, j)); this->_avatarUrl = tmp.avatarUrl; this->_nick = tmp.nickornamecard; } std::vector Bot::getFriendList() const { nlohmann::json j; j["botid"] = this->id; std::string temp = KtOperation::ktOperation(KtOperation::QueryBFL, j); return Tools::StringToVector(std::move(temp)); } std::string Bot::FriendListToString() { return Tools::VectorToString(getFriendList()); } std::vector Bot::getGroupList() const { nlohmann::json j; j["botid"] = this->id; std::string temp = KtOperation::ktOperation(KtOperation::QueryBGL, j); return Tools::StringToVector(std::move(temp)); } std::string Bot::GroupListToString() const { return Tools::VectorToString(getGroupList()); } } // namespace MiraiCP //from include/CPPPlugin.cpp namespace MiraiCP { // 静态区代码一般不会使用logger,虽是ub,但应该不会造成影响; // 静态区强行调用的话,因为loggerInterface尚未传入,若logger尚未初始化,访问Logger::logger将是ub // 可以考虑把logger做成纯静态实现?以及最好做成 Logger * const Logger *CPPPlugin::pluginLogger = &Logger::logger; std::unique_ptr CPPPlugin::plugin = nullptr; } // namespace MiraiCP //from include/Command.cpp namespace MiraiCP { CommandManager CommandManager::commandManager = CommandManager(); } //from include/Contact.cpp namespace MiraiCP { using json = nlohmann::json; MessageSource Contact::sendMsg0(const std::string &msg, int retryTime, bool miraicode) const { if (msg.empty()) { throw IllegalArgumentException("不能发送空信息, 位置: Contact::SendMsg", MIRAICP_EXCEPTION_WHERE); } std::string re = LowLevelAPI::send0(msg, this->toJson(), retryTime, miraicode, "reach a error area, Contact::SendMiraiCode"); ErrorHandle(re, ""); return MessageSource::deserializeFromString(re); } MessageSource Contact::quoteAndSend0(const std::string &msg, MessageSource ms) { json obj; json sign; obj["messageSource"] = ms.serializeToString(); obj["msg"] = msg; sign["MiraiCode"] = true; sign["groupid"] = this->groupid(); obj["sign"] = sign.dump(); std::string re = KtOperation::ktOperation(KtOperation::SendWithQuote, obj); ErrorHandle(re, ""); return MessageSource::deserializeFromString(re); } Image Contact::uploadImg(const std::string &path) const { std::string re = LowLevelAPI::uploadImg0(path, this->toString()); if (re == "E2") throw UploadException("上传图片大小超过30MB,路径:" + path, MIRAICP_EXCEPTION_WHERE); return Image::deserialize(re); } FlashImage Contact::uploadFlashImg(const std::string &path) const { std::string re = LowLevelAPI::uploadImg0(path, this->toString()); if (re == "E2") throw UploadException("上传图片大小超过30MB,路径:" + path, MIRAICP_EXCEPTION_WHERE); return FlashImage::deserialize(re); } Contact Contact::deserialize(const std::string &source) { json j; try { j = json::parse(source); } catch (json::parse_error &e) { Logger::logger.error("json序列化错误 Contact::deserializationFromString"); Logger::logger.error(source); Logger::logger.error(e.what()); } return Contact::deserialize(j); } Contact Contact::deserialize(nlohmann::json j) { return Contact(j["type"], j["id"], j["groupid"], j["nickornamecard"], j["botid"], j["anonymous"]); } MessageSource Contact::sendVoice0(const std::string &path) { json j; json source; source["path"] = path; j["source"] = source.dump(); j["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::Voice, j); if (re == "E1") { throw UploadException("上传语音文件格式不对(必须为.amr/.silk)或文件不存在", MIRAICP_EXCEPTION_WHERE); } else if (re == "E2") { throw UploadException("上传语音文件大小超过服务器限制,一般限制在1MB上下", MIRAICP_EXCEPTION_WHERE); } return MessageSource::deserializeFromString(re); } } // namespace MiraiCP //from include/Event.cpp namespace MiraiCP { using json = nlohmann::json; Event Event::processor; void GroupInviteEvent::operation0(const std::string &source, QQID botid, bool accept) { nlohmann::json j; j["text"] = source; j["accept"] = accept; j["botid"] = botid; std::string re = KtOperation::ktOperation(KtOperation::Gioperation, j); if (re == "E") Logger::logger.error("群聊邀请事件同意失败(可能因为重复处理),id:" + source); } MessageChain PrivateMessageEvent::nextMessage(long time, bool halt) const { json j; j["contactSource"] = this->sender.toString(); j["time"] = time; j["halt"] = halt; std::string r = KtOperation::ktOperation(KtOperation::NextMsg, j); if (r == "E1") throw TimeOutException("取下一条信息超时", MIRAICP_EXCEPTION_WHERE); json re = json::parse(r); return MessageChain::deserializationFromMessageSourceJson(json::parse(re["messageSource"].get())).plus(MessageSource::deserializeFromString(re["messageSource"])); } MessageChain GroupMessageEvent::nextMessage(long time, bool halt) const { json j; j["contactSource"] = this->group.toString(); j["time"] = time; j["halt"] = halt; std::string r = KtOperation::ktOperation(KtOperation::NextMsg, j); if (r == "E1") throw TimeOutException("取下一条信息超时", MIRAICP_EXCEPTION_WHERE); json re = json::parse(r); return MessageChain::deserializationFromMessageSourceJson(json::parse(re["messageSource"].get())).plus(MessageSource::deserializeFromString(re["messageSource"])); } MessageChain GroupMessageEvent::senderNextMessage(long time, bool halt) const { json j; j["contactSource"] = this->sender.toString(); j["time"] = time; j["halt"] = halt; std::string r = KtOperation::ktOperation(KtOperation::NextMsg, j); if (r == "E1") throw TimeOutException("取下一条信息超时", MIRAICP_EXCEPTION_WHERE); json re = json::parse(r); return MessageChain::deserializationFromMessageSourceJson(json::parse(re["messageSource"].get())).plus(MessageSource::deserializeFromString(re["messageSource"])); } void NewFriendRequestEvent::operation0(const std::string &source, QQID botid, bool accept, bool ban) { nlohmann::json j; j["text"] = source; j["accept"] = accept; j["botid"] = botid; j["ban"] = ban; std::string re = KtOperation::ktOperation(KtOperation::Nfroperation, j); if (re == "E") Logger::logger.error("好友申请事件同意失败(可能因为重复处理),id:" + source); } void MemberJoinRequestEvent::operate(std::string_view s, QQID botid, bool sign, const std::string &msg) { nlohmann::json j; j["source"] = s; j["botid"] = botid; j["sign"] = sign; j["msg"] = msg; KtOperation::ktOperation(KtOperation::MemberJoinRequest, j); } void Event::incomingEvent(json j, int type) { switch (type) { case eventTypes::GroupMessageEvent: { //GroupMessage Event::broadcast( GroupMessageEvent(j["group"]["botid"], Group(Group::deserialize(j["group"])), Member(Member::deserialize(j["member"])), MessageChain::deserializationFromMessageSourceJson(json::parse(j["source"].get())) .plus(MessageSource::deserializeFromString(j["source"].get())))); break; } case eventTypes::PrivateMessageEvent: { //私聊消息 Event::broadcast( PrivateMessageEvent(j["friend"]["botid"], Friend(Friend::deserialize(j["friend"])), MessageChain::deserializationFromMessageSourceJson(json::parse(j["source"].get())) .plus(MessageSource::deserializeFromString(j["source"].get())))); break; } case eventTypes::GroupInviteEvent: //群聊邀请 Event::broadcast( GroupInviteEvent( j["source"]["botid"], j["request"], j["source"]["inviternick"], j["source"]["inviterid"], j["source"]["groupname"], j["source"]["groupid"])); break; case eventTypes::NewFriendRequestEvent: //好友 Event::broadcast( NewFriendRequestEvent( j["source"]["botid"], j["request"], j["source"]["fromid"], j["source"]["fromgroupid"], j["source"]["fromnick"], j["source"]["message"])); break; case eventTypes::MemberJoinEvent: //新成员加入 Event::broadcast( MemberJoinEvent( j["group"]["botid"], j["jointype"], Member(Member::deserialize(j["member"])), Group(Group::deserialize(j["group"])), j["inviterid"])); break; case eventTypes::MemberLeaveEvent: //群成员退出 Event::broadcast(MemberLeaveEvent( j["group"]["botid"], j["leavetype"], j["memberid"], Group(Group::deserialize(j["group"])), j["operatorid"])); break; case eventTypes::RecallEvent: Event::broadcast(RecallEvent( j["botid"], j["etype"], j["time"], j["authorid"], j["operatorid"], j["ids"], j["internalids"], j["groupid"])); break; case eventTypes::BotJoinGroupEvent: Event::broadcast(BotJoinGroupEvent( j["group"]["botid"], j["etype"], Group(Group::deserialize(j["group"])), j["inviterid"])); break; case eventTypes::GroupTempMessageEvent: Event::broadcast(GroupTempMessageEvent( j["group"]["botid"], Group(Group::deserialize(j["group"])), Member(Member::deserialize(j["member"])), MessageChain::deserializationFromMessageSourceJson(json::parse(j["message"].get())) .plus(MessageSource::deserializeFromString(j["source"])))); break; case eventTypes::TimeOutEvent: Event::broadcast(TimeOutEvent(j["msg"])); break; case eventTypes::BotOnlineEvent: Event::broadcast(BotOnlineEvent(j["botid"])); break; case eventTypes::NudgeEvent: Event::broadcast(NudgeEvent(Contact::deserialize(j["from"]), Contact::deserialize(j["target"]), Contact::deserialize(j["subject"]), j["botid"])); break; case eventTypes::BotLeaveEvent: Event::broadcast(BotLeaveEvent(j["groupid"], j["botid"])); break; case eventTypes::MemberJoinRequestEvent: { std::optional a; std::optional b; Contact temp = Contact::deserialize(j["group"]); if (temp.id() == 0) a = std::nullopt; else a = Group(temp); temp = Contact::deserialize(j["inviter"]); if (temp.id() == 0) b = std::nullopt; else b = Member(temp); Event::broadcast(MemberJoinRequestEvent(a, b, temp.botid(), j["requester"], j["requestData"])); break; } case eventTypes::MessagePreSendEvent: { Event::broadcast(MessagePreSendEvent(Contact::deserialize(j["target"]), MessageChain::deserializationFromMessageSourceJson(j["message"].get(), false), j["botid"])); break; } case eventTypes::Command: { // command std::optional c = std::nullopt; if (j.contains("contact")) c = Contact::deserialize(j["contact"]); CommandManager::commandManager[j["bindId"]]->onCommand(c, Bot(j["botid"]), MessageChain::deserializationFromMessageSourceJson((j.contains("message") ? j["message"].get() : ""), false)); break; } default: throw APIException("Unreachable code", MIRAICP_EXCEPTION_WHERE); } } } // namespace MiraiCP //from include/Exception.cpp namespace MiraiCP { void MiraiCPExceptionBase::basicRaise() const { Logger::logger.error(this->what()); } void MiraiCPExceptionBase::raise() const { this->basicRaise(); if (!filename.empty() && lineNum != 0) { Logger::logger.error("文件名:" + filename + "\n行号:" + std::to_string(lineNum)); } } } // namespace MiraiCP //from include/ForwardedMessage.cpp #include namespace MiraiCP { using json = nlohmann::json; json ForwardedMessage::nodesToJson() { json value; for (const ForwardedNode &node: nodes) { json temp; temp["id"] = node.id; temp["time"] = node.time; if (node.isForwarded()) { temp["isForwardedMessage"] = true; temp["message"] = std::get>(node.message)->nodesToJson().dump(); if (display.has_value()) temp["display"] = display->toJson(); } else temp["message"] = std::get(node.message).toMiraiCode(); temp["name"] = node.name; value.emplace_back(std::move(temp)); } json tmp = this->sendmsg; tmp["value"] = std::move(value); return tmp; } //发送这个聊天记录 MessageSource ForwardedMessage::sendTo(Contact *c) { json temp; json text; text["id"] = c->id(); text["groupid"] = c->groupid(); text["type"] = c->type(); text["content"] = this->nodesToJson(); temp["text"] = text.dump(); temp["botid"] = c->botid(); if (display.has_value()) temp["display"] = display->toJson(); std::string re = KtOperation::ktOperation(KtOperation::Buildforward, temp); ErrorHandle(re, ""); return MessageSource::deserializeFromString(re); } ForwardedMessage ForwardedMessage::deserializationFromMessageSourceJson(const json &j) { std::vector nodes; try { for (auto &&a: j) { if (a["messageChain"][0].contains("kind") && a["messageChain"][0]["kind"] == "FORWARD") { nodes.emplace_back(a["senderId"], a["senderName"], ForwardedMessage::deserializationFromMessageSourceJson(a["messageChain"][1]["nodeList"]), a["time"], ForwardedMessageDisplayStrategy::defaultStrategy()); } else { nodes.emplace_back(a["senderId"], a["senderName"], MessageChain::deserializationFromMessageSourceJson(a["messageChain"], false), a["time"]); } } } catch (json::exception &e) { throw APIException("ForwardedMessage格式化异常", MIRAICP_EXCEPTION_WHERE); } return {std::move(nodes), ForwardedMessageDisplayStrategy::defaultStrategy()}; } OnlineForwardedMessage OnlineForwardedMessage::deserializationFromMessageSourceJson(const json &j) { std::vector nodes; try { for (auto &&a: j[1]["nodeList"]) { if (a["messageChain"][0].contains("kind") && a["messageChain"][0]["kind"] == "FORWARD") { nodes.emplace_back(a["senderId"], a["senderName"], ForwardedMessage::deserializationFromMessageSourceJson(a["messageChain"][1]["nodeList"]), a["time"], ForwardedMessageDisplayStrategy::defaultStrategy()); } else { nodes.emplace_back(a["senderId"], a["senderName"], MessageChain::deserializationFromMessageSourceJson(a["messageChain"], false), a["time"]); } } } catch (json::parse_error &e) { throw APIException("OnlineForwardedMessage格式化异常", MIRAICP_EXCEPTION_WHERE); } //if (j[0].contains("resourceId") && !j[0]["resourceId"].is_null()) return OnlineForwardedMessage(j[0]["origin"], /*j[0]["resourceId"],*/ std::move(nodes)); //else // return OnlineForwardedMessage(j[0]["origin"], std::nullopt, std::move(nodes)); } ForwardedNode::ForwardedNode(QQID id, std::string name, ForwardedMessage _message, int t, std::optional display) : id(id), name(std::move(name)), message(std::make_shared(std::move(_message))), time(t), isForwardedMessage(true), display(std::move(display)) {} /* ForwardedNode::ForwardedNode(Contact *c, MessageChain message, int t) : id(c->id()), name(c->nickOrNameCard()), message(std::move(message)), time(t) {} ForwardedNode::ForwardedNode(QQID id, std::string name, ForwardedMessage &message, int t) : id(id), name(std::move(name)), forwardedMsg(&message), time(t) {} */ bool OnlineForwardedMessage::operator==(const OnlineForwardedMessage &m) const { if (this->nodelist.size() != m.nodelist.size()) return false; int i = 0; return std::all_of(this->nodelist.begin(), this->nodelist.end(), [&](const auto &n) { return n.message == m[i++].message; }); // for (int i = 0; i < this->nodelist.size(); i++) // if (this->nodelist[i].message != m[i].message) // return false; // return true; } ForwardedMessage OnlineForwardedMessage::toForwardedMessage(std::optional display) const { return {this->nodelist, std::move(display)}; } } // namespace MiraiCP //from include/Friend.cpp namespace MiraiCP { using json = nlohmann::json; /*好友类实现*/ Friend::Friend(QQID id, QQID botid) : Contact() { this->_type = MIRAI_FRIEND; this->_id = id; this->_botid = botid; refreshInfo(); } void Friend::deleteFriend() { json j; j["source"] = this->toString(); j["quit"] = true; KtOperation::ktOperation(KtOperation::RefreshInfo, j); } void Friend::refreshInfo() { std::string temp = LowLevelAPI::getInfoSource(this->toString()); if (temp == "E1") { throw FriendException(MIRAICP_EXCEPTION_WHERE); } LowLevelAPI::info tmp = LowLevelAPI::info0(temp); this->_nickOrNameCard = tmp.nickornamecard; this->_avatarUrl = tmp.avatarUrl; } void Friend::sendNudge() { json j; j["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::SendNudge, j); if (re == "E1") throw IllegalStateException("发送戳一戳失败,登录协议不为phone/ipad", MIRAICP_EXCEPTION_WHERE); } } // namespace MiraiCP //from include/Group.cpp namespace MiraiCP { using json = nlohmann::json; std::string Group::MemberListToString() { return Tools::VectorToString(getMemberList()); } std::vector Group::getAnnouncementsList() { json j; j["source"] = this->toString(); j["announcement"] = true; std::string re = KtOperation::ktOperation(KtOperation::RefreshInfo, j); std::vector oa; for (const json &e: json::parse(re)) { oa.push_back(Group::OnlineAnnouncement::deserializeFromJson(e)); } return oa; } void Group::OnlineAnnouncement::deleteThis() { json j, i; i["botid"] = this->botid; i["groupid"] = this->groupid; i["fid"] = this->fid; i["type"] = 1; j["identify"] = i.dump(); std::string re = KtOperation::ktOperation(KtOperation::Announcement, j); if (re == "E1") throw IllegalArgumentException("无法根据fid找到群公告(群公告不存在)", MIRAICP_EXCEPTION_WHERE); if (re == "E3") throw IllegalStateException("群公告状态异常", MIRAICP_EXCEPTION_WHERE); } json Group::AnnouncementParams::serializeToJson() { json j; j["sendToNewMember"] = this->send2new; j["isPinned"] = this->pinned; j["showEditCard"] = this->showEditCard; j["showPopup"] = this->showPopup; j["requireConfirmation"] = this->requireConfirm; return j; } Group::OnlineAnnouncement Group::OfflineAnnouncement::publishTo(const Group &g) { json j, i, s; i["botid"] = g.botid(); i["groupid"] = g.id(); i["type"] = 2; j["identify"] = i.dump(); s["content"] = this->content; s["params"] = this->params.serializeToJson(); j["source"] = s.dump(); std::string re = KtOperation::ktOperation(KtOperation::Announcement, j); return Group::OnlineAnnouncement::deserializeFromJson(json::parse(re)); } Group::OnlineAnnouncement Group::OnlineAnnouncement::deserializeFromJson(const json &j) { Group::AnnouncementParams ap( j["params"]["sendToNewMember"], j["params"]["requireConfirmation"], j["params"]["isPinned"], j["params"]["showEditCard"], j["params"]["showPopup"]); return Group::OnlineAnnouncement( j["content"], ap, j["groupid"], j["senderid"], j["botid"], j["time"], j["fid"], j["confirmationNum"], j["imageid"]); } std::vector Group::getMemberList() { nlohmann::json j; j["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::QueryML, j); if (re == "E1") { throw GroupException(MIRAICP_EXCEPTION_WHERE); } return Tools::StringToVector(std::move(re)); } Group::Group(QQID groupid, QQID botid) : Contact() { this->_type = MIRAI_GROUP; this->_id = groupid; this->_botid = botid; refreshInfo(); } void Group::quit() { nlohmann::json j; j["source"] = this->toString(); j["quit"] = true; KtOperation::ktOperation(KtOperation::RefreshInfo, j); } void Group::refreshInfo() { std::string re = LowLevelAPI::getInfoSource(this->toString()); LowLevelAPI::info tmp = LowLevelAPI::info0(re); this->_nickOrNameCard = std::move(tmp.nickornamecard); this->_avatarUrl = std::move(tmp.avatarUrl); nlohmann::json j = nlohmann::json::parse(re)["setting"]; this->setting.name = j["name"]; this->setting.isMuteAll = j["isMuteAll"]; this->setting.isAllowMemberInvite = j["isAllowMemberInvite"]; this->setting.isAutoApproveEnabled = j["isAutoApproveEnabled"]; this->setting.isAnonymousChatEnabled = j["isAnonymousChatEnabled"]; } void Group::updateSetting() { json j; json tmp; j["name"] = this->setting.name; j["isMuteAll"] = this->setting.isMuteAll; j["isAllowMemberInvite"] = this->setting.isAllowMemberInvite; j["isAutoApproveEnabled"] = this->setting.isAutoApproveEnabled; j["isAnonymousChatEnabled"] = this->setting.isAnonymousChatEnabled; tmp["source"] = j.dump(); tmp["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::GroupSetting, tmp); refreshInfo(); } RemoteFile Group::sendFile(const std::string &path, const std::string &filepath) { json tmp; json source; source["path"] = path; source["filepath"] = filepath; tmp["source"] = source.dump(); tmp["contactSource"] = this->toString(); std::string callback = KtOperation::ktOperation(KtOperation::SendFile, tmp); if (callback == "E2") throw UploadException("找不到" + filepath + "位置:C-uploadfile", MIRAICP_EXCEPTION_WHERE); if (callback == "E3") throw UploadException("Upload error:路径格式异常,应为'/xxx.xxx'或'/xx/xxx.xxx'目前只支持群文件和单层路径, path:" + path, MIRAICP_EXCEPTION_WHERE); return RemoteFile::deserializeFromString(callback); } RemoteFile Group::getFile(const std::string &path, const std::string &id) { // source 参数 if (path.empty() || path == "/") return this->getFileById(id); json tmp; json j; tmp["id"] = id; tmp["path"] = path; j["source"] = tmp.dump(); j["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::RemoteFileInfo, j); if (re == "E2") throw RemoteAssetException("Get error: 文件路径不存在, path:" + path + ",id:" + id, MIRAICP_EXCEPTION_WHERE); return RemoteFile::deserializeFromString(re); } RemoteFile Group::getFileById(const std::string &id) { json tmp; json j; tmp["id"] = id; j["source"] = tmp.dump(); j["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::RemoteFileInfo, j); if (re == "E1") throw RemoteAssetException("Get error: 文件路径不存在, id:" + id, MIRAICP_EXCEPTION_WHERE); return RemoteFile::deserializeFromString(re); } Member Group::getOwner() { json j; j["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::QueryOwner, j); return Member(stoi(re), this->id(), this->botid()); } std::string Group::getFileListString(const std::string &path) { json temp; json j; temp["id"] = "-1"; temp["path"] = path; j["source"] = temp.dump(); j["contactSource"] = this->toString(); return KtOperation::ktOperation(KtOperation::RemoteFileInfo, j); } std::vector Group::getFileList(const std::string &path) { std::vector re = std::vector(); std::string tmp = getFileListString(path); json root = json::parse(tmp); for (auto &i: root) { file_short_info t; t.path = i[0]; t.id = i[1]; re.push_back(t); } return re; } Member Group::getMember(QQID memberid) { return Member(memberid, this->id(), this->botid()); } Member Group::operator[](QQID a) { return getMember(a); } } // namespace MiraiCP //from include/KtOperation.cpp namespace MiraiCP::KtOperation { std::string ktOperation(operation_set type, nlohmann::json data, bool catchErr, const std::string &errorInfo) { nlohmann::json j; j["type"] = type; j["data"] = std::move(data); auto tmp = j.dump(); std::string re = LibLoader::LoaderApi::pluginOperation(tmp.c_str()); if (catchErr) ErrorHandle(re, errorInfo); return re; } } // namespace MiraiCP::KtOperation //from include/Logger.cpp namespace MiraiCP { Logger Logger::logger; void Logger::log_interface(const std::string &content, int level) { LibLoader::LoaderApi::loggerInterface(content.c_str(), ("plugin/" + MiraiCP::CPPPlugin::config.getName()).c_str(), -1, level); } void IdLogger::log_interface(const std::string &content, int level) { LibLoader::LoaderApi::loggerInterface(content.c_str(), "", static_cast(id), level); } } // namespace MiraiCP //from include/LowLevelAPI.cpp #include namespace MiraiCP { using json = nlohmann::json; std::string LowLevelAPI::send0(const std::string &content, json c, int retryTime, bool miraicode, const std::string &errorInfo) { nlohmann::json j; nlohmann::json tmp; tmp["content"] = content; tmp["contact"] = std::move(c); j["source"] = tmp.dump(); j["miraiCode"] = miraicode; j["retryTime"] = retryTime; return KtOperation::ktOperation(KtOperation::Send, j, true, errorInfo); } LowLevelAPI::info LowLevelAPI::info0(const std::string &source) { info re; ErrorHandle(source, ""); nlohmann::json j = nlohmann::json::parse(source); re.avatarUrl = j["avatarUrl"]; re.nickornamecard = j["nickornamecard"]; return re; } std::string LowLevelAPI::getInfoSource(const std::string &c) { nlohmann::json j; j["source"] = c; return KtOperation::ktOperation(KtOperation::RefreshInfo, j); } std::string LowLevelAPI::uploadImg0(const std::string &path, const std::string &c) { nlohmann::json j; j["fileName"] = path; j["source"] = c; return KtOperation::ktOperation(KtOperation::UploadImg, j); } } // namespace MiraiCP //from include/Member.cpp namespace MiraiCP { using json = nlohmann::json; /*成员类实现*/ Member::Member(QQID id, QQID groupid, QQID botid) : Contact() { this->_type = MIRAI_MEMBER; this->_id = id; this->_groupid = groupid; this->_botid = botid; refreshInfo(); } void Member::refreshInfo() { if (isAnonymous) return; std::string temp = LowLevelAPI::getInfoSource(this->toString()); if (temp == "E1") throw MemberException(1, MIRAICP_EXCEPTION_WHERE); if (temp == "E2") throw MemberException(2, MIRAICP_EXCEPTION_WHERE); LowLevelAPI::info tmp = LowLevelAPI::info0(temp); this->_nickOrNameCard = tmp.nickornamecard; this->_avatarUrl = tmp.avatarUrl; this->permission = getPermission(); if (temp == "E1") { throw MemberException(1, MIRAICP_EXCEPTION_WHERE); } if (temp == "E2") { throw MemberException(2, MIRAICP_EXCEPTION_WHERE); } } unsigned int Member::getPermission() const { if (isAnonymous) return 0; json j; j["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::QueryM, j); return stoi(re); } void Member::mute(int time) { json j; j["time"] = time; j["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::MuteM, j); if (re == "E4") throw MuteException(MIRAICP_EXCEPTION_WHERE); } void Member::kick(const std::string &reason) { json j; j["message"] = reason; j["contactSource"] = this->toString(); KtOperation::ktOperation(KtOperation::KickM, j); } void Member::modifyAdmin(bool admin) { if (isAnonymous) return; json j; j["admin"] = admin; j["contactSource"] = this->toString(); KtOperation::ktOperation(KtOperation::ModifyAdmin, j); refreshInfo(); } void Member::changeNameCard(std::string_view newName) { if (isAnonymous) return; json j; j["contactSource"] = this->toString(); j["newName"] = newName; KtOperation::ktOperation(KtOperation::ChangeNameCard, j); refreshInfo(); } void Member::sendNudge() { if (isAnonymous) return; json j; j["contactSource"] = this->toString(); std::string re = KtOperation::ktOperation(KtOperation::SendNudge, j); if (re == "E1") throw IllegalStateException("发送戳一戳失败,登录协议不为phone", MIRAICP_EXCEPTION_WHERE); } } // namespace MiraiCP //from include/MessageChain.cpp namespace MiraiCP { using json = nlohmann::json; std::string MessageChain::toMiraiCode() const { return Tools::VectorToString(this->toMiraiCodeVector(), ""); } MessageSource MessageChain::quoteAndSend0(std::string msg, QQID groupid) { json obj; json sign; obj["messageSource"] = this->source->serializeToString(); obj["msg"] = std::move(msg); sign["MiraiCode"] = true; sign["groupid"] = groupid; obj["sign"] = sign.dump(); std::string re = KtOperation::ktOperation(KtOperation::SendWithQuote, obj); return MessageSource::deserializeFromString(re); } //message chain MessageChain MessageChain::deserializationFromMiraiCode(const std::string &m) { size_t pos = 0; size_t lastPos = -1; MessageChain mc; if (m.length() <= 7) { return MessageChain(PlainText(m)); } do { if (m.length() - 7 - pos > 0 && m.substr(pos, 7) == "[mirai:") { if (pos - lastPos > 1) mc.add(PlainText(m.substr(lastPos + 1, pos - lastPos - 1))); // plain text size_t back = MessageChain::findEnd(m, pos); if (back == -1) throw IllegalStateException("", MIRAICP_EXCEPTION_WHERE); std::string tmp = m.substr(pos, back - pos); tmp = Tools::replace(std::move(tmp), "[mirai:", ""); size_t i = tmp.find(':'); // first : int t = SingleMessage::getKey(tmp.substr(0, i)); switch (t) { case 0: // no miraiCode key is PlainText Logger::logger.error("无法预料的错误, 信息: " + m); break; case 1: mc.add(At(std::stoll(tmp.substr(i + 1, tmp.length() - i - 1)))); break; case 2: mc.add(AtAll()); break; case 3: mc.add(Image(tmp.substr(i + 1, tmp.length() - i - 1))); break; case 4: mc.add(LightApp(tmp.substr(i + 1, tmp.length() - i - 1))); break; case 5: { size_t comma = tmp.find(','); mc.add(ServiceMessage(std::stoi(tmp.substr(i + 1, comma - i - 1)), tmp.substr(comma + 1, tmp.length() - comma - 1))); break; } case 6: { //[mirai:file:/b53231e8-46dd-11ec-8ba5-5452007bd6c0,102,run.bat,55] size_t comma1 = tmp.find(','); size_t comma2 = tmp.find(',', comma1 + 1); size_t comma3 = tmp.find(',', comma2 + 1); mc.add(RemoteFile(tmp.substr(i + 1, comma1 - i - 1), std::stoi(tmp.substr(comma1 + 1, comma2 - comma1 - 1)), tmp.substr(comma2 + 1, comma3 - comma2 - 1), std::stoll(tmp.substr(comma3 + 1, tmp.length() - comma3 - 1)))); break; } case 7: mc.add(Face(std::stoi(tmp.substr(i + 1, tmp.length() - i - 1)))); break; case 8: mc.add(FlashImage(tmp.substr(i + 1, tmp.length() - i - 1))); break; case 9: { //[mirai:musicshare:name,title,summary,jUrl,pUrl,mUrl,brief] auto temp = Tools::split(tmp.substr(i + 1, tmp.length() - i - 1), ","); mc.add(MusicShare(temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], temp[6])); break; } default: Logger::logger.error( "MiraiCP碰到了意料之中的错误(原因:部分SimpleMessage在MiraiCode解析支持之外)\n请到MiraiCP(github.com/Nambers/MiraiCP)发送issue并复制本段信息使MiraiCP可以支持这种消息: MiraiCode:" + m); mc.add(UnSupportMessage("[mirai:" + tmp)); break; } pos = back; lastPos = pos; if (t == 1) lastPos++; } pos++; } while (pos < m.length()); if (lastPos + 1 < m.length()) mc.add(PlainText(m.substr(lastPos + 1, m.length() - lastPos - 1))); // plain text return mc; } MessageChain MessageChain::deserializationFromMessageSourceJson(const json &tmp, bool origin) { json j = tmp; if (origin) j = tmp["originalMessage"]; MessageChain mc; if (j.empty()) return mc; if (j[0]["type"] == "MessageOrigin") { if (j[0]["kind"] == "MUSIC_SHARE") { mc.add(MusicShare(j[1]["kind"], j[1]["title"], j[1]["summary"], j[1]["jumpUrl"], j[1]["pictureUrl"], j[1]["musicUrl"], j[1]["brief"])); return mc; } mc.add(OnlineForwardedMessage::deserializationFromMessageSourceJson(j)); return mc; } for (auto node: j) { if (node["type"] == "SimpleServiceMessage") { mc.add(ServiceMessage(node["serviceId"], node["content"])); continue; } if (node["type"] == "LightApp") { mc.add(LightApp(node["content"])); continue; } if (node["type"] == "OnlineAudio") { mc.add(OnlineAudio(node["filename"], node["fileMd5"], node["fileSize"], node["codec"], node["length"], node["urlForDownload"])); continue; } if (node["type"] == "FileMessage") { mc.add(Group(tmp["targetId"].get(), tmp["botId"].get()).getFileById(node["id"]).plus(node["internalId"])); continue; } if (node["type"] == "MarketFace") { mc.add(MarketFace(node["delegate"]["faceId"])); break; } switch (SingleMessage::getKey(node["type"])) { case -2: mc.add(QuoteReply(MessageSource::deserializeFromString(node["source"].dump()))); break; case 0: mc.add(PlainText(node["content"].get())); break; case 1: mc.add(At(node["target"])); break; case 2: mc.add(AtAll()); break; case 3: mc.add(Image(node["imageId"])); break; case 7: mc.add(Face(node["id"])); break; case 8: mc.add(FlashImage(node["imageId"])); break; default: Logger::logger.warning( "MiraiCP碰到了意料之中的错误(原因:接受到的SimpleMessage在MessageSource解析支持之外)\n请到MiraiCP(github.com/Nambers/MiraiCP)发送issue并复制本段信息使MiraiCP可以支持这种消息: MessageSource:" + j.dump()); mc.add(UnSupportMessage(node.dump())); } } return mc; } } // namespace MiraiCP //from include/MessageSource.cpp namespace MiraiCP { using json = nlohmann::json; void MessageSource::recall() const { json j; j["source"] = this->serializeToString(); std::string re = KtOperation::ktOperation(KtOperation::Recall, j); if (re == "Y") return; if (re == "E2") throw RecallException(MIRAICP_EXCEPTION_WHERE); } MessageSource::MessageSource(std::string ids, std::string internalids, std::string source) : ids(std::move(ids)), internalids(std::move(internalids)), source(std::move(source)) {} std::string MessageSource::serializeToString() const { return source; } MessageSource MessageSource::deserializeFromString(const std::string &source) { json j = json::parse(source); try { return {j["ids"].dump(), j["internalIds"].dump(), source}; } catch (json::type_error &e) { Logger::logger.error("消息源序列化出错,格式不符合(MessageSource::deserializeFromString)"); Logger::logger.error(source); Logger::logger.error(e.what()); throw IllegalArgumentException(std::string("消息源序列化出错,格式不符合(MessageSource::deserializeFromString), ") + e.what(), MIRAICP_EXCEPTION_WHERE); } } } // namespace MiraiCP //from include/MiraiCode.cpp namespace MiraiCP { std::string MiraiCode::toString() { return Tools::escapeFromMiraiCode(this->content); } MiraiCode::MiraiCode(const std::string &a, bool convert) { // NOLINT(google-explicit-constructor) if (!convert) content = a; else content = Tools::escapeToMiraiCode(a); } } // namespace MiraiCP //from include/SingleMessage.cpp #include namespace MiraiCP { using json = nlohmann::json; // 静态成员 std::unordered_map SingleMessage::messageType{ {-5, "MarketFace"}, {-4, "OnlineForwardedMessage"}, {-3, "OnlineAudio"}, {-2, "QuoteReply"}, {-1, "unSupportMessage"}, {0, "plainText"}, {1, "at"}, {2, "atAll"}, {3, "image"}, {4, "app"}, {5, "service"}, {6, "file"}, {7, "face"}, {8, "FlashImage"}, {9, "MusicShare"}}; QuoteReply::QuoteReply(const SingleMessage &m) : SingleMessage(m) { if (m.type != -2) throw IllegalArgumentException("cannot convert type(" + std::to_string(m.type) + "to QuoteReply", MIRAICP_EXCEPTION_WHERE); source = MessageSource::deserializeFromString(m.content); } // 结束静态成员 nlohmann::json PlainText::toJson() const { nlohmann::json j; j["key"] = "plaintext"; j["content"] = content; return j; } int SingleMessage::getKey(const std::string &value) { for (const auto &a: messageType) { if (Tools::iequal(a.second, value)) return a.first; } return -1; } std::string SingleMessage::toMiraiCode() const { // Logger::logger.info("base"); if (type > 0) if (type == 1) return "[mirai:at:" + content + "] "; else if (type == 2) return "[mirai:atall] "; else return "[mirai:" + messageType[type] + this->prefix + Tools::escapeToMiraiCode(content) + "]"; else return content; } PlainText::PlainText(const SingleMessage &sg) : SingleMessage(sg) { if (sg.type != 0) throw IllegalArgumentException( "Cannot convert(" + MiraiCP::SingleMessage::messageType[sg.type] + ") to PlainText", MIRAICP_EXCEPTION_WHERE); this->content = sg.content; } nlohmann::json At::toJson() const { nlohmann::json j; j["key"] = "at"; j["content"] = std::to_string(this->target); return j; } At::At(const SingleMessage &sg) : SingleMessage(sg) { if (sg.type != 1) throw IllegalArgumentException( "Cannot convert(" + MiraiCP::SingleMessage::messageType[sg.type] + ") to At", MIRAICP_EXCEPTION_WHERE); this->target = std::stol(sg.content); } nlohmann::json AtAll::toJson() const { nlohmann::json j; j["key"] = "atall"; return j; } nlohmann::json Image::toJson() const { nlohmann::json j; j["key"] = "image"; j["imageid"] = this->id; j["size"] = this->size; j["width"] = this->width; j["height"] = this->height; j["type"] = this->imageType; return j; } Image::Image(const SingleMessage &sg) : SingleMessage(sg) { if (sg.type != 3 && sg.type != 8) throw IllegalArgumentException("传入的SingleMessage应该是Image类型", MIRAICP_EXCEPTION_WHERE); this->id = sg.content; this->size = this->width = this->height = 0; this->imageType = 5; } bool Image::isUploaded(QQID botid) { if (!this->md5.has_value()) this->refreshInfo(); if (this->size == 0) throw IllegalArgumentException("size不能为0", MIRAICP_EXCEPTION_WHERE); nlohmann::json tmp = this->toJson(); tmp["botid"] = botid; std::string re = KtOperation::ktOperation(KtOperation::ImageUploaded, tmp); return re == "true"; } nlohmann::json FlashImage::toJson() const { nlohmann::json j; j["key"] = "Flashimage"; j["imageid"] = this->id; j["size"] = this->size; j["width"] = this->width; j["height"] = this->height; j["type"] = this->imageType; return j; } nlohmann::json LightApp::toJson() const { nlohmann::json j; j["key"] = "lightapp"; j["content"] = this->content; return j; } LightApp::LightApp(const SingleMessage &sg) : SingleMessage(sg) { if (sg.type != 3) throw IllegalArgumentException( "Cannot convert(" + MiraiCP::SingleMessage::messageType[sg.type] + ") to LighApp", MIRAICP_EXCEPTION_WHERE); } std::string LightApp::toMiraiCode() const { return "[mirai:app:" + Tools::escapeToMiraiCode(content) + "]"; } nlohmann::json ServiceMessage::toJson() const { nlohmann::json j; j["key"] = "servicemessage"; j["content"] = this->content; j["id"] = this->id; return j; } std::string ServiceMessage::toMiraiCode() const { return "[mirai:service" + this->prefix + Tools::escapeToMiraiCode(content) + "]"; } ServiceMessage::ServiceMessage(const SingleMessage &sg) : SingleMessage(sg) { if (sg.type != 4) throw IllegalArgumentException( "Cannot convert(" + MiraiCP::SingleMessage::messageType[sg.type] + ") to ServiceMessage", MIRAICP_EXCEPTION_WHERE); } nlohmann::json Face::toJson() const { nlohmann::json j; j["key"] = "face"; j["id"] = this->id; return j; } nlohmann::json UnSupportMessage::toJson() const { nlohmann::json j; j["key"] = "unsupportmessage"; j["content"] = this->content; return j; } //远程文件(群文件) RemoteFile::RemoteFile(const std::string &i, unsigned int ii, std::string n, long long s, std::string p, struct Dinfo d, struct Finfo f) : SingleMessage(RemoteFile::type(), i + "," + std::to_string(ii) + "," + Tools::escapeToMiraiCode(std::move(n)) + "," + std::to_string(s)), id(i), internalid(ii), name(std::move(n)), size(s), path(std::move(p)), dinfo(std::move(d)), finfo(f) {} RemoteFile::RemoteFile(const std::string &i, unsigned int ii, std::string n, long long s) : SingleMessage(6, i + "," + std::to_string(ii) + "," + Tools::escapeToMiraiCode(std::move(n)) + "," + std::to_string(s)), id(i), internalid(ii), name(std::move(n)), size(s) {} RemoteFile RemoteFile::deserializeFromString(const std::string &source) { json j; try { j = json::parse(source); } catch (json::parse_error &e) { Logger::logger.error("格式化json失败,RemoteFile::deserializeFromString"); Logger::logger.error(source); Logger::logger.error(e.what()); throw e; } try { auto re = RemoteFile(j["id"], j["internalid"], j["name"], j["finfo"]["size"]); if (j.contains("dinfo")) { struct Dinfo d { j["dinfo"]["url"], j["dinfo"]["md5"], j["dinfo"]["sha1"] }; re.dinfo = d; } if (j["finfo"].contains("uploaderid")) { struct Finfo f { j["finfo"]["size"], j["finfo"]["uploaderid"], j["finfo"]["expirytime"], j["finfo"]["uploadtime"], j["finfo"]["lastmodifytime"] }; re.finfo = f; } if (j.contains("path")) re.path = j["path"]; return re; } catch (json::type_error &e) { Logger::logger.error("json格式化失败,位置:RemoteFile"); Logger::logger.error(source); Logger::logger.error(e.what()); throw e; } } RemoteFile RemoteFile::plus(unsigned int ii) { RemoteFile tmp(*this); tmp.internalid = ii; tmp.content = id + "," + std::to_string(ii) + "," + Tools::escapeToMiraiCode(std::move(name)) + "," + std::to_string(size); return tmp; } std::string RemoteFile::serializeToString() { json j; if (this->dinfo.has_value()) { j["dinfo"]["url"] = this->dinfo->url; j["dinfo"]["md5"] = this->dinfo->md5; j["dinfo"]["shar1"] = this->dinfo->sha1; } if (this->finfo.has_value()) { j["finfo"]["size"] = this->finfo->size; j["finfo"]["uploaderid"] = this->finfo->uploaderid; j["finfo"]["expirytime"] = this->finfo->expirytime; j["finfo"]["uploadtime"] = this->finfo->uploadtime; j["finfo"]["lastmodifytime"] = this->finfo->lastmodifytime; } j["id"] = this->id; j["internalid"] = this->internalid; j["name"] = this->name; j["size"] = this->size; if (this->path.has_value()) j["path"] = this->path.value(); return j.dump(); } /*图片类实现*/ void Image::refreshInfo() { std::string re = KtOperation::ktOperation(KtOperation::QueryImgInfo, this->toJson()); if (re == "E1") throw RemoteAssetException("图片id格式错误", MIRAICP_EXCEPTION_WHERE); json j = json::parse(re); this->url = j["url"]; this->md5 = j["md5"]; this->size = j["size"]; this->width = j["width"]; this->height = j["height"]; this->imageType = j["type"]; } Image Image::deserialize(const std::string &str) { json j = json::parse(str); return Image( j["imageid"], j["size"], j["width"], j["height"], j["type"]); } FlashImage FlashImage::deserialize(const std::string &str) { json j = json::parse(str); return FlashImage( j["imageid"], j["size"], j["width"], j["height"], j["type"]); } } // namespace MiraiCP //from include/Tools.cpp #include #include namespace MiraiCP::Tools { // 工具函数实现 std::string replace(std::string str, std::string_view from, std::string_view to) { size_t start_pos = 0; while ((start_pos = str.find(from, start_pos)) != std::string::npos) { str.replace(start_pos, from.length(), to); start_pos += to.length(); // Handles case where 'to' is a substd::string of 'from' } return str; } std::vector StringToVector(std::string temp) { std::vector result; temp.erase(temp.begin()); temp.pop_back(); std::regex ws_re("[,]+"); std::vector v(std::sregex_token_iterator(temp.begin(), temp.end(), ws_re, -1), std::sregex_token_iterator()); result.reserve(v.size()); std::for_each(v.begin(), v.end(), [&](auto &&s) { result.emplace_back(std::stoull(s)); }); // for (auto &&s: v) // result.emplace_back(std::stoull(s)); return result; } std::string escapeFromMiraiCode(const std::string &s) { //[ \[ //] \] //: \: //, \, //\ \\ / return replace(replace(replace(replace(replace(s, "\\\\", "\\"), "\\,", ","), "\\:", ":"), "\\]", "]"), "\\[", "["); } std::string escapeToMiraiCode(const std::string &s) { //[ \[ //] \] //: \: //, \, //\ \\ / return replace(replace(replace(replace(replace(s, "\\", "\\\\"), ",", "\\,"), ":", "\\:"), "]", "\\]"), "[", "\\["); } bool starts_with(std::string_view f, std::string_view s) { return f.rfind(s, 0) == 0; } bool icompareChar(const char &c1, const char &c2) { return c1 == c2 || std::toupper(c1) == std::toupper(c2); } bool iequal(std::string_view str1, std::string_view str2) { return ((str1.size() == str2.size()) && std::equal(str1.begin(), str1.end(), str2.begin(), &icompareChar)); } std::vector split(const std::string &text, const std::string &delim) { std::regex ws_re(delim + "+"); return {std::sregex_token_iterator(text.begin(), text.end(), ws_re, -1), std::sregex_token_iterator()}; } } // namespace MiraiCP::Tools //from include/loaderApi.cpp #include #include #include namespace LibLoader::LoaderApi { static const interface_funcs *loader_apis = nullptr; void set_loader_apis(const LibLoader::LoaderApi::interface_funcs *apis) { loader_apis = apis; } void reset_loader_apis() { loader_apis = nullptr; } /// 这个函数是给本cpp以外的文件使用的,大概率用不到 const interface_funcs *get_loader_apis() { return loader_apis; } using MiraiCP::PluginNotAuthorizedException; using MiraiCP::PluginNotEnabledException; // check the func ptr existance before use it inline void checkApi(void *funcptr) { if (loader_apis == nullptr) [[unlikely]] { throw PluginNotEnabledException(MIRAICP_EXCEPTION_WHERE); } else if (funcptr == nullptr) [[unlikely]] { throw PluginNotAuthorizedException(MIRAICP_EXCEPTION_WHERE); } } /// interfaces for plugins MiraiCPString pluginOperation(const MiraiCPString &s) { checkApi((void *) loader_apis->_pluginOperation); return loader_apis->_pluginOperation(s); } void loggerInterface(const MiraiCPString &content, const MiraiCPString &name, long long id, int level) { checkApi((void *) loader_apis->_loggerInterface); loader_apis->_loggerInterface(content, name, id, level); } // todo(ea): 返回可用类型而不是中间类型 MiraiCPString showAllPluginId() { checkApi((void *) loader_apis->_showAllPluginId); return loader_apis->_showAllPluginId(); } void enablePluginById(const MiraiCPString &id) { checkApi((void *) loader_apis->_enablePluginById); loader_apis->_enablePluginById(id); } void disablePluginById(const MiraiCPString &id) { checkApi((void *) loader_apis->_disablePluginById); loader_apis->_disablePluginById(id); } void enableAllPlugins() { checkApi((void *) loader_apis->_enableAllPlugins); loader_apis->_enableAllPlugins(); } void disableAllPlugins() { checkApi((void *) loader_apis->_disableAllPlugins); loader_apis->_disableAllPlugins(); } void loadNewPlugin(const MiraiCPString &path, bool activateNow) { checkApi((void *) loader_apis->_loadNewPlugin); loader_apis->_loadNewPlugin(path, activateNow); } void unloadPluginById(const MiraiCPString &id) { checkApi((void *) loader_apis->_unloadPluginById); loader_apis->_unloadPluginById(id); } void reloadPluginById(const MiraiCPString &id) { checkApi((void *) loader_apis->_reloadPluginById); loader_apis->_reloadPluginById(id); } } // namespace LibLoader::LoaderApi //from include/utils.cpp // 开始对接libloader接口代码 using json = nlohmann::json; namespace LibLoader::LoaderApi { const interface_funcs *get_loader_apis(); void set_loader_apis(const LibLoader::LoaderApi::interface_funcs *apis); void reset_loader_apis(); } // namespace LibLoader::LoaderApi extern "C" { /// 插件开启入口 MIRAICP_EXPORT void FUNC_ENTRANCE(const LibLoader::LoaderApi::interface_funcs &funcs) { static_assert(std::is_same_v); using namespace MiraiCP; Event::clear(); LibLoader::LoaderApi::set_loader_apis(&funcs); assert(LibLoader::LoaderApi::get_loader_apis() != nullptr); Logger::logger.info("开始启动插件: " + MiraiCP::CPPPlugin::config.getId()); try { enrollPlugin(); // plugin == nullptr 无插件实例加载 if (CPPPlugin::plugin != nullptr) { CPPPlugin::plugin->onEnable(); } } catch (const MiraiCPExceptionBase &e) { e.raise(); Logger::logger.error("插件(id=" + CPPPlugin::config.getId() + ", name=" + CPPPlugin::config.name + ")启动失败"); throw IllegalStateException(e.what(), e.filename, e.lineNum); } catch (const std::exception &e) { Logger::logger.error(e.what()); Logger::logger.error("插件(id=" + CPPPlugin::config.getId() + ", name=" + CPPPlugin::config.name + ")启动失败"); throw IllegalStateException(e.what(), MIRAICP_EXCEPTION_WHERE); } catch (...) { Logger::logger.error("插件(id=" + CPPPlugin::config.getId() + ", name=" + CPPPlugin::config.name + ")启动失败"); throw IllegalStateException("", MIRAICP_EXCEPTION_WHERE); } } /// 插件结束(也可能是暂时的disable) MIRAICP_EXPORT void FUNC_EXIT() { static_assert(std::is_same_v); using namespace MiraiCP; Logger::logger.info("开始禁用插件:" + MiraiCP::CPPPlugin::config.getId()); Event::clear(); if (CPPPlugin::plugin != nullptr) CPPPlugin::plugin->onDisable(); CPPPlugin::plugin.reset(); // 无法保证用户插件析构函数是否调用api,在plugin.reset()之前不可reset loader api LibLoader::LoaderApi::reset_loader_apis(); } /// 消息解析分流 /// env != null, call from jni MIRAICP_EXPORT void FUNC_EVENT(const char *c) { static_assert(std::is_same_v); using namespace MiraiCP; std::string content = c; json j; try { j = json::parse(content); } catch (json::parse_error &e) { Logger::logger.error("消息解析分流:格式化json错误"); Logger::logger.error("For debug: " + content); Logger::logger.error(e.what()); return; } int type = j["type"].get(); if (type != eventTypes::Command && Event::noRegistered(type)) return; try { Event::incomingEvent(std::move(j), type); } catch (json::type_error &e) { Logger::logger.error("json格式化异常,位置C-Handle"); Logger::logger.error(e.what()); Logger::logger.error("info:", content); } catch (MiraiCPExceptionBase &e) { Event::broadcast(MiraiCPExceptionEvent(&e)); e.raise(); } catch (const std::exception &e) { Logger::logger.error(e.what()); Logger::logger.error("info:", content); } // 如果产生了无法处理的异常,直接退出插件 // loader端将处理这个异常并将绑定的线程结束掉 // 如果存在其他线程,可能导致段错误 } /// 获取 Plugin Info /// 如果未正确定义,插件无法正确加载 /// 该函数不可调用loader api;因为会在入口函数调用前先调用,loader api未初始化 MIRAICP_EXPORT const MiraiCP::PluginConfig &PLUGIN_INFO() { static_assert(std::is_same_v); if (MiraiCP::CPPPlugin::config.getId().empty()) throw std::exception(); return MiraiCP::CPPPlugin::config; } } //结束对接JNI接口代码 //from common/PluginConfig.cpp namespace MiraiCP { using json = nlohmann::json; json PluginConfig::serialize() { json j; j["name"] = name; j["version"] = version; j["author"] = author; j["description"] = description; j["time"] = time; j["id"] = id; return j; } std::string PluginConfig::serialize2string() { return serialize().dump(); } } // namespace MiraiCP //from common/miraicpString.cpp // Copyright (c) 2022. Eritque arcus and contributors. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or any later version(in your opinion). // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // #ifdef MIRAICP_LIB_LOADER #endif #include namespace MiraiCP { void swap(MiraiCPString &a, MiraiCPString &b) noexcept { std::swap(a.str, b.str); std::swap(a._size, b._size); std::swap(a.free_this, b.free_this); } // avoid calling this if _size == 0 void MiraiCPString::construction() { str = (char *) ::std::malloc(sizeof(char) * (_size + 1)); if (str == nullptr) { #ifdef MIRAICP_LIB_LOADER LibLoader::logger.error("MiraiCPString::construction: malloc failed when trying to malloc size " + std::to_string(_size + 1)); #endif throw std::bad_alloc(); } } MiraiCPString::~MiraiCPString() { // do not inherit MiraiCPString!! if (str != nullptr) { // ensure deconstruction is paired to construction free_this(str); str = nullptr; } } MiraiCPString::MiraiCPString(const MiraiCPString &other) : str(nullptr), _size(other._size), free_this(std_free_ptr) { if (_size == 0) return; construction(); assert(str != nullptr); memcpy(str, other.str, _size * sizeof(char)); str[_size] = 0; } MiraiCPString::MiraiCPString(MiraiCPString &&temp) noexcept : MiraiCPString() { swap(*this, temp); } MiraiCPString::MiraiCPString(const char *char_str) : MiraiCPString() { if (char_str == nullptr) return; _size = strlen(char_str); if (0 == _size) return; construction(); assert(str != nullptr); memcpy(str, char_str, _size * sizeof(char)); str[_size] = 0; } MiraiCPString::MiraiCPString(const std::string &string_str) { _size = string_str.size(); if (!_size) return; construction(); assert(str != nullptr); memcpy(str, string_str.c_str(), _size * sizeof(char)); str[_size] = 0; } const char *MiraiCPString::copyToCharPtr() const { if (str == nullptr || _size == 0) return new char[1]{0}; char *t = new char[_size + 1]; memcpy(t, str, (_size + 1) * sizeof(char)); return t; } bool MiraiCPString::operator==(const MiraiCPString &another) const { return another._size == _size && (_size == 0 || strcmp(another.str, str) == 0); } MiraiCPString &MiraiCPString::operator=(const MiraiCPString &another) { MiraiCPString temp(another); std::swap(*this, temp); return *this; } MiraiCPString &MiraiCPString::operator=(MiraiCPString &&another) noexcept { std::swap(*this, another); return *this; } } // namespace MiraiCP