mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
feat: broken highway
This commit is contained in:
parent
f9e44820c1
commit
d30d467a21
@ -9,6 +9,7 @@ import { OidbSvcTrpcTcp0X9067_202_Rsp_Body } from '@/core/packet/proto/oidb/Oidb
|
||||
import { OidbSvcTrpcTcpBase, OidbSvcTrpcTcpBaseRsp } from '@/core/packet/proto/oidb/OidbBase';
|
||||
import { OidbSvcTrpcTcp0XFE1_2RSP } from '@/core/packet/proto/oidb/Oidb.fe1_2';
|
||||
import { PacketForwardNode } from "@/core/packet/msg/entity/forward";
|
||||
import {LogWrapper} from "@/common/log";
|
||||
|
||||
interface OffsetType {
|
||||
[key: string]: {
|
||||
@ -22,6 +23,7 @@ const typedOffset: OffsetType = offset;
|
||||
export class NTQQPacketApi {
|
||||
context: InstanceContext;
|
||||
core: NapCatCore;
|
||||
logger: LogWrapper
|
||||
serverUrl: string | undefined;
|
||||
qqVersion: string | undefined;
|
||||
packetPacker: PacketPacker;
|
||||
@ -30,7 +32,8 @@ export class NTQQPacketApi {
|
||||
constructor(context: InstanceContext, core: NapCatCore) {
|
||||
this.context = context;
|
||||
this.core = core;
|
||||
this.packetPacker = new PacketPacker();
|
||||
this.logger = core.context.logger;
|
||||
this.packetPacker = new PacketPacker(this.logger);
|
||||
this.packetSession = undefined;
|
||||
const config = this.core.configLoader.configData;
|
||||
if (config && config.packetServer && config.packetServer.length > 0) {
|
||||
|
@ -24,7 +24,7 @@ export class PacketClient {
|
||||
private readonly maxReconnectAttempts: number = 5;//现在暂时不可配置
|
||||
private readonly cb = new LRUCache<string, (json: RecvPacketData) => Promise<void>>(500); // trace_id-type callback
|
||||
private readonly clientUrl: string = '';
|
||||
private readonly napCatCore: NapCatCore;
|
||||
readonly napCatCore: NapCatCore;
|
||||
private readonly logger: LogWrapper;
|
||||
|
||||
constructor(url: string, core: NapCatCore) {
|
||||
|
279
src/core/packet/highway/client.ts
Normal file
279
src/core/packet/highway/client.ts
Normal file
@ -0,0 +1,279 @@
|
||||
import * as net from "node:net";
|
||||
import * as stream from 'node:stream';
|
||||
import * as crypto from 'node:crypto';
|
||||
import * as tea from '@/core/packet/utils/crypto/tea';
|
||||
import {BlockSize, PacketHighwaySig} from "@/core/packet/highway/session";
|
||||
import {NapProtoMsg} from "@/core/packet/proto/NapProto";
|
||||
import {ReqDataHighwayHead, RespDataHighwayHead} from "@/core/packet/proto/highway/highway";
|
||||
import {LogWrapper} from "@/common/log";
|
||||
import {createHash} from "crypto";
|
||||
import {toHexString} from "image-size/dist/types/utils";
|
||||
|
||||
interface PacketHighwayTrans {
|
||||
uin: string;
|
||||
cmd: number;
|
||||
data: stream.Readable;
|
||||
sum: Uint8Array;
|
||||
size: number;
|
||||
ticket: Uint8Array;
|
||||
loginSig?: Uint8Array;
|
||||
ext: Uint8Array;
|
||||
encrypt: boolean;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
class PacketHighwayTransform extends stream.Transform {
|
||||
private seq: number = 0;
|
||||
private readonly trans: PacketHighwayTrans;
|
||||
private offset: number = 0;
|
||||
|
||||
constructor(trans: PacketHighwayTrans) {
|
||||
super();
|
||||
this.trans = trans;
|
||||
}
|
||||
|
||||
private nextSeq() {
|
||||
console.log(`[Highway] nextSeq: ${this.seq}`);
|
||||
this.seq += 2;
|
||||
return this.seq;
|
||||
}
|
||||
|
||||
private encryptTrans(trans: PacketHighwayTrans, key: Uint8Array) {
|
||||
if (!trans.encrypt) return;
|
||||
trans.ext = tea.encrypt(Buffer.from(trans.ext), Buffer.from(key));
|
||||
}
|
||||
|
||||
buildHead(trans: PacketHighwayTrans, offset: number, length: number, md5Hash: Uint8Array): Uint8Array {
|
||||
return new NapProtoMsg(ReqDataHighwayHead).encode({
|
||||
msgBaseHead: {
|
||||
version: 1,
|
||||
uin: trans.uin, // TODO:
|
||||
command: "PicUp.DataUp",
|
||||
seq: this.nextSeq(),
|
||||
retryTimes: 0,
|
||||
appId: 537234773,
|
||||
dataFlag: 16,
|
||||
commandId: trans.cmd,
|
||||
},
|
||||
msgSegHead: {
|
||||
filesize: BigInt(trans.size),
|
||||
dataOffset: BigInt(offset),
|
||||
dataLength: length,
|
||||
serviceTicket: trans.ticket,
|
||||
md5: md5Hash,
|
||||
fileMd5: trans.sum,
|
||||
},
|
||||
bytesReqExtendInfo: trans.ext,
|
||||
timestamp: BigInt(Date.now()),
|
||||
msgLoginSigHead: {
|
||||
uint32LoginSigType: 8,
|
||||
appId: 1600001615,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
_transform(data: Buffer, encoding: BufferEncoding, callback: stream.TransformCallback) {
|
||||
let offset = 0; // Offset within the current chunk
|
||||
console.log(`[Highway] CALLED!!! _transform data.length = ${data.length}`);
|
||||
while (offset < data.length) {
|
||||
console.log(`[Highway] _transform offset = ${offset}, data.length = ${data.length}`);
|
||||
const chunkSize = data.length > BlockSize ? BlockSize : data.length;
|
||||
console.log(`[Highway] _transform calced chunkSize = ${chunkSize}`);
|
||||
const chunk = data.slice(offset, offset + chunkSize);
|
||||
const chunkMd5 = createHash('md5').update(chunk).digest();
|
||||
const head = this.buildHead(this.trans, this.offset, chunk.length, chunkMd5);
|
||||
console.log(`[Highway] _transform: ${this.offset} | ${data.length} | ${chunkMd5.toString('hex')}`);
|
||||
this.offset += chunk.length;
|
||||
offset += chunk.length;
|
||||
const headerBuffer = Buffer.allocUnsafe(9);
|
||||
headerBuffer.writeUInt8(40);
|
||||
headerBuffer.writeUInt32BE(head.length, 1);
|
||||
headerBuffer.writeUInt32BE(chunk.length, 5);
|
||||
this.push(headerBuffer);
|
||||
this.push(head);
|
||||
this.push(chunk);
|
||||
this.push(Buffer.from([41]));
|
||||
}
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
|
||||
export class PacketHighwayClient {
|
||||
sig: PacketHighwaySig;
|
||||
ip: string = 'htdata3.qq.com';
|
||||
port: number = 80;
|
||||
logger: LogWrapper;
|
||||
|
||||
constructor(sig: PacketHighwaySig, logger: LogWrapper) {
|
||||
this.sig = sig;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
changeServer(server: string, port: number) {
|
||||
this.ip = server;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
framePack(head: Buffer, body: Buffer): Buffer[] {
|
||||
const buffers: Buffer[] = [];
|
||||
const buffer0 = Buffer.alloc(9);
|
||||
buffer0[0] = 0x28;
|
||||
buffer0.writeUInt32BE(head.length, 1);
|
||||
buffer0.writeUInt32BE(body.length, 5);
|
||||
buffers.push(buffer0);
|
||||
buffers.push(head);
|
||||
buffers.push(body);
|
||||
buffers.push(Buffer.from([0x29]));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
frameUnpack(frame: Buffer): [Buffer, Buffer] {
|
||||
const headLen = frame.readUInt32BE(1);
|
||||
const bodyLen = frame.readUInt32BE(5);
|
||||
return [frame.slice(9, 9 + headLen), frame.slice(9 + headLen, 9 + headLen + bodyLen)];
|
||||
}
|
||||
|
||||
async postHighwayContent(frame: Buffer[], serverURL: string, end: boolean): Promise<Buffer> {
|
||||
try {
|
||||
const combinedBuffer = Buffer.concat(frame);
|
||||
const response: Response = await fetch(serverURL, {
|
||||
method: 'POST',
|
||||
headers: new Headers({
|
||||
'Connection': end ? 'close' : 'keep-alive',
|
||||
'Accept-Encoding': 'identity',
|
||||
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2)',
|
||||
}),
|
||||
body: combinedBuffer,
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status} - ${response.statusText}`);
|
||||
}
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
return Buffer.from(arrayBuffer);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async httpUploadBlock(trans: PacketHighwayTrans, offset: number, block: Buffer): Promise<void> {
|
||||
const highwayTransForm = new PacketHighwayTransform(trans);
|
||||
const isEnd = offset + block.length === trans.size;
|
||||
const md5 = crypto.createHash('md5').update(block).digest();
|
||||
const payload = highwayTransForm.buildHead(trans, offset, block.length, md5);
|
||||
this.logger.log(`[Highway] httpUploadBlock: payload = ${toHexString(payload)}`);
|
||||
const frame = this.framePack(Buffer.from(payload), block);
|
||||
const addr = this.sig.serverAddr[0];
|
||||
this.logger.log(`[Highway] httpUploadBlock: ${offset} | ${block.length} | ${toHexString(md5)}`);
|
||||
const resp = await this.postHighwayContent(frame, `http://${addr.ip}:${addr.port}/cgi-bin/httpconn?htcmd=0x6FF0087&uin=3767830885`, isEnd);
|
||||
const [head, body] = this.frameUnpack(resp);
|
||||
const headData = new NapProtoMsg(RespDataHighwayHead).decode(head);
|
||||
this.logger.log(`[Highway] ${headData.errorCode} | ${headData.msgSegHead?.retCode} | ${headData.bytesRspExtendInfo} | ${head.toString('hex')} | ${body.toString('hex')}`);
|
||||
if (headData.errorCode !== 0) {
|
||||
throw new Error(`[Highway] upload failed (code: ${headData.errorCode})`);
|
||||
}
|
||||
}
|
||||
|
||||
async httpUpload(cmd: number, data: stream.Readable, fileSize: number, md5: Uint8Array, extendInfo: Uint8Array): Promise<void> {
|
||||
const trans: PacketHighwayTrans = {
|
||||
uin: this.sig.uin,
|
||||
cmd: cmd,
|
||||
data: data,
|
||||
sum: md5,
|
||||
size: fileSize,
|
||||
ticket: this.sig.sigSession!,
|
||||
ext: extendInfo,
|
||||
encrypt: false,
|
||||
timeout: 360, // TODO:
|
||||
};
|
||||
let offset = 0;
|
||||
console.log(`[Highway] httpUpload trans=${JSON.stringify(trans)}`);
|
||||
for await (const chunk of data) {
|
||||
let buffer = chunk as Buffer;
|
||||
try {
|
||||
await this.httpUploadBlock(trans, offset, buffer);
|
||||
} catch (err) {
|
||||
console.error(`Error uploading block at offset ${offset}: ${err}`);
|
||||
throw err;
|
||||
}
|
||||
offset += buffer.length;
|
||||
}
|
||||
}
|
||||
|
||||
async tcpUpload(cmd: number, data: stream.Readable, fileSize: number, md5: Uint8Array, extendInfo: Uint8Array): Promise<void> {
|
||||
const trans: PacketHighwayTrans = {
|
||||
uin: this.sig.uin,
|
||||
cmd: cmd,
|
||||
data: data,
|
||||
sum: md5,
|
||||
size: fileSize,
|
||||
ticket: this.sig.sigSession!,
|
||||
ext: extendInfo,
|
||||
encrypt: false,
|
||||
timeout: 360, // TODO:
|
||||
};
|
||||
const highwayTransForm = new PacketHighwayTransform(trans);
|
||||
return new Promise((resolve, reject) => {
|
||||
const socket = net.connect(this.port, this.ip, () => {
|
||||
trans.data.pipe(highwayTransForm).pipe(socket, {end: false});
|
||||
})
|
||||
const handleRspHeader = (header: Buffer) => {
|
||||
console.log(`[Highway] handleRspHeader: ${header.toString('hex')}`);
|
||||
const rsp = new NapProtoMsg(RespDataHighwayHead).decode(header);
|
||||
if (rsp.errorCode !== 0) {
|
||||
this.logger.logWarn(`highway upload failed (code: ${rsp.errorCode})`);
|
||||
trans.data.unpipe(highwayTransForm).destroy();
|
||||
highwayTransForm.unpipe(socket).destroy();
|
||||
socket.end();
|
||||
reject(new Error(`highway upload failed (code: ${rsp.errorCode})`));
|
||||
} else {
|
||||
const percent = ((Number(rsp.msgSegHead?.dataOffset) + Number(rsp.msgSegHead?.dataLength)) / Number(rsp.msgSegHead?.filesize)).toFixed(2);
|
||||
this.logger.log(`[Highway] ${rsp.errorCode} | ${percent} | ${Buffer.from(header).toString('hex')}`);
|
||||
if (rsp.msgSegHead?.flag === 1) {
|
||||
this.logger.log('[Highway] tcpUpload finished.');
|
||||
socket.end();
|
||||
resolve();
|
||||
}
|
||||
// if (Number(rsp.msgSegHead?.dataOffset) + Number(rsp.msgSegHead?.dataLength) > Number(rsp.msgSegHead?.filesize)) {
|
||||
// this.logger.log('[Highway] tcpUpload finished.');
|
||||
// socket.end();
|
||||
// resolve();
|
||||
// }
|
||||
}
|
||||
};
|
||||
let buf = Buffer.alloc(0);
|
||||
socket.on('data', (chunk: Buffer) => {
|
||||
try {
|
||||
buf = buf.length ? Buffer.concat([buf, chunk]) : chunk;
|
||||
while (buf.length >= 5) {
|
||||
const len = buf.readInt32BE(1);
|
||||
if (buf.length >= len + 10) {
|
||||
handleRspHeader(buf.slice(9, len + 9));
|
||||
buf = buf.slice(len + 10);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.logError(`[Highway] upload error: ${e}`);
|
||||
}
|
||||
})
|
||||
socket.on('close', () => {
|
||||
this.logger.log('[Highway] socket closed.');
|
||||
resolve();
|
||||
})
|
||||
socket.on('error', (err) => {
|
||||
this.logger.logError('[Highway] socket.on tcpUpload error:', err);
|
||||
})
|
||||
trans.data.on('error', (err) => {
|
||||
this.logger.logError('[Highway] readable tcpUpload error:', err);
|
||||
socket.end();
|
||||
})
|
||||
if (trans.timeout) {
|
||||
setTimeout(() => {
|
||||
this.logger.logError('[Highway] tcpUpload timeout!');
|
||||
socket.end();
|
||||
}, trans.timeout * 1000);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
143
src/core/packet/highway/session.ts
Normal file
143
src/core/packet/highway/session.ts
Normal file
@ -0,0 +1,143 @@
|
||||
import * as fs from "node:fs";
|
||||
import {LogWrapper} from "@/common/log";
|
||||
import {PacketClient} from "@/core/packet/client";
|
||||
import {PacketPacker} from "@/core/packet/packer";
|
||||
import {NapProtoEncodeStructType, NapProtoMsg} from "@/core/packet/proto/NapProto";
|
||||
import {HttpConn0x6ff_501Response} from "@/core/packet/proto/action/action";
|
||||
import {PacketHighwayClient} from "@/core/packet/highway/client";
|
||||
import {ChatType, Peer} from "@/core";
|
||||
import {IPv4, NTV2RichMediaResp} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
||||
import {OidbSvcTrpcTcpBaseRsp} from "@/core/packet/proto/oidb/OidbBase";
|
||||
import {PacketMsgPicElement} from "@/core/packet/msg/element";
|
||||
import {NTHighwayIPv4, NTV2RichMediaHighwayExt} from "@/core/packet/proto/highway/highway";
|
||||
|
||||
export const BlockSize = 1024 * 1024;
|
||||
|
||||
interface HighwayServerAddr {
|
||||
ip: string
|
||||
port: number
|
||||
}
|
||||
|
||||
export interface PacketHighwaySig {
|
||||
uin: string;
|
||||
sigSession: Uint8Array | null
|
||||
sessionKey: Uint8Array | null
|
||||
serverAddr: HighwayServerAddr[]
|
||||
}
|
||||
|
||||
export class PacketHighwaySession {
|
||||
protected packetClient: PacketClient;
|
||||
protected logger: LogWrapper;
|
||||
protected packer: PacketPacker;
|
||||
protected sig: PacketHighwaySig;
|
||||
protected packetHighwayClient: PacketHighwayClient;
|
||||
|
||||
constructor(logger: LogWrapper, client: PacketClient) {
|
||||
this.packetClient = client;
|
||||
this.logger = logger;
|
||||
this.packer = new PacketPacker(logger);
|
||||
this.sig = {
|
||||
uin: this.packetClient.napCatCore.selfInfo.uin,
|
||||
sigSession: null,
|
||||
sessionKey: null,
|
||||
serverAddr: [],
|
||||
}
|
||||
this.packetHighwayClient = new PacketHighwayClient(this.sig, this.logger);
|
||||
}
|
||||
|
||||
get available(): boolean {
|
||||
return this.packetClient.available && this.sig.sigSession !== null &&
|
||||
this.sig.sessionKey !== null && this.sig.serverAddr.length > 0;
|
||||
}
|
||||
|
||||
private int32ip2str(ip: number) {
|
||||
ip = ip & 0xffffffff;
|
||||
return [ip & 0xff, (ip & 0xff00) >> 8, (ip & 0xff0000) >> 16, ((ip & 0xff000000) >> 24) & 0xff].join('.');
|
||||
}
|
||||
|
||||
private oidbIpv4s2HighwayIpv4s(ipv4s: NapProtoEncodeStructType<typeof IPv4>[]): NapProtoEncodeStructType<typeof NTHighwayIPv4>[] {
|
||||
return ipv4s.map((ipv4) => {
|
||||
return {
|
||||
domain: {
|
||||
isEnable: true,
|
||||
ip: this.int32ip2str(ipv4.outIP!),
|
||||
}
|
||||
} as NapProtoEncodeStructType<typeof NTHighwayIPv4>
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: add signal to auto prepare when ready
|
||||
// TODO: refactor
|
||||
async prepareUpload(): Promise<void> {
|
||||
this.logger.log('[Highway] prepare tcpUpload!');
|
||||
const packet = this.packer.packHttp0x6ff_501();
|
||||
const req = await this.packetClient.sendPacket('HttpConn.0x6ff_501', packet, true);
|
||||
const u8RspData = Buffer.from(req.hex_data, 'hex');
|
||||
const rsp = new NapProtoMsg(HttpConn0x6ff_501Response).decode(u8RspData);
|
||||
this.sig.sigSession = rsp.httpConn.sigSession
|
||||
this.sig.sessionKey = rsp.httpConn.sessionKey
|
||||
// this.logger.log(`[Highway] sigSession ${Buffer.from(this.sigSession).toString('hex')},
|
||||
// sessionKey ${Buffer.from(this.sessionKey).toString('hex')}`)
|
||||
for (const info of rsp.httpConn.serverInfos) {
|
||||
if (info.serviceType !== 1) continue;
|
||||
for (const addr of info.serverAddrs) {
|
||||
this.logger.log(`[Highway PrepareUpload] server addr add: ${this.int32ip2str(addr.ip)}:${addr.port}`);
|
||||
this.sig.serverAddr.push({
|
||||
ip: this.int32ip2str(addr.ip),
|
||||
port: addr.port
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async uploadGroupImageReq(groupUin: number, img: PacketMsgPicElement): Promise<void> {
|
||||
if (!this.available) {
|
||||
this.logger.logError('[Highway] not ready to Upload image!');
|
||||
return;
|
||||
}
|
||||
const preReq = await this.packer.packUploadGroupImgReq(groupUin, img);
|
||||
const preRespRaw = await this.packetClient.sendPacket('OidbSvcTrpcTcp.0x11c4_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.log(`[Highway] get upload ukey: ${ukey}, need upload!`);
|
||||
this.logger.log(preRespData.upload.msgInfo)
|
||||
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: this.oidbIpv4s2HighwayIpv4s(preRespData.upload.ipv4S)
|
||||
},
|
||||
msgInfoBody: preRespData.upload.msgInfo.msgInfoBody,
|
||||
blockSize: BlockSize,
|
||||
hash: {
|
||||
fileSha1: [sha1]
|
||||
}
|
||||
})
|
||||
console.log('extend', Buffer.from(extend).toString('hex'))
|
||||
await this.packetHighwayClient.httpUpload(1004, fs.createReadStream(img.path, { highWaterMark: BlockSize }), img.size, md5, extend);
|
||||
} else {
|
||||
this.logger.logError(`[Highway] get upload invalid ukey ${ukey}, don't need upload!`);
|
||||
}
|
||||
img.msgInfo = preRespData.upload.msgInfo;
|
||||
// img.groupPicExt = new NapProtoMsg(CustomFace).decode(preRespData.tcpUpload.compatQMsg)
|
||||
}
|
||||
|
||||
async uploadImage(peer: Peer, img: PacketMsgPicElement): Promise<void> {
|
||||
await this.prepareUpload();
|
||||
if (!this.available) {
|
||||
this.logger.logError('[Highway] not ready to tcpUpload image!');
|
||||
return;
|
||||
}
|
||||
if (peer.chatType === ChatType.KCHATTYPEGROUP) {
|
||||
await this.uploadGroupImageReq(Number(peer.peerUid), img);
|
||||
}
|
||||
// const uploadReq
|
||||
}
|
||||
}
|
@ -1,12 +1,21 @@
|
||||
import * as crypto from "crypto";
|
||||
import {PushMsgBody} from "@/core/packet/proto/message/message";
|
||||
import {NapProtoEncodeStructType} from "@/core/packet/proto/NapProto";
|
||||
import * as crypto from "crypto";
|
||||
import {PacketForwardNode} from "@/core/packet/msg/entity/forward";
|
||||
import {LogWrapper} from "@/common/log";
|
||||
|
||||
export class PacketMsgBuilder {
|
||||
private logger: LogWrapper;
|
||||
|
||||
constructor(logger: LogWrapper) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
buildFakeMsg(selfUid: string, element: PacketForwardNode[]): NapProtoEncodeStructType<typeof PushMsgBody>[] {
|
||||
return element.map((node): NapProtoEncodeStructType<typeof PushMsgBody> => {
|
||||
const avatar = `https://q.qlogo.cn/headimg_dl?dst_uin=${node.senderId}&spec=640&img_type=jpg`;
|
||||
const msgElement = node.msg.map((msg) => msg.buildElement() ?? []);
|
||||
// this.logger.logDebug(`NOW MSG ELEMENT: ${JSON.stringify(msgElement)}`);
|
||||
return {
|
||||
responseHead: {
|
||||
fromUid: "",
|
||||
@ -41,9 +50,7 @@ export class PacketMsgBuilder {
|
||||
},
|
||||
body: {
|
||||
richText: {
|
||||
elems: node.msg.map(
|
||||
(msg) => msg.buildElement() ?? []
|
||||
)
|
||||
elems: msgElement
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,7 +1,9 @@
|
||||
import assert from "node:assert";
|
||||
import {NapProtoEncodeStructType, NapProtoMsg} from "@/core/packet/proto/NapProto";
|
||||
import { Elem, MentionExtra } from "@/core/packet/proto/message/element";
|
||||
import {CustomFace, Elem, MentionExtra, NotOnlineImage} from "@/core/packet/proto/message/element";
|
||||
import {
|
||||
AtType,
|
||||
PicType,
|
||||
SendArkElement,
|
||||
SendFaceElement,
|
||||
SendFileElement,
|
||||
@ -12,6 +14,7 @@ import {
|
||||
SendTextElement,
|
||||
SendVideoElement
|
||||
} from "@/core";
|
||||
import {MsgInfo} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||
|
||||
// raw <-> packet
|
||||
// TODO: check ob11 -> raw impl!
|
||||
@ -34,7 +37,6 @@ export class PacketMsgTextElement extends IPacketMsgElement<SendTextElement> {
|
||||
|
||||
constructor(element: SendTextElement) {
|
||||
super(element);
|
||||
console.log(JSON.stringify(element));
|
||||
this.text = element.textElement.content;
|
||||
}
|
||||
|
||||
@ -74,17 +76,48 @@ export class PacketMsgAtElement extends PacketMsgTextElement {
|
||||
}
|
||||
}
|
||||
|
||||
export class PacketMsgPicElement extends IPacketMsgElement<SendPicElement> {
|
||||
path: string;
|
||||
name: string
|
||||
size: number;
|
||||
md5: string;
|
||||
width: number;
|
||||
height: number;
|
||||
picType: PicType;
|
||||
sha1: string | null = null;
|
||||
msgInfo: NapProtoEncodeStructType<typeof MsgInfo> | null = null;
|
||||
groupPicExt: NapProtoEncodeStructType<typeof CustomFace> | null = null;
|
||||
c2cPicExt: NapProtoEncodeStructType<typeof NotOnlineImage> | null = null;
|
||||
|
||||
constructor(element: SendPicElement) {
|
||||
super(element);
|
||||
this.path = element.picElement.sourcePath;
|
||||
this.name = element.picElement.fileName;
|
||||
this.size = Number(element.picElement.fileSize);
|
||||
this.md5 = element.picElement.md5HexStr ?? '';
|
||||
this.width = element.picElement.picWidth;
|
||||
this.height = element.picElement.picHeight;
|
||||
this.picType = element.picElement.picType;
|
||||
}
|
||||
|
||||
buildElement(): NapProtoEncodeStructType<typeof Elem> {
|
||||
assert(this.msgInfo !== null, 'msgInfo is null, expected not null');
|
||||
return {
|
||||
commonElem: {
|
||||
serviceType: 48,
|
||||
pbElem: new NapProtoMsg(MsgInfo).encode(this.msgInfo),
|
||||
businessType: 10,
|
||||
}
|
||||
} as NapProtoEncodeStructType<typeof Elem>
|
||||
}
|
||||
}
|
||||
|
||||
export class PacketMsgPttElement extends IPacketMsgElement<SendPttElement> {
|
||||
constructor(element: SendPttElement) {
|
||||
super(element);
|
||||
}
|
||||
}
|
||||
|
||||
export class PacketMsgPicElement extends IPacketMsgElement<SendPicElement> {
|
||||
constructor(element: SendPicElement) {
|
||||
super(element);
|
||||
}
|
||||
}
|
||||
|
||||
export class PacketMsgReplyElement extends IPacketMsgElement<SendReplyElement> {
|
||||
constructor(element: SendReplyElement) {
|
||||
|
@ -1,21 +1,29 @@
|
||||
import * as zlib from "node:zlib";
|
||||
import * as crypto from "node:crypto";
|
||||
import {calculateSha1} from "@/core/packet/utils/crypto/hash"
|
||||
import {NapProtoMsg} from "@/core/packet/proto/NapProto";
|
||||
import {OidbSvcTrpcTcpBase} from "@/core/packet/proto/oidb/OidbBase";
|
||||
import {OidbSvcTrpcTcp0X9067_202} from "@/core/packet/proto/oidb/Oidb.0x9067_202";
|
||||
import {OidbSvcTrpcTcp0X8FC_2, OidbSvcTrpcTcp0X8FC_2_Body} from "@/core/packet/proto/oidb/Oidb.0x8FC_2";
|
||||
import {OidbSvcTrpcTcp0XFE1_2} from "@/core/packet/proto/oidb/Oidb.fe1_2";
|
||||
import {OidbSvcTrpcTcp0XED3_1} from "@/core/packet/proto/oidb/Oidb.ed3_1";
|
||||
import {NTV2RichMediaReq} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||
import {HttpConn0x6ff_501} from "@/core/packet/proto/action/action";
|
||||
import {LongMsgResult, SendLongMsgReq} from "@/core/packet/proto/message/action";
|
||||
import {PacketMsgBuilder} from "@/core/packet/msg/builder";
|
||||
import {PacketForwardNode} from "@/core/packet/msg/entity/forward";
|
||||
import {PacketMsgPicElement} from "@/core/packet/msg/element";
|
||||
import {LogWrapper} from "@/common/log";
|
||||
|
||||
export type PacketHexStr = string & { readonly hexNya: unique symbol };
|
||||
|
||||
export class PacketPacker {
|
||||
private readonly logger: LogWrapper;
|
||||
private readonly packetBuilder: PacketMsgBuilder;
|
||||
|
||||
constructor() {
|
||||
this.packetBuilder = new PacketMsgBuilder();
|
||||
constructor(logger: LogWrapper) {
|
||||
this.logger = logger;
|
||||
this.packetBuilder = new PacketMsgBuilder(logger);
|
||||
}
|
||||
|
||||
private toHexStr(byteArray: Uint8Array): PacketHexStr {
|
||||
@ -87,7 +95,7 @@ export class PacketPacker {
|
||||
}
|
||||
|
||||
packUploadForwardMsg(selfUid: string, msg: PacketForwardNode[], groupUin: number = 0): PacketHexStr {
|
||||
// console.log("packUploadForwardMsg START!!!", selfUid, msg, groupUin);
|
||||
// this.logger.logDebug("packUploadForwardMsg START!!!", selfUid, msg, groupUin);
|
||||
const msgBody = this.packetBuilder.buildFakeMsg(selfUid, msg);
|
||||
const longMsgResultData = new NapProtoMsg(LongMsgResult).encode(
|
||||
{
|
||||
@ -99,9 +107,9 @@ export class PacketPacker {
|
||||
}
|
||||
}
|
||||
);
|
||||
// console.log("packUploadForwardMsg LONGMSGRESULT!!!", this.toHexStr(longMsgResultData));
|
||||
this.logger.logDebug("packUploadForwardMsg LONGMSGRESULT!!!", this.toHexStr(longMsgResultData));
|
||||
const payload = zlib.gzipSync(Buffer.from(longMsgResultData));
|
||||
// console.log("packUploadForwardMsg PAYLOAD!!!", payload);
|
||||
// this.logger.logDebug("packUploadForwardMsg PAYLOAD!!!", payload);
|
||||
const req = new NapProtoMsg(SendLongMsgReq).encode(
|
||||
{
|
||||
info: {
|
||||
@ -117,7 +125,94 @@ export class PacketPacker {
|
||||
}
|
||||
}
|
||||
);
|
||||
// console.log("packUploadForwardMsg REQ!!!", req);
|
||||
// this.logger.logDebug("packUploadForwardMsg REQ!!!", req);
|
||||
return this.toHexStr(req);
|
||||
}
|
||||
|
||||
// highway part
|
||||
packHttp0x6ff_501() {
|
||||
return this.toHexStr(new NapProtoMsg(HttpConn0x6ff_501).encode({
|
||||
httpConn: {
|
||||
field1: 0,
|
||||
field2: 0,
|
||||
field3: 16,
|
||||
field4: 1,
|
||||
field6: 3,
|
||||
serviceTypes: [1, 5, 10, 21],
|
||||
// tgt: "", // TODO: do we really need tgt? seems not
|
||||
field9: 2,
|
||||
field10: 9,
|
||||
field11: 8,
|
||||
ver: "1.0.1"
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
async packUploadGroupImgReq(groupUin: number, img: PacketMsgPicElement) {
|
||||
const req = new NapProtoMsg(NTV2RichMediaReq).encode(
|
||||
{
|
||||
reqHead: {
|
||||
common: {
|
||||
requestId: 1,
|
||||
command: 100
|
||||
},
|
||||
scene: {
|
||||
requestType: 2,
|
||||
businessType: 1,
|
||||
sceneType: 2,
|
||||
group: {
|
||||
groupUin: groupUin
|
||||
},
|
||||
},
|
||||
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: 2,
|
||||
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(0x11c4, 100, req, true, false));
|
||||
}
|
||||
}
|
||||
|
114
src/core/packet/proto/action/action.ts
Normal file
114
src/core/packet/proto/action/action.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { ScalarType } from "@protobuf-ts/runtime";
|
||||
import { ProtoField } from "../NapProto";
|
||||
import {ContentHead, MessageBody, MessageControl, RoutingHead} from "@/core/packet/proto/message/message";
|
||||
|
||||
export const FaceRoamRequest = {
|
||||
comm: ProtoField(1, () => PlatInfo, true),
|
||||
selfUin: ProtoField(2, ScalarType.UINT32),
|
||||
subCmd: ProtoField(3, ScalarType.UINT32),
|
||||
field6: ProtoField(6, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const PlatInfo = {
|
||||
imPlat: ProtoField(1, ScalarType.UINT32),
|
||||
osVersion: ProtoField(2, ScalarType.STRING, true),
|
||||
qVersion: ProtoField(3, ScalarType.STRING, true),
|
||||
};
|
||||
|
||||
export const FaceRoamResponse = {
|
||||
retCode: ProtoField(1, ScalarType.UINT32),
|
||||
errMsg: ProtoField(2, ScalarType.STRING),
|
||||
subCmd: ProtoField(3, ScalarType.UINT32),
|
||||
userInfo: ProtoField(6, () => FaceRoamUserInfo),
|
||||
};
|
||||
|
||||
export const FaceRoamUserInfo = {
|
||||
fileName: ProtoField(1, ScalarType.STRING, false, true),
|
||||
deleteFile: ProtoField(2, ScalarType.STRING, false, true),
|
||||
bid: ProtoField(3, ScalarType.STRING),
|
||||
maxRoamSize: ProtoField(4, ScalarType.UINT32),
|
||||
emojiType: ProtoField(5, ScalarType.UINT32, false, true),
|
||||
};
|
||||
|
||||
export const SendMessageRequest = {
|
||||
state: ProtoField(1, ScalarType.INT32),
|
||||
sizeCache: ProtoField(2, ScalarType.INT32),
|
||||
unknownFields: ProtoField(3, ScalarType.BYTES),
|
||||
routingHead: ProtoField(4, () => RoutingHead),
|
||||
contentHead: ProtoField(5, () => ContentHead),
|
||||
messageBody: ProtoField(6, () => MessageBody),
|
||||
msgSeq: ProtoField(7, ScalarType.INT32),
|
||||
msgRand: ProtoField(8, ScalarType.INT32),
|
||||
syncCookie: ProtoField(9, ScalarType.BYTES),
|
||||
msgVia: ProtoField(10, ScalarType.INT32),
|
||||
dataStatist: ProtoField(11, ScalarType.INT32),
|
||||
messageControl: ProtoField(12, () => MessageControl),
|
||||
multiSendSeq: ProtoField(13, ScalarType.INT32),
|
||||
};
|
||||
|
||||
export const SendMessageResponse = {
|
||||
result: ProtoField(1, ScalarType.INT32),
|
||||
errMsg: ProtoField(2, ScalarType.STRING, true),
|
||||
timestamp1: ProtoField(3, ScalarType.UINT32),
|
||||
field10: ProtoField(10, ScalarType.UINT32),
|
||||
groupSequence: ProtoField(11, ScalarType.UINT32, true),
|
||||
timestamp2: ProtoField(12, ScalarType.UINT32),
|
||||
privateSequence: ProtoField(14, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const SetStatus = {
|
||||
status: ProtoField(1, ScalarType.UINT32),
|
||||
extStatus: ProtoField(2, ScalarType.UINT32),
|
||||
batteryStatus: ProtoField(3, ScalarType.UINT32),
|
||||
customExt: ProtoField(4, () => SetStatusCustomExt, true),
|
||||
};
|
||||
|
||||
export const SetStatusCustomExt = {
|
||||
faceId: ProtoField(1, ScalarType.UINT32),
|
||||
text: ProtoField(2, ScalarType.STRING, true),
|
||||
field3: ProtoField(3, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const SetStatusResponse = {
|
||||
message: ProtoField(2, ScalarType.STRING),
|
||||
};
|
||||
|
||||
export const HttpConn = {
|
||||
field1: ProtoField(1, ScalarType.INT32),
|
||||
field2: ProtoField(2, ScalarType.INT32),
|
||||
field3: ProtoField(3, ScalarType.INT32),
|
||||
field4: ProtoField(4, ScalarType.INT32),
|
||||
tgt: ProtoField(5, ScalarType.STRING),
|
||||
field6: ProtoField(6, ScalarType.INT32),
|
||||
serviceTypes: ProtoField(7, ScalarType.INT32, false, true),
|
||||
field9: ProtoField(9, ScalarType.INT32),
|
||||
field10: ProtoField(10, ScalarType.INT32),
|
||||
field11: ProtoField(11, ScalarType.INT32),
|
||||
ver: ProtoField(15, ScalarType.STRING),
|
||||
};
|
||||
|
||||
export const HttpConn0x6ff_501 = {
|
||||
httpConn: ProtoField(0x501, () => HttpConn),
|
||||
};
|
||||
|
||||
export const HttpConn0x6ff_501Response = {
|
||||
httpConn: ProtoField(0x501, () => HttpConnResponse),
|
||||
};
|
||||
|
||||
export const HttpConnResponse = {
|
||||
sigSession: ProtoField(1, ScalarType.BYTES),
|
||||
sessionKey: ProtoField(2, ScalarType.BYTES),
|
||||
serverInfos: ProtoField(3, () => ServerInfo, false, true),
|
||||
};
|
||||
|
||||
export const ServerAddr = {
|
||||
type: ProtoField(1, ScalarType.UINT32),
|
||||
ip: ProtoField(2, ScalarType.FIXED32),
|
||||
port: ProtoField(3, ScalarType.UINT32),
|
||||
area: ProtoField(4, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const ServerInfo = {
|
||||
serviceType: ProtoField(1, ScalarType.UINT32),
|
||||
serverAddrs: ProtoField(2, () => ServerAddr, false, true),
|
||||
};
|
155
src/core/packet/proto/highway/highway.ts
Normal file
155
src/core/packet/proto/highway/highway.ts
Normal file
@ -0,0 +1,155 @@
|
||||
import {ScalarType} from "@protobuf-ts/runtime";
|
||||
import {ProtoField} from "../NapProto";
|
||||
import {MsgInfo, MsgInfoBody} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||
|
||||
export const DataHighwayHead = {
|
||||
version: ProtoField(1, ScalarType.UINT32),
|
||||
uin: ProtoField(2, ScalarType.STRING, true),
|
||||
command: ProtoField(3, ScalarType.STRING, true),
|
||||
seq: ProtoField(4, ScalarType.UINT32, true),
|
||||
retryTimes: ProtoField(5, ScalarType.UINT32, true),
|
||||
appId: ProtoField(6, ScalarType.UINT32),
|
||||
dataFlag: ProtoField(7, ScalarType.UINT32),
|
||||
commandId: ProtoField(8, ScalarType.UINT32),
|
||||
buildVer: ProtoField(9, ScalarType.BYTES, true),
|
||||
}
|
||||
|
||||
export const FileUploadExt = {
|
||||
unknown1: ProtoField(1, ScalarType.INT32),
|
||||
unknown2: ProtoField(2, ScalarType.INT32),
|
||||
unknown3: ProtoField(3, ScalarType.INT32),
|
||||
entry: ProtoField(100, () => FileUploadEntry),
|
||||
unknown200: ProtoField(200, ScalarType.INT32),
|
||||
}
|
||||
|
||||
export const FileUploadEntry = {
|
||||
busiBuff: ProtoField(100, () => ExcitingBusiInfo),
|
||||
fileEntry: ProtoField(200, () => ExcitingFileEntry),
|
||||
clientInfo: ProtoField(300, () => ExcitingClientInfo),
|
||||
fileNameInfo: ProtoField(400, () => ExcitingFileNameInfo),
|
||||
host: ProtoField(500, () => ExcitingHostConfig),
|
||||
}
|
||||
|
||||
export const ExcitingBusiInfo = {
|
||||
busId: ProtoField(1, ScalarType.INT32),
|
||||
senderUin: ProtoField(100, ScalarType.UINT64),
|
||||
receiverUin: ProtoField(200, ScalarType.UINT64),
|
||||
groupCode: ProtoField(400, ScalarType.UINT64),
|
||||
}
|
||||
|
||||
export const ExcitingFileEntry = {
|
||||
fileSize: ProtoField(100, ScalarType.UINT64),
|
||||
md5: ProtoField(200, ScalarType.BYTES),
|
||||
checkKey: ProtoField(300, ScalarType.BYTES),
|
||||
md5S2: ProtoField(400, ScalarType.BYTES),
|
||||
fileId: ProtoField(600, ScalarType.STRING),
|
||||
uploadKey: ProtoField(700, ScalarType.BYTES),
|
||||
}
|
||||
|
||||
export const ExcitingClientInfo = {
|
||||
clientType: ProtoField(100, ScalarType.INT32),
|
||||
appId: ProtoField(200, ScalarType.STRING),
|
||||
terminalType: ProtoField(300, ScalarType.INT32),
|
||||
clientVer: ProtoField(400, ScalarType.STRING),
|
||||
unknown: ProtoField(600, ScalarType.INT32),
|
||||
}
|
||||
|
||||
export const ExcitingFileNameInfo = {
|
||||
fileName: ProtoField(100, ScalarType.STRING),
|
||||
}
|
||||
|
||||
export const ExcitingHostConfig = {
|
||||
hosts: ProtoField(200, () => ExcitingHostInfo, false, true),
|
||||
}
|
||||
|
||||
export const ExcitingHostInfo = {
|
||||
url: ProtoField(1, () => ExcitingUrlInfo),
|
||||
port: ProtoField(2, ScalarType.UINT32),
|
||||
}
|
||||
|
||||
export const ExcitingUrlInfo = {
|
||||
unknown: ProtoField(1, ScalarType.INT32),
|
||||
host: ProtoField(2, ScalarType.STRING),
|
||||
}
|
||||
|
||||
export const LoginSigHead = {
|
||||
uint32LoginSigType: ProtoField(1, ScalarType.UINT32),
|
||||
bytesLoginSig: ProtoField(2, ScalarType.BYTES),
|
||||
appId: ProtoField(3, ScalarType.UINT32),
|
||||
}
|
||||
|
||||
export const NTV2RichMediaHighwayExt = {
|
||||
fileUuid: ProtoField(1, ScalarType.STRING),
|
||||
uKey: ProtoField(2, ScalarType.STRING),
|
||||
network: ProtoField(5, () => NTHighwayNetwork),
|
||||
msgInfoBody: ProtoField(6, () => MsgInfoBody, false, true),
|
||||
blockSize: ProtoField(10, ScalarType.UINT32),
|
||||
hash: ProtoField(11, () => NTHighwayHash),
|
||||
}
|
||||
|
||||
export const NTHighwayHash = {
|
||||
fileSha1: ProtoField(1, ScalarType.BYTES, false, true),
|
||||
}
|
||||
|
||||
export const NTHighwayNetwork = {
|
||||
ipv4s: ProtoField(1, () => NTHighwayIPv4, false, true),
|
||||
}
|
||||
|
||||
export const NTHighwayIPv4 = {
|
||||
domain: ProtoField(1, () => NTHighwayDomain),
|
||||
port: ProtoField(2, ScalarType.UINT32),
|
||||
}
|
||||
|
||||
export const NTHighwayDomain = {
|
||||
isEnable: ProtoField(1, ScalarType.BOOL),
|
||||
ip: ProtoField(2, ScalarType.STRING),
|
||||
}
|
||||
|
||||
export const ReqDataHighwayHead = {
|
||||
msgBaseHead: ProtoField(1, () => DataHighwayHead, true),
|
||||
msgSegHead: ProtoField(2, () => SegHead, true),
|
||||
bytesReqExtendInfo: ProtoField(3, ScalarType.BYTES, true),
|
||||
timestamp: ProtoField(4, ScalarType.UINT64),
|
||||
msgLoginSigHead: ProtoField(5, () => LoginSigHead, true),
|
||||
}
|
||||
|
||||
export const RespDataHighwayHead = {
|
||||
msgBaseHead: ProtoField(1, () => DataHighwayHead, true),
|
||||
msgSegHead: ProtoField(2, () => SegHead, true),
|
||||
errorCode: ProtoField(3, ScalarType.UINT32),
|
||||
allowRetry: ProtoField(4, ScalarType.UINT32),
|
||||
cacheCost: ProtoField(5, ScalarType.UINT32),
|
||||
htCost: ProtoField(6, ScalarType.UINT32),
|
||||
bytesRspExtendInfo: ProtoField(7, ScalarType.BYTES, true),
|
||||
timestamp: ProtoField(8, ScalarType.UINT64),
|
||||
range: ProtoField(9, ScalarType.UINT64),
|
||||
isReset: ProtoField(10, ScalarType.UINT32),
|
||||
}
|
||||
|
||||
export const SegHead = {
|
||||
serviceId: ProtoField(1, ScalarType.UINT32, true),
|
||||
filesize: ProtoField(2, ScalarType.UINT64),
|
||||
dataOffset: ProtoField(3, ScalarType.UINT64, true),
|
||||
dataLength: ProtoField(4, ScalarType.UINT32),
|
||||
retCode: ProtoField(5, ScalarType.UINT32, true),
|
||||
serviceTicket: ProtoField(6, ScalarType.BYTES),
|
||||
flag: ProtoField(7, ScalarType.UINT32, true),
|
||||
md5: ProtoField(8, ScalarType.BYTES),
|
||||
fileMd5: ProtoField(9, ScalarType.BYTES),
|
||||
cacheAddr: ProtoField(10, ScalarType.UINT32, true),
|
||||
queryTimes: ProtoField(11, ScalarType.UINT32),
|
||||
updateCacheIp: ProtoField(12, ScalarType.UINT32),
|
||||
cachePort: ProtoField(13, ScalarType.UINT32, true),
|
||||
}
|
||||
|
||||
export const GroupAvatarExtra = {
|
||||
type: ProtoField(1, ScalarType.UINT32),
|
||||
groupUin: ProtoField(2, ScalarType.UINT32),
|
||||
field3: ProtoField(3, () => GroupAvatarExtraField3),
|
||||
field5: ProtoField(5, ScalarType.UINT32),
|
||||
field6: ProtoField(6, ScalarType.UINT32),
|
||||
}
|
||||
|
||||
export const GroupAvatarExtraField3 = {
|
||||
field1: ProtoField(1, ScalarType.UINT32),
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { ScalarType } from "@protobuf-ts/runtime";
|
||||
import { ProtoField } from "../NapProto";
|
||||
import { MultiMediaReqHead } from "./common/Ntv2.RichMedia";
|
||||
import { MultiMediaReqHead } from "./common/Ntv2.RichMediaReq";
|
||||
|
||||
//Req
|
||||
export const OidbSvcTrpcTcp0X9067_202 = {
|
||||
|
@ -1,83 +0,0 @@
|
||||
import { ScalarType } from "@protobuf-ts/runtime";
|
||||
import { ProtoField } from "../../NapProto";
|
||||
|
||||
export const NTV2RichMediaReq = {
|
||||
ReqHead: ProtoField(1, ScalarType.BYTES),
|
||||
DownloadRKeyReq: ProtoField(4, ScalarType.BYTES),
|
||||
};
|
||||
export const MultiMediaReqHead = {
|
||||
Common: ProtoField(1, () => CommonHead),
|
||||
Scene: ProtoField(2, () => SceneInfo),
|
||||
Client: ProtoField(3, () => ClientMeta),
|
||||
};
|
||||
export const CommonHead = {
|
||||
RequestId: ProtoField(1, ScalarType.UINT32),
|
||||
Command: ProtoField(2, ScalarType.UINT32),
|
||||
};
|
||||
export const SceneInfo = {
|
||||
RequestType: ProtoField(101, ScalarType.UINT32),
|
||||
BusinessType: ProtoField(102, ScalarType.UINT32),
|
||||
SceneType: ProtoField(200, ScalarType.UINT32),
|
||||
};
|
||||
export const ClientMeta = {
|
||||
AgentType: ProtoField(1, ScalarType.UINT32),
|
||||
};
|
||||
export const C2CUserInfo = {
|
||||
AccountType: ProtoField(1, ScalarType.UINT32),
|
||||
TargetUid: ProtoField(2, ScalarType.STRING),
|
||||
};
|
||||
export const GroupInfo = {
|
||||
GroupUin: ProtoField(1, ScalarType.UINT32),
|
||||
};
|
||||
export const DownloadReq = {
|
||||
Node: ProtoField(1, ScalarType.BYTES),
|
||||
Download: ProtoField(2, ScalarType.BYTES),
|
||||
};
|
||||
export const FileInfo = {
|
||||
FileSize: ProtoField(1, ScalarType.UINT32),
|
||||
FileHash: ProtoField(2, ScalarType.STRING),
|
||||
FileSha1: ProtoField(3, ScalarType.STRING),
|
||||
FileName: ProtoField(4, ScalarType.STRING),
|
||||
Type: ProtoField(5, ScalarType.BYTES),
|
||||
Width: ProtoField(6, ScalarType.UINT32),
|
||||
Height: ProtoField(7, ScalarType.UINT32),
|
||||
Time: ProtoField(8, ScalarType.UINT32),
|
||||
Original: ProtoField(9, ScalarType.UINT32),
|
||||
};
|
||||
export const IndexNode = {
|
||||
Info: ProtoField(1, ScalarType.BYTES),
|
||||
FileUuid: ProtoField(2, ScalarType.STRING),
|
||||
StoreId: ProtoField(3, ScalarType.UINT32),
|
||||
UploadTime: ProtoField(4, ScalarType.UINT32),
|
||||
Ttl: ProtoField(5, ScalarType.UINT32),
|
||||
subType: ProtoField(6, ScalarType.UINT32),
|
||||
};
|
||||
export const FileType = {
|
||||
Type: ProtoField(1, ScalarType.UINT32),
|
||||
PicFormat: ProtoField(2, ScalarType.UINT32),
|
||||
VideoFormat: ProtoField(3, ScalarType.UINT32),
|
||||
VoiceFormat: ProtoField(4, ScalarType.UINT32),
|
||||
};
|
||||
export const DownloadExt = {
|
||||
Pic: ProtoField(1, ScalarType.BYTES),
|
||||
Video: ProtoField(2, ScalarType.BYTES),
|
||||
Ptt: ProtoField(3, ScalarType.BYTES),
|
||||
};
|
||||
export const VideoDownloadExt = {
|
||||
BusiType: ProtoField(1, ScalarType.UINT32),
|
||||
SceneType: ProtoField(2, ScalarType.UINT32),
|
||||
SubBusiType: ProtoField(3, ScalarType.UINT32),
|
||||
};
|
||||
export const PicDownloadExt = {};
|
||||
export const PttDownloadExt = {};
|
||||
export const PicUrlExtInfo = {
|
||||
OriginalParameter: ProtoField(1, ScalarType.STRING),
|
||||
BigParameter: ProtoField(2, ScalarType.STRING),
|
||||
ThumbParameter: ProtoField(3, ScalarType.STRING),
|
||||
};
|
||||
export const VideoExtInfo = {
|
||||
VideoCodecFormat: ProtoField(1, ScalarType.UINT32),
|
||||
};
|
||||
export const MsgInfo = {
|
||||
|
||||
};
|
214
src/core/packet/proto/oidb/common/Ntv2.RichMediaReq.ts
Normal file
214
src/core/packet/proto/oidb/common/Ntv2.RichMediaReq.ts
Normal file
@ -0,0 +1,214 @@
|
||||
import {ScalarType} from "@protobuf-ts/runtime";
|
||||
import {ProtoField} from "../../NapProto";
|
||||
|
||||
export const NTV2RichMediaReq = {
|
||||
ReqHead: ProtoField(1, () => MultiMediaReqHead),
|
||||
Upload: ProtoField(2, () => UploadReq),
|
||||
Download: ProtoField(3, () => DownloadReq),
|
||||
DownloadRKey: ProtoField(4, () => DownloadRKeyReq),
|
||||
Delete: ProtoField(5, () => DeleteReq),
|
||||
UploadCompleted: ProtoField(6, () => UploadCompletedReq),
|
||||
MsgInfoAuth: ProtoField(7, () => MsgInfoAuthReq),
|
||||
UploadKeyRenewal: ProtoField(8, () => UploadKeyRenewalReq),
|
||||
DownloadSafe: ProtoField(9, () => DownloadSafeReq),
|
||||
Extension: ProtoField(99, ScalarType.BYTES, true),
|
||||
};
|
||||
|
||||
export const MultiMediaReqHead = {
|
||||
Common: ProtoField(1, () => CommonHead),
|
||||
Scene: ProtoField(2, () => SceneInfo),
|
||||
Client: ProtoField(3, () => ClientMeta),
|
||||
};
|
||||
|
||||
export const CommonHead = {
|
||||
RequestId: ProtoField(1, ScalarType.UINT32),
|
||||
Command: ProtoField(2, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const SceneInfo = {
|
||||
RequestType: ProtoField(101, ScalarType.UINT32),
|
||||
BusinessType: ProtoField(102, ScalarType.UINT32),
|
||||
SceneType: ProtoField(200, ScalarType.UINT32),
|
||||
C2C: ProtoField(201, () => C2CUserInfo, true),
|
||||
Group: ProtoField(202, () => NTGroupInfo, true),
|
||||
};
|
||||
|
||||
export const C2CUserInfo = {
|
||||
AccountType: ProtoField(1, ScalarType.UINT32),
|
||||
TargetUid: ProtoField(2, ScalarType.STRING),
|
||||
};
|
||||
|
||||
export const NTGroupInfo = {
|
||||
GroupUin: ProtoField(1, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const ClientMeta = {
|
||||
AgentType: ProtoField(1, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const DownloadReq = {
|
||||
Node: ProtoField(1, () => IndexNode),
|
||||
Download: ProtoField(2, () => DownloadExt),
|
||||
};
|
||||
|
||||
export const IndexNode = {
|
||||
Info: ProtoField(1, () => FileInfo),
|
||||
FileUuid: ProtoField(2, ScalarType.STRING),
|
||||
StoreId: ProtoField(3, ScalarType.UINT32),
|
||||
UploadTime: ProtoField(4, ScalarType.UINT32),
|
||||
Ttl: ProtoField(5, ScalarType.UINT32),
|
||||
SubType: ProtoField(6, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const FileInfo = {
|
||||
FileSize: ProtoField(1, ScalarType.UINT32),
|
||||
FileHash: ProtoField(2, ScalarType.STRING),
|
||||
FileSha1: ProtoField(3, ScalarType.STRING),
|
||||
FileName: ProtoField(4, ScalarType.STRING),
|
||||
Type: ProtoField(5, () => FileType),
|
||||
Width: ProtoField(6, ScalarType.UINT32),
|
||||
Height: ProtoField(7, ScalarType.UINT32),
|
||||
Time: ProtoField(8, ScalarType.UINT32),
|
||||
Original: ProtoField(9, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const FileType = {
|
||||
Type: ProtoField(1, ScalarType.UINT32),
|
||||
PicFormat: ProtoField(2, ScalarType.UINT32),
|
||||
VideoFormat: ProtoField(3, ScalarType.UINT32),
|
||||
VoiceFormat: ProtoField(4, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const DownloadExt = {
|
||||
Pic: ProtoField(1, () => PicDownloadExt),
|
||||
Video: ProtoField(2, () => VideoDownloadExt),
|
||||
Ptt: ProtoField(3, () => PttDownloadExt),
|
||||
};
|
||||
|
||||
export const VideoDownloadExt = {
|
||||
BusiType: ProtoField(1, ScalarType.UINT32),
|
||||
SceneType: ProtoField(2, ScalarType.UINT32),
|
||||
SubBusiType: ProtoField(3, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const PicDownloadExt = {};
|
||||
|
||||
export const PttDownloadExt = {};
|
||||
|
||||
export const DownloadRKeyReq = {
|
||||
Types: ProtoField(1, ScalarType.INT32, false, true),
|
||||
};
|
||||
|
||||
export const DeleteReq = {
|
||||
Index: ProtoField(1, () => IndexNode, false, true),
|
||||
NeedRecallMsg: ProtoField(2, ScalarType.BOOL),
|
||||
MsgSeq: ProtoField(3, ScalarType.UINT64),
|
||||
MsgRandom: ProtoField(4, ScalarType.UINT64),
|
||||
MsgTime: ProtoField(5, ScalarType.UINT64),
|
||||
};
|
||||
|
||||
export const UploadCompletedReq = {
|
||||
SrvSendMsg: ProtoField(1, ScalarType.BOOL),
|
||||
ClientRandomId: ProtoField(2, ScalarType.UINT64),
|
||||
MsgInfo: ProtoField(3, () => MsgInfo),
|
||||
ClientSeq: ProtoField(4, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const MsgInfoAuthReq = {
|
||||
Msg: ProtoField(1, ScalarType.BYTES),
|
||||
AuthTime: ProtoField(2, ScalarType.UINT64),
|
||||
};
|
||||
|
||||
export const DownloadSafeReq = {
|
||||
Index: ProtoField(1, () => IndexNode),
|
||||
};
|
||||
|
||||
export const UploadKeyRenewalReq = {
|
||||
OldUKey: ProtoField(1, ScalarType.STRING),
|
||||
SubType: ProtoField(2, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const MsgInfo = {
|
||||
MsgInfoBody: ProtoField(1, () => MsgInfoBody, false, true),
|
||||
ExtBizInfo: ProtoField(2, () => ExtBizInfo),
|
||||
};
|
||||
|
||||
export const MsgInfoBody = {
|
||||
Index: ProtoField(1, () => IndexNode),
|
||||
Picture: ProtoField(2, () => PictureInfo),
|
||||
Video: ProtoField(3, () => VideoInfo),
|
||||
Audio: ProtoField(4, () => AudioInfo),
|
||||
FileExist: ProtoField(5, ScalarType.BOOL),
|
||||
HashSum: ProtoField(6, ScalarType.BYTES),
|
||||
};
|
||||
|
||||
export const VideoInfo = {};
|
||||
|
||||
export const AudioInfo = {};
|
||||
|
||||
export const PictureInfo = {
|
||||
UrlPath: ProtoField(1, ScalarType.STRING),
|
||||
Ext: ProtoField(2, () => PicUrlExtInfo),
|
||||
Domain: ProtoField(3, ScalarType.STRING),
|
||||
};
|
||||
|
||||
export const PicUrlExtInfo = {
|
||||
OriginalParameter: ProtoField(1, ScalarType.STRING),
|
||||
BigParameter: ProtoField(2, ScalarType.STRING),
|
||||
ThumbParameter: ProtoField(3, ScalarType.STRING),
|
||||
};
|
||||
|
||||
export const VideoExtInfo = {
|
||||
VideoCodecFormat: ProtoField(1, ScalarType.UINT32),
|
||||
}
|
||||
|
||||
export const ExtBizInfo = {
|
||||
Pic: ProtoField(1, () => PicExtBizInfo),
|
||||
Video: ProtoField(2, () => VideoExtBizInfo),
|
||||
Ptt: ProtoField(3, () => PttExtBizInfo),
|
||||
BusiType: ProtoField(10, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const PttExtBizInfo = {
|
||||
SrcUin: ProtoField(1, ScalarType.UINT64),
|
||||
PttScene: ProtoField(2, ScalarType.UINT32),
|
||||
PttType: ProtoField(3, ScalarType.UINT32),
|
||||
ChangeVoice: ProtoField(4, ScalarType.UINT32),
|
||||
Waveform: ProtoField(5, ScalarType.BYTES),
|
||||
AutoConvertText: ProtoField(6, ScalarType.UINT32),
|
||||
BytesReserve: ProtoField(11, ScalarType.BYTES),
|
||||
BytesPbReserve: ProtoField(12, ScalarType.BYTES),
|
||||
BytesGeneralFlags: ProtoField(13, ScalarType.BYTES),
|
||||
};
|
||||
|
||||
export const VideoExtBizInfo = {
|
||||
FromScene: ProtoField(1, ScalarType.UINT32),
|
||||
ToScene: ProtoField(2, ScalarType.UINT32),
|
||||
BytesPbReserve: ProtoField(3, ScalarType.BYTES),
|
||||
};
|
||||
|
||||
export const PicExtBizInfo = {
|
||||
BizType: ProtoField(1, ScalarType.UINT32),
|
||||
TextSummary: ProtoField(2, ScalarType.STRING),
|
||||
BytesPbReserveC2c: ProtoField(11, ScalarType.BYTES),
|
||||
BytesPbReserveTroop: ProtoField(12, ScalarType.BYTES),
|
||||
FromScene: ProtoField(1001, ScalarType.UINT32),
|
||||
ToScene: ProtoField(1002, ScalarType.UINT32),
|
||||
OldFileId: ProtoField(1003, ScalarType.UINT32),
|
||||
};
|
||||
|
||||
export const UploadReq = {
|
||||
UploadInfo: ProtoField(1, () => UploadInfo, false, true),
|
||||
TryFastUploadCompleted: ProtoField(2, ScalarType.BOOL),
|
||||
SrvSendMsg: ProtoField(3, ScalarType.BOOL),
|
||||
ClientRandomId: ProtoField(4, ScalarType.UINT64),
|
||||
CompatQMsgSceneType: ProtoField(5, ScalarType.UINT32),
|
||||
ExtBizInfo: ProtoField(6, () => ExtBizInfo),
|
||||
ClientSeq: ProtoField(7, ScalarType.UINT32),
|
||||
NoNeedCompatMsg: ProtoField(8, ScalarType.BOOL),
|
||||
};
|
||||
|
||||
export const UploadInfo = {
|
||||
FileInfo: ProtoField(1, () => FileInfo),
|
||||
SubFileType: ProtoField(2, ScalarType.UINT32),
|
||||
};
|
114
src/core/packet/proto/oidb/common/Ntv2.RichMediaResp.ts
Normal file
114
src/core/packet/proto/oidb/common/Ntv2.RichMediaResp.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import {ScalarType} from "@protobuf-ts/runtime";
|
||||
import {ProtoField} from "../../NapProto";
|
||||
import {CommonHead, MsgInfo, PicUrlExtInfo, VideoExtInfo} from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||
|
||||
export const NTV2RichMediaResp = {
|
||||
respHead: ProtoField(1, () => MultiMediaRespHead),
|
||||
upload: ProtoField(2, () => UploadResp),
|
||||
download: ProtoField(3, () => DownloadResp),
|
||||
downloadRKey: ProtoField(4, () => DownloadRKeyResp),
|
||||
delete: ProtoField(5, () => DeleteResp),
|
||||
uploadCompleted: ProtoField(6, () => UploadCompletedResp),
|
||||
msgInfoAuth: ProtoField(7, () => MsgInfoAuthResp),
|
||||
uploadKeyRenewal: ProtoField(8, () => UploadKeyRenewalResp),
|
||||
downloadSafe: ProtoField(9, () => DownloadSafeResp),
|
||||
extension: ProtoField(99, ScalarType.BYTES, true),
|
||||
}
|
||||
|
||||
export const MultiMediaRespHead = {
|
||||
common: ProtoField(1, () => CommonHead),
|
||||
retCode: ProtoField(2, ScalarType.UINT32),
|
||||
message: ProtoField(3, ScalarType.STRING),
|
||||
}
|
||||
|
||||
export const DownloadResp = {
|
||||
rKeyParam: ProtoField(1, ScalarType.STRING),
|
||||
rKeyTtlSecond: ProtoField(2, ScalarType.UINT32),
|
||||
info: ProtoField(3, () => DownloadInfo),
|
||||
rKeyCreateTime: ProtoField(4, ScalarType.UINT32),
|
||||
}
|
||||
|
||||
export const DownloadInfo = {
|
||||
domain: ProtoField(1, ScalarType.STRING),
|
||||
urlPath: ProtoField(2, ScalarType.STRING),
|
||||
httpsPort: ProtoField(3, ScalarType.UINT32),
|
||||
ipv4s: ProtoField(4, () => IPv4, false, true),
|
||||
ipv6s: ProtoField(5, () => IPv6, false, true),
|
||||
picUrlExtInfo: ProtoField(6, () => PicUrlExtInfo),
|
||||
videoExtInfo: ProtoField(7, () => VideoExtInfo),
|
||||
}
|
||||
|
||||
export const IPv4 = {
|
||||
outIP: ProtoField(1, ScalarType.UINT32),
|
||||
outPort: ProtoField(2, ScalarType.UINT32),
|
||||
inIP: ProtoField(3, ScalarType.UINT32),
|
||||
inPort: ProtoField(4, ScalarType.UINT32),
|
||||
ipType: ProtoField(5, ScalarType.UINT32),
|
||||
}
|
||||
|
||||
export const IPv6 = {
|
||||
outIP: ProtoField(1, ScalarType.BYTES),
|
||||
outPort: ProtoField(2, ScalarType.UINT32),
|
||||
inIP: ProtoField(3, ScalarType.BYTES),
|
||||
inPort: ProtoField(4, ScalarType.UINT32),
|
||||
ipType: ProtoField(5, ScalarType.UINT32),
|
||||
}
|
||||
|
||||
export const UploadResp = {
|
||||
uKey: ProtoField(1, ScalarType.STRING, true),
|
||||
uKeyTtlSecond: ProtoField(2, ScalarType.UINT32),
|
||||
ipv4s: ProtoField(3, () => IPv4, false, true),
|
||||
ipv6s: ProtoField(4, () => IPv6, false, true),
|
||||
msgSeq: ProtoField(5, ScalarType.UINT64),
|
||||
msgInfo: ProtoField(6, () => MsgInfo),
|
||||
ext: ProtoField(7, () => RichMediaStorageTransInfo, false, true),
|
||||
compatQMsg: ProtoField(8, ScalarType.BYTES),
|
||||
subFileInfos: ProtoField(10, () => SubFileInfo, false, true),
|
||||
}
|
||||
|
||||
export const RichMediaStorageTransInfo = {
|
||||
subType: ProtoField(1, ScalarType.UINT32),
|
||||
extType: ProtoField(2, ScalarType.UINT32),
|
||||
extValue: ProtoField(3, ScalarType.BYTES),
|
||||
}
|
||||
|
||||
export const SubFileInfo = {
|
||||
subType: ProtoField(1, ScalarType.UINT32),
|
||||
uKey: ProtoField(2, ScalarType.STRING),
|
||||
uKeyTtlSecond: ProtoField(3, ScalarType.UINT32),
|
||||
ipv4s: ProtoField(4, () => IPv4, false, true),
|
||||
ipv6s: ProtoField(5, () => IPv6, false, true),
|
||||
}
|
||||
|
||||
export const DownloadSafeResp = {
|
||||
}
|
||||
|
||||
export const UploadKeyRenewalResp = {
|
||||
ukey: ProtoField(1, ScalarType.STRING),
|
||||
ukeyTtlSec: ProtoField(2, ScalarType.UINT64),
|
||||
}
|
||||
|
||||
export const MsgInfoAuthResp = {
|
||||
authCode: ProtoField(1, ScalarType.UINT32),
|
||||
msg: ProtoField(2, ScalarType.BYTES),
|
||||
resultTime: ProtoField(3, ScalarType.UINT64),
|
||||
}
|
||||
|
||||
export const UploadCompletedResp = {
|
||||
msgSeq: ProtoField(1, ScalarType.UINT64),
|
||||
}
|
||||
|
||||
export const DeleteResp = {
|
||||
}
|
||||
|
||||
export const DownloadRKeyResp = {
|
||||
rKeys: ProtoField(1, () => RKeyInfo, false, true),
|
||||
}
|
||||
|
||||
export const RKeyInfo = {
|
||||
rkey: ProtoField(1, ScalarType.STRING),
|
||||
rkeyTtlSec: ProtoField(2, ScalarType.UINT64),
|
||||
storeId: ProtoField(3, ScalarType.UINT32),
|
||||
rkeyCreateTime: ProtoField(4, ScalarType.UINT32, true),
|
||||
type: ProtoField(5, ScalarType.UINT32, true),
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
import { PacketClient } from "@/core/packet/client";
|
||||
import { PacketHighwayClient } from "@/core/packet/highway/highwayClient";
|
||||
import { PacketHighwaySession } from "@/core/packet/highway/session";
|
||||
import { LogWrapper } from "@/common/log";
|
||||
|
||||
export class PacketSession {
|
||||
readonly logger: LogWrapper;
|
||||
readonly client: PacketClient;
|
||||
private readonly highwayClient: PacketHighwayClient;
|
||||
readonly highwaySession: PacketHighwaySession;
|
||||
|
||||
constructor(logger: LogWrapper, client: PacketClient) {
|
||||
this.logger = logger;
|
||||
this.client = client;
|
||||
this.highwayClient = new PacketHighwayClient(this.logger, this.client);
|
||||
this.highwaySession = new PacketHighwaySession(this.logger, this.client);
|
||||
}
|
||||
}
|
||||
|
16
src/core/packet/utils/crypto/hash.ts
Normal file
16
src/core/packet/utils/crypto/hash.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// love from https://github.com/LagrangeDev/lagrangejs & https://github.com/takayama-lily/oicq
|
||||
import * as crypto from 'crypto';
|
||||
import * as stream from 'stream';
|
||||
import * as fs from 'fs';
|
||||
|
||||
function sha1Stream(readable: stream.Readable) {
|
||||
return new Promise((resolve, reject) => {
|
||||
readable.on('error', reject);
|
||||
readable.pipe(crypto.createHash('sha1').on('error', reject).on('data', resolve));
|
||||
}) as Promise<Buffer>;
|
||||
}
|
||||
|
||||
export function calculateSha1(filePath: string): Promise<Buffer> {
|
||||
const readable = fs.createReadStream(filePath);
|
||||
return sha1Stream(readable);
|
||||
}
|
86
src/core/packet/utils/crypto/tea.ts
Normal file
86
src/core/packet/utils/crypto/tea.ts
Normal file
@ -0,0 +1,86 @@
|
||||
// love from https://github.com/LagrangeDev/lagrangejs/blob/main/src/core/tea.ts & https://github.com/takayama-lily/oicq/blob/main/lib/core/tea.ts
|
||||
const BUF7 = Buffer.alloc(7);
|
||||
const deltas = [
|
||||
0x9e3779b9, 0x3c6ef372, 0xdaa66d2b, 0x78dde6e4, 0x1715609d, 0xb54cda56, 0x5384540f, 0xf1bbcdc8, 0x8ff34781,
|
||||
0x2e2ac13a, 0xcc623af3, 0x6a99b4ac, 0x08d12e65, 0xa708a81e, 0x454021d7, 0xe3779b90,
|
||||
];
|
||||
|
||||
function _toUInt32(num: number) {
|
||||
return num >>> 0;
|
||||
}
|
||||
|
||||
function _encrypt(x: number, y: number, k0: number, k1: number, k2: number, k3: number): [number, number] {
|
||||
for (let i = 0; i < 16; ++i) {
|
||||
let aa = ((_toUInt32(((y << 4) >>> 0) + k0) ^ _toUInt32(y + deltas[i])) >>> 0) ^ _toUInt32(~~(y / 32) + k1);
|
||||
aa >>>= 0;
|
||||
x = _toUInt32(x + aa);
|
||||
let bb = ((_toUInt32(((x << 4) >>> 0) + k2) ^ _toUInt32(x + deltas[i])) >>> 0) ^ _toUInt32(~~(x / 32) + k3);
|
||||
bb >>>= 0;
|
||||
y = _toUInt32(y + bb);
|
||||
}
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export function encrypt(data: Buffer, key: Buffer) {
|
||||
let n = (6 - data.length) >>> 0;
|
||||
n = (n % 8) + 2;
|
||||
const v = Buffer.concat([Buffer.from([(n - 2) | 0xf8]), Buffer.allocUnsafe(n), data, BUF7]);
|
||||
const k0 = key.readUInt32BE(0);
|
||||
const k1 = key.readUInt32BE(4);
|
||||
const k2 = key.readUInt32BE(8);
|
||||
const k3 = key.readUInt32BE(12);
|
||||
let r1 = 0, r2 = 0, t1 = 0, t2 = 0;
|
||||
for (let i = 0; i < v.length; i += 8) {
|
||||
const a1 = v.readUInt32BE(i);
|
||||
const a2 = v.readUInt32BE(i + 4);
|
||||
const b1 = a1 ^ r1;
|
||||
const b2 = a2 ^ r2;
|
||||
const [x, y] = _encrypt(b1 >>> 0, b2 >>> 0, k0, k1, k2, k3);
|
||||
r1 = x ^ t1;
|
||||
r2 = y ^ t2;
|
||||
t1 = b1;
|
||||
t2 = b2;
|
||||
v.writeInt32BE(r1, i);
|
||||
v.writeInt32BE(r2, i + 4);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
function _decrypt(x: number, y: number, k0: number, k1: number, k2: number, k3: number) {
|
||||
for (let i = 15; i >= 0; --i) {
|
||||
const aa = ((_toUInt32(((x << 4) >>> 0) + k2) ^ _toUInt32(x + deltas[i])) >>> 0) ^ _toUInt32(~~(x / 32) + k3);
|
||||
y = (y - aa) >>> 0;
|
||||
const bb = ((_toUInt32(((y << 4) >>> 0) + k0) ^ _toUInt32(y + deltas[i])) >>> 0) ^ _toUInt32(~~(y / 32) + k1);
|
||||
x = (x - bb) >>> 0;
|
||||
}
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
export function decrypt(encrypted: Buffer, key: Buffer) {
|
||||
if (encrypted.length % 8) throw ERROR_ENCRYPTED_LENGTH;
|
||||
const k0 = key.readUInt32BE(0);
|
||||
const k1 = key.readUInt32BE(4);
|
||||
const k2 = key.readUInt32BE(8);
|
||||
const k3 = key.readUInt32BE(12);
|
||||
let r1 = 0, r2 = 0, t1 = 0, t2 = 0, x = 0, y = 0;
|
||||
for (let i = 0; i < encrypted.length; i += 8) {
|
||||
const a1 = encrypted.readUInt32BE(i);
|
||||
const a2 = encrypted.readUInt32BE(i + 4);
|
||||
const b1 = a1 ^ x;
|
||||
const b2 = a2 ^ y;
|
||||
[x, y] = _decrypt(b1 >>> 0, b2 >>> 0, k0, k1, k2, k3);
|
||||
r1 = x ^ t1;
|
||||
r2 = y ^ t2;
|
||||
t1 = a1;
|
||||
t2 = a2;
|
||||
encrypted.writeInt32BE(r1, i);
|
||||
encrypted.writeInt32BE(r2, i + 4);
|
||||
}
|
||||
if (Buffer.compare(encrypted.subarray(encrypted.length - 7), BUF7) !== 0) throw ERROR_ENCRYPTED_ILLEGAL
|
||||
// if (Buffer.compare(encrypted.slice(encrypted.length - 7), BUF7) !== 0) throw ERROR_ENCRYPTED_ILLEGAL;
|
||||
return encrypted.subarray((encrypted[0] & 0x07) + 3, encrypted.length - 7);
|
||||
// return encrypted.slice((encrypted[0] & 0x07) + 3, encrypted.length - 7);
|
||||
}
|
||||
|
||||
const ERROR_ENCRYPTED_LENGTH = new Error('length of encrypted data must be a multiple of 8');
|
||||
const ERROR_ENCRYPTED_ILLEGAL = new Error('encrypted data is illegal');
|
@ -1,14 +1,16 @@
|
||||
import BaseAction from '../BaseAction';
|
||||
import {ActionName} from '../types';
|
||||
import {FromSchema, JSONSchema} from 'json-schema-to-ts';
|
||||
|
||||
import {ChatType, SendTextElement} from "@/core";
|
||||
import {PacketMsgPicElement, PacketMsgTextElement} from "@/core/packet/msg/element";
|
||||
|
||||
const SchemaData = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
group_id: {type: ['number', 'string']},
|
||||
pic: {type: 'string'},
|
||||
},
|
||||
required: ['group_id'],
|
||||
required: ['group_id', 'pic'],
|
||||
} as const satisfies JSONSchema;
|
||||
|
||||
type Payload = FromSchema<typeof SchemaData>;
|
||||
@ -21,17 +23,47 @@ export class UploadForwardMsg extends BaseAction<Payload, any> {
|
||||
if (!this.core.apis.PacketApi.available) {
|
||||
throw new Error('PacketClient is not init');
|
||||
}
|
||||
throw new Error('Not implemented');
|
||||
// return await this.core.apis.PacketApi.sendUploadForwardMsg([{
|
||||
// groupId: 0,
|
||||
// senderId: 0,
|
||||
// senderName: "NapCat",
|
||||
// time: Math.floor(Date.now() / 1000),
|
||||
// msg: [new PacketMsgTextElement({
|
||||
// textElement: {
|
||||
// content: "Nya~"
|
||||
// }
|
||||
// } as SendTextElement)]
|
||||
// }], 0);
|
||||
// throw new Error('Not implemented');
|
||||
const peer = {
|
||||
chatType: ChatType.KCHATTYPEGROUP,
|
||||
peerUid: "10001", // TODO: must be a valid group id
|
||||
}
|
||||
const img = await this.core.apis.FileApi.createValidSendPicElement(
|
||||
{
|
||||
deleteAfterSentFiles: [],
|
||||
peer: peer
|
||||
},
|
||||
"", // TODO:
|
||||
"www",
|
||||
0,
|
||||
)
|
||||
const sendImg = new PacketMsgPicElement(img);
|
||||
console.log(JSON.stringify(img));
|
||||
await this.core.apis.PacketApi.packetSession?.highwaySession.uploadImage(
|
||||
peer, sendImg
|
||||
)
|
||||
return await this.core.apis.PacketApi.sendUploadForwardMsg([
|
||||
{
|
||||
groupId: 10001,
|
||||
senderId: 10001,
|
||||
senderName: "qwq",
|
||||
time: Math.floor(Date.now() / 1000),
|
||||
msg: [new PacketMsgTextElement({
|
||||
textElement: {
|
||||
content: "Love from Napcat.Packet~"
|
||||
}
|
||||
} as SendTextElement)]
|
||||
},
|
||||
{
|
||||
groupId: 10001,
|
||||
senderId: 10001,
|
||||
senderName: "qwq",
|
||||
time: Math.floor(Date.now() / 1000),
|
||||
msg: [new PacketMsgTextElement({
|
||||
textElement: {
|
||||
content: "Nya~"
|
||||
}
|
||||
} as SendTextElement), sendImg]
|
||||
}], 10001); // TODO: must be a valid group id
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user