mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
feat: build & upload file
This commit is contained in:
parent
5cca8457e7
commit
9f8f938c47
@ -12,7 +12,12 @@ import { LogWrapper } from "@/common/log";
|
|||||||
import { SendLongMsgResp } from "@/core/packet/proto/message/action";
|
import { SendLongMsgResp } from "@/core/packet/proto/message/action";
|
||||||
import { PacketMsg } from "@/core/packet/msg/message";
|
import { PacketMsg } from "@/core/packet/msg/message";
|
||||||
import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
||||||
import { PacketMsgPicElement, PacketMsgPttElement, PacketMsgVideoElement } from "@/core/packet/msg/element";
|
import {
|
||||||
|
PacketMsgFileElement,
|
||||||
|
PacketMsgPicElement,
|
||||||
|
PacketMsgPttElement,
|
||||||
|
PacketMsgVideoElement
|
||||||
|
} from "@/core/packet/msg/element";
|
||||||
|
|
||||||
|
|
||||||
interface OffsetType {
|
interface OffsetType {
|
||||||
@ -134,6 +139,12 @@ export class NTQQPacketApi {
|
|||||||
peerUid: groupUin ? String(groupUin) : this.core.selfInfo.uid
|
peerUid: groupUin ? String(groupUin) : this.core.selfInfo.uid
|
||||||
}, e));
|
}, e));
|
||||||
}
|
}
|
||||||
|
if (e instanceof PacketMsgFileElement){
|
||||||
|
reqList.push(this.packetSession?.highwaySession.uploadFile({
|
||||||
|
chatType: groupUin ? ChatType.KCHATTYPEGROUP : ChatType.KCHATTYPEC2C,
|
||||||
|
peerUid: groupUin ? String(groupUin) : this.core.selfInfo.uid
|
||||||
|
}, e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.all(reqList); // TODO: use promise.allSettled
|
return Promise.all(reqList); // TODO: use promise.allSettled
|
||||||
|
@ -8,10 +8,17 @@ import { HttpConn0x6ff_501Response } from "@/core/packet/proto/action/action";
|
|||||||
import { PacketHighwayClient } from "@/core/packet/highway/client";
|
import { PacketHighwayClient } from "@/core/packet/highway/client";
|
||||||
import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
||||||
import { OidbSvcTrpcTcpBaseRsp } from "@/core/packet/proto/oidb/OidbBase";
|
import { OidbSvcTrpcTcpBaseRsp } from "@/core/packet/proto/oidb/OidbBase";
|
||||||
import { PacketMsgPicElement, PacketMsgPttElement, PacketMsgVideoElement } from "@/core/packet/msg/element";
|
import {
|
||||||
import { NTV2RichMediaHighwayExt } from "@/core/packet/proto/highway/highway";
|
PacketMsgFileElement,
|
||||||
|
PacketMsgPicElement,
|
||||||
|
PacketMsgPttElement,
|
||||||
|
PacketMsgVideoElement
|
||||||
|
} from "@/core/packet/msg/element";
|
||||||
|
import { FileUploadExt, NTV2RichMediaHighwayExt } from "@/core/packet/proto/highway/highway";
|
||||||
import { int32ip2str, oidbIpv4s2HighwayIpv4s } from "@/core/packet/highway/utils";
|
import { int32ip2str, oidbIpv4s2HighwayIpv4s } from "@/core/packet/highway/utils";
|
||||||
import { calculateSha1StreamBytes } from "@/core/packet/utils/crypto/hash";
|
import { calculateSha1, calculateSha1StreamBytes, computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash";
|
||||||
|
import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
||||||
|
import { OidbSvcTrpcTcp0XE37_800Response, OidbSvcTrpcTcp0XE37Response } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
|
||||||
|
|
||||||
export const BlockSize = 1024 * 1024;
|
export const BlockSize = 1024 * 1024;
|
||||||
|
|
||||||
@ -22,6 +29,7 @@ interface HighwayServerAddr {
|
|||||||
|
|
||||||
export interface PacketHighwaySig {
|
export interface PacketHighwaySig {
|
||||||
uin: string;
|
uin: string;
|
||||||
|
uid: string;
|
||||||
sigSession: Uint8Array | null
|
sigSession: Uint8Array | null
|
||||||
sessionKey: Uint8Array | null
|
sessionKey: Uint8Array | null
|
||||||
serverAddr: HighwayServerAddr[]
|
serverAddr: HighwayServerAddr[]
|
||||||
@ -40,6 +48,7 @@ export class PacketHighwaySession {
|
|||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.sig = {
|
this.sig = {
|
||||||
uin: this.packetClient.napCatCore.selfInfo.uin,
|
uin: this.packetClient.napCatCore.selfInfo.uin,
|
||||||
|
uid: this.packetClient.napCatCore.selfInfo.uid,
|
||||||
sigSession: null,
|
sigSession: null,
|
||||||
sessionKey: null,
|
sessionKey: null,
|
||||||
serverAddr: [],
|
serverAddr: [],
|
||||||
@ -117,6 +126,17 @@ export class PacketHighwaySession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async uploadFile(peer: Peer, file: PacketMsgFileElement): Promise<void> {
|
||||||
|
await this.checkAvailable();
|
||||||
|
if (peer.chatType === ChatType.KCHATTYPEGROUP) {
|
||||||
|
await this.uploadGroupFileReq(Number(peer.peerUid), file);
|
||||||
|
} else if (peer.chatType === ChatType.KCHATTYPEC2C) {
|
||||||
|
await this.uploadC2CFileReq(peer.peerUid, file);
|
||||||
|
} else {
|
||||||
|
throw new Error(`[Highway] unsupported chatType: ${peer.chatType}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async uploadGroupImageReq(groupUin: number, img: PacketMsgPicElement): Promise<void> {
|
private async uploadGroupImageReq(groupUin: number, img: PacketMsgPicElement): Promise<void> {
|
||||||
const preReq = await this.packer.packUploadGroupImgReq(groupUin, img);
|
const preReq = await this.packer.packUploadGroupImgReq(groupUin, img);
|
||||||
const preRespRaw = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0x11c4_100', preReq, true);
|
const preRespRaw = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0x11c4_100', preReq, true);
|
||||||
@ -400,4 +420,142 @@ export class PacketHighwaySession {
|
|||||||
}
|
}
|
||||||
ptt.msgInfo = preRespData.upload.msgInfo;
|
ptt.msgInfo = preRespData.upload.msgInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async uploadGroupFileReq(groupUin: number, file: PacketMsgFileElement): Promise<void> {
|
||||||
|
file.isGroupFile = true;
|
||||||
|
file.fileMd5 = await computeMd5AndLengthWithLimit(file.filePath);
|
||||||
|
file.fileSha1 = await calculateSha1(file.filePath);
|
||||||
|
const preReq = await this.packer.packUploadGroupFileReq(groupUin, file);
|
||||||
|
const preRespRaw = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0x6d6_0', preReq, true);
|
||||||
|
const preResp = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(
|
||||||
|
Buffer.from(preRespRaw.hex_data, 'hex')
|
||||||
|
);
|
||||||
|
const preRespData = new NapProtoMsg(OidbSvcTrpcTcp0x6D6Response).decode(preResp.body);
|
||||||
|
if (!preRespData?.upload?.boolFileExist) {
|
||||||
|
this.logger.logDebug(`[Highway] uploadGroupFileReq file not exist, need upload!`);
|
||||||
|
const ext = new NapProtoMsg(FileUploadExt).encode({
|
||||||
|
unknown1: 100,
|
||||||
|
unknown2: 1,
|
||||||
|
entry: {
|
||||||
|
busiBuff: {
|
||||||
|
senderUin: BigInt(this.sig.uin),
|
||||||
|
receiverUin: BigInt(groupUin),
|
||||||
|
groupCode: BigInt(groupUin),
|
||||||
|
},
|
||||||
|
fileEntry: {
|
||||||
|
fileSize: BigInt(file.fileSize),
|
||||||
|
md5: file.fileMd5,
|
||||||
|
md5S2: file.fileMd5,
|
||||||
|
checkKey: preRespData.upload.checkKey,
|
||||||
|
fileId: preRespData.upload.fileId,
|
||||||
|
uploadKey: preRespData.upload.fileKey,
|
||||||
|
},
|
||||||
|
clientInfo: {
|
||||||
|
clientType: 3,
|
||||||
|
appId: "100",
|
||||||
|
terminalType: 3,
|
||||||
|
clientVer: "1.1.1",
|
||||||
|
unknown: 4
|
||||||
|
},
|
||||||
|
fileNameInfo: {
|
||||||
|
fileName: file.fileName
|
||||||
|
},
|
||||||
|
host: {
|
||||||
|
hosts: [
|
||||||
|
{
|
||||||
|
url: {
|
||||||
|
host: preRespData.upload.uploadIp,
|
||||||
|
unknown: 1,
|
||||||
|
},
|
||||||
|
port: preRespData.upload.uploadPort,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unknown200: 0,
|
||||||
|
})
|
||||||
|
await this.packetHighwayClient.upload(
|
||||||
|
71,
|
||||||
|
fs.createReadStream(file.filePath, {highWaterMark: BlockSize}),
|
||||||
|
file.fileSize,
|
||||||
|
file.fileMd5,
|
||||||
|
ext
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.logger.logDebug(`[Highway] uploadGroupFileReq file exist, don't need upload!`);
|
||||||
|
}
|
||||||
|
file.fileUuid = preRespData.upload.fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async uploadC2CFileReq(peerUid: string, file: PacketMsgFileElement): Promise<void> {
|
||||||
|
file.isGroupFile = false;
|
||||||
|
file.fileMd5 = await computeMd5AndLengthWithLimit(file.filePath);
|
||||||
|
file.fileSha1 = await calculateSha1(file.filePath);
|
||||||
|
const preReq = await this.packer.packUploadC2CFileReq(this.sig.uid, peerUid, file);
|
||||||
|
const preRespRaw = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0xe37_1700', preReq, true);
|
||||||
|
const preResp = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(
|
||||||
|
Buffer.from(preRespRaw.hex_data, 'hex')
|
||||||
|
);
|
||||||
|
console.log("OidbSvcTrpcTcp.0xe37_1700", preRespRaw);
|
||||||
|
const preRespData = new NapProtoMsg(OidbSvcTrpcTcp0XE37Response).decode(preResp.body);
|
||||||
|
if (!preRespData.upload?.boolFileExist) {
|
||||||
|
this.logger.logDebug(`[Highway] uploadC2CFileReq file not exist, need upload!`);
|
||||||
|
const ext = new NapProtoMsg(FileUploadExt).encode({
|
||||||
|
unknown1: 100,
|
||||||
|
unknown2: 1,
|
||||||
|
entry: {
|
||||||
|
busiBuff: {
|
||||||
|
senderUin: BigInt(this.sig.uin),
|
||||||
|
},
|
||||||
|
fileEntry: {
|
||||||
|
fileSize: BigInt(file.fileSize),
|
||||||
|
md5: file.fileMd5,
|
||||||
|
md5S2: file.fileMd5,
|
||||||
|
checkKey: file.fileSha1,
|
||||||
|
fileId: preRespData.upload?.uuid,
|
||||||
|
uploadKey: preRespData.upload?.mediaPlatformUploadKey,
|
||||||
|
},
|
||||||
|
clientInfo: {
|
||||||
|
clientType: 3,
|
||||||
|
appId: "100",
|
||||||
|
terminalType: 3,
|
||||||
|
clientVer: "1.1.1",
|
||||||
|
unknown: 4
|
||||||
|
},
|
||||||
|
fileNameInfo: {
|
||||||
|
fileName: file.fileName
|
||||||
|
},
|
||||||
|
host: {
|
||||||
|
hosts: [
|
||||||
|
{
|
||||||
|
url: {
|
||||||
|
host: preRespData.upload?.uploadIp,
|
||||||
|
unknown: 1,
|
||||||
|
},
|
||||||
|
port: preRespData.upload?.uploadPort,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
unknown200: 1,
|
||||||
|
unknown3: 0
|
||||||
|
})
|
||||||
|
await this.packetHighwayClient.upload(
|
||||||
|
95,
|
||||||
|
fs.createReadStream(file.filePath, {highWaterMark: BlockSize}),
|
||||||
|
file.fileSize,
|
||||||
|
file.fileMd5,
|
||||||
|
ext
|
||||||
|
);
|
||||||
|
}
|
||||||
|
file.fileUuid = preRespData.upload?.uuid;
|
||||||
|
file.fileHash = preRespData.upload?.fileAddon;
|
||||||
|
const FetchExistFileReq = this.packer.packOfflineFileDownloadReq(file.fileUuid!, file.fileHash!, this.sig.uid, peerUid);
|
||||||
|
const resp = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0xe37_800', FetchExistFileReq, true);
|
||||||
|
console.log("OidbSvcTrpcTcp.0xe37_800", resp);
|
||||||
|
const oidb_resp = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(Buffer.from(resp.hex_data, 'hex'));
|
||||||
|
file._e37_800_rsp = new NapProtoMsg(OidbSvcTrpcTcp0XE37_800Response).decode(oidb_resp.body);
|
||||||
|
file._private_send_uid = this.sig.uid;
|
||||||
|
file._private_recv_uid = peerUid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ import * as crypto from "crypto";
|
|||||||
import { PushMsgBody } from "@/core/packet/proto/message/message";
|
import { PushMsgBody } from "@/core/packet/proto/message/message";
|
||||||
import { NapProtoEncodeStructType } from "@/core/packet/proto/NapProto";
|
import { NapProtoEncodeStructType } from "@/core/packet/proto/NapProto";
|
||||||
import { LogWrapper } from "@/common/log";
|
import { LogWrapper } from "@/common/log";
|
||||||
import { PacketMsg } from "@/core/packet/msg/message";
|
import { PacketMsg, PacketSendMsgElement } from "@/core/packet/msg/message";
|
||||||
|
import { IPacketMsgElement } from "@/core/packet/msg/element";
|
||||||
|
|
||||||
export class PacketMsgBuilder {
|
export class PacketMsgBuilder {
|
||||||
private logger: LogWrapper;
|
private logger: LogWrapper;
|
||||||
@ -14,6 +15,9 @@ export class PacketMsgBuilder {
|
|||||||
buildFakeMsg(selfUid: string, element: PacketMsg[]): NapProtoEncodeStructType<typeof PushMsgBody>[] {
|
buildFakeMsg(selfUid: string, element: PacketMsg[]): NapProtoEncodeStructType<typeof PushMsgBody>[] {
|
||||||
return element.map((node): NapProtoEncodeStructType<typeof PushMsgBody> => {
|
return element.map((node): NapProtoEncodeStructType<typeof PushMsgBody> => {
|
||||||
const avatar = `https://q.qlogo.cn/headimg_dl?dst_uin=${node.senderUin}&spec=640&img_type=jpg`;
|
const avatar = `https://q.qlogo.cn/headimg_dl?dst_uin=${node.senderUin}&spec=640&img_type=jpg`;
|
||||||
|
const msgContent = node.msg.reduceRight((acc: undefined | Uint8Array, msg: IPacketMsgElement<PacketSendMsgElement>) => {
|
||||||
|
return acc !== undefined ? acc : msg.buildContent();
|
||||||
|
}, undefined);
|
||||||
const msgElement = node.msg.flatMap(msg => msg.buildElement() ?? []);
|
const msgElement = node.msg.flatMap(msg => msg.buildElement() ?? []);
|
||||||
return {
|
return {
|
||||||
responseHead: {
|
responseHead: {
|
||||||
@ -50,7 +54,8 @@ export class PacketMsgBuilder {
|
|||||||
body: {
|
body: {
|
||||||
richText: {
|
richText: {
|
||||||
elems: msgElement
|
elems: msgElement
|
||||||
}
|
},
|
||||||
|
msgContent: msgContent,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -28,6 +28,8 @@ import {
|
|||||||
import {MsgInfo} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
import {MsgInfo} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||||
import {PacketMsg, PacketSendMsgElement} from "@/core/packet/msg/message";
|
import {PacketMsg, PacketSendMsgElement} from "@/core/packet/msg/message";
|
||||||
import {ForwardMsgBuilder} from "@/common/forward-msg-builder";
|
import {ForwardMsgBuilder} from "@/common/forward-msg-builder";
|
||||||
|
import { FileExtra, GroupFileExtra } from "@/core/packet/proto/message/component";
|
||||||
|
import { OidbSvcTrpcTcp0XE37_800Response } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
|
||||||
|
|
||||||
// raw <-> packet
|
// raw <-> packet
|
||||||
// TODO: SendStructLongMsgElement
|
// TODO: SendStructLongMsgElement
|
||||||
@ -39,8 +41,8 @@ export abstract class IPacketMsgElement<T extends PacketSendMsgElement> {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildElement(): NapProtoEncodeStructType<typeof Elem>[] | undefined {
|
buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
|
||||||
return undefined;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
toPreview(): string {
|
toPreview(): string {
|
||||||
@ -351,8 +353,85 @@ export class PacketMsgPttElement extends IPacketMsgElement<SendPttElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PacketMsgFileElement extends IPacketMsgElement<SendFileElement> {
|
export class PacketMsgFileElement extends IPacketMsgElement<SendFileElement> {
|
||||||
|
fileName: string;
|
||||||
|
filePath: string;
|
||||||
|
fileSize: number;
|
||||||
|
fileSha1?: Uint8Array;
|
||||||
|
fileMd5?: Uint8Array;
|
||||||
|
fileUuid?: string;
|
||||||
|
fileHash?: string;
|
||||||
|
isGroupFile?: boolean;
|
||||||
|
_private_send_uid?: string;
|
||||||
|
_private_recv_uid?: string;
|
||||||
|
_e37_800_rsp?: NapProtoEncodeStructType<typeof OidbSvcTrpcTcp0XE37_800Response>
|
||||||
|
|
||||||
constructor(element: SendFileElement) {
|
constructor(element: SendFileElement) {
|
||||||
super(element);
|
super(element);
|
||||||
|
this.fileName = element.fileElement.fileName;
|
||||||
|
this.filePath = element.fileElement.filePath;
|
||||||
|
this.fileSize = +element.fileElement.fileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildContent(): Uint8Array | undefined {
|
||||||
|
if (this.isGroupFile) return undefined;
|
||||||
|
return new NapProtoMsg(FileExtra).encode({
|
||||||
|
file: {
|
||||||
|
fileType: 0,
|
||||||
|
fileUuid: this.fileUuid,
|
||||||
|
fileMd5: this.fileMd5,
|
||||||
|
fileName: this.fileName,
|
||||||
|
fileSize: BigInt(this.fileSize),
|
||||||
|
subcmd: 1,
|
||||||
|
dangerEvel: 0,
|
||||||
|
expireTime: Math.floor(Date.now() / 1000) + 7 * 24 * 60 * 60,
|
||||||
|
fileHash: this.fileHash,
|
||||||
|
},
|
||||||
|
field6: {
|
||||||
|
field2: {
|
||||||
|
field1: this._e37_800_rsp?.body?.field30?.field110,
|
||||||
|
fileUuid: this.fileUuid,
|
||||||
|
fileName: this.fileName,
|
||||||
|
field6: this._e37_800_rsp?.body?.field30?.field3,
|
||||||
|
field7: this._e37_800_rsp?.body?.field30?.field101,
|
||||||
|
field8: this._e37_800_rsp?.body?.field30?.field100,
|
||||||
|
timestamp1: this._e37_800_rsp?.body?.field30?.timestamp1,
|
||||||
|
fileHash: this.fileHash,
|
||||||
|
selfUid: this._private_send_uid,
|
||||||
|
destUid: this._private_recv_uid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
buildElement(): NapProtoEncodeStructType<typeof Elem>[] {
|
||||||
|
if (!this.isGroupFile) return [];
|
||||||
|
const lb = Buffer.alloc(2);
|
||||||
|
const transElemVal = new NapProtoMsg(GroupFileExtra).encode({
|
||||||
|
field1: 6,
|
||||||
|
fileName: this.fileName,
|
||||||
|
inner: {
|
||||||
|
info: {
|
||||||
|
busId: 102,
|
||||||
|
fileId: this.fileUuid,
|
||||||
|
fileSize: BigInt(this.fileSize),
|
||||||
|
fileName: this.fileName,
|
||||||
|
fileSha: this.fileSha1,
|
||||||
|
extInfoString: "",
|
||||||
|
fileMd5: this.fileMd5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
lb.writeUInt16BE(transElemVal.length);
|
||||||
|
return [{
|
||||||
|
transElem: {
|
||||||
|
elemType: 24,
|
||||||
|
elemValue: Buffer.concat([Buffer.from([0x01]), lb, transElemVal]) // TLV
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
toPreview(): string {
|
||||||
|
return `[文件]${this.fileName}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as zlib from "node:zlib";
|
import * as zlib from "node:zlib";
|
||||||
import * as crypto from "node:crypto";
|
import * as crypto from "node:crypto";
|
||||||
import { calculateSha1 } from "@/core/packet/utils/crypto/hash";
|
import { calculateSha1, computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash";
|
||||||
import { NapProtoMsg } from "@/core/packet/proto/NapProto";
|
import { NapProtoMsg } from "@/core/packet/proto/NapProto";
|
||||||
import { OidbSvcTrpcTcpBase } from "@/core/packet/proto/oidb/OidbBase";
|
import { OidbSvcTrpcTcpBase } from "@/core/packet/proto/oidb/OidbBase";
|
||||||
import { OidbSvcTrpcTcp0X9067_202 } from "@/core/packet/proto/oidb/Oidb.0x9067_202";
|
import { OidbSvcTrpcTcp0X9067_202 } from "@/core/packet/proto/oidb/Oidb.0x9067_202";
|
||||||
@ -11,13 +11,20 @@ import { NTV2RichMediaReq } from "@/core/packet/proto/oidb/common/Ntv2.RichMedia
|
|||||||
import { HttpConn0x6ff_501 } from "@/core/packet/proto/action/action";
|
import { HttpConn0x6ff_501 } from "@/core/packet/proto/action/action";
|
||||||
import { LongMsgResult, SendLongMsgReq } from "@/core/packet/proto/message/action";
|
import { LongMsgResult, SendLongMsgReq } from "@/core/packet/proto/message/action";
|
||||||
import { PacketMsgBuilder } from "@/core/packet/msg/builder";
|
import { PacketMsgBuilder } from "@/core/packet/msg/builder";
|
||||||
import { PacketMsgPicElement, PacketMsgPttElement, PacketMsgVideoElement } from "@/core/packet/msg/element";
|
import {
|
||||||
|
PacketMsgFileElement,
|
||||||
|
PacketMsgPicElement,
|
||||||
|
PacketMsgPttElement,
|
||||||
|
PacketMsgVideoElement
|
||||||
|
} from "@/core/packet/msg/element";
|
||||||
import { LogWrapper } from "@/common/log";
|
import { LogWrapper } from "@/common/log";
|
||||||
import { PacketMsg } from "@/core/packet/msg/message";
|
import { PacketMsg } from "@/core/packet/msg/message";
|
||||||
import { OidbSvcTrpcTcp0x6D6 } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
import { OidbSvcTrpcTcp0x6D6 } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
||||||
import { OidbSvcTrpcTcp0XE37_1200 } from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
import { OidbSvcTrpcTcp0XE37_1200 } from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
||||||
import { PacketMsgConverter } from "@/core/packet/msg/converter";
|
import { PacketMsgConverter } from "@/core/packet/msg/converter";
|
||||||
import { PacketClient } from "@/core/packet/client";
|
import { PacketClient } from "@/core/packet/client";
|
||||||
|
import { OidbSvcTrpcTcp0XE37_1700 } from "@/core/packet/proto/oidb/Oidb.0xE37_1700";
|
||||||
|
import { OidbSvcTrpcTcp0XE37_800 } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
|
||||||
|
|
||||||
export type PacketHexStr = string & { readonly hexNya: unique symbol };
|
export type PacketHexStr = string & { readonly hexNya: unique symbol };
|
||||||
|
|
||||||
@ -596,6 +603,65 @@ export class PacketPacker {
|
|||||||
return this.toHexStr(this.packOidbPacket(0x126D, 100, req, true, false));
|
return this.toHexStr(this.packOidbPacket(0x126D, 100, req, true, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async packUploadGroupFileReq(groupUin: number, file: PacketMsgFileElement): Promise<PacketHexStr> {
|
||||||
|
const body = new NapProtoMsg(OidbSvcTrpcTcp0x6D6).encode({
|
||||||
|
file: {
|
||||||
|
groupUin: groupUin,
|
||||||
|
appId: 4,
|
||||||
|
busId: 102,
|
||||||
|
entrance: 6,
|
||||||
|
targetDirectory: '/', // TODO:
|
||||||
|
fileName: file.fileName,
|
||||||
|
localDirectory: `/${file.fileName}`,
|
||||||
|
fileSize: BigInt(file.fileSize),
|
||||||
|
fileMd5: file.fileMd5,
|
||||||
|
fileSha1: await calculateSha1(file.filePath),
|
||||||
|
fileSha3: Buffer.alloc(0),
|
||||||
|
field15: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return this.toHexStr(this.packOidbPacket(0x6D6, 0, body, true, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
async packUploadC2CFileReq(selfUid: string, peerUid: string, file: PacketMsgFileElement): Promise<PacketHexStr> {
|
||||||
|
const body = new NapProtoMsg(OidbSvcTrpcTcp0XE37_1700).encode({
|
||||||
|
command: 1700,
|
||||||
|
seq: 0,
|
||||||
|
upload: {
|
||||||
|
senderUid: selfUid,
|
||||||
|
receiverUid: peerUid,
|
||||||
|
fileSize: file.fileSize,
|
||||||
|
fileName: file.fileName,
|
||||||
|
md510MCheckSum: await computeMd5AndLengthWithLimit(file.filePath, 10*1024*1024),
|
||||||
|
sha1CheckSum: file.fileSha1,
|
||||||
|
localPath: "/",
|
||||||
|
md5CheckSum: file.fileMd5,
|
||||||
|
sha3CheckSum: Buffer.alloc(0)
|
||||||
|
},
|
||||||
|
businessId: 3,
|
||||||
|
clientType: 1,
|
||||||
|
flagSupportMediaPlatform: 1
|
||||||
|
})
|
||||||
|
return this.toHexStr(this.packOidbPacket(0xE37, 1700, body, false, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
packOfflineFileDownloadReq(fileUUID: string, fileHash: string, senderUid: string, receiverUid: string): PacketHexStr {
|
||||||
|
const req = this.packOidbPacket(0xE37, 800, new NapProtoMsg(OidbSvcTrpcTcp0XE37_800).encode({
|
||||||
|
subCommand: 800,
|
||||||
|
field2: 0,
|
||||||
|
body: {
|
||||||
|
senderUid: senderUid,
|
||||||
|
receiverUid: receiverUid,
|
||||||
|
fileUuid: fileUUID,
|
||||||
|
fileHash: fileHash,
|
||||||
|
},
|
||||||
|
field101: 3,
|
||||||
|
field102: 1,
|
||||||
|
field200: 1,
|
||||||
|
}), false, false);
|
||||||
|
return this.toHexStr(req);
|
||||||
|
}
|
||||||
|
|
||||||
packGroupFileDownloadReq(groupUin: number, fileUUID: string): PacketHexStr {
|
packGroupFileDownloadReq(groupUin: number, fileUUID: string): PacketHexStr {
|
||||||
return this.toHexStr(
|
return this.toHexStr(
|
||||||
this.packOidbPacket(0x6D6, 2, new NapProtoMsg(OidbSvcTrpcTcp0x6D6).encode({
|
this.packOidbPacket(0x6D6, 2, new NapProtoMsg(OidbSvcTrpcTcp0x6D6).encode({
|
||||||
|
@ -113,8 +113,26 @@ export const Permission = {
|
|||||||
|
|
||||||
export const FileExtra = {
|
export const FileExtra = {
|
||||||
file: ProtoField(1, () => NotOnlineFile),
|
file: ProtoField(1, () => NotOnlineFile),
|
||||||
|
field6: ProtoField(6, () => PrivateFileExtra),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PrivateFileExtra = {
|
||||||
|
field2: ProtoField(2, () => PrivateFileExtraField2),
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PrivateFileExtraField2 = {
|
||||||
|
field1: ProtoField(1, ScalarType.UINT32),
|
||||||
|
fileUuid: ProtoField(4, ScalarType.STRING),
|
||||||
|
fileName: ProtoField(5, ScalarType.STRING),
|
||||||
|
field6: ProtoField(6, ScalarType.UINT32),
|
||||||
|
field7: ProtoField(7, ScalarType.BYTES),
|
||||||
|
field8: ProtoField(8, ScalarType.BYTES),
|
||||||
|
timestamp1: ProtoField(9, ScalarType.UINT32),
|
||||||
|
fileHash: ProtoField(14, ScalarType.STRING),
|
||||||
|
selfUid: ProtoField(15, ScalarType.STRING),
|
||||||
|
destUid: ProtoField(16, ScalarType.STRING),
|
||||||
|
}
|
||||||
|
|
||||||
export const GroupFileExtra = {
|
export const GroupFileExtra = {
|
||||||
field1: ProtoField(1, ScalarType.UINT32),
|
field1: ProtoField(1, ScalarType.UINT32),
|
||||||
fileName: ProtoField(2, ScalarType.STRING),
|
fileName: ProtoField(2, ScalarType.STRING),
|
||||||
@ -132,8 +150,9 @@ export const GroupFileExtraInfo = {
|
|||||||
fileSize: ProtoField(3, ScalarType.UINT64),
|
fileSize: ProtoField(3, ScalarType.UINT64),
|
||||||
fileName: ProtoField(4, ScalarType.STRING),
|
fileName: ProtoField(4, ScalarType.STRING),
|
||||||
field5: ProtoField(5, ScalarType.UINT32),
|
field5: ProtoField(5, ScalarType.UINT32),
|
||||||
field7: ProtoField(7, ScalarType.STRING),
|
fileSha: ProtoField(6, ScalarType.BYTES),
|
||||||
fileMd5: ProtoField(8, ScalarType.STRING),
|
extInfoString: ProtoField(7, ScalarType.STRING),
|
||||||
|
fileMd5: ProtoField(8, ScalarType.BYTES),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ImageExtraUrl = {
|
export const ImageExtraUrl = {
|
||||||
|
62
src/core/packet/proto/oidb/Oidb.0XE37_800.ts
Normal file
62
src/core/packet/proto/oidb/Oidb.0XE37_800.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
|
import { ProtoField } from "../NapProto";
|
||||||
|
import {OidbSvcTrpcTcp0XE37_800_1200Metadata} from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0XE37_800 = {
|
||||||
|
subCommand: ProtoField(1, ScalarType.UINT32),
|
||||||
|
field2: ProtoField(2, ScalarType.INT32),
|
||||||
|
body: ProtoField(10, () => OidbSvcTrpcTcp0XE37_800Body, true),
|
||||||
|
field101: ProtoField(101, ScalarType.INT32),
|
||||||
|
field102: ProtoField(102, ScalarType.INT32),
|
||||||
|
field200: ProtoField(200, ScalarType.INT32)
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0XE37_800Body = {
|
||||||
|
senderUid: ProtoField(10, ScalarType.STRING, true),
|
||||||
|
receiverUid: ProtoField(20, ScalarType.STRING, true),
|
||||||
|
fileUuid: ProtoField(30, ScalarType.STRING, true),
|
||||||
|
fileHash: ProtoField(40, ScalarType.STRING, true)
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0XE37Response = {
|
||||||
|
command: ProtoField(1, ScalarType.UINT32),
|
||||||
|
seq: ProtoField(2, ScalarType.INT32),
|
||||||
|
upload: ProtoField(19, () => ApplyUploadRespV3, true),
|
||||||
|
businessId: ProtoField(101, ScalarType.INT32),
|
||||||
|
clientType: ProtoField(102, ScalarType.INT32),
|
||||||
|
flagSupportMediaPlatform: ProtoField(200, ScalarType.INT32)
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ApplyUploadRespV3 = {
|
||||||
|
retCode: ProtoField(10, ScalarType.INT32),
|
||||||
|
retMsg: ProtoField(20, ScalarType.STRING, true),
|
||||||
|
totalSpace: ProtoField(30, ScalarType.INT64),
|
||||||
|
usedSpace: ProtoField(40, ScalarType.INT64),
|
||||||
|
uploadedSize: ProtoField(50, ScalarType.INT64),
|
||||||
|
uploadIp: ProtoField(60, ScalarType.STRING, true),
|
||||||
|
uploadDomain: ProtoField(70, ScalarType.STRING, true),
|
||||||
|
uploadPort: ProtoField(80, ScalarType.UINT32),
|
||||||
|
uuid: ProtoField(90, ScalarType.STRING, true),
|
||||||
|
uploadKey: ProtoField(100, ScalarType.BYTES, true),
|
||||||
|
boolFileExist: ProtoField(110, ScalarType.BOOL),
|
||||||
|
packSize: ProtoField(120, ScalarType.INT32),
|
||||||
|
uploadIpList: ProtoField(130, ScalarType.STRING, false, true), // repeated
|
||||||
|
uploadHttpsPort: ProtoField(140, ScalarType.INT32),
|
||||||
|
uploadHttpsDomain: ProtoField(150, ScalarType.STRING, true),
|
||||||
|
uploadDns: ProtoField(160, ScalarType.STRING, true),
|
||||||
|
uploadLanip: ProtoField(170, ScalarType.STRING, true),
|
||||||
|
fileAddon: ProtoField(200, ScalarType.STRING, true),
|
||||||
|
mediaPlatformUploadKey: ProtoField(220, ScalarType.BYTES, true)
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0XE37_800Response = {
|
||||||
|
command: ProtoField(1, ScalarType.UINT32, true),
|
||||||
|
subCommand: ProtoField(2, ScalarType.UINT32, true),
|
||||||
|
body: ProtoField(10, () => OidbSvcTrpcTcp0XE37_800ResponseBody, true),
|
||||||
|
field50: ProtoField(50, ScalarType.UINT32, true),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0XE37_800ResponseBody = {
|
||||||
|
field10: ProtoField(10, ScalarType.UINT32, true),
|
||||||
|
field30: ProtoField(30, () => OidbSvcTrpcTcp0XE37_800_1200Metadata, true),
|
||||||
|
}
|
@ -30,7 +30,7 @@ export const OidbSvcTrpcTcp0XE37_1200ResponseBody = {
|
|||||||
field10: ProtoField(10, ScalarType.UINT32, true),
|
field10: ProtoField(10, ScalarType.UINT32, true),
|
||||||
state: ProtoField(20, ScalarType.STRING, true),
|
state: ProtoField(20, ScalarType.STRING, true),
|
||||||
result: ProtoField(30, () => OidbSvcTrpcTcp0XE37_1200Result, true),
|
result: ProtoField(30, () => OidbSvcTrpcTcp0XE37_1200Result, true),
|
||||||
metadata: ProtoField(40, () => OidbSvcTrpcTcp0XE37_1200Metadata, true),
|
metadata: ProtoField(40, () => OidbSvcTrpcTcp0XE37_800_1200Metadata, true),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OidbSvcTrpcTcp0XE37_1200Result = {
|
export const OidbSvcTrpcTcp0XE37_1200Result = {
|
||||||
@ -43,7 +43,7 @@ export const OidbSvcTrpcTcp0XE37_1200Result = {
|
|||||||
extra: ProtoField(120, ScalarType.BYTES, true),
|
extra: ProtoField(120, ScalarType.BYTES, true),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const OidbSvcTrpcTcp0XE37_1200Metadata = {
|
export const OidbSvcTrpcTcp0XE37_800_1200Metadata = {
|
||||||
uin: ProtoField(1, ScalarType.UINT32, true),
|
uin: ProtoField(1, ScalarType.UINT32, true),
|
||||||
field2: ProtoField(2, ScalarType.UINT32, true),
|
field2: ProtoField(2, ScalarType.UINT32, true),
|
||||||
field3: ProtoField(3, ScalarType.UINT32, true),
|
field3: ProtoField(3, ScalarType.UINT32, true),
|
||||||
|
23
src/core/packet/proto/oidb/Oidb.0xE37_1700.ts
Normal file
23
src/core/packet/proto/oidb/Oidb.0xE37_1700.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
|
import { ProtoField } from "../NapProto";
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0XE37_1700 = {
|
||||||
|
command: ProtoField(1, ScalarType.UINT32, true),
|
||||||
|
seq: ProtoField(2, ScalarType.INT32, true),
|
||||||
|
upload: ProtoField(19, () => ApplyUploadReqV3, true),
|
||||||
|
businessId: ProtoField(101, ScalarType.INT32, true),
|
||||||
|
clientType: ProtoField(102, ScalarType.INT32, true),
|
||||||
|
flagSupportMediaPlatform: ProtoField(200, ScalarType.INT32, true),
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ApplyUploadReqV3 = {
|
||||||
|
senderUid: ProtoField(10, ScalarType.STRING, true),
|
||||||
|
receiverUid: ProtoField(20, ScalarType.STRING, true),
|
||||||
|
fileSize: ProtoField(30, ScalarType.UINT32, true),
|
||||||
|
fileName: ProtoField(40, ScalarType.STRING, true),
|
||||||
|
md510MCheckSum: ProtoField(50, ScalarType.BYTES, true),
|
||||||
|
sha1CheckSum: ProtoField(60, ScalarType.BYTES, true),
|
||||||
|
localPath: ProtoField(70, ScalarType.STRING, true),
|
||||||
|
md5CheckSum: ProtoField(110, ScalarType.BYTES, true),
|
||||||
|
sha3CheckSum: ProtoField(120, ScalarType.BYTES, true),
|
||||||
|
}
|
@ -11,11 +11,23 @@ function sha1Stream(readable: stream.Readable) {
|
|||||||
}) as Promise<Buffer>;
|
}) as Promise<Buffer>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function md5Stream(readable: stream.Readable) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
readable.on('error', reject);
|
||||||
|
readable.pipe(crypto.createHash('md5').on('error', reject).on('data', resolve));
|
||||||
|
}) as Promise<Buffer>;
|
||||||
|
}
|
||||||
|
|
||||||
export function calculateSha1(filePath: string): Promise<Buffer> {
|
export function calculateSha1(filePath: string): Promise<Buffer> {
|
||||||
const readable = fs.createReadStream(filePath);
|
const readable = fs.createReadStream(filePath);
|
||||||
return sha1Stream(readable);
|
return sha1Stream(readable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function computeMd5AndLengthWithLimit(filePath: string, limit?: number): Promise<Buffer> {
|
||||||
|
const readStream = fs.createReadStream(filePath, limit ? { start: 0, end: limit - 1 } : {});
|
||||||
|
return md5Stream(readStream);
|
||||||
|
}
|
||||||
|
|
||||||
export function calculateSha1StreamBytes(filePath: string): Promise<Buffer[]> {
|
export function calculateSha1StreamBytes(filePath: string): Promise<Buffer[]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const readable = fs.createReadStream(filePath);
|
const readable = fs.createReadStream(filePath);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user