mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-06 07:29:38 +00:00
176 lines
7.2 KiB
TypeScript
176 lines
7.2 KiB
TypeScript
import { InstanceContext, NapCatCore } from '..';
|
|
import * as os from 'os';
|
|
import offset from '@/core/external/offset.json';
|
|
import * as crypto from 'crypto';
|
|
import { PacketClient } from '../helper/packet';
|
|
import { NapProtoMsg } from '../proto/NapProto';
|
|
import { OidbSvcTrpcTcp0X9067_202, OidbSvcTrpcTcp0X9067_202_Rsp_Body } from '../proto/oidb/Oidb.0x9067_202';
|
|
import { OidbSvcTrpcTcpBase, OidbSvcTrpcTcpBaseRsp } from '../proto/oidb/OidbBase';
|
|
import { OidbSvcTrpcTcp0XFE1_2, OidbSvcTrpcTcp0XFE1_2RSP } from '../proto/oidb/Oidb.fe1_2';
|
|
import { OidbSvcTrpcTcp0X8FC_2, OidbSvcTrpcTcp0X8FC_2_Body } from '../proto/oidb/Oidb.0x8FC_2';
|
|
|
|
interface OffsetType {
|
|
[key: string]: {
|
|
recv: string;
|
|
send: string;
|
|
};
|
|
}
|
|
|
|
const typedOffset: OffsetType = offset;
|
|
export class NTQQPacketApi {
|
|
context: InstanceContext;
|
|
core: NapCatCore;
|
|
serverUrl: string | undefined;
|
|
qqversion: string | undefined;
|
|
isInit: boolean = false;
|
|
PacketClient: PacketClient | undefined;
|
|
constructor(context: InstanceContext, core: NapCatCore) {
|
|
this.context = context;
|
|
this.core = core;
|
|
let config = this.core.configLoader.configData;
|
|
if (config && config.packetServer && config.packetServer.length > 0) {
|
|
let serverurl = this.core.configLoader.configData.packetServer ?? '127.0.0.1:8086';
|
|
this.InitSendPacket(serverurl, this.context.basicInfoWrapper.getFullQQVesion())
|
|
.then()
|
|
.catch(this.core.context.logger.logError.bind(this.core.context.logger));
|
|
}
|
|
}
|
|
async InitSendPacket(serverUrl: string, qqversion: string) {
|
|
this.serverUrl = serverUrl;
|
|
this.qqversion = qqversion;
|
|
let offsetTable: OffsetType = offset;
|
|
let table = offsetTable[qqversion + '-' + os.arch()];
|
|
if (!table) return false;
|
|
let url = 'ws://' + this.serverUrl + '/ws';
|
|
this.PacketClient = new PacketClient(url, this.core.context.logger);
|
|
await this.PacketClient.connect();
|
|
await this.PacketClient.init(process.pid, table.recv, table.send);
|
|
this.isInit = true;
|
|
this.InitOtherServer()
|
|
return this.isInit;
|
|
}
|
|
async InitOtherServer() {
|
|
this.core.apis.FileApi.rkeyManager.regOutputRkey(
|
|
async () => {
|
|
let rkeylist = await this.core.apis.PacketApi.sendRkeyPacket();
|
|
if (rkeylist.length > 0) {
|
|
return rkeylist;
|
|
}
|
|
return undefined;
|
|
});
|
|
}
|
|
randText(len: number) {
|
|
let text = '';
|
|
let possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
for (let i = 0; i < len; i++) {
|
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
}
|
|
return text;
|
|
}
|
|
async sendPacket(cmd: string, data: string, rsp = false): Promise<any> {
|
|
// wtfk tx
|
|
// 校验失败和异常 可能返回undefined
|
|
return new Promise((resolve, reject) => {
|
|
if (!this.isInit || !this.PacketClient?.isConnected) {
|
|
this.core.context.logger.logError('PacketClient is not init');
|
|
return undefined;
|
|
}
|
|
let md5 = crypto.createHash('md5').update(data).digest('hex');
|
|
let trace_id = (this.randText(4) + md5 + data).slice(0, data.length / 2);
|
|
this.PacketClient?.sendCommand(cmd, data, trace_id, rsp, 5000, async () => {
|
|
await this.core.context.session.getMsgService().sendSsoCmdReqByContend(cmd, trace_id);
|
|
}).then((res) => resolve(res)).catch((e) => reject(e));
|
|
});
|
|
}
|
|
async sendRkeyPacket() {
|
|
let u8 = await this.core.apis.PacketApi.buildRkeyPacket()
|
|
let ret = await this.core.apis.PacketApi.sendPacket('OidbSvcTrpcTcp.0x9067_202', Buffer.from(u8).toString('hex'), true);
|
|
if (!ret?.hex_data) return []
|
|
let body = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(Buffer.from(ret.hex_data, 'hex')).body;
|
|
//console.log('ret: ', Buffer.from(body).toString('hex'));
|
|
let retdata = new NapProtoMsg(OidbSvcTrpcTcp0X9067_202_Rsp_Body).decode(body)
|
|
//console.log('ret: ', JSON.stringify(retdata.data.rkeyList));
|
|
return retdata.data.rkeyList;
|
|
}
|
|
async buildRkeyPacket() {
|
|
let oidb_0x9067_202 = new NapProtoMsg(OidbSvcTrpcTcp0X9067_202).encode({
|
|
reqHead: {
|
|
common: {
|
|
requestId: 1,
|
|
command: 202
|
|
},
|
|
scene: {
|
|
requestType: 2,
|
|
businessType: 1,
|
|
sceneType: 0
|
|
},
|
|
client: {
|
|
agentType: 2
|
|
}
|
|
},
|
|
downloadRKeyReq: {
|
|
key: [10, 20, 2]
|
|
},
|
|
});
|
|
let oidb_packet = new NapProtoMsg(OidbSvcTrpcTcpBase).encode({
|
|
command: 0x9067,
|
|
subCommand: 202,
|
|
body: oidb_0x9067_202,
|
|
isReserved: 1
|
|
});
|
|
return oidb_packet;
|
|
}
|
|
async buildSetSpecialTittlePacket(groupCode: string, uid: string, tittle: string) {
|
|
let oidb_0x8FC_2_body = new NapProtoMsg(OidbSvcTrpcTcp0X8FC_2_Body).encode({
|
|
targetUid: uid,
|
|
specialTitle: tittle,
|
|
expiredTime: -1,
|
|
uinName: tittle
|
|
});
|
|
let oidb_0x8FC_2 = new NapProtoMsg(OidbSvcTrpcTcp0X8FC_2).encode({
|
|
groupUin: +groupCode,
|
|
body: oidb_0x8FC_2_body
|
|
});
|
|
let oidb_packet = new NapProtoMsg(OidbSvcTrpcTcpBase).encode({
|
|
command: 0x8FC,
|
|
subCommand: 2,
|
|
body: oidb_0x8FC_2,
|
|
});
|
|
return oidb_packet;
|
|
}
|
|
async buildStatusPacket(uin: number) {
|
|
|
|
let oidb_0xfe1_2 = new NapProtoMsg(OidbSvcTrpcTcp0XFE1_2).encode({
|
|
uin: uin,
|
|
key: [{ key: 27372 }]
|
|
});
|
|
let oidb_packet = new NapProtoMsg(OidbSvcTrpcTcpBase).encode({
|
|
command: 0xfe1,
|
|
subCommand: 2,
|
|
body: oidb_0xfe1_2,
|
|
isReserved: 1
|
|
});
|
|
return oidb_packet;
|
|
}
|
|
async sendStatusPacket(uin: number): Promise<{ status: number; ext_status: number; } | undefined> {
|
|
let status = 0;
|
|
try {
|
|
let packet = Buffer.from(await this.core.apis.PacketApi.buildStatusPacket(uin)).toString('hex');
|
|
let ret = await this.core.apis.PacketApi.sendPacket('OidbSvcTrpcTcp.0xfe1_2', packet, true);
|
|
console.log('ret: ', ret);
|
|
let data = Buffer.from(ret.hex_data, 'hex');
|
|
let ext = new NapProtoMsg(OidbSvcTrpcTcp0XFE1_2RSP).decode(new NapProtoMsg(OidbSvcTrpcTcpBase).decode(data).body).data.status.value;
|
|
// ext & 0xff00 + ext >> 16 & 0xff
|
|
let extBigInt = BigInt(ext); // 转换为 BigInt
|
|
if (extBigInt <= 10n) {
|
|
return { status: Number(extBigInt) * 10, ext_status: 0 };
|
|
}
|
|
status = Number((extBigInt & 0xff00n) + ((extBigInt >> 16n) & 0xffn)); // 使用 BigInt 操作符
|
|
return { status: 10, ext_status: status };
|
|
} catch (error) {
|
|
return undefined
|
|
}
|
|
return { status: status, ext_status: 0 };
|
|
}
|
|
}
|