diff --git a/src/core/apis/file.ts b/src/core/apis/file.ts index 209d6844..ad689bc8 100644 --- a/src/core/apis/file.ts +++ b/src/core/apis/file.ts @@ -63,6 +63,73 @@ export class NTQQFileApi { } } + async getFileUrl(chatType: ChatType, peer: string, fileUUID?: string, file10MMd5?: string | undefined) { + try { + if (chatType === ChatType.KCHATTYPEGROUP && fileUUID) { + return this.core.apis.PacketApi.pkt.operation.GetGroupFileUrl(+peer, fileUUID); + } else if (file10MMd5 && fileUUID) { + return this.core.apis.PacketApi.pkt.operation.GetPrivateFileUrl(peer, fileUUID, file10MMd5); + } + } catch (error) { + this.context.logger.logError('获取文件URL失败', (error as Error).message); + } + } + throw new Error('fileUUID or file10MMd5 is undefined'); + } + + async getPttUrl(chatType: ChatType, peer: string, fileUUID?: string) { + if (this.core.apis.PacketApi.available) { + try { + if (chatType === ChatType.KCHATTYPEGROUP && fileUUID) { + return this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+peer, { + fileUuid: fileUUID, + storeId: 1, + uploadTime: 0, + ttl: 0, + subType: 0, + }); + } else if (fileUUID) { + return this.core.apis.PacketApi.pkt.operation.GetPttUrl(peer, { + fileUuid: fileUUID, + storeId: 1, + uploadTime: 0, + ttl: 0, + subType: 0, + }); + } + } catch (error) { + this.context.logger.logError('获取文件URL失败', (error as Error).message); + } + } + throw new Error('packet cant get ptt url'); + } + + async getVideoUrlPakcet(chatType: ChatType, peer: string, fileUUID?: string) { + if (this.core.apis.PacketApi.available) { + try { + if (chatType === ChatType.KCHATTYPEGROUP && fileUUID) { + return this.core.apis.PacketApi.pkt.operation.GetGroupVideoUrl(+peer, { + fileUuid: fileUUID, + storeId: 1, + uploadTime: 0, + ttl: 0, + subType: 0, + }); + } else if (fileUUID) { + return this.core.apis.PacketApi.pkt.operation.GetVideoUrl(peer, { + fileUuid: fileUUID, + storeId: 1, + uploadTime: 0, + ttl: 0, + subType: 0, + }); + } + } catch (error) { + this.context.logger.logError('获取文件URL失败', (error as Error).message); + } + } + throw new Error('packet cant get ptt url'); + } async copyFile(filePath: string, destPath: string) { await this.core.util.copyFile(filePath, destPath); diff --git a/src/core/packet/context/operationContext.ts b/src/core/packet/context/operationContext.ts index d89e8899..b74fe641 100644 --- a/src/core/packet/context/operationContext.ts +++ b/src/core/packet/context/operationContext.ts @@ -124,6 +124,20 @@ export class PacketOperationContext { return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`; } + async GetPttUrl(selfUid: string, node: NapProtoEncodeStructType) { + const req = trans.DownloadPtt.build(selfUid, 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 GetVideoUrl(selfUid: string, node: NapProtoEncodeStructType) { + const req = trans.DownloadVideo.build(selfUid, 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 GetGroupImageUrl(groupUin: number, node: NapProtoEncodeStructType) { const req = trans.DownloadGroupImage.build(groupUin, node); const resp = await this.context.client.sendOidbPacket(req, true); @@ -131,6 +145,21 @@ export class PacketOperationContext { return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`; } + async GetGroupPttUrl(groupUin: number, node: NapProtoEncodeStructType) { + const req = trans.DownloadGroupPtt.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 GetGroupVideoUrl(groupUin: number, node: NapProtoEncodeStructType) { + const req = trans.DownloadGroupVideo.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); @@ -154,7 +183,7 @@ export class PacketOperationContext { private async SendPreprocess(msg: PacketMsg[], groupUin: number = 0) { const ps = msg.map((m) => { - return m.msg.map(async(e) => { + return m.msg.map(async (e) => { if (e instanceof PacketMsgReplyElement && !e.targetElems) { this.context.logger.debug(`Cannot find reply element's targetElems, prepare to fetch it...`); if (!e.targetPeer?.peerUid) { @@ -222,6 +251,7 @@ export class PacketOperationContext { const res = trans.DownloadGroupFile.parse(resp); return `https://${res.download.downloadDns}/ftn_handler/${Buffer.from(res.download.downloadUrl).toString('hex')}/?fname=`; } + async GetPrivateFileUrl(self_id: string, fileUUID: string, md5: string) { const req = trans.DownloadPrivateFile.build(self_id, fileUUID, md5); const resp = await this.context.client.sendOidbPacket(req, true); @@ -229,13 +259,6 @@ export class PacketOperationContext { return `http://${res.body?.result?.server}:${res.body?.result?.port}${res.body?.result?.url?.slice(8)}&isthumb=0`; } - async GetGroupPttUrl(groupUin: number, node: NapProtoEncodeStructType) { - const req = trans.DownloadGroupPtt.build(groupUin, node); - const resp = await this.context.client.sendOidbPacket(req, true); - const res = trans.DownloadGroupPtt.parse(resp); - return `https://${res.download.info.domain}${res.download.info.urlPath}${res.download.rKeyParam}`; - } - async GetMiniAppAdaptShareInfo(param: MiniAppReqParams) { const req = trans.GetMiniAppAdaptShareInfo.build(param); const resp = await this.context.client.sendOidbPacket(req, true); diff --git a/src/core/packet/transformer/highway/DownloadGroupVideo.ts b/src/core/packet/transformer/highway/DownloadGroupVideo.ts new file mode 100644 index 00000000..22fe2e8e --- /dev/null +++ b/src/core/packet/transformer/highway/DownloadGroupVideo.ts @@ -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 DownloadGroupVideo extends PacketTransformer { + constructor() { + super(); + } + + build(groupUin: number, node: NapProtoEncodeStructType): OidbPacket { + const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({ + reqHead: { + common: { + requestId: 1, + command: 200 + }, + scene: { + requestType: 2, + businessType: 2, + sceneType: 2, + group: { + groupUin: groupUin + } + }, + client: { + agentType: 2, + } + }, + download: { + node: node, + download: { + video: { + busiType: 0, + sceneType: 0 + } + } + } + }); + return OidbBase.build(0x11EA, 200, body, true, false); + } + + parse(data: Buffer) { + const oidbBody = OidbBase.parse(data).body; + return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody); + } +} + +export default new DownloadGroupVideo(); diff --git a/src/core/packet/transformer/highway/DownloadPtt.ts b/src/core/packet/transformer/highway/DownloadPtt.ts new file mode 100644 index 00000000..41ab6c7e --- /dev/null +++ b/src/core/packet/transformer/highway/DownloadPtt.ts @@ -0,0 +1,51 @@ +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 DownloadPtt extends PacketTransformer { + constructor() { + super(); + } + + build(selfUid: string, node: NapProtoEncodeStructType): OidbPacket { + const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({ + reqHead: { + common: { + requestId: 1, + command: 200 + }, + scene: { + requestType: 1, + businessType: 3, + sceneType: 1, + c2C: { + accountType: 2, + targetUid: selfUid + }, + }, + client: { + agentType: 2, + } + }, + download: { + node: node, + download: { + video: { + busiType: 0, + sceneType: 0 + } + } + } + }); + return OidbBase.build(0x126D, 200, body, true, false); + } + + parse(data: Buffer) { + const oidbBody = OidbBase.parse(data).body; + return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody); + } +} + +export default new DownloadPtt(); diff --git a/src/core/packet/transformer/highway/DownloadVideo.ts b/src/core/packet/transformer/highway/DownloadVideo.ts new file mode 100644 index 00000000..0731b258 --- /dev/null +++ b/src/core/packet/transformer/highway/DownloadVideo.ts @@ -0,0 +1,51 @@ +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 DownloadVideo extends PacketTransformer { + constructor() { + super(); + } + + build(selfUid: string, node: NapProtoEncodeStructType): OidbPacket { + const body = new NapProtoMsg(proto.NTV2RichMediaReq).encode({ + reqHead: { + common: { + requestId: 1, + command: 200 + }, + scene: { + requestType: 2, + businessType: 2, + sceneType: 1, + c2C: { + accountType: 2, + targetUid: selfUid + }, + }, + client: { + agentType: 2, + } + }, + download: { + node: node, + download: { + video: { + busiType: 0, + sceneType: 0 + } + } + } + }); + return OidbBase.build(0x11E9, 200, body, true, false); + } + + parse(data: Buffer) { + const oidbBody = OidbBase.parse(data).body; + return new NapProtoMsg(proto.NTV2RichMediaResp).decode(oidbBody); + } +} + +export default new DownloadVideo(); diff --git a/src/core/packet/transformer/highway/index.ts b/src/core/packet/transformer/highway/index.ts index 7ca56645..ef202112 100644 --- a/src/core/packet/transformer/highway/index.ts +++ b/src/core/packet/transformer/highway/index.ts @@ -13,3 +13,6 @@ export { default as UploadPrivatePtt } from './UploadPrivatePtt'; export { default as UploadPrivateVideo } from './UploadPrivateVideo'; export { default as DownloadImage } from './DownloadImage'; export { default as DownloadGroupImage } from './DownloadGroupImage'; +export { default as DownloadVideo } from './DownloadVideo'; +export { default as DownloadGroupVideo } from './DownloadGroupVideo'; +export { default as DownloadPtt } from './DownloadPtt'; \ No newline at end of file diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index 90ddc16d..fa24ef80 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -150,12 +150,31 @@ export class OneBotMsgApi { }; FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileUuid); FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileName); + if (this.core.apis.PacketApi.available) { + let url; + try { + url = await this.core.apis.FileApi.getFileUrl(msg.chatType, msg.peerUid, element.fileUuid, element.file10MMd5) + } catch (error) { + url = ''; + } + if (url) { + return { + type: OB11MessageDataType.file, + data: { + file: element.fileName, + file_id: element.fileUuid, + file_size: element.fileSize, + url: url, + }, + } + } + } return { type: OB11MessageDataType.file, data: { file: element.fileName, file_id: element.fileUuid, - file_size: element.fileSize, + file_size: element.fileSize }, }; }, @@ -331,7 +350,17 @@ export class OneBotMsgApi { //开始兜底 if (!videoDownUrl) { - videoDownUrl = element.filePath; + if (this.core.apis.PacketApi.available) { + try { + videoDownUrl = await this.core.apis.FileApi.getVideoUrlPakcet(msg.chatType, msg.peerUid, element.fileUuid); + } catch (e) { + this.core.context.logger.logError('获取视频url失败', (e as Error).stack); + videoDownUrl = element.filePath; + } + } else { + videoDownUrl = element.filePath; + } + } const fileCode = FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, element.fileUuid, element.fileName); return { @@ -351,6 +380,28 @@ export class OneBotMsgApi { guildId: '', }; const fileCode = FileNapCatOneBotUUID.encode(peer, msg.msgId, elementWrapper.elementId, '', element.fileName); + let pttUrl = ''; + if (this.core.apis.PacketApi.available) { + try { + pttUrl = await this.core.apis.FileApi.getPttUrl(msg.chatType, msg.peerUid, element.fileUuid); + } catch (e) { + this.core.context.logger.logError('获取视频url失败', (e as Error).stack); + pttUrl = element.filePath; + } + } else { + pttUrl = element.filePath; + } + if (pttUrl) { + return { + type: OB11MessageDataType.voice, + data: { + file: fileCode, + path: element.filePath, + url: pttUrl, + file_size: element.fileSize, + }, + } + } return { type: OB11MessageDataType.voice, data: {