mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
refactor: raw message parsers
This commit is contained in:
parent
e3ca5df713
commit
2a7f8d0c99
@ -101,7 +101,7 @@ class MessageUniqueWrapper {
|
|||||||
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
|
return ret.map((t) => t?.MsgId).filter((t) => t !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
createMsg(peer: Peer, msgId: string): number | undefined {
|
createMsg(peer: Peer, msgId: string) {
|
||||||
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
const key = `${msgId}|${peer.chatType}|${peer.peerUid}`;
|
||||||
const hash = crypto.createHash('md5').update(key).digest();
|
const hash = crypto.createHash('md5').update(key).digest();
|
||||||
//设置第一个bit为0 保证shortId为正数
|
//设置第一个bit为0 保证shortId为正数
|
||||||
|
@ -99,7 +99,7 @@ const _handlers: {
|
|||||||
coreContext,
|
coreContext,
|
||||||
(await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context)).path,
|
(await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context)).path,
|
||||||
sendMsg.data.summary || '',
|
sendMsg.data.summary || '',
|
||||||
sendMsg.data.subType || 0,
|
sendMsg.data.sub_type || 0,
|
||||||
);
|
);
|
||||||
context.deleteAfterSentFiles.push(PicEle.picElement.sourcePath);
|
context.deleteAfterSentFiles.push(PicEle.picElement.sourcePath);
|
||||||
return PicEle;
|
return PicEle;
|
||||||
|
@ -6,10 +6,354 @@ import { OB11Constructor } from '../helper';
|
|||||||
import { EventType } from '@/onebot/event/OB11BaseEvent';
|
import { EventType } from '@/onebot/event/OB11BaseEvent';
|
||||||
import { encodeCQCode } from '@/onebot/helper/cqcode';
|
import { encodeCQCode } from '@/onebot/helper/cqcode';
|
||||||
|
|
||||||
|
type RawToOb11Converters = {
|
||||||
|
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
|
||||||
|
element: Exclude<MessageElement[Key], null | undefined>,
|
||||||
|
msg: RawMessage,
|
||||||
|
elementWrapper: MessageElement
|
||||||
|
) => PromiseLike<OB11MessageData | null>
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyCanBeParsed(key: string, parser: RawToOb11Converters): key is keyof RawToOb11Converters {
|
||||||
|
return key in parser;
|
||||||
|
}
|
||||||
|
|
||||||
export class OneBotMsgApi {
|
export class OneBotMsgApi {
|
||||||
obContext: NapCatOneBot11Adapter;
|
obContext: NapCatOneBot11Adapter;
|
||||||
coreContext: NapCatCore;
|
coreContext: NapCatCore;
|
||||||
|
|
||||||
|
rawToOb11Converters: RawToOb11Converters = {
|
||||||
|
textElement: async element => {
|
||||||
|
if (element.atType === AtType.notAt) {
|
||||||
|
let text = element.content;
|
||||||
|
if (!text.trim()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 兼容 9.7.x 换行符
|
||||||
|
if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) {
|
||||||
|
text = text.replace(/\r/g, '\n');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.text,
|
||||||
|
data: { text }
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let qq: string = 'all';
|
||||||
|
if (element.atType !== AtType.atAll) {
|
||||||
|
const { atNtUid, /* content */ } = element;
|
||||||
|
let atQQ = element.atUid;
|
||||||
|
if (!atQQ || atQQ === '0') {
|
||||||
|
atQQ = await this.coreContext.apis.UserApi.getUinByUidV2(atNtUid);
|
||||||
|
}
|
||||||
|
if (atQQ) {
|
||||||
|
qq = atQQ as `${number}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.at,
|
||||||
|
data: {
|
||||||
|
qq: qq,
|
||||||
|
// name: content.slice(1);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
picElement: async (element, msg) => {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.image,
|
||||||
|
data: {
|
||||||
|
file: element.fileName,
|
||||||
|
sub_type: element.picSubType,
|
||||||
|
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId),
|
||||||
|
url: await this.coreContext.apis.FileApi.getImageUrl(element),
|
||||||
|
file_size: element.fileSize,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (e: any) {
|
||||||
|
this.coreContext.context.logger.logError('获取图片url失败', e.stack);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fileElement: async (element, msg, elementWrapper) => {
|
||||||
|
await this.coreContext.apis.FileApi.addFileCache(
|
||||||
|
{
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
chatType: msg.chatType,
|
||||||
|
guildId: '',
|
||||||
|
},
|
||||||
|
msg.msgId,
|
||||||
|
msg.msgSeq,
|
||||||
|
msg.senderUid,
|
||||||
|
elementWrapper.elementId,
|
||||||
|
elementWrapper.elementType.toString(),
|
||||||
|
element.fileSize,
|
||||||
|
element.fileName,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.file,
|
||||||
|
data: {
|
||||||
|
file: element.fileName,
|
||||||
|
path: element.filePath,
|
||||||
|
url: element.filePath,
|
||||||
|
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId),
|
||||||
|
file_size: element.fileSize,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
faceElement: async element => {
|
||||||
|
const faceIndex = element.faceIndex;
|
||||||
|
if (faceIndex === FaceIndex.dice) {
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.dice,
|
||||||
|
data: {
|
||||||
|
result: element.resultId!,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (faceIndex === FaceIndex.RPS) {
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.RPS,
|
||||||
|
data: {
|
||||||
|
result: element.resultId!,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.face,
|
||||||
|
data: {
|
||||||
|
id: element.faceIndex.toString()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
marketFaceElement: async (_, msg, elementWrapper) => {
|
||||||
|
await this.coreContext.apis.FileApi.addFileCache(
|
||||||
|
{
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
chatType: msg.chatType,
|
||||||
|
guildId: '',
|
||||||
|
},
|
||||||
|
msg.msgId,
|
||||||
|
msg.msgSeq,
|
||||||
|
msg.senderUid,
|
||||||
|
elementWrapper.elementId,
|
||||||
|
elementWrapper.elementType.toString(),
|
||||||
|
'0',
|
||||||
|
'marketface',
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.image,
|
||||||
|
data: {
|
||||||
|
file: 'marketface',
|
||||||
|
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId),
|
||||||
|
path: elementWrapper.elementId,
|
||||||
|
url: elementWrapper.elementId,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
replyElement: async (element, msg) => {
|
||||||
|
const NTQQMsgApi = this.coreContext.apis.MsgApi;
|
||||||
|
const records = msg.records.find(msgRecord => msgRecord.msgId === element?.sourceMsgIdInRecords);
|
||||||
|
const peer = {
|
||||||
|
chatType: msg.chatType,
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
guildId: '',
|
||||||
|
};
|
||||||
|
if (!records) {
|
||||||
|
this.coreContext.context.logger.logError('获取不到引用的消息', element.replayMsgSeq);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let replyMsg: RawMessage | undefined;
|
||||||
|
// Attempt 1
|
||||||
|
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
guildId: '',
|
||||||
|
chatType: msg.chatType,
|
||||||
|
}, element.replayMsgSeq, 1, true, true))
|
||||||
|
.msgList
|
||||||
|
.find(msg => msg.msgRandom === records.msgRandom);
|
||||||
|
|
||||||
|
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||||
|
// Attempt 2
|
||||||
|
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replayMsgSeq)).msgList[0];
|
||||||
|
|
||||||
|
if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') {
|
||||||
|
// Attempt 3
|
||||||
|
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList;
|
||||||
|
if (replyMsgList.length < 1) {
|
||||||
|
this.coreContext.context.logger.logError('回复消息消息验证失败', element.replayMsgSeq);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
replyMsg = replyMsgList.filter(e => e.msgSeq == records.msgSeq)
|
||||||
|
.sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.reply,
|
||||||
|
data: {
|
||||||
|
id: MessageUnique.createMsg({
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
guildId: '',
|
||||||
|
chatType: msg.chatType,
|
||||||
|
}, replyMsg.msgId).toString()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
videoElement: async (element, msg, elementWrapper) => {
|
||||||
|
const NTQQFileApi = this.coreContext.apis.FileApi;
|
||||||
|
|
||||||
|
//读取视频链接并兜底
|
||||||
|
let videoUrlWrappers: Awaited<ReturnType<typeof NTQQFileApi.getVideoUrl>> | undefined;
|
||||||
|
|
||||||
|
if (msg.peerUin === '284840486') {
|
||||||
|
//TODO: 合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Tagged
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
videoUrlWrappers = await NTQQFileApi.getVideoUrl({
|
||||||
|
chatType: msg.chatType,
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
guildId: '0',
|
||||||
|
}, msg.msgId, elementWrapper.elementId);
|
||||||
|
} catch (error) {
|
||||||
|
this.coreContext.context.logger.logWarn('获取视频 URL 失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
//读取在线URL
|
||||||
|
let videoDownUrl: string | undefined;
|
||||||
|
|
||||||
|
if (videoUrlWrappers) {
|
||||||
|
const videoDownUrlTemp = videoUrlWrappers.find((urlWrapper) => {
|
||||||
|
return !!(urlWrapper.url);
|
||||||
|
});
|
||||||
|
if (videoDownUrlTemp) {
|
||||||
|
videoDownUrl = videoDownUrlTemp.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//开始兜底
|
||||||
|
if (!videoDownUrl) {
|
||||||
|
videoDownUrl = element.filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
await NTQQFileApi.addFileCache(
|
||||||
|
{
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
chatType: msg.chatType,
|
||||||
|
guildId: '',
|
||||||
|
},
|
||||||
|
msg.msgId,
|
||||||
|
msg.msgSeq,
|
||||||
|
msg.senderUid,
|
||||||
|
elementWrapper.elementId,
|
||||||
|
elementWrapper.elementType.toString(),
|
||||||
|
element.fileSize || '0',
|
||||||
|
element.fileName,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.video,
|
||||||
|
data: {
|
||||||
|
file: element.fileName,
|
||||||
|
path: videoDownUrl,
|
||||||
|
url: videoDownUrl,
|
||||||
|
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId),
|
||||||
|
file_size: element.fileSize,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
pttElement: async (element, msg, elementWrapper) => {
|
||||||
|
await this.coreContext.apis.FileApi.addFileCache(
|
||||||
|
{
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
chatType: msg.chatType,
|
||||||
|
guildId: '',
|
||||||
|
},
|
||||||
|
msg.msgId,
|
||||||
|
msg.msgSeq,
|
||||||
|
msg.senderUid,
|
||||||
|
elementWrapper.elementId,
|
||||||
|
elementWrapper.elementType.toString(),
|
||||||
|
element.fileSize || '0',
|
||||||
|
element.fileUuid || '',
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.voice,
|
||||||
|
data: {
|
||||||
|
file: element.fileName,
|
||||||
|
path: element.filePath,
|
||||||
|
file_id: UUIDConverter.encode(msg.peerUin, msg.msgId),
|
||||||
|
file_size: element.fileSize,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
multiForwardMsgElement: async (_, msg) => {
|
||||||
|
const NTQQMsgApi = this.coreContext.apis.MsgApi;
|
||||||
|
const message_data: OB11MessageData = {
|
||||||
|
data: {} as any,
|
||||||
|
type: 'unknown' as any,
|
||||||
|
};
|
||||||
|
message_data['type'] = OB11MessageDataType.forward;
|
||||||
|
message_data['data']['id'] = msg.msgId;
|
||||||
|
const parentMsgPeer = msg.parentMsgPeer ?? {
|
||||||
|
chatType: msg.chatType,
|
||||||
|
guildId: '',
|
||||||
|
peerUid: msg.peerUid,
|
||||||
|
};
|
||||||
|
//判断是否在合并消息内
|
||||||
|
msg.parentMsgIdList = msg.parentMsgIdList ?? [];
|
||||||
|
//首次列表不存在则开始创建
|
||||||
|
msg.parentMsgIdList.push(msg.msgId);
|
||||||
|
//let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2];
|
||||||
|
//加入自身MsgId
|
||||||
|
const multiMsgs = (await NTQQMsgApi.getMultiMsg(parentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList;
|
||||||
|
//拉取下级消息
|
||||||
|
if (!multiMsgs) return null;
|
||||||
|
//拉取失败则跳过
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.forward,
|
||||||
|
data: {
|
||||||
|
id: msg.msgId,
|
||||||
|
content: (await Promise.all(multiMsgs.map(
|
||||||
|
async multiMsgItem => {
|
||||||
|
multiMsgItem.parentMsgPeer = parentMsgPeer;
|
||||||
|
multiMsgItem.parentMsgIdList = msg.parentMsgIdList;
|
||||||
|
multiMsgItem.id = MessageUnique.createMsg(parentMsgPeer, multiMsgItem.msgId); //该ID仅用查看 无法调用
|
||||||
|
return await this.parseMessage(multiMsgItem);
|
||||||
|
}
|
||||||
|
))).filter(item => item !== undefined),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
arkElement: async (element) => {
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.json,
|
||||||
|
data: {
|
||||||
|
data: element.bytesData
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
markdownElement: async (element) => {
|
||||||
|
return {
|
||||||
|
type: OB11MessageDataType.markdown,
|
||||||
|
data: {
|
||||||
|
content: element.content
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
constructor(obContext: NapCatOneBot11Adapter, coreContext: NapCatCore) {
|
constructor(obContext: NapCatOneBot11Adapter, coreContext: NapCatCore) {
|
||||||
this.obContext = obContext;
|
this.obContext = obContext;
|
||||||
this.coreContext = coreContext;
|
this.coreContext = coreContext;
|
||||||
@ -17,10 +361,10 @@ export class OneBotMsgApi {
|
|||||||
|
|
||||||
async parseMessage(
|
async parseMessage(
|
||||||
msg: RawMessage,
|
msg: RawMessage,
|
||||||
messagePostFormat: string = this.obContext.configLoader.configData.messagePostFormat
|
messagePostFormat: string = this.obContext.configLoader.configData.messagePostFormat,
|
||||||
) {
|
) {
|
||||||
if (msg.senderUin == "0" || msg.senderUin == "") return;
|
if (msg.senderUin == '0' || msg.senderUin == '') return;
|
||||||
if (msg.peerUin == "0" || msg.peerUin == "") return;
|
if (msg.peerUin == '0' || msg.peerUin == '') return;
|
||||||
//跳过空消息
|
//跳过空消息
|
||||||
const NTQQGroupApi = this.coreContext.apis.GroupApi;
|
const NTQQGroupApi = this.coreContext.apis.GroupApi;
|
||||||
const NTQQUserApi = this.coreContext.apis.UserApi;
|
const NTQQUserApi = this.coreContext.apis.UserApi;
|
||||||
@ -65,404 +409,34 @@ export class OneBotMsgApi {
|
|||||||
resMsg.sender.nickname = ret.tmpChatInfo!.fromNick;
|
resMsg.sender.nickname = ret.tmpChatInfo!.fromNick;
|
||||||
} else {
|
} else {
|
||||||
resMsg.group_id = 284840486; //兜底数据
|
resMsg.group_id = 284840486; //兜底数据
|
||||||
resMsg.sender.nickname = "临时会话";
|
resMsg.sender.nickname = '临时会话';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const element of msg.elements) {
|
|
||||||
let message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
if (element.textElement && element.textElement?.atType !== AtType.notAt) {
|
|
||||||
const textAtMsgData = await this.obContext.apiContext.MsgApi.parseTextElemntWithAt(msg, element);
|
|
||||||
if (textAtMsgData) message_data = textAtMsgData;
|
|
||||||
} else if (element.textElement) {
|
|
||||||
const textMsgData = await this.obContext.apiContext.MsgApi.parseTextElement(msg, element);
|
|
||||||
if (textMsgData) message_data = textMsgData;
|
|
||||||
} else if (element.replyElement) {
|
|
||||||
const replyMsgData = await this.obContext.apiContext.MsgApi.parseReplyElement(msg, element);
|
|
||||||
if (replyMsgData) message_data = replyMsgData;
|
|
||||||
} else if (element.picElement) {
|
|
||||||
const PicMsgData = await this.obContext.apiContext.MsgApi.parsePicElement(msg, element);
|
|
||||||
if (PicMsgData) message_data = PicMsgData;
|
|
||||||
} else if (element.fileElement) {
|
|
||||||
const FileMsgData = await this.obContext.apiContext.MsgApi.parseFileElement(msg, element);
|
|
||||||
if (FileMsgData) message_data = FileMsgData;
|
|
||||||
} else if (element.videoElement) {
|
|
||||||
const videoMsgData = await this.obContext.apiContext.MsgApi.parseVideoElement(msg, element);
|
|
||||||
if (videoMsgData) message_data = videoMsgData;
|
|
||||||
} else if (element.pttElement) {
|
|
||||||
const pttMsgData = await this.obContext.apiContext.MsgApi.parsePTTElement(msg, element);
|
|
||||||
if (pttMsgData) message_data = pttMsgData;
|
|
||||||
} else if (element.arkElement) {
|
|
||||||
const arkMsgData = await this.obContext.apiContext.MsgApi.parseArkElement(msg, element);
|
|
||||||
if (arkMsgData) message_data = arkMsgData;
|
|
||||||
} else if (element.faceElement) {
|
|
||||||
const faceMsgData = await this.obContext.apiContext.MsgApi.parseFaceElement(msg, element);
|
|
||||||
if (faceMsgData) message_data = faceMsgData;
|
|
||||||
} else if (element.marketFaceElement) {
|
|
||||||
const marketFaceMsgData = await this.obContext.apiContext.MsgApi.parseMarketFaceElement(msg, element);
|
|
||||||
if (marketFaceMsgData) message_data = marketFaceMsgData;
|
|
||||||
} else if (element.markdownElement) {
|
|
||||||
message_data['type'] = OB11MessageDataType.markdown;
|
|
||||||
message_data['data']['data'] = element.markdownElement.content;
|
|
||||||
} else if (element.multiForwardMsgElement) {
|
|
||||||
const multiForwardMsgData = await this.obContext.apiContext.MsgApi.parseMultForwardElement(msg, element, messagePostFormat);
|
|
||||||
if (multiForwardMsgData) message_data = multiForwardMsgData;
|
|
||||||
}
|
|
||||||
if ((message_data.type as string) !== 'unknown' && message_data.data) {
|
|
||||||
const cqCode = encodeCQCode(message_data);
|
|
||||||
|
|
||||||
if (messagePostFormat === 'string') {
|
const msgSegments = (await Promise.all(msg.elements.map(
|
||||||
(resMsg.message as string) += cqCode;
|
async (element) => {
|
||||||
} else (resMsg.message as OB11MessageData[]).push(message_data);
|
for (const key in element) {
|
||||||
resMsg.raw_message += cqCode;
|
if (keyCanBeParsed(key, this.rawToOb11Converters)) {
|
||||||
|
return await this.rawToOb11Converters[key]?.(
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
element[key],
|
||||||
|
msg,
|
||||||
|
element
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
))).filter(entry => !!entry);
|
||||||
|
|
||||||
|
const msgAsCQCode = msgSegments.map(msg => encodeCQCode(msg)).join('').trim();
|
||||||
|
|
||||||
|
if (messagePostFormat === 'string') {
|
||||||
|
resMsg.message = msgAsCQCode;
|
||||||
|
} else {
|
||||||
|
resMsg.message = msgSegments;
|
||||||
|
resMsg.raw_message = msgAsCQCode;
|
||||||
}
|
}
|
||||||
resMsg.raw_message = resMsg.raw_message.trim();
|
|
||||||
return resMsg;
|
return resMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseFileElement(msg: RawMessage, element: MessageElement) {
|
|
||||||
const fileElement = element.fileElement;
|
|
||||||
if (!fileElement) return undefined;
|
|
||||||
const NTQQFileApi = this.coreContext.apis.FileApi;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
message_data['type'] = OB11MessageDataType.file;
|
|
||||||
message_data['data']['file'] = fileElement.fileName;
|
|
||||||
message_data['data']['path'] = fileElement.filePath;
|
|
||||||
message_data['data']['url'] = fileElement.filePath;
|
|
||||||
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
|
|
||||||
message_data['data']['file_size'] = fileElement.fileSize;
|
|
||||||
await NTQQFileApi.addFileCache(
|
|
||||||
{
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
chatType: msg.chatType,
|
|
||||||
guildId: '',
|
|
||||||
},
|
|
||||||
msg.msgId,
|
|
||||||
msg.msgSeq,
|
|
||||||
msg.senderUid,
|
|
||||||
element.elementId,
|
|
||||||
element.elementType.toString(),
|
|
||||||
fileElement.fileSize,
|
|
||||||
fileElement.fileName
|
|
||||||
);
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
async parseTextElemntWithAt(msg: RawMessage, element: MessageElement) {
|
|
||||||
const textElement = element.textElement;
|
|
||||||
if (!textElement) return undefined;
|
|
||||||
const NTQQUserApi = this.coreContext.apis.UserApi;
|
|
||||||
let qq: `${number}` | 'all';
|
|
||||||
// let name: string | undefined;
|
|
||||||
if (textElement.atType == AtType.atAll) {
|
|
||||||
qq = 'all';
|
|
||||||
} else {
|
|
||||||
const { atNtUid, content } = textElement;
|
|
||||||
let atQQ = textElement.atUid;
|
|
||||||
if (!atQQ || atQQ === '0') {
|
|
||||||
atQQ = await NTQQUserApi.getUinByUidV2(atNtUid);
|
|
||||||
}
|
|
||||||
if (atQQ) {
|
|
||||||
qq = atQQ as `${number}`;
|
|
||||||
// name = content.replace('@', '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: OB11MessageDataType.at,
|
|
||||||
data: {
|
|
||||||
qq: qq!,
|
|
||||||
// name,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
async parseTextElement(msg: RawMessage, element: MessageElement) {
|
|
||||||
const textElement = element.textElement;
|
|
||||||
if (!textElement) return undefined;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
message_data['type'] = OB11MessageDataType.text;
|
|
||||||
|
|
||||||
let text = textElement.content;
|
|
||||||
if (!text.trim()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 兼容 9.7.x 换行符
|
|
||||||
if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) {
|
|
||||||
text = text.replace(/\r/g, '\n');
|
|
||||||
}
|
|
||||||
message_data['data']['text'] = text;
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
async parsePicElement(msg: RawMessage, element: MessageElement) {
|
|
||||||
const picElement = element.picElement;
|
|
||||||
if (!picElement) return undefined;
|
|
||||||
const NTQQFileApi = this.coreContext.apis.FileApi;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
message_data['type'] = OB11MessageDataType.image;
|
|
||||||
// message_data["data"]["file"] = element.picElement.sourcePath
|
|
||||||
message_data['data']['file'] = picElement.fileName;
|
|
||||||
message_data['data']['subType'] = picElement.picSubType;
|
|
||||||
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
|
|
||||||
// message_data["data"]["path"] = element.picElement.sourcePath
|
|
||||||
try {
|
|
||||||
message_data['data']['url'] = await NTQQFileApi.getImageUrl(picElement);
|
|
||||||
} catch (e: any) {
|
|
||||||
this.coreContext.context.logger.logError('获取图片url失败', e.stack);
|
|
||||||
}
|
|
||||||
//console.log(message_data['data']['url'])
|
|
||||||
// message_data["data"]["file_id"] = element.picElement.fileUuid
|
|
||||||
message_data['data']['file_size'] = picElement.fileSize;
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
async parseMarketFaceElement(msg: RawMessage, element: MessageElement) {
|
|
||||||
const NTQQFileApi = this.coreContext.apis.FileApi;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
message_data['type'] = OB11MessageDataType.image;
|
|
||||||
message_data['data']['file'] = 'marketface';
|
|
||||||
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
|
|
||||||
message_data['data']['path'] = element.elementId;
|
|
||||||
message_data['data']['url'] = element.elementId;
|
|
||||||
await NTQQFileApi.addFileCache(
|
|
||||||
{
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
chatType: msg.chatType,
|
|
||||||
guildId: '',
|
|
||||||
},
|
|
||||||
msg.msgId,
|
|
||||||
msg.msgSeq,
|
|
||||||
msg.senderUid,
|
|
||||||
element.elementId,
|
|
||||||
element.elementType.toString(),
|
|
||||||
'0',
|
|
||||||
'marketface'
|
|
||||||
);
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
async parseReplyElement(msg: RawMessage, element: MessageElement) {
|
|
||||||
const replyElement = element.replyElement;
|
|
||||||
if (!replyElement) return undefined;
|
|
||||||
const NTQQMsgApi = this.coreContext.apis.MsgApi;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
message_data['type'] = OB11MessageDataType.reply;
|
|
||||||
//log("收到回复消息", element.replyElement);
|
|
||||||
try {
|
|
||||||
const records = msg.records.find(msgRecord => msgRecord.msgId === replyElement?.sourceMsgIdInRecords);
|
|
||||||
const peer = {
|
|
||||||
chatType: msg.chatType,
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
guildId: '',
|
|
||||||
};
|
|
||||||
let replyMsg: RawMessage | undefined;
|
|
||||||
if (!records) throw new Error('找不到回复消息');
|
|
||||||
replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
guildId: '',
|
|
||||||
chatType: msg.chatType,
|
|
||||||
}, replyElement.replayMsgSeq, 1, true, true)).msgList.find(msg => msg.msgRandom === records.msgRandom);
|
|
||||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
|
||||||
replyMsg = (await NTQQMsgApi.getSingleMsg(peer, replyElement.replayMsgSeq)).msgList[0];
|
|
||||||
}
|
|
||||||
if (msg.peerUin == '284840486') {
|
|
||||||
//合并消息内侧 消息具体定位不到
|
|
||||||
}
|
|
||||||
if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') {
|
|
||||||
const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList;
|
|
||||||
if (replyMsgList.length < 1) {
|
|
||||||
throw new Error('回复消息消息验证失败');
|
|
||||||
}
|
|
||||||
replyMsg = replyMsgList.filter(e => e.msgSeq == records.msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0];
|
|
||||||
}
|
|
||||||
message_data['data']['id'] = MessageUnique.createMsg({
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
guildId: '',
|
|
||||||
chatType: msg.chatType,
|
|
||||||
}, replyMsg.msgId)?.toString();
|
|
||||||
//log("找到回复消息", message_data['data']['id'], replyMsg.msgList[0].msgId)
|
|
||||||
} catch (e: any) {
|
|
||||||
message_data['type'] = 'unknown' as any;
|
|
||||||
message_data['data'] = undefined;
|
|
||||||
this.coreContext.context.logger.logError('获取不到引用的消息', e.stack, replyElement.replayMsgSeq);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
async parseVideoElement(msg: RawMessage, element: MessageElement) {
|
|
||||||
const videoElement = element.videoElement;
|
|
||||||
if (!videoElement) return undefined;
|
|
||||||
const NTQQFileApi = this.coreContext.apis.FileApi;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
//读取视频链接并兜底
|
|
||||||
let videoUrl; //Array
|
|
||||||
if (msg.peerUin === '284840486') {
|
|
||||||
//合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Taged TODO
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
|
|
||||||
videoUrl = await NTQQFileApi.getVideoUrl({
|
|
||||||
chatType: msg.chatType,
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
guildId: '0',
|
|
||||||
}, msg.msgId, element.elementId);
|
|
||||||
} catch (error) {
|
|
||||||
videoUrl = undefined;
|
|
||||||
}
|
|
||||||
//读取在线URL
|
|
||||||
let videoDownUrl = undefined;
|
|
||||||
|
|
||||||
if (videoUrl) {
|
|
||||||
const videoDownUrlTemp = videoUrl.find((url) => {
|
|
||||||
return !!url.url;
|
|
||||||
});
|
|
||||||
if (videoDownUrlTemp) {
|
|
||||||
videoDownUrl = videoDownUrlTemp.url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//开始兜底
|
|
||||||
if (!videoDownUrl) {
|
|
||||||
videoDownUrl = videoElement.filePath;
|
|
||||||
}
|
|
||||||
message_data['type'] = OB11MessageDataType.video;
|
|
||||||
message_data['data']['file'] = videoElement.fileName;
|
|
||||||
message_data['data']['path'] = videoDownUrl;
|
|
||||||
message_data['data']['url'] = videoDownUrl;
|
|
||||||
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
|
|
||||||
message_data['data']['file_size'] = videoElement.fileSize;
|
|
||||||
|
|
||||||
await NTQQFileApi.addFileCache(
|
|
||||||
{
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
chatType: msg.chatType,
|
|
||||||
guildId: '',
|
|
||||||
},
|
|
||||||
msg.msgId,
|
|
||||||
msg.msgSeq,
|
|
||||||
msg.senderUid,
|
|
||||||
element.elementId,
|
|
||||||
element.elementType.toString(),
|
|
||||||
videoElement.fileSize || '0',
|
|
||||||
videoElement.fileName
|
|
||||||
);
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
async parsePTTElement(msg: RawMessage, element: MessageElement) {
|
|
||||||
const pttElement = element.pttElement;
|
|
||||||
if (!pttElement) return undefined;
|
|
||||||
const NTQQFileApi = this.coreContext.apis.FileApi;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
|
|
||||||
message_data['type'] = OB11MessageDataType.voice;
|
|
||||||
message_data['data']['file'] = pttElement.fileName;
|
|
||||||
message_data['data']['path'] = pttElement.filePath;
|
|
||||||
//message_data['data']['file_id'] = element.pttElement.fileUuid;
|
|
||||||
message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId);
|
|
||||||
message_data['data']['file_size'] = pttElement.fileSize;
|
|
||||||
await NTQQFileApi.addFileCache({
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
chatType: msg.chatType,
|
|
||||||
guildId: '',
|
|
||||||
},
|
|
||||||
msg.msgId,
|
|
||||||
msg.msgSeq,
|
|
||||||
msg.senderUid,
|
|
||||||
element.elementId,
|
|
||||||
element.elementType.toString(),
|
|
||||||
pttElement.fileSize || '0',
|
|
||||||
pttElement.fileUuid || ''
|
|
||||||
);
|
|
||||||
//以uuid作为文件名
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
async parseFaceElement(msg: RawMessage, element: MessageElement) {
|
|
||||||
const faceElement = element.faceElement;
|
|
||||||
if (!faceElement) return undefined;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
const faceId = faceElement.faceIndex;
|
|
||||||
if (faceId === FaceIndex.dice) {
|
|
||||||
message_data['type'] = OB11MessageDataType.dice;
|
|
||||||
message_data['data']['result'] = faceElement.resultId;
|
|
||||||
} else if (faceId === FaceIndex.RPS) {
|
|
||||||
message_data['type'] = OB11MessageDataType.RPS;
|
|
||||||
message_data['data']['result'] = faceElement.resultId;
|
|
||||||
} else {
|
|
||||||
message_data['type'] = OB11MessageDataType.face;
|
|
||||||
message_data['data']['id'] = faceElement.faceIndex.toString();
|
|
||||||
}
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
async parseMultForwardElement(msg: RawMessage, element: MessageElement, messagePostFormat: any) {
|
|
||||||
const NTQQMsgApi = this.coreContext.apis.MsgApi;
|
|
||||||
const faceElement = element.multiForwardMsgElement;
|
|
||||||
if (!faceElement) return undefined;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
message_data['type'] = OB11MessageDataType.forward;
|
|
||||||
message_data['data']['id'] = msg.msgId;
|
|
||||||
const ParentMsgPeer = msg.parentMsgPeer ?? {
|
|
||||||
chatType: msg.chatType,
|
|
||||||
guildId: '',
|
|
||||||
peerUid: msg.peerUid,
|
|
||||||
};
|
|
||||||
//判断是否在合并消息内
|
|
||||||
msg.parentMsgIdList = msg.parentMsgIdList ?? [];
|
|
||||||
//首次列表不存在则开始创建
|
|
||||||
msg.parentMsgIdList.push(msg.msgId);
|
|
||||||
//let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2];
|
|
||||||
//加入自身MsgId
|
|
||||||
const MultiMsgs = (await NTQQMsgApi.getMultiMsg(ParentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList;
|
|
||||||
//拉取下级消息
|
|
||||||
if (!MultiMsgs) return undefined;
|
|
||||||
//拉取失败则跳过
|
|
||||||
message_data['data']['content'] = [];
|
|
||||||
for (const MultiMsg of MultiMsgs) {
|
|
||||||
//对每条拉取的消息传递ParentMsgPeer修正Peer
|
|
||||||
MultiMsg.parentMsgPeer = ParentMsgPeer;
|
|
||||||
MultiMsg.parentMsgIdList = msg.parentMsgIdList;
|
|
||||||
MultiMsg.id = MessageUnique.createMsg(ParentMsgPeer, MultiMsg.msgId); //该ID仅用查看 无法调用
|
|
||||||
const msgList = await this.parseMessage(MultiMsg, messagePostFormat);
|
|
||||||
if (!msgList) continue;
|
|
||||||
message_data['data']['content'].push(msgList);
|
|
||||||
//console.log("合并消息", msgList);
|
|
||||||
}
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
async parseArkElement(msg: RawMessage, element: MessageElement) {
|
|
||||||
const arkElement = element.arkElement;
|
|
||||||
if (!arkElement) return undefined;
|
|
||||||
const message_data: OB11MessageData = {
|
|
||||||
data: {} as any,
|
|
||||||
type: 'unknown' as any,
|
|
||||||
};
|
|
||||||
message_data['type'] = OB11MessageDataType.json;
|
|
||||||
message_data['data']['data'] = arkElement.bytesData;
|
|
||||||
return message_data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ export interface OB11MessageImage extends OB11MessageFileBase {
|
|||||||
type: OB11MessageDataType.image
|
type: OB11MessageDataType.image
|
||||||
data: OB11MessageFileBase['data'] & {
|
data: OB11MessageFileBase['data'] & {
|
||||||
summary?: string; // 图片摘要
|
summary?: string; // 图片摘要
|
||||||
subType?: PicSubType
|
sub_type?: PicSubType
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ export interface OB11MessageVideo extends OB11MessageFileBase {
|
|||||||
export interface OB11MessageAt {
|
export interface OB11MessageAt {
|
||||||
type: OB11MessageDataType.at;
|
type: OB11MessageDataType.at;
|
||||||
data: {
|
data: {
|
||||||
qq: `${number}` | 'all'
|
qq: string, // `${number}` | 'all'
|
||||||
name?: string
|
name?: string
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -162,14 +162,14 @@ export interface OB11MessageJson {
|
|||||||
export interface OB11MessageDice {
|
export interface OB11MessageDice {
|
||||||
type: OB11MessageDataType.dice,
|
type: OB11MessageDataType.dice,
|
||||||
data: {
|
data: {
|
||||||
result: number
|
result: number /* intended */ | string /* in fact */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OB11MessageRPS {
|
export interface OB11MessageRPS {
|
||||||
type: OB11MessageDataType.RPS,
|
type: OB11MessageDataType.RPS,
|
||||||
data: {
|
data: {
|
||||||
result: number
|
result: number | string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user