mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2024-11-21 09:36:35 +00:00
refactor: further decoupling of Packet
and Core
parts
This commit is contained in:
parent
1fbc339a42
commit
397c2cf5f0
@ -377,7 +377,7 @@ export class NTQQFileApi {
|
|||||||
online_rkey: false
|
online_rkey: false
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
if (this.core.apis.PacketApi.packetClient?.available) {
|
if (this.core.apis.PacketApi.available) {
|
||||||
if ((!this.packetRkey || this.packetRkey[0].time > Date.now() / 1000)) {
|
if ((!this.packetRkey || this.packetRkey[0].time > Date.now() / 1000)) {
|
||||||
this.packetRkey = await this.core.apis.PacketApi.sendRkeyPacket();
|
this.packetRkey = await this.core.apis.PacketApi.sendRkeyPacket();
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as crypto from 'crypto';
|
|
||||||
import {InstanceContext, NapCatCore} from '..';
|
import {InstanceContext, NapCatCore} from '..';
|
||||||
import offset from '@/core/external/offset.json';
|
import offset from '@/core/external/offset.json';
|
||||||
import {PacketClient, RecvPacketData} from '@/core/packet/client';
|
import {PacketClient, RecvPacketData} from '@/core/packet/client';
|
||||||
|
import {PacketSession} from "@/core/packet/session";
|
||||||
import {PacketHexStr, PacketPacker} from "@/core/packet/packer";
|
import {PacketHexStr, PacketPacker} from "@/core/packet/packer";
|
||||||
import {NapProtoMsg} from '@/core/packet/proto/NapProto';
|
import {NapProtoMsg} from '@/core/packet/proto/NapProto';
|
||||||
import {OidbSvcTrpcTcp0X9067_202_Rsp_Body} from '@/core/packet/proto/oidb/Oidb.0x9067_202';
|
import {OidbSvcTrpcTcp0X9067_202_Rsp_Body} from '@/core/packet/proto/oidb/Oidb.0x9067_202';
|
||||||
import {OidbSvcTrpcTcpBase, OidbSvcTrpcTcpBaseRsp} from '@/core/packet/proto/oidb/OidbBase';
|
import {OidbSvcTrpcTcpBase, OidbSvcTrpcTcpBaseRsp} from '@/core/packet/proto/oidb/OidbBase';
|
||||||
import {OidbSvcTrpcTcp0XFE1_2RSP} from '@/core/packet/proto/oidb/Oidb.fe1_2';
|
import {OidbSvcTrpcTcp0XFE1_2RSP} from '@/core/packet/proto/oidb/Oidb.fe1_2';
|
||||||
|
|
||||||
import {PacketForwardNode} from "@/core/packet/msg/entity/forward";
|
import {PacketForwardNode} from "@/core/packet/msg/entity/forward";
|
||||||
|
|
||||||
interface OffsetType {
|
interface OffsetType {
|
||||||
@ -25,23 +24,29 @@ export class NTQQPacketApi {
|
|||||||
core: NapCatCore;
|
core: NapCatCore;
|
||||||
serverUrl: string | undefined;
|
serverUrl: string | undefined;
|
||||||
qqVersion: string | undefined;
|
qqVersion: string | undefined;
|
||||||
isInit: boolean = false;
|
|
||||||
packetPacker: PacketPacker;
|
packetPacker: PacketPacker;
|
||||||
packetClient: PacketClient | undefined;
|
packetSession: PacketSession | undefined;
|
||||||
|
|
||||||
constructor(context: InstanceContext, core: NapCatCore) {
|
constructor(context: InstanceContext, core: NapCatCore) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.core = core;
|
this.core = core;
|
||||||
this.packetPacker = new PacketPacker();
|
this.packetPacker = new PacketPacker();
|
||||||
|
this.packetSession = undefined;
|
||||||
let config = this.core.configLoader.configData;
|
let config = this.core.configLoader.configData;
|
||||||
if (config && config.packetServer && config.packetServer.length > 0) {
|
if (config && config.packetServer && config.packetServer.length > 0) {
|
||||||
let serverUrl = this.core.configLoader.configData.packetServer ?? '127.0.0.1:8086';
|
let serverUrl = this.core.configLoader.configData.packetServer ?? '127.0.0.1:8086';
|
||||||
this.InitSendPacket(serverUrl, this.context.basicInfoWrapper.getFullQQVesion())
|
this.InitSendPacket(serverUrl, this.context.basicInfoWrapper.getFullQQVesion())
|
||||||
.then()
|
.then()
|
||||||
.catch(this.core.context.logger.logError.bind(this.core.context.logger));
|
.catch(this.core.context.logger.logError.bind(this.core.context.logger));
|
||||||
|
} else {
|
||||||
|
this.core.context.logger.logWarn('PacketServer is not set, will not init NapCat.Packet!');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get available(): boolean {
|
||||||
|
return this.packetSession?.client.available ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
async InitSendPacket(serverUrl: string, qqversion: string) {
|
async InitSendPacket(serverUrl: string, qqversion: string) {
|
||||||
this.serverUrl = serverUrl;
|
this.serverUrl = serverUrl;
|
||||||
this.qqVersion = qqversion;
|
this.qqVersion = qqversion;
|
||||||
@ -49,40 +54,18 @@ export class NTQQPacketApi {
|
|||||||
let table = offsetTable[qqversion + '-' + os.arch()];
|
let table = offsetTable[qqversion + '-' + os.arch()];
|
||||||
if (!table) return false;
|
if (!table) return false;
|
||||||
let url = 'ws://' + this.serverUrl + '/ws';
|
let url = 'ws://' + this.serverUrl + '/ws';
|
||||||
this.packetClient = new PacketClient(url, this.core.context.logger);
|
this.packetSession = new PacketSession(this.core.context.logger, new PacketClient(url, this.core));
|
||||||
await this.packetClient.connect();
|
await this.packetSession.client.connect();
|
||||||
await this.packetClient.init(process.pid, table.recv, table.send);
|
await this.packetSession.client.init(process.pid, table.recv, table.send);
|
||||||
this.isInit = true;
|
return true;
|
||||||
return this.isInit;
|
|
||||||
}
|
|
||||||
|
|
||||||
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: PacketHexStr, rsp = false): Promise<RecvPacketData> {
|
async sendPacket(cmd: string, data: PacketHexStr, rsp = false): Promise<RecvPacketData> {
|
||||||
// wtfk tx
|
return this.packetSession!.client!.sendPacket(cmd, data, rsp);
|
||||||
// 校验失败和异常 可能返回undefined
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (!this.isInit || !this.packetClient?.available) {
|
|
||||||
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 sendPokePacket(group: number, peer: number) {
|
async sendPokePacket(group: number, peer: number) {
|
||||||
let data = this.core.apis.PacketApi.packetPacker.packPokePacket(group, peer);
|
let data = this.packetPacker.packPokePacket(group, peer);
|
||||||
let ret = await this.sendPacket('OidbSvcTrpcTcp.0xed3_1', data, false);
|
let ret = await this.sendPacket('OidbSvcTrpcTcp.0xed3_1', data, false);
|
||||||
//console.log('ret: ', ret);
|
//console.log('ret: ', ret);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import {LogWrapper} from "@/common/log";
|
import {LogWrapper} from "@/common/log";
|
||||||
import {LRUCache} from "@/common/lru-cache";
|
import {LRUCache} from "@/common/lru-cache";
|
||||||
import WebSocket, {Data} from "ws";
|
import WebSocket, {Data} from "ws";
|
||||||
import {createHash} from "crypto";
|
import crypto, {createHash} from "crypto";
|
||||||
|
import {NapCatCore} from "@/core";
|
||||||
|
import {PacketHexStr} from "@/core/packet/packer";
|
||||||
|
|
||||||
export interface RecvPacket {
|
export interface RecvPacket {
|
||||||
type: string, // 仅recv
|
type: string, // 仅recv
|
||||||
@ -22,17 +24,28 @@ export class PacketClient {
|
|||||||
private maxReconnectAttempts: number = 5;
|
private maxReconnectAttempts: number = 5;
|
||||||
private cb = new LRUCache<string, (json: RecvPacketData) => Promise<void>>(500); // trace_id-type callback
|
private cb = new LRUCache<string, (json: RecvPacketData) => Promise<void>>(500); // trace_id-type callback
|
||||||
private readonly clientUrl: string = '';
|
private readonly clientUrl: string = '';
|
||||||
|
private readonly napCatCore: NapCatCore
|
||||||
private readonly logger: LogWrapper;
|
private readonly logger: LogWrapper;
|
||||||
|
|
||||||
constructor(url: string, logger: LogWrapper) {
|
constructor(url: string, core: NapCatCore) {
|
||||||
this.clientUrl = url;
|
this.clientUrl = url;
|
||||||
this.logger = logger;
|
this.napCatCore = core;
|
||||||
|
this.logger = core.context.logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
get available(): boolean {
|
get available(): boolean {
|
||||||
return this.isConnected && this.websocket !== undefined;
|
return this.isConnected && this.websocket !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private 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;
|
||||||
|
}
|
||||||
|
|
||||||
connect(): Promise<void> {
|
connect(): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.logger.log.bind(this.logger)(`Attempting to connect to ${this.clientUrl}`);
|
this.logger.log.bind(this.logger)(`Attempting to connect to ${this.clientUrl}`);
|
||||||
@ -91,7 +104,6 @@ export class PacketClient {
|
|||||||
if (!this.isConnected || !this.websocket) {
|
if (!this.isConnected || !this.websocket) {
|
||||||
throw new Error("WebSocket is not connected");
|
throw new Error("WebSocket is not connected");
|
||||||
}
|
}
|
||||||
|
|
||||||
const initMessage = {
|
const initMessage = {
|
||||||
action: 'init',
|
action: 'init',
|
||||||
pid: pid,
|
pid: pid,
|
||||||
@ -147,4 +159,20 @@ export class PacketClient {
|
|||||||
this.logger.logError.bind(this.logger)(`Error parsing message: ${error}`);
|
this.logger.logError.bind(this.logger)(`Error parsing message: ${error}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendPacket(cmd: string, data: PacketHexStr, rsp = false): Promise<RecvPacketData> {
|
||||||
|
// wtfk tx
|
||||||
|
// 校验失败和异常 可能返回undefined
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!this.available) {
|
||||||
|
this.logger.logError('NapCat.Packet 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.sendCommand(cmd, data, trace_id, rsp, 5000, async () => {
|
||||||
|
await this.napCatCore.context.session.getMsgService().sendSsoCmdReqByContend(cmd, trace_id);
|
||||||
|
}).then((res) => resolve(res)).catch((e) => reject(e));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
15
src/core/packet/session.ts
Normal file
15
src/core/packet/session.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import {PacketClient} from "@/core/packet/client";
|
||||||
|
import {PacketHighwayClient} from "@/core/packet/highway/highwayClient";
|
||||||
|
import {LogWrapper} from "@/common/log";
|
||||||
|
|
||||||
|
export class PacketSession {
|
||||||
|
readonly logger: LogWrapper;
|
||||||
|
readonly client: PacketClient;
|
||||||
|
private highwayClient: PacketHighwayClient
|
||||||
|
|
||||||
|
constructor(logger: LogWrapper, client: PacketClient) {
|
||||||
|
this.logger = logger;
|
||||||
|
this.client = client;
|
||||||
|
this.highwayClient = new PacketHighwayClient(this.logger, this.client);
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@ export class GetRkey extends BaseAction<Payload, Array<any>> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
if (!this.core.apis.PacketApi.packetClient?.available) {
|
if (!this.core.apis.PacketApi.available) {
|
||||||
throw new Error('PacketClient is not init');
|
throw new Error('PacketClient is not init');
|
||||||
}
|
}
|
||||||
return await this.core.apis.PacketApi.sendRkeyPacket();
|
return await this.core.apis.PacketApi.sendRkeyPacket();
|
||||||
|
@ -17,7 +17,7 @@ export class GetUserStatus extends BaseAction<Payload, { status: number; ext_sta
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
if (!this.core.apis.PacketApi.packetClient?.available) {
|
if (!this.core.apis.PacketApi?.available) {
|
||||||
throw new Error('PacketClient is not init');
|
throw new Error('PacketClient is not init');
|
||||||
}
|
}
|
||||||
return await this.core.apis.PacketApi.sendStatusPacket(+payload.user_id);
|
return await this.core.apis.PacketApi.sendStatusPacket(+payload.user_id);
|
||||||
|
@ -18,7 +18,7 @@ export class SetSpecialTittle extends BaseAction<Payload, any> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
if (!this.core.apis.PacketApi.packetClient?.available) {
|
if (!this.core.apis.PacketApi.available) {
|
||||||
throw new Error('PacketClient is not init');
|
throw new Error('PacketClient is not init');
|
||||||
}
|
}
|
||||||
let uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
let uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString());
|
||||||
|
@ -20,7 +20,7 @@ export class UploadForwardMsg extends BaseAction<Payload, any> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
if (!this.core.apis.PacketApi.packetClient?.available) {
|
if (!this.core.apis.PacketApi.available) {
|
||||||
throw new Error('PacketClient is not init');
|
throw new Error('PacketClient is not init');
|
||||||
}
|
}
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
|
@ -18,7 +18,7 @@ export class GroupPoke extends BaseAction<Payload, any> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
if (!this.core.apis.PacketApi.packetClient?.available) {
|
if (!this.core.apis.PacketApi.available) {
|
||||||
throw new Error('PacketClient is not init');
|
throw new Error('PacketClient is not init');
|
||||||
}
|
}
|
||||||
await this.core.apis.PacketApi.sendPokePacket(+payload.group_id, +payload.user_id);
|
await this.core.apis.PacketApi.sendPokePacket(+payload.group_id, +payload.user_id);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user