1608 lines
68 KiB
C++
1608 lines
68 KiB
C++
|
// 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 <http://www.gnu.org/licenses/>.
|
|||
|
//
|
|||
|
#include <MiraiCP.hpp>
|
|||
|
//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<QQID> 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<QQID> 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> 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<std::string>())).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<std::string>())).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<std::string>())).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>(
|
|||
|
GroupMessageEvent(j["group"]["botid"],
|
|||
|
Group(Group::deserialize(j["group"])),
|
|||
|
Member(Member::deserialize(j["member"])),
|
|||
|
MessageChain::deserializationFromMessageSourceJson(json::parse(j["source"].get<std::string>()))
|
|||
|
.plus(MessageSource::deserializeFromString(j["source"].get<std::string>()))));
|
|||
|
break;
|
|||
|
}
|
|||
|
case eventTypes::PrivateMessageEvent: {
|
|||
|
//私聊消息
|
|||
|
Event::broadcast<PrivateMessageEvent>(
|
|||
|
PrivateMessageEvent(j["friend"]["botid"],
|
|||
|
Friend(Friend::deserialize(j["friend"])),
|
|||
|
MessageChain::deserializationFromMessageSourceJson(json::parse(j["source"].get<std::string>()))
|
|||
|
.plus(MessageSource::deserializeFromString(j["source"].get<std::string>()))));
|
|||
|
break;
|
|||
|
}
|
|||
|
case eventTypes::GroupInviteEvent:
|
|||
|
//群聊邀请
|
|||
|
Event::broadcast<GroupInviteEvent>(
|
|||
|
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>(
|
|||
|
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>(
|
|||
|
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>(MemberLeaveEvent(
|
|||
|
j["group"]["botid"],
|
|||
|
j["leavetype"],
|
|||
|
j["memberid"],
|
|||
|
Group(Group::deserialize(j["group"])),
|
|||
|
j["operatorid"]));
|
|||
|
break;
|
|||
|
case eventTypes::RecallEvent:
|
|||
|
Event::broadcast<RecallEvent>(RecallEvent(
|
|||
|
j["botid"],
|
|||
|
j["etype"],
|
|||
|
j["time"],
|
|||
|
j["authorid"],
|
|||
|
j["operatorid"],
|
|||
|
j["ids"],
|
|||
|
j["internalids"],
|
|||
|
j["groupid"]));
|
|||
|
break;
|
|||
|
case eventTypes::BotJoinGroupEvent:
|
|||
|
Event::broadcast<BotJoinGroupEvent>(BotJoinGroupEvent(
|
|||
|
j["group"]["botid"],
|
|||
|
j["etype"],
|
|||
|
Group(Group::deserialize(j["group"])),
|
|||
|
j["inviterid"]));
|
|||
|
break;
|
|||
|
case eventTypes::GroupTempMessageEvent:
|
|||
|
Event::broadcast<GroupTempMessageEvent>(GroupTempMessageEvent(
|
|||
|
j["group"]["botid"],
|
|||
|
Group(Group::deserialize(j["group"])),
|
|||
|
Member(Member::deserialize(j["member"])),
|
|||
|
MessageChain::deserializationFromMessageSourceJson(json::parse(j["message"].get<std::string>()))
|
|||
|
.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<Group> a;
|
|||
|
std::optional<Member> 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<std::string>(), false), j["botid"]));
|
|||
|
break;
|
|||
|
}
|
|||
|
case eventTypes::Command: {
|
|||
|
// command
|
|||
|
std::optional<Contact> 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<std::string>() : ""), 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 <utility>
|
|||
|
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<std::shared_ptr<ForwardedMessage>>(node.message)->nodesToJson().dump();
|
|||
|
if (display.has_value())
|
|||
|
temp["display"] = display->toJson();
|
|||
|
} else
|
|||
|
temp["message"] = std::get<MessageChain>(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<ForwardedNode> 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<ForwardedNode> 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<ForwardedMessageDisplayStrategy> display)
|
|||
|
: id(id), name(std::move(name)),
|
|||
|
message(std::make_shared<ForwardedMessage>(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<ForwardedMessageDisplayStrategy> 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::OnlineAnnouncement> Group::getAnnouncementsList() {
|
|||
|
json j;
|
|||
|
j["source"] = this->toString();
|
|||
|
j["announcement"] = true;
|
|||
|
std::string re = KtOperation::ktOperation(KtOperation::RefreshInfo, j);
|
|||
|
std::vector<OnlineAnnouncement> 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<unsigned long long> 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::file_short_info> Group::getFileList(const std::string &path) {
|
|||
|
std::vector<file_short_info> re = std::vector<file_short_info>();
|
|||
|
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<long long>(id), level);
|
|||
|
}
|
|||
|
} // namespace MiraiCP
|
|||
|
//from include/LowLevelAPI.cpp
|
|||
|
#include <utility>
|
|||
|
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<QQID>(), tmp["botId"].get<QQID>()).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<std::string>()));
|
|||
|
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 <json.hpp>
|
|||
|
namespace MiraiCP {
|
|||
|
using json = nlohmann::json;
|
|||
|
// 静态成员
|
|||
|
std::unordered_map<int, std::string> 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 <regex>
|
|||
|
#include <utf8.h>
|
|||
|
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<QQID> StringToVector(std::string temp) {
|
|||
|
std::vector<QQID> result;
|
|||
|
temp.erase(temp.begin());
|
|||
|
temp.pop_back();
|
|||
|
std::regex ws_re("[,]+");
|
|||
|
std::vector<std::string> 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<std::string> 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 <string>
|
|||
|
#include <utility>
|
|||
|
#include <vector>
|
|||
|
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<decltype(&FUNC_ENTRANCE), LibLoader::plugin_entrance_func_ptr>);
|
|||
|
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<decltype(&FUNC_EXIT), LibLoader::plugin_func_ptr>);
|
|||
|
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<decltype(&FUNC_EVENT), LibLoader::plugin_event_func_ptr>);
|
|||
|
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<int>();
|
|||
|
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>(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<decltype(&PLUGIN_INFO), LibLoader::plugin_info_func_ptr>);
|
|||
|
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 <http://www.gnu.org/licenses/>.
|
|||
|
//
|
|||
|
#ifdef MIRAICP_LIB_LOADER
|
|||
|
#endif
|
|||
|
#include <cstring>
|
|||
|
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
|