diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index 750c3c9c..3bcc1f21 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -9,12 +9,10 @@ import { MemberExtSourceType, NapCatCore, } from '@/core'; -import { isNumeric, sleep, solveAsyncProblem } from '@/common/helper'; -import { LimitedHashTable } from '@/common/message-unique'; -import { NTEventWrapper } from '@/common/event'; -import { NapProtoMsg } from '../proto/NapProto'; -import { OidbSvcTrpcTcpBase } from '../proto/oidb/OidbBase'; -import { OidbSvcTrpcTcp0XED3_1 } from '../proto/oidb/Oidb.ed3_1'; +import {isNumeric, solveAsyncProblem} from '@/common/helper'; +import {LimitedHashTable} from '@/common/message-unique'; +import {NTEventWrapper} from '@/common/event'; + interface recvPacket { type: string,//仅recv trace_id_md5?: string, @@ -24,6 +22,7 @@ interface recvPacket { cmd: string } } + export class NTQQGroupApi { context: InstanceContext; core: NapCatCore; @@ -47,6 +46,7 @@ export class NTQQGroupApi { this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`); // process.pid 调试点 } + async getCoreAndBaseInfo(uids: string[]) { return await this.core.eventWrapper.callNoListenerEvent( 'NodeIKernelProfileService/getCoreAndBaseInfo', @@ -54,26 +54,13 @@ export class NTQQGroupApi { uids, ); } - async sendPocketRkey() { - 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); + + async sendPacketRkey() { + let data = this.core.apis.PacketApi.packetPacker.packRkeyPacket() + let ret = await this.core.apis.PacketApi.sendPacket('OidbSvcTrpcTcp.0x9067_202', data, true); //console.log('ret: ', ret); } - async sendPacketPoke(group: number, peer: number) { - let oidb_0xed3 = new NapProtoMsg(OidbSvcTrpcTcp0XED3_1).encode({ - uin: peer, - groupUin: group, - friendUin: group, - ext: 0 - }); - let oidb_packet = new NapProtoMsg(OidbSvcTrpcTcpBase).encode({ - command: 0xed3, - subCommand: 1, - body: oidb_0xed3 - }); - let hex = Buffer.from(oidb_packet).toString('hex'); - let retdata = await this.core.apis.PacketApi.sendPacket('OidbSvcTrpcTcp.0xed3_1', hex, false); - } + async fetchGroupEssenceList(groupCode: string) { const pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!; return this.context.session.getGroupService().fetchGroupEssenceList({ @@ -196,6 +183,7 @@ export class NTQQGroupApi { } return member; } + async getGroupRecommendContactArkJson(groupCode: string) { return this.context.session.getGroupService().getGroupRecommendContactArkJson(groupCode); } @@ -301,6 +289,7 @@ export class NTQQGroupApi { } return member; } + async searchGroup(groupCode: string) { const [, ret] = await this.core.eventWrapper.callNormalEventV2( 'NodeIKernelSearchService/searchGroup', @@ -318,6 +307,7 @@ export class NTQQGroupApi { ); return ret.groupInfos.find(g => g.groupCode === groupCode); } + async getGroupMemberEx(GroupCode: string, uid: string, forced = false, retry = 2) { const data = await solveAsyncProblem((eventWrapper: NTEventWrapper, GroupCode: string, uid: string, forced = false) => { return eventWrapper.callNormalEventV2( @@ -339,6 +329,7 @@ export class NTQQGroupApi { } return undefined; } + async getGroupMembersV2(groupQQ: string, num = 3000): Promise> { const groupService = this.context.session.getGroupService(); const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow'); diff --git a/src/core/apis/packet.ts b/src/core/apis/packet.ts index a0886a98..30919a80 100644 --- a/src/core/apis/packet.ts +++ b/src/core/apis/packet.ts @@ -1,13 +1,13 @@ -import { InstanceContext, NapCatCore } from '..'; +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'; +import {PacketClient} from '../helper/packet/client'; +import {PacketHexStr, PacketPacker} from "@/core/helper/packet/packer"; +import {NapProtoMsg} from '../proto/NapProto'; +import {OidbSvcTrpcTcp0X9067_202_Rsp_Body} from '../proto/oidb/Oidb.0x9067_202'; +import {OidbSvcTrpcTcpBase, OidbSvcTrpcTcpBaseRsp} from '../proto/oidb/OidbBase'; +import {OidbSvcTrpcTcp0XFE1_2RSP} from '../proto/oidb/Oidb.fe1_2'; interface OffsetType { [key: string]: { @@ -17,16 +17,20 @@ interface OffsetType { } const typedOffset: OffsetType = offset; + export class NTQQPacketApi { context: InstanceContext; core: NapCatCore; serverUrl: string | undefined; qqversion: string | undefined; isInit: boolean = false; - PacketClient: PacketClient | undefined; + packetPacker: PacketPacker; + packetClient: PacketClient | undefined; + constructor(context: InstanceContext, core: NapCatCore) { this.context = context; this.core = core; + this.packetPacker = new PacketPacker(); 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'; @@ -35,6 +39,7 @@ export class NTQQPacketApi { .catch(this.core.context.logger.logError.bind(this.core.context.logger)); } } + async InitSendPacket(serverUrl: string, qqversion: string) { this.serverUrl = serverUrl; this.qqversion = qqversion; @@ -42,12 +47,13 @@ export class NTQQPacketApi { 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.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; return this.isInit; } + randText(len: number) { let text = ''; let possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; @@ -56,95 +62,44 @@ export class NTQQPacketApi { } return text; } - async sendPacket(cmd: string, data: string, rsp = false): Promise { + + async sendPacket(cmd: string, data: PacketHexStr, rsp = false): Promise { // wtfk tx // 校验失败和异常 可能返回undefined return new Promise((resolve, reject) => { - if (!this.isInit || !this.PacketClient?.isConnected) { - this.core.context.logger.logError('PacketClient is not init'); + 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 () => { + 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 sendPokePacket(group: number, peer: number) { + let data = this.core.apis.PacketApi.packetPacker.packPokePacket(group, peer); + let ret = await this.core.apis.PacketApi.sendPacket('OidbSvcTrpcTcp.0xed3_1', data, false); + //console.log('ret: ', ret); } + + async sendRkeyPacket() { + let packet = this.packetPacker.packRkeyPacket(); + let ret = await this.core.apis.PacketApi.sendPacket('OidbSvcTrpcTcp.0x9067_202', packet, 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 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 packet = this.packetPacker.packStatusPacket(uin); 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'); @@ -152,13 +107,12 @@ export class NTQQPacketApi { // ext & 0xff00 + ext >> 16 & 0xff let extBigInt = BigInt(ext); // 转换为 BigInt if (extBigInt <= 10n) { - return { status: Number(extBigInt) * 10, ext_status: 0 }; + return {status: Number(extBigInt) * 10, ext_status: 0}; } status = Number((extBigInt & 0xff00n) + ((extBigInt >> 16n) & 0xffn)); // 使用 BigInt 操作符 - return { status: 10, ext_status: status }; + return {status: 10, ext_status: status}; } catch (error) { return undefined } - return { status: status, ext_status: 0 }; } } diff --git a/src/core/helper/packet.ts b/src/core/helper/packet/client.ts similarity index 99% rename from src/core/helper/packet.ts rename to src/core/helper/packet/client.ts index 5a09a379..86ed65aa 100644 --- a/src/core/helper/packet.ts +++ b/src/core/helper/packet/client.ts @@ -124,4 +124,4 @@ export class PacketClient { this.logger.logError.bind(this.logger)(`Error parsing message: ${error}`); } } -} \ No newline at end of file +} diff --git a/src/core/helper/packet/packer.ts b/src/core/helper/packet/packer.ts new file mode 100644 index 00000000..69f8e2c4 --- /dev/null +++ b/src/core/helper/packet/packer.ts @@ -0,0 +1,78 @@ +import {NapProtoMsg} from "@/core/proto/NapProto"; +import {OidbSvcTrpcTcpBase} from "@/core/proto/oidb/OidbBase"; +import {OidbSvcTrpcTcp0X9067_202} from "@/core/proto/oidb/Oidb.0x9067_202"; +import {OidbSvcTrpcTcp0X8FC_2, OidbSvcTrpcTcp0X8FC_2_Body} from "@/core/proto/oidb/Oidb.0x8FC_2"; +import {OidbSvcTrpcTcp0XFE1_2} from "@/core/proto/oidb/Oidb.fe1_2"; +import {OidbSvcTrpcTcp0XED3_1} from "@/core/proto/oidb/Oidb.ed3_1"; + +export type PacketHexStr = string & { readonly hexNya: unique symbol }; + +export class PacketPacker { + private toHexStr(byteArray: Uint8Array): PacketHexStr { + return Buffer.from(byteArray).toString('hex') as PacketHexStr; + } + + packOidbPacket(cmd: number, subCmd: number, body: Uint8Array, isUid: boolean = true, isLafter: boolean = false): Uint8Array { + return new NapProtoMsg(OidbSvcTrpcTcpBase).encode({ + command: cmd, + subCommand: subCmd, + body: body, + isReserved: isUid ? 1 : 0 + }); + } + + packPokePacket(group: number, peer: number): PacketHexStr { + const oidb_0xed3 = new NapProtoMsg(OidbSvcTrpcTcp0XED3_1).encode({ + uin: peer, + groupUin: group, + friendUin: group, + ext: 0 + }); + return this.toHexStr(this.packOidbPacket(0xed3, 1, oidb_0xed3)); + } + + packRkeyPacket(): PacketHexStr { + const 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] + }, + }); + return this.toHexStr(this.packOidbPacket(0x9067, 202, oidb_0x9067_202)); + } + + packSetSpecialTittlePacket(groupCode: string, uid: string, tittle: string): PacketHexStr { + const oidb_0x8FC_2_body = new NapProtoMsg(OidbSvcTrpcTcp0X8FC_2_Body).encode({ + targetUid: uid, + specialTitle: tittle, + expiredTime: -1, + uinName: tittle + }); + const oidb_0x8FC_2 = new NapProtoMsg(OidbSvcTrpcTcp0X8FC_2).encode({ + groupUin: +groupCode, + body: oidb_0x8FC_2_body + }); + return this.toHexStr(this.packOidbPacket(0x8FC, 2, oidb_0x8FC_2)); + } + + packStatusPacket(uin: number): PacketHexStr { + let oidb_0xfe1_2 = new NapProtoMsg(OidbSvcTrpcTcp0XFE1_2).encode({ + uin: uin, + key: [{key: 27372}] + }); + return this.toHexStr(this.packOidbPacket(0xfe1, 2, oidb_0xfe1_2)); + } +} diff --git a/src/onebot/action/extends/GetRkey.ts b/src/onebot/action/extends/GetRkey.ts index efe606ce..f4cb4ae3 100644 --- a/src/onebot/action/extends/GetRkey.ts +++ b/src/onebot/action/extends/GetRkey.ts @@ -15,9 +15,9 @@ export class GetRkey extends BaseAction> { payloadSchema = SchemaData; async _handle(payload: Payload) { - if (!this.core.apis.PacketApi.PacketClient?.isConnected) { + if (!this.core.apis.PacketApi.packetClient?.isConnected) { throw new Error('PacketClient is not init'); } return await this.core.apis.PacketApi.sendRkeyPacket(); } -} \ No newline at end of file +} diff --git a/src/onebot/action/extends/GetUserStatus.ts b/src/onebot/action/extends/GetUserStatus.ts index 09272357..05660238 100644 --- a/src/onebot/action/extends/GetUserStatus.ts +++ b/src/onebot/action/extends/GetUserStatus.ts @@ -17,9 +17,9 @@ export class GetUserStatus extends BaseAction { payloadSchema = SchemaData; async _handle(payload: Payload) { - if (!this.core.apis.PacketApi.PacketClient?.isConnected) { - throw new Error('PacketClient is not init'); + if (!this.core.apis.PacketApi.packetClient?.isConnected) { + throw new Error('packetClient is not init'); } - await this.core.apis.GroupApi.sendPacketPoke(+payload.group_id, +payload.user_id); + await this.core.apis.PacketApi.sendPokePacket(+payload.group_id, +payload.user_id); } }