diff --git a/src/core/packet/highway/session.ts b/src/core/packet/highway/session.ts index 40e3527c..3d8a0f4b 100644 --- a/src/core/packet/highway/session.ts +++ b/src/core/packet/highway/session.ts @@ -87,8 +87,11 @@ export class PacketHighwaySession { await this.checkAvailable(); if (peer.chatType === ChatType.KCHATTYPEGROUP) { await this.uploadGroupImageReq(Number(peer.peerUid), img); + } else if (peer.chatType === ChatType.KCHATTYPEC2C) { + await this.uploadC2CImageReq(peer.peerUid, img); + } else { + throw new Error(`[Highway] unsupported chatType: ${peer.chatType}`); } - // TODO: handle c2c pic upload } private async uploadGroupImageReq(groupUin: number, img: PacketMsgPicElement): Promise { @@ -129,4 +132,40 @@ export class PacketHighwaySession { img.msgInfo = preRespData.upload.msgInfo; // img.groupPicExt = new NapProtoMsg(CustomFace).decode(preRespData.tcpUpload.compatQMsg) } + + private async uploadC2CImageReq(peerUid: string, img: PacketMsgPicElement): Promise { + const preReq = await this.packer.packUploadC2CImgReq(peerUid, img); + const preRespRaw = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0x11c5_100', preReq, true); + const preResp = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode( + Buffer.from(preRespRaw.hex_data, 'hex') + ); + const preRespData = new NapProtoMsg(NTV2RichMediaResp).decode(preResp.body); + const ukey = preRespData.upload.uKey; + if (ukey && ukey != "") { + this.logger.logDebug(`[Highway] get upload ukey: ${ukey}, need upload!`); + const index = preRespData.upload.msgInfo.msgInfoBody[0].index; + const sha1 = Buffer.from(index.info.fileSha1, 'hex'); + const md5 = Buffer.from(index.info.fileHash, 'hex'); + const extend = new NapProtoMsg(NTV2RichMediaHighwayExt).encode({ + fileUuid: index.fileUuid, + uKey: ukey, + network: { + ipv4S: oidbIpv4s2HighwayIpv4s(preRespData.upload.ipv4S) + }, + msgInfoBody: preRespData.upload.msgInfo.msgInfoBody, + blockSize: BlockSize, + hash: { + fileSha1: [sha1] + } + }) + await this.packetHighwayClient.upload( + 1003, + fs.createReadStream(img.path, {highWaterMark: BlockSize}), + img.size, + md5, + extend + ); + } + img.msgInfo = preRespData.upload.msgInfo; + } } diff --git a/src/core/packet/packer.ts b/src/core/packet/packer.ts index 4808474b..eb9a59f2 100644 --- a/src/core/packet/packer.ts +++ b/src/core/packet/packer.ts @@ -215,4 +215,72 @@ export class PacketPacker { ) return this.toHexStr(this.packOidbPacket(0x11c4, 100, req, true, false)); } + + async packUploadC2CImgReq(peerUin: string, img: PacketMsgPicElement) { + const req = new NapProtoMsg(NTV2RichMediaReq).encode({ + reqHead: { + common: { + requestId: 1, + command: 100 + }, + scene: { + requestType: 2, + businessType: 1, + sceneType: 1, + c2C: { + accountType: 2, + targetUid: peerUin + }, + }, + client: { + agentType: 2, + } + }, + upload: { + uploadInfo: [ + { + fileInfo: { + fileSize: Number(img.size), + fileHash: img.md5, + fileSha1: this.toHexStr(await calculateSha1(img.path)), + fileName: img.name, + type: { + type: 1, + picFormat: img.picType, //TODO: extend NapCat imgType /cc @MliKiowa + videoFormat: 0, + voiceFormat: 0, + }, + width: img.width, + height: img.height, + time: 0, + original: 1 + }, + subFileType: 0, + } + ], + tryFastUploadCompleted: true, + srvSendMsg: false, + clientRandomId: crypto.randomBytes(8).readBigUInt64BE() & BigInt('0x7FFFFFFFFFFFFFFF'), + compatQMsgSceneType: 1, + extBizInfo: { + pic: { + bytesPbReserveTroop: Buffer.from("0800180020004200500062009201009a0100a2010c080012001800200028003a00", 'hex'), + textSummary: "Nya~", // TODO: + }, + video: { + bytesPbReserve: Buffer.alloc(0), + }, + ptt: { + bytesPbReserve: Buffer.alloc(0), + bytesReserve: Buffer.alloc(0), + bytesGeneralFlags: Buffer.alloc(0), + } + }, + clientSeq: 0, + noNeedCompatMsg: false, + } + } + ) + return this.toHexStr(this.packOidbPacket(0x11c5, 100, req, true, false)); + } }