mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Merge pull request #765 from NapNeko/fix/multi-forward-protocol-fetch
fix: #721
This commit is contained in:
@@ -66,7 +66,7 @@ export abstract class ConfigBase<T> {
|
||||
|
||||
private handleError(e: unknown, message: string): void {
|
||||
if (e instanceof SyntaxError) {
|
||||
this.core.context.logger.logError(`[Core] [Config] 操作配置文件格式错误,请检查配置文件:`, e.message);
|
||||
this.core.context.logger.logError('[Core] [Config] 操作配置文件格式错误,请检查配置文件:', e.message);
|
||||
} else {
|
||||
this.core.context.logger.logError(`[Core] [Config] ${message}:`, (e as Error).message);
|
||||
}
|
||||
|
@@ -43,7 +43,7 @@ export class NTQQFileApi {
|
||||
this.rkeyManager = new RkeyManager([
|
||||
'https://rkey.napneko.icu/rkeys'
|
||||
],
|
||||
this.context.logger
|
||||
this.context.logger
|
||||
);
|
||||
}
|
||||
|
||||
@@ -300,18 +300,18 @@ export class NTQQFileApi {
|
||||
element.elementType === ElementType.FILE
|
||||
) {
|
||||
switch (element.elementType) {
|
||||
case ElementType.PIC:
|
||||
case ElementType.PIC:
|
||||
element.picElement!.sourcePath = elementResults?.[elementIndex] ?? '';
|
||||
break;
|
||||
case ElementType.VIDEO:
|
||||
break;
|
||||
case ElementType.VIDEO:
|
||||
element.videoElement!.filePath = elementResults?.[elementIndex] ?? '';
|
||||
break;
|
||||
case ElementType.PTT:
|
||||
break;
|
||||
case ElementType.PTT:
|
||||
element.pttElement!.filePath = elementResults?.[elementIndex] ?? '';
|
||||
break;
|
||||
case ElementType.FILE:
|
||||
break;
|
||||
case ElementType.FILE:
|
||||
element.fileElement!.filePath = elementResults?.[elementIndex] ?? '';
|
||||
break;
|
||||
break;
|
||||
}
|
||||
elementIndex++;
|
||||
}
|
||||
|
@@ -1,20 +1,22 @@
|
||||
import * as crypto from 'crypto';
|
||||
import { PacketContext } from '@/core/packet/context/packetContext';
|
||||
import {PacketContext} from '@/core/packet/context/packetContext';
|
||||
import * as trans from '@/core/packet/transformer';
|
||||
import { PacketMsg } from '@/core/packet/message/message';
|
||||
import {PacketMsg} from '@/core/packet/message/message';
|
||||
import {
|
||||
PacketMsgFileElement,
|
||||
PacketMsgPicElement,
|
||||
PacketMsgPttElement,
|
||||
PacketMsgVideoElement
|
||||
} from '@/core/packet/message/element';
|
||||
import { ChatType } from '@/core';
|
||||
import { MiniAppRawData, MiniAppReqParams } from '@/core/packet/entities/miniApp';
|
||||
import { AIVoiceChatType } from '@/core/packet/entities/aiChat';
|
||||
import { NapProtoDecodeStructType, NapProtoEncodeStructType } from '@napneko/nap-proto-core';
|
||||
import { IndexNode, MsgInfo } from '@/core/packet/transformer/proto';
|
||||
import { OidbPacket } from '@/core/packet/transformer/base';
|
||||
import { ImageOcrResult } from '@/core/packet/entities/ocrResult';
|
||||
import {ChatType, MsgSourceType, NTMsgType, RawMessage} from '@/core';
|
||||
import {MiniAppRawData, MiniAppReqParams} from '@/core/packet/entities/miniApp';
|
||||
import {AIVoiceChatType} from '@/core/packet/entities/aiChat';
|
||||
import {NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg} from '@napneko/nap-proto-core';
|
||||
import {IndexNode, LongMsgResult, MsgInfo} from '@/core/packet/transformer/proto';
|
||||
import {OidbPacket} from '@/core/packet/transformer/base';
|
||||
import {ImageOcrResult} from '@/core/packet/entities/ocrResult';
|
||||
import {gunzipSync} from 'zlib';
|
||||
import {PacketMsgConverter} from '@/core/packet/message/converter';
|
||||
|
||||
export class PacketOperationContext {
|
||||
private readonly context: PacketContext;
|
||||
@@ -57,10 +59,10 @@ export class PacketOperationContext {
|
||||
const res = trans.GetStrangerInfo.parse(resp);
|
||||
const extBigInt = BigInt(res.data.status.value);
|
||||
if (extBigInt <= 10n) {
|
||||
return { status: Number(extBigInt) * 10, ext_status: 0 };
|
||||
return {status: Number(extBigInt) * 10, ext_status: 0};
|
||||
}
|
||||
status = Number((extBigInt & 0xff00n) + ((extBigInt >> 16n) & 0xffn));
|
||||
return { status: 10, ext_status: status };
|
||||
return {status: 10, ext_status: status};
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
@@ -77,13 +79,13 @@ export class PacketOperationContext {
|
||||
const reqList = msg.flatMap(m =>
|
||||
m.msg.map(e => {
|
||||
if (e instanceof PacketMsgPicElement) {
|
||||
return this.context.highway.uploadImage({ chatType, peerUid }, e);
|
||||
return this.context.highway.uploadImage({chatType, peerUid}, e);
|
||||
} else if (e instanceof PacketMsgVideoElement) {
|
||||
return this.context.highway.uploadVideo({ chatType, peerUid }, e);
|
||||
return this.context.highway.uploadVideo({chatType, peerUid}, e);
|
||||
} else if (e instanceof PacketMsgPttElement) {
|
||||
return this.context.highway.uploadPtt({ chatType, peerUid }, e);
|
||||
return this.context.highway.uploadPtt({chatType, peerUid}, e);
|
||||
} else if (e instanceof PacketMsgFileElement) {
|
||||
return this.context.highway.uploadFile({ chatType, peerUid }, e);
|
||||
return this.context.highway.uploadFile({chatType, peerUid}, e);
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean)
|
||||
@@ -116,6 +118,13 @@ export class PacketOperationContext {
|
||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||
}
|
||||
|
||||
async GetGroupImageUrl(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||
const req = trans.DownloadGroupImage.build(groupUin, node);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.DownloadImage.parse(resp);
|
||||
return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`;
|
||||
}
|
||||
|
||||
async ImageOCR(imgUrl: string) {
|
||||
const req = trans.ImageOCR.build(imgUrl);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
@@ -195,4 +204,74 @@ export class PacketOperationContext {
|
||||
return res.msgInfo;
|
||||
}
|
||||
}
|
||||
|
||||
async FetchForwardMsg(res_id: string): Promise<RawMessage[]> {
|
||||
const req = trans.DownloadForwardMsg.build(this.context.napcore.basicInfo.uid, res_id);
|
||||
const resp = await this.context.client.sendOidbPacket(req, true);
|
||||
const res = trans.DownloadForwardMsg.parse(resp);
|
||||
const inflate = gunzipSync(res.result.payload);
|
||||
const result = new NapProtoMsg(LongMsgResult).decode(inflate);
|
||||
|
||||
const main = result.action.find((r) => r.actionCommand === 'MultiMsg');
|
||||
if (!main?.actionData.msgBody) {
|
||||
throw new Error('msgBody is empty');
|
||||
}
|
||||
|
||||
const messagesPromises = main.actionData.msgBody.map(async (msg) => {
|
||||
if (!msg?.body?.richText?.elems) {
|
||||
throw new Error('msg.body.richText.elems is empty');
|
||||
}
|
||||
const rawChains = new PacketMsgConverter().packetMsgToRaw(msg?.body?.richText?.elems);
|
||||
const elements = await Promise.all(
|
||||
rawChains.map(async ([element, rawElem]) => {
|
||||
if (element.picElement && rawElem?.commonElem?.pbElem) {
|
||||
const extra = new NapProtoMsg(MsgInfo).decode(rawElem.commonElem.pbElem);
|
||||
const index = extra?.msgInfoBody[0]?.index;
|
||||
if (msg?.responseHead.grp !== undefined) {
|
||||
const groupUin = msg?.responseHead.grp?.groupUin ?? 0;
|
||||
element.picElement = {
|
||||
...element.picElement,
|
||||
originImageUrl: await this.GetGroupImageUrl(groupUin, index!)
|
||||
};
|
||||
} else {
|
||||
element.picElement = {
|
||||
...element.picElement,
|
||||
originImageUrl: await this.GetImageUrl(this.context.napcore.basicInfo.uid, index!)
|
||||
};
|
||||
}
|
||||
return element;
|
||||
}
|
||||
return element;
|
||||
})
|
||||
);
|
||||
return {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
elements: elements,
|
||||
guildId: '',
|
||||
isOnlineMsg: false,
|
||||
msgId: '7467703692092974645', // TODO: no necessary
|
||||
msgRandom: '0',
|
||||
msgSeq: String(msg.contentHead.sequence ?? 0),
|
||||
msgTime: String(msg.contentHead.timeStamp ?? 0),
|
||||
msgType: NTMsgType.KMSGTYPEMIX,
|
||||
parentMsgIdList: [],
|
||||
parentMsgPeer: {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: String(msg?.responseHead.grp?.groupUin ?? 0),
|
||||
},
|
||||
peerName: '',
|
||||
peerUid: '1094950020',
|
||||
peerUin: '1094950020',
|
||||
recallTime: '0',
|
||||
records: [],
|
||||
sendNickName: msg?.responseHead.grp?.memberName ?? '',
|
||||
sendRemarkName: msg?.responseHead.grp?.memberName ?? '',
|
||||
senderUid: '',
|
||||
senderUin: '1094950020',
|
||||
sourceType: MsgSourceType.K_DOWN_SOURCETYPE_UNKNOWN,
|
||||
subMsgType: 1,
|
||||
};
|
||||
});
|
||||
return await Promise.all(messagesPromises);
|
||||
}
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import {
|
||||
Peer,
|
||||
ChatType,
|
||||
ElementType,
|
||||
MessageElement,
|
||||
Peer,
|
||||
RawMessage,
|
||||
SendArkElement,
|
||||
SendFaceElement,
|
||||
@@ -31,7 +31,9 @@ import {
|
||||
PacketMsgVideoElement,
|
||||
PacketMultiMsgElement
|
||||
} from '@/core/packet/message/element';
|
||||
import { PacketMsg, PacketSendMsgElement } from '@/core/packet/message/message';
|
||||
import {PacketMsg, PacketSendMsgElement} from '@/core/packet/message/message';
|
||||
import {NapProtoDecodeStructType} from '@napneko/nap-proto-core';
|
||||
import {Elem} from '@/core/packet/transformer/proto';
|
||||
|
||||
const SupportedElementTypes = [
|
||||
ElementType.TEXT,
|
||||
@@ -154,4 +156,16 @@ export class PacketMsgConverter {
|
||||
}).filter((e) => e !== null)
|
||||
};
|
||||
}
|
||||
|
||||
packetMsgToRaw(msg: NapProtoDecodeStructType<typeof Elem>[]): [MessageElement, NapProtoDecodeStructType<typeof Elem> | null][] {
|
||||
const converters = [PacketMsgTextElement.parseElement,
|
||||
PacketMsgAtElement.parseElement, PacketMsgReplyElement.parseElement, PacketMsgPicElement.parseElement];
|
||||
return msg.map((element) => {
|
||||
for (const converter of converters) {
|
||||
const result = converter(element);
|
||||
if (result) return result;
|
||||
}
|
||||
return null;
|
||||
}).filter((e) => e !== null);
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +1,22 @@
|
||||
import * as zlib from 'node:zlib';
|
||||
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import {NapProtoDecodeStructType, NapProtoEncodeStructType, NapProtoMsg} from '@napneko/nap-proto-core';
|
||||
import {
|
||||
CustomFace,
|
||||
Elem,
|
||||
FileExtra,
|
||||
GroupFileExtra,
|
||||
MarkdownData,
|
||||
MentionExtra,
|
||||
MsgInfo,
|
||||
NotOnlineImage,
|
||||
OidbSvcTrpcTcp0XE37_800Response,
|
||||
QBigFaceExtra,
|
||||
QSmallFaceExtra,
|
||||
MsgInfo,
|
||||
OidbSvcTrpcTcp0XE37_800Response,
|
||||
FileExtra,
|
||||
GroupFileExtra
|
||||
} from '@/core/packet/transformer/proto';
|
||||
import {
|
||||
ElementType,
|
||||
FaceType,
|
||||
MessageElement,
|
||||
NTMsgAtType,
|
||||
PicType,
|
||||
SendArkElement,
|
||||
@@ -29,8 +31,11 @@ import {
|
||||
SendTextElement,
|
||||
SendVideoElement
|
||||
} from '@/core';
|
||||
import { ForwardMsgBuilder } from '@/common/forward-msg-builder';
|
||||
import { PacketMsg, PacketSendMsgElement } from '@/core/packet/message/message';
|
||||
import {ForwardMsgBuilder} from '@/common/forward-msg-builder';
|
||||
import {PacketMsg, PacketSendMsgElement} from '@/core/packet/message/message';
|
||||
|
||||
export type ParseElementFnR = [MessageElement, NapProtoDecodeStructType<typeof Elem> | null] | undefined;
|
||||
type ParseElementFn = (elem: NapProtoDecodeStructType<typeof Elem>) => ParseElementFnR;
|
||||
|
||||
// raw <-> packet
|
||||
// TODO: SendStructLongMsgElement
|
||||
@@ -51,6 +56,8 @@ export abstract class IPacketMsgElement<T extends PacketSendMsgElement> {
|
||||
return [];
|
||||
}
|
||||
|
||||
static parseElement: ParseElementFn;
|
||||
|
||||
toPreview(): string {
|
||||
return '[暂不支持该消息类型喵~]';
|
||||
}
|
||||
@@ -72,11 +79,30 @@ export class PacketMsgTextElement extends IPacketMsgElement<SendTextElement> {
|
||||
}];
|
||||
}
|
||||
|
||||
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||
if (elem.text?.str && (elem.text?.attr6Buf === undefined || elem.text?.attr6Buf?.length === 0)) {
|
||||
return [{
|
||||
textElement: {
|
||||
content: elem.text?.str,
|
||||
atType: NTMsgAtType.ATTYPEUNKNOWN,
|
||||
atUid: '',
|
||||
atTinyId: '',
|
||||
atNtUid: '',
|
||||
},
|
||||
elementType: ElementType.UNKNOWN,
|
||||
elementId: '',
|
||||
}, null];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
override toPreview(): string {
|
||||
return this.text;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class PacketMsgAtElement extends PacketMsgTextElement {
|
||||
targetUid: string;
|
||||
atAll: boolean;
|
||||
@@ -101,6 +127,22 @@ export class PacketMsgAtElement extends PacketMsgTextElement {
|
||||
}
|
||||
}];
|
||||
}
|
||||
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||
if (elem.text?.str && (elem.text?.attr6Buf?.length ?? 100) >= 11) {
|
||||
return [{
|
||||
textElement: {
|
||||
content: elem.text?.str,
|
||||
atType: NTMsgAtType.ATTYPEONE,
|
||||
atUid: String(Buffer.from(elem.text!.attr6Buf!).readUInt32BE(7)), // FIXME: hack
|
||||
atTinyId: '',
|
||||
atNtUid: '',
|
||||
},
|
||||
elementType: ElementType.UNKNOWN,
|
||||
elementId: '',
|
||||
}, null];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
|
||||
@@ -143,6 +185,22 @@ export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
|
||||
}];
|
||||
}
|
||||
|
||||
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||
if (elem.srcMsg && elem.srcMsg.pbReserve) {
|
||||
const reserve = elem.srcMsg.pbReserve;
|
||||
return [{
|
||||
replyElement: {
|
||||
replayMsgSeq: String(reserve.friendSeq ?? elem.srcMsg?.origSeqs?.[0] ?? 0),
|
||||
replayMsgId: String(reserve.messageId ?? 0),
|
||||
senderUin: String(elem?.srcMsg ?? 0)
|
||||
},
|
||||
elementType: ElementType.UNKNOWN,
|
||||
elementId: '',
|
||||
}, null];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
override toPreview(): string {
|
||||
return '[回复消息]';
|
||||
}
|
||||
@@ -198,6 +256,46 @@ export class PacketMsgFaceElement extends IPacketMsgElement<SendFaceElement> {
|
||||
}
|
||||
}
|
||||
|
||||
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||
if (elem.face?.index) {
|
||||
return [{
|
||||
faceElement: {
|
||||
faceIndex: elem.face.index,
|
||||
faceType: FaceType.Normal
|
||||
},
|
||||
elementType: ElementType.UNKNOWN,
|
||||
elementId: '',
|
||||
}, null];
|
||||
}
|
||||
if (elem?.commonElem?.serviceType === 37 && elem?.commonElem?.pbElem) {
|
||||
const qface = new NapProtoMsg(QBigFaceExtra).decode(elem?.commonElem?.pbElem);
|
||||
if (qface?.faceId) {
|
||||
return [{
|
||||
faceElement: {
|
||||
faceIndex: qface.faceId,
|
||||
faceType: FaceType.Normal
|
||||
},
|
||||
elementType: ElementType.UNKNOWN,
|
||||
elementId: '',
|
||||
}, null];
|
||||
}
|
||||
}
|
||||
if (elem?.commonElem?.serviceType === 33 && elem?.commonElem?.pbElem) {
|
||||
const qface = new NapProtoMsg(QSmallFaceExtra).decode(elem?.commonElem?.pbElem);
|
||||
if (qface?.faceId) {
|
||||
return [{
|
||||
faceElement: {
|
||||
faceIndex: qface.faceId,
|
||||
faceType: FaceType.Normal
|
||||
},
|
||||
elementType: ElementType.UNKNOWN,
|
||||
elementId: '',
|
||||
}, null];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
override toPreview(): string {
|
||||
return '[表情]';
|
||||
}
|
||||
@@ -286,6 +384,60 @@ export class PacketMsgPicElement extends IPacketMsgElement<SendPicElement> {
|
||||
}];
|
||||
}
|
||||
|
||||
static override parseElement = (elem: NapProtoDecodeStructType<typeof Elem>): ParseElementFnR => {
|
||||
if (elem?.commonElem?.serviceType === 48 || [10, 20].includes(elem?.commonElem?.businessType ?? 0)) {
|
||||
const extra = new NapProtoMsg(MsgInfo).decode(elem.commonElem!.pbElem!);
|
||||
const msgInfoBody = extra.msgInfoBody[0];
|
||||
const index = msgInfoBody?.index;
|
||||
return [{
|
||||
picElement: {
|
||||
fileSize: index?.info.fileSize ?? 0,
|
||||
picWidth: index?.info?.width ?? 0,
|
||||
picHeight: index?.info?.height ?? 0,
|
||||
fileName: index?.info?.fileHash ?? '',
|
||||
sourcePath: '',
|
||||
original: false,
|
||||
picType: PicType.NEWPIC_APNG,
|
||||
fileUuid: '',
|
||||
fileSubId: '',
|
||||
thumbFileSize: 0,
|
||||
summary: '[图片]',
|
||||
thumbPath: new Map(),
|
||||
},
|
||||
elementType: ElementType.UNKNOWN,
|
||||
elementId: '',
|
||||
}, elem];
|
||||
}
|
||||
if (elem?.notOnlineImage) {
|
||||
const img = elem?.notOnlineImage; // url in originImageUrl
|
||||
const preImg: MessageElement = {
|
||||
picElement: {
|
||||
fileSize: img.fileLen ?? 0,
|
||||
picWidth: img.picWidth ?? 0,
|
||||
picHeight: img.picHeight ?? 0,
|
||||
fileName: Buffer.from(img.picMd5!).toString('hex') ?? '',
|
||||
sourcePath: '',
|
||||
original: false,
|
||||
picType: PicType.NEWPIC_APNG,
|
||||
fileUuid: '',
|
||||
fileSubId: '',
|
||||
thumbFileSize: 0,
|
||||
summary: '[图片]',
|
||||
thumbPath: new Map(),
|
||||
},
|
||||
elementType: ElementType.UNKNOWN,
|
||||
elementId: '',
|
||||
};
|
||||
if (img.origUrl?.includes('&fileid=')) {
|
||||
preImg.picElement!.originImageUrl = `https://multimedia.nt.qq.com.cn${img.origUrl}`;
|
||||
} else {
|
||||
preImg.picElement!.originImageUrl = `https://gchat.qpic.cn${img.origUrl}`;
|
||||
}
|
||||
return [preImg, elem];
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
override toPreview(): string {
|
||||
return this.summary;
|
||||
}
|
||||
|
50
src/core/packet/transformer/highway/DownloadGroupImage.ts
Normal file
50
src/core/packet/transformer/highway/DownloadGroupImage.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import * as proto from '@/core/packet/transformer/proto';
|
||||
import { NapProtoEncodeStructType, NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { OidbPacket, PacketTransformer } from '@/core/packet/transformer/base';
|
||||
import OidbBase from '@/core/packet/transformer/oidb/oidbBase';
|
||||
import { IndexNode } from '@/core/packet/transformer/proto';
|
||||
|
||||
class DownloadGroupImage extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
build(group_uin: number, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
|
||||
const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({
|
||||
reqHead: {
|
||||
common: {
|
||||
requestId: 1,
|
||||
command: 200
|
||||
},
|
||||
scene: {
|
||||
requestType: 2,
|
||||
businessType: 1,
|
||||
sceneType: 2,
|
||||
group: {
|
||||
groupUin: group_uin
|
||||
}
|
||||
},
|
||||
client: {
|
||||
agentType: 2,
|
||||
}
|
||||
},
|
||||
download: {
|
||||
node: node,
|
||||
download: {
|
||||
video: {
|
||||
busiType: 0,
|
||||
sceneType: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return OidbBase.build(0x11C4, 200, body, true, false);
|
||||
}
|
||||
|
||||
parse(data: Buffer) {
|
||||
const oidbBody = OidbBase.parse(data).body;
|
||||
return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody);
|
||||
}
|
||||
}
|
||||
|
||||
export default new DownloadGroupImage();
|
@@ -14,7 +14,7 @@ class DownloadImage extends PacketTransformer<typeof proto.NTV2RichMediaResp> {
|
||||
reqHead: {
|
||||
common: {
|
||||
requestId: 1,
|
||||
command: 100
|
||||
command: 200
|
||||
},
|
||||
scene: {
|
||||
requestType: 2,
|
||||
|
@@ -12,3 +12,4 @@ export { default as UploadPrivateImage } from './UploadPrivateImage';
|
||||
export { default as UploadPrivatePtt } from './UploadPrivatePtt';
|
||||
export { default as UploadPrivateVideo } from './UploadPrivateVideo';
|
||||
export { default as DownloadImage } from './DownloadImage';
|
||||
export { default as DownloadGroupImage } from './DownloadGroupImage';
|
||||
|
37
src/core/packet/transformer/message/DownloadForwardMsg.ts
Normal file
37
src/core/packet/transformer/message/DownloadForwardMsg.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as proto from '@/core/packet/transformer/proto';
|
||||
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { OidbPacket, PacketHexStrBuilder, PacketTransformer } from '@/core/packet/transformer/base';
|
||||
|
||||
class DownloadForwardMsg extends PacketTransformer<typeof proto.RecvLongMsgResp> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
build(uid: string, resId: string): OidbPacket {
|
||||
const req = new NapProtoMsg(proto.RecvLongMsgReq).encode({
|
||||
info: {
|
||||
uid: {
|
||||
uid: uid
|
||||
},
|
||||
resId: resId,
|
||||
acquire: true
|
||||
},
|
||||
settings: {
|
||||
field1: 2,
|
||||
field2: 0,
|
||||
field3: 0,
|
||||
field4: 0
|
||||
}
|
||||
});
|
||||
return {
|
||||
cmd: 'trpc.group.long_msg_interface.MsgService.SsoRecvLongMsg',
|
||||
data: PacketHexStrBuilder(req)
|
||||
};
|
||||
}
|
||||
|
||||
parse(data: Buffer) {
|
||||
return new NapProtoMsg(proto.RecvLongMsgResp).decode(data);
|
||||
}
|
||||
}
|
||||
|
||||
export default new DownloadForwardMsg();
|
@@ -1 +1,2 @@
|
||||
export { default as UploadForwardMsg } from './UploadForwardMsg';
|
||||
export { default as DownloadForwardMsg } from './DownloadForwardMsg';
|
@@ -2,7 +2,7 @@ import { ProtoField, ScalarType } from '@napneko/nap-proto-core';
|
||||
import { PushMsgBody } from '@/core/packet/transformer/proto';
|
||||
|
||||
export const LongMsgResult = {
|
||||
action: ProtoField(2, () => LongMsgAction)
|
||||
action: ProtoField(2, () => LongMsgAction, false, true)
|
||||
};
|
||||
|
||||
export const LongMsgAction = {
|
||||
|
@@ -3,6 +3,7 @@ import { OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageForward,
|
||||
import { ActionName } from '@/onebot/action/router';
|
||||
import { MessageUnique } from '@/common/message-unique';
|
||||
import { Static, Type } from '@sinclair/typebox';
|
||||
import { ChatType, ElementType, MsgSourceType, NTMsgType, RawMessage } from '@/core';
|
||||
|
||||
const SchemaData = Type.Object({
|
||||
message_id: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
||||
@@ -57,24 +58,72 @@ export class GoCQHTTPGetForwardMsgAction extends OneBotAction<Payload, {
|
||||
throw new Error('message_id is required');
|
||||
}
|
||||
|
||||
const fakeForwardMsg = (res_id: string) => {
|
||||
return {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
elements: [{
|
||||
elementType: ElementType.MULTIFORWARD,
|
||||
elementId: '',
|
||||
multiForwardMsgElement: {
|
||||
resId: res_id,
|
||||
fileName: '',
|
||||
xmlContent: '',
|
||||
}
|
||||
}],
|
||||
guildId: '',
|
||||
isOnlineMsg: false,
|
||||
msgId: '', // TODO: no necessary
|
||||
msgRandom: '0',
|
||||
msgSeq: '',
|
||||
msgTime: '',
|
||||
msgType: NTMsgType.KMSGTYPEMIX,
|
||||
parentMsgIdList: [],
|
||||
parentMsgPeer: {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: '',
|
||||
},
|
||||
peerName: '',
|
||||
peerUid: '284840486',
|
||||
peerUin: '284840486',
|
||||
recallTime: '0',
|
||||
records: [],
|
||||
sendNickName: '',
|
||||
sendRemarkName: '',
|
||||
senderUid: '',
|
||||
senderUin: '1094950020',
|
||||
sourceType: MsgSourceType.K_DOWN_SOURCETYPE_UNKNOWN,
|
||||
subMsgType: 1,
|
||||
} as RawMessage;
|
||||
};
|
||||
|
||||
const protocolFallbackLogic = async (res_id: string) => {
|
||||
const ob = (await this.obContext.apis.MsgApi.parseMessageV2(fakeForwardMsg(res_id)))?.arrayMsg;
|
||||
if (ob) {
|
||||
return {
|
||||
messages: (ob?.message?.[0] as OB11MessageForward)?.data?.content
|
||||
};
|
||||
}
|
||||
throw new Error('protocolFallbackLogic: 找不到相关的聊天记录');
|
||||
};
|
||||
|
||||
const rootMsgId = MessageUnique.getShortIdByMsgId(msgId.toString());
|
||||
const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId ?? +msgId);
|
||||
if (!rootMsg) {
|
||||
throw new Error('msg not found');
|
||||
return await protocolFallbackLogic(msgId.toString());
|
||||
}
|
||||
const data = await this.core.apis.MsgApi.getMsgsByMsgId(rootMsg.Peer, [rootMsg.MsgId]);
|
||||
|
||||
if (!data || data.result !== 0) {
|
||||
throw new Error('找不到相关的聊天记录' + data?.errMsg);
|
||||
return await protocolFallbackLogic(msgId.toString());
|
||||
}
|
||||
|
||||
const singleMsg = data.msgList[0];
|
||||
if (!singleMsg) {
|
||||
throw new Error('找不到相关的聊天记录');
|
||||
return await protocolFallbackLogic(msgId.toString());
|
||||
}
|
||||
const resMsg = (await this.obContext.apis.MsgApi.parseMessageV2(singleMsg))?.arrayMsg;//强制array 以便处理
|
||||
if (!(resMsg?.message?.[0] as OB11MessageForward)?.data?.content) {
|
||||
throw new Error('找不到相关的聊天记录');
|
||||
return await protocolFallbackLogic(msgId.toString());
|
||||
}
|
||||
return {
|
||||
messages: (resMsg?.message?.[0] as OB11MessageForward)?.data?.content
|
||||
|
@@ -1,41 +1,50 @@
|
||||
import { FileNapCatOneBotUUID } from '@/common/file-uuid';
|
||||
import { MessageUnique } from '@/common/message-unique';
|
||||
import {FileNapCatOneBotUUID} from '@/common/file-uuid';
|
||||
import {MessageUnique} from '@/common/message-unique';
|
||||
import {
|
||||
NTMsgAtType,
|
||||
ChatType,
|
||||
CustomMusicSignPostData,
|
||||
ElementType,
|
||||
FaceIndex,
|
||||
FaceType,
|
||||
GrayTipElement,
|
||||
GroupNotify,
|
||||
IdMusicSignPostData,
|
||||
MessageElement,
|
||||
NapCatCore,
|
||||
NTGrayTipElementSubTypeV2,
|
||||
NTMsgAtType,
|
||||
Peer,
|
||||
RawMessage,
|
||||
SendMessageElement,
|
||||
SendTextElement,
|
||||
FaceType,
|
||||
GrayTipElement,
|
||||
GroupNotify,
|
||||
} from '@/core';
|
||||
import faceConfig from '@/core/external/face_config.json';
|
||||
import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageFileBase, OB11MessageForward, OB11MessageImage, OB11MessageVideo, } from '@/onebot';
|
||||
import { OB11Construct } from '@/onebot/helper/data';
|
||||
import { EventType } from '@/onebot/event/OneBotEvent';
|
||||
import { encodeCQCode } from '@/onebot/helper/cqcode';
|
||||
import { uriToLocalFile } from '@/common/file';
|
||||
import { RequestUtil } from '@/common/request';
|
||||
import fsPromise, { constants } from 'node:fs/promises';
|
||||
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
|
||||
import { ForwardMsgBuilder } from '@/common/forward-msg-builder';
|
||||
import { NapProtoMsg } from '@napneko/nap-proto-core';
|
||||
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
|
||||
import { OB11GroupDecreaseEvent, GroupDecreaseSubType } from '../event/notice/OB11GroupDecreaseEvent';
|
||||
import { GroupAdmin } from '@/core/packet/transformer/proto/message/groupAdmin';
|
||||
import { OB11GroupAdminNoticeEvent } from '../event/notice/OB11GroupAdminNoticeEvent';
|
||||
import { GroupChange, GroupChangeInfo, GroupInvite, PushMsgBody } from '@/core/packet/transformer/proto';
|
||||
import { OB11GroupRequestEvent } from '../event/request/OB11GroupRequest';
|
||||
import { LRUCache } from '@/common/lru-cache';
|
||||
import {
|
||||
NapCatOneBot11Adapter,
|
||||
OB11Message,
|
||||
OB11MessageData,
|
||||
OB11MessageDataType,
|
||||
OB11MessageFileBase,
|
||||
OB11MessageForward,
|
||||
OB11MessageImage,
|
||||
OB11MessageVideo,
|
||||
} from '@/onebot';
|
||||
import {OB11Construct} from '@/onebot/helper/data';
|
||||
import {EventType} from '@/onebot/event/OneBotEvent';
|
||||
import {encodeCQCode} from '@/onebot/helper/cqcode';
|
||||
import {uriToLocalFile} from '@/common/file';
|
||||
import {RequestUtil} from '@/common/request';
|
||||
import fsPromise, {constants} from 'node:fs/promises';
|
||||
import {OB11FriendAddNoticeEvent} from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
|
||||
import {ForwardMsgBuilder} from '@/common/forward-msg-builder';
|
||||
import {NapProtoMsg} from '@napneko/nap-proto-core';
|
||||
import {OB11GroupIncreaseEvent} from '../event/notice/OB11GroupIncreaseEvent';
|
||||
import {GroupDecreaseSubType, OB11GroupDecreaseEvent} from '../event/notice/OB11GroupDecreaseEvent';
|
||||
import {GroupAdmin} from '@/core/packet/transformer/proto/message/groupAdmin';
|
||||
import {OB11GroupAdminNoticeEvent} from '../event/notice/OB11GroupAdminNoticeEvent';
|
||||
import {GroupChange, GroupChangeInfo, GroupInvite, PushMsgBody} from '@/core/packet/transformer/proto';
|
||||
import {OB11GroupRequestEvent} from '../event/request/OB11GroupRequest';
|
||||
import {LRUCache} from '@/common/lru-cache';
|
||||
|
||||
type RawToOb11Converters = {
|
||||
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
|
||||
@@ -84,12 +93,12 @@ export class OneBotMsgApi {
|
||||
}
|
||||
return {
|
||||
type: OB11MessageDataType.text,
|
||||
data: { text },
|
||||
data: {text},
|
||||
};
|
||||
} else {
|
||||
let qq: string = 'all';
|
||||
if (element.atType !== NTMsgAtType.ATTYPEALL) {
|
||||
const { atNtUid, atUid } = element;
|
||||
const {atNtUid, atUid} = element;
|
||||
qq = !atUid || atUid === '0' ? await this.core.apis.UserApi.getUinByUidV2(atNtUid) : atUid;
|
||||
}
|
||||
return {
|
||||
@@ -197,7 +206,7 @@ export class OneBotMsgApi {
|
||||
peerUid: msg.peerUid,
|
||||
guildId: '',
|
||||
};
|
||||
const { emojiId } = _;
|
||||
const {emojiId} = _;
|
||||
const dir = emojiId.substring(0, 2);
|
||||
const url = `https://gxh.vip.qq.com/club/item/parcel/item/${dir}/${emojiId}/raw300.gif`;
|
||||
const filename = `${dir}-${emojiId}.gif`;
|
||||
@@ -264,7 +273,6 @@ export class OneBotMsgApi {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 丢弃该消息段
|
||||
if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) {
|
||||
this.core.context.logger.logError(
|
||||
@@ -355,18 +363,25 @@ export class OneBotMsgApi {
|
||||
};
|
||||
},
|
||||
|
||||
multiForwardMsgElement: async (_, msg, _wrapper, context) => {
|
||||
multiForwardMsgElement: async (element, msg, _wrapper, context) => {
|
||||
const parentMsgPeer = msg.parentMsgPeer ?? {
|
||||
chatType: msg.chatType,
|
||||
guildId: '',
|
||||
peerUid: msg.peerUid,
|
||||
};
|
||||
const multiMsgs = await this.getMultiMessages(msg, parentMsgPeer);
|
||||
let multiMsgs = await this.getMultiMessages(msg, parentMsgPeer);
|
||||
// 拉取失败则跳过
|
||||
if (!multiMsgs) return null;
|
||||
if (!multiMsgs || multiMsgs.length === 0) {
|
||||
try {
|
||||
multiMsgs = await this.core.apis.PacketApi.pkt.operation.FetchForwardMsg(element.resId);
|
||||
} catch (e) {
|
||||
this.core.context.logger.logError('Protocol FetchForwardMsg fallback failed!', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
const forward: OB11MessageForward = {
|
||||
type: OB11MessageDataType.forward,
|
||||
data: { id: msg.msgId }
|
||||
data: {id: msg.msgId}
|
||||
};
|
||||
if (!context.parseMultMsg) return forward;
|
||||
forward.data.content = await this.parseMultiMessageContent(
|
||||
@@ -397,7 +412,7 @@ export class OneBotMsgApi {
|
||||
};
|
||||
|
||||
ob11ToRawConverters: Ob11ToRawConverters = {
|
||||
[OB11MessageDataType.text]: async ({ data: { text } }) => ({
|
||||
[OB11MessageDataType.text]: async ({data: {text}}) => ({
|
||||
elementType: ElementType.TEXT,
|
||||
elementId: '',
|
||||
textElement: {
|
||||
@@ -409,7 +424,7 @@ export class OneBotMsgApi {
|
||||
},
|
||||
}),
|
||||
|
||||
[OB11MessageDataType.at]: async ({ data: { qq: atQQ } }, context) => {
|
||||
[OB11MessageDataType.at]: async ({data: {qq: atQQ}}, context) => {
|
||||
function at(atUid: string, atNtUid: string, atType: NTMsgAtType, atName: string): SendTextElement {
|
||||
return {
|
||||
elementType: ElementType.TEXT,
|
||||
@@ -436,7 +451,7 @@ export class OneBotMsgApi {
|
||||
return at(atQQ, uid, NTMsgAtType.ATTYPEONE, info.nick || '');
|
||||
},
|
||||
|
||||
[OB11MessageDataType.reply]: async ({ data: { id } }) => {
|
||||
[OB11MessageDataType.reply]: async ({data: {id}}) => {
|
||||
const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id));
|
||||
if (!replyMsgM) {
|
||||
this.core.context.logger.logWarn('回复消息不存在', id);
|
||||
@@ -458,7 +473,7 @@ export class OneBotMsgApi {
|
||||
undefined;
|
||||
},
|
||||
|
||||
[OB11MessageDataType.face]: async ({ data: { id, resultId, chainCount } }) => {
|
||||
[OB11MessageDataType.face]: async ({data: {id, resultId, chainCount}}) => {
|
||||
const parsedFaceId = +id;
|
||||
// 从face_config.json中获取表情名称
|
||||
const sysFaces = faceConfig.sysface;
|
||||
@@ -522,12 +537,12 @@ export class OneBotMsgApi {
|
||||
},
|
||||
|
||||
[OB11MessageDataType.file]: async (sendMsg, context) => {
|
||||
const { path, fileName } = await this.handleOb11FileLikeMessage(sendMsg, context);
|
||||
const {path, fileName} = await this.handleOb11FileLikeMessage(sendMsg, context);
|
||||
return await this.core.apis.FileApi.createValidSendFileElement(context, path, fileName);
|
||||
},
|
||||
|
||||
[OB11MessageDataType.video]: async (sendMsg, context) => {
|
||||
const { path, fileName } = await this.handleOb11FileLikeMessage(sendMsg, context);
|
||||
const {path, fileName} = await this.handleOb11FileLikeMessage(sendMsg, context);
|
||||
|
||||
let thumb = sendMsg.data.thumb;
|
||||
if (thumb) {
|
||||
@@ -545,7 +560,7 @@ export class OneBotMsgApi {
|
||||
this.core.apis.FileApi.createValidSendPttElement(
|
||||
(await this.handleOb11FileLikeMessage(sendMsg, context)).path),
|
||||
|
||||
[OB11MessageDataType.json]: async ({ data: { data } }) => ({
|
||||
[OB11MessageDataType.json]: async ({data: {data}}) => ({
|
||||
elementType: ElementType.ARK,
|
||||
elementId: '',
|
||||
arkElement: {
|
||||
@@ -588,13 +603,13 @@ export class OneBotMsgApi {
|
||||
}),
|
||||
|
||||
// Need signing
|
||||
[OB11MessageDataType.markdown]: async ({ data: { content } }) => ({
|
||||
[OB11MessageDataType.markdown]: async ({data: {content}}) => ({
|
||||
elementType: ElementType.MARKDOWN,
|
||||
elementId: '',
|
||||
markdownElement: { content },
|
||||
markdownElement: {content},
|
||||
}),
|
||||
|
||||
[OB11MessageDataType.music]: async ({ data }, context) => {
|
||||
[OB11MessageDataType.music]: async ({data}, context) => {
|
||||
// 保留, 直到...找到更好的解决方案
|
||||
if (data.id !== undefined) {
|
||||
if (!['qq', '163', 'kugou', 'kuwo', 'migu'].includes(data.type)) {
|
||||
@@ -618,8 +633,8 @@ export class OneBotMsgApi {
|
||||
|
||||
let postData: IdMusicSignPostData | CustomMusicSignPostData;
|
||||
if (data.id === undefined && data.content) {
|
||||
const { content, ...others } = data;
|
||||
postData = { singer: content, ...others };
|
||||
const {content, ...others} = data;
|
||||
postData = {singer: content, ...others};
|
||||
} else {
|
||||
postData = data;
|
||||
}
|
||||
@@ -631,7 +646,7 @@ export class OneBotMsgApi {
|
||||
try {
|
||||
const musicJson = await RequestUtil.HttpGetJson<string>(signUrl, 'POST', postData);
|
||||
return this.ob11ToRawConverters.json({
|
||||
data: { data: musicJson },
|
||||
data: {data: musicJson},
|
||||
type: OB11MessageDataType.json
|
||||
}, context);
|
||||
} catch (e) {
|
||||
@@ -642,10 +657,10 @@ export class OneBotMsgApi {
|
||||
|
||||
[OB11MessageDataType.node]: async () => undefined,
|
||||
|
||||
[OB11MessageDataType.forward]: async ({ data }, context) => {
|
||||
[OB11MessageDataType.forward]: async ({data}, context) => {
|
||||
const jsonData = ForwardMsgBuilder.fromResId(data.id);
|
||||
return this.ob11ToRawConverters.json({
|
||||
data: { data: JSON.stringify(jsonData) },
|
||||
data: {data: JSON.stringify(jsonData)},
|
||||
type: OB11MessageDataType.json
|
||||
}, context);
|
||||
},
|
||||
@@ -665,17 +680,17 @@ export class OneBotMsgApi {
|
||||
|
||||
[OB11MessageDataType.miniapp]: async () => undefined,
|
||||
|
||||
[OB11MessageDataType.contact]: async ({ data: { type = 'qq', id } }, context) => {
|
||||
[OB11MessageDataType.contact]: async ({data: {type = 'qq', id}}, context) => {
|
||||
if (type === 'qq') {
|
||||
const arkJson = await this.core.apis.UserApi.getBuddyRecommendContactArkJson(id.toString(), '');
|
||||
return this.ob11ToRawConverters.json({
|
||||
data: { data: arkJson.arkMsg },
|
||||
data: {data: arkJson.arkMsg},
|
||||
type: OB11MessageDataType.json
|
||||
}, context);
|
||||
} else if (type === 'group') {
|
||||
const arkJson = await this.core.apis.GroupApi.getGroupRecommendContactArkJson(id.toString());
|
||||
return this.ob11ToRawConverters.json({
|
||||
data: { data: arkJson.arkJson },
|
||||
data: {data: arkJson.arkJson},
|
||||
type: OB11MessageDataType.json
|
||||
}, context);
|
||||
}
|
||||
@@ -692,7 +707,10 @@ export class OneBotMsgApi {
|
||||
if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) {
|
||||
if (grayTipElement.jsonGrayTipElement.busiId == 1061) {
|
||||
const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
||||
if (PokeEvent) { return PokeEvent; };
|
||||
if (PokeEvent) {
|
||||
return PokeEvent;
|
||||
}
|
||||
;
|
||||
} else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') {
|
||||
return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid)));
|
||||
}
|
||||
@@ -849,7 +867,7 @@ export class OneBotMsgApi {
|
||||
element[key],
|
||||
msg,
|
||||
element,
|
||||
{ parseMultMsg }
|
||||
{parseMultMsg}
|
||||
);
|
||||
if (key === 'faceElement' && !parsedElement) {
|
||||
return null;
|
||||
@@ -902,13 +920,13 @@ export class OneBotMsgApi {
|
||||
) => Promise<SendMessageElement | undefined>;
|
||||
const callResult = converter(
|
||||
sendMsg,
|
||||
{ peer, deleteAfterSentFiles },
|
||||
{peer, deleteAfterSentFiles},
|
||||
)?.catch(undefined);
|
||||
callResultList.push(callResult);
|
||||
}
|
||||
const ret = await Promise.all(callResultList);
|
||||
const sendElements: SendMessageElement[] = ret.filter(ele => !!ele);
|
||||
return { sendElements, deleteAfterSentFiles };
|
||||
return {sendElements, deleteAfterSentFiles};
|
||||
}
|
||||
|
||||
async sendMsgWithOb11UniqueId(peer: Peer, sendElements: SendMessageElement[], deleteAfterSentFiles: string[]) {
|
||||
@@ -970,8 +988,8 @@ export class OneBotMsgApi {
|
||||
}
|
||||
|
||||
private async handleOb11FileLikeMessage(
|
||||
{ data: inputdata }: OB11MessageFileBase,
|
||||
{ deleteAfterSentFiles }: SendMessageContext
|
||||
{data: inputdata}: OB11MessageFileBase,
|
||||
{deleteAfterSentFiles}: SendMessageContext
|
||||
) {
|
||||
let realUri = [inputdata.url, inputdata.file, inputdata.path].find(uri => uri && uri.trim()) ?? '';
|
||||
if (!realUri) {
|
||||
@@ -980,28 +998,29 @@ export class OneBotMsgApi {
|
||||
}
|
||||
|
||||
const downloadFile = async (uri: string) => {
|
||||
const { path, fileName, errMsg, success } = await uriToLocalFile(this.core.NapCatTempPath, uri);
|
||||
const {path, fileName, errMsg, success} = await uriToLocalFile(this.core.NapCatTempPath, uri);
|
||||
if (!success) {
|
||||
this.core.context.logger.logError('文件下载失败', errMsg);
|
||||
throw new Error('文件下载失败: ' + errMsg);
|
||||
}
|
||||
return { path, fileName };
|
||||
return {path, fileName};
|
||||
};
|
||||
try {
|
||||
const { path, fileName } = await downloadFile(realUri);
|
||||
const {path, fileName} = await downloadFile(realUri);
|
||||
deleteAfterSentFiles.push(path);
|
||||
return { path, fileName: inputdata.name ?? fileName };
|
||||
return {path, fileName: inputdata.name ?? fileName};
|
||||
} catch {
|
||||
realUri = await this.handleObfuckName(realUri);
|
||||
const { path, fileName } = await downloadFile(realUri);
|
||||
const {path, fileName} = await downloadFile(realUri);
|
||||
deleteAfterSentFiles.push(path);
|
||||
return { path, fileName: inputdata.name ?? fileName };
|
||||
return {path, fileName: inputdata.name ?? fileName};
|
||||
}
|
||||
}
|
||||
|
||||
async handleObfuckName(name: string) {
|
||||
const contextMsgFile = FileNapCatOneBotUUID.decode(name);
|
||||
if (contextMsgFile && contextMsgFile.msgId && contextMsgFile.elementId) {
|
||||
const { peer, msgId, elementId } = contextMsgFile;
|
||||
const {peer, msgId, elementId} = contextMsgFile;
|
||||
const rawMessage = (await this.core.apis.MsgApi.getMsgsByMsgId(peer, [msgId]))?.msgList.find(msg => msg.msgId === msgId);
|
||||
const mixElement = rawMessage?.elements.find(e => e.elementId === elementId);
|
||||
const mixElementInner = mixElement?.videoElement ?? mixElement?.fileElement ?? mixElement?.pttElement ?? mixElement?.picElement;
|
||||
@@ -1009,18 +1028,19 @@ export class OneBotMsgApi {
|
||||
let url = '';
|
||||
if (mixElement?.picElement && rawMessage) {
|
||||
const tempData =
|
||||
await this.obContext.apis.MsgApi.rawToOb11Converters.picElement?.(mixElement?.picElement, rawMessage, mixElement, { parseMultMsg: false }) as OB11MessageImage | undefined;
|
||||
await this.obContext.apis.MsgApi.rawToOb11Converters.picElement?.(mixElement?.picElement, rawMessage, mixElement, {parseMultMsg: false}) as OB11MessageImage | undefined;
|
||||
url = tempData?.data.url ?? '';
|
||||
}
|
||||
if (mixElement?.videoElement && rawMessage) {
|
||||
const tempData =
|
||||
await this.obContext.apis.MsgApi.rawToOb11Converters.videoElement?.(mixElement?.videoElement, rawMessage, mixElement, { parseMultMsg: false }) as OB11MessageVideo | undefined;
|
||||
await this.obContext.apis.MsgApi.rawToOb11Converters.videoElement?.(mixElement?.videoElement, rawMessage, mixElement, {parseMultMsg: false}) as OB11MessageVideo | undefined;
|
||||
url = tempData?.data.url ?? '';
|
||||
}
|
||||
return url !== '' ? url : await this.core.apis.FileApi.downloadMedia(msgId, peer.chatType, peer.peerUid, elementId, '', '');
|
||||
}
|
||||
throw new Error('文件名解析失败');
|
||||
}
|
||||
|
||||
groupChangDecreseType2String(type: number): GroupDecreaseSubType {
|
||||
switch (type) {
|
||||
case 130:
|
||||
|
Reference in New Issue
Block a user