This commit is contained in:
手瓜一十雪
2024-10-11 23:03:09 +08:00
parent d36d9c5fa1
commit 2ac3812987
11 changed files with 139 additions and 6 deletions

View File

@@ -9,9 +9,12 @@ import {
MemberExtSourceType,
NapCatCore,
} from '@/core';
import { isNumeric, solveAsyncProblem } from '@/common/helper';
import { isNumeric, sleep, solveAsyncProblem } from '@/common/helper';
import { LimitedHashTable } from '@/common/message-unique';
import { NTEventWrapper } from '@/common/event';
import { encodeGroupPoke } from '../proto/Poke';
import { randomUUID } from 'crypto';
import { RequestUtil } from '@/common/request';
export class NTQQGroupApi {
context: InstanceContext;
@@ -20,6 +23,7 @@ export class NTQQGroupApi {
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
groups: Group[] = [];
essenceLRU = new LimitedHashTable<number, string>(1000);
session: any;
constructor(context: InstanceContext, core: NapCatCore) {
this.context = context;
@@ -33,6 +37,14 @@ export class NTQQGroupApi {
this.groupCache.set(group.groupCode, group);
}
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
console.log('pid', process.pid);
// this.session = await frida.attach(process.pid);
// setTimeout(async () => {
// let data = Buffer.from('089601', 'hex').toString('utf-8');//optional int32 a = 1;
// console.log('data', Buffer.from(data).toString('hex'));
// let ret = await this.core.context.session.getMsgService().sendSsoCmdReqByContend("OidbSvcTrpcTcp.0xfe1_2", data);
// console.log('sendSsoCmdReqByContend', ret);
// }, 20000);
}
async getCoreAndBaseInfo(uids: string[]) {
return await this.core.eventWrapper.callNoListenerEvent(
@@ -41,6 +53,15 @@ export class NTQQGroupApi {
uids,
);
}
async sendPacketPoke(group: string, peer: string) {
let data = encodeGroupPoke(group, peer);
let hex = Buffer.from(data).toString('hex');
let retdata = await this.core.apis.PacketApi.sendPacket('OidbSvcTrpcTcp.0xed3_1', hex);
//await RequestUtil.HttpGetJson('http://127.0.0.1:8086/send', 'POST', { data: hex }, { 'Content-Type': 'application/json' }, false, true);
//let ret = await this.core.context.session.getMsgService().sendSsoCmdReqByContend('LightAppSvc.mini_app_i', hex.slice(0, hex.length / 2));
// let ret = await this.core.context.session.getMsgService().sendSsoCmdReqByContend('OidbSvcTrpcTcp.0xfe1_2', hex.toString('hex'));
console.log('sendPacketPoke', retdata);
}
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({

72
src/core/apis/packet.ts Normal file
View File

@@ -0,0 +1,72 @@
import { InstanceContext, NapCatCore } from '..';
import { RequestUtil } from '@/common/request';
import offset from '@/core/external/offset.json';
import * as crypto from 'crypto';
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;
constructor(context: InstanceContext, core: NapCatCore) {
this.context = context;
this.core = core;
this.InitSendPacket('127.0.0.1:8086', '9.9.15-28418', '1001').then().catch();
}
async InitSendPacket(serverUrl: string, qqversion: string, uin: string) {
this.serverUrl = serverUrl;
this.qqversion = qqversion;
let offsetTable: OffsetType = offset;
if (!offsetTable[qqversion]) return false;
let url = 'http://' + this.serverUrl + '/init';
let postdata = { recv: offsetTable[qqversion].recv, send: offsetTable[qqversion].send, qqver: qqversion, uin: uin, pid: process.pid };
try {
let ret = await RequestUtil.HttpGetJson<any>(url, 'POST', postdata, { 'Content-Type': 'application/json' }, true, true);
if (ret.status !== 'ok') throw new Error('InitSendPacket failed' + JSON.stringify(ret, null, 2));
} catch (error) {
let logger = this.core.context.logger;
logger.logError.bind(logger)('InitSendPacket', error);
return false;
}
this.isInit = true;
return this.isInit;
}
async 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, rep = false) {
return new Promise(async (resolve, reject) => {
//获取data的HASH
let md5 = crypto.createHash('md5').update(data).digest('hex');
let url = 'http://' + this.serverUrl + '/send';
let geturl = 'http://' + this.serverUrl + '/get';
let trace_id = (await this.randText(4) + md5 + data).slice(0, data.length / 2);
let postdata = { data: data, trace_id: trace_id, cmd: cmd };
RequestUtil.HttpGetJson<any>(url, 'POST', postdata, { 'Content-Type': 'application/json' }, true, true).then((res) => {
if (!rep) {
this.core.context.session.getMsgService().sendSsoCmdReqByContend(cmd, trace_id).then(e => resolve(res)).catch(e => reject(e))
} else {
let getpostdata = { data: data, trace_id: trace_id, cmd: cmd };
RequestUtil.HttpGetJson<any>(geturl, 'POST', getpostdata, { 'Content-Type': 'application/json' }, true, true).then((rsp) => {
resolve(rsp)
}).catch((e) => reject(e));
}
}).catch((e) => reject(e));
});
}
}

10
src/core/external/offset.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"9.9.15-28418":{
"recv": "37A9004",
"send": "37A4BD0"
},
"9.9.15-28498":{
"recv": "37A9004",
"send": "37A4BD0"
}
}

View File

@@ -29,6 +29,7 @@ import { NapCatConfigLoader } from '@/core/helper/config';
import os from 'node:os';
import { NodeIKernelGroupListener, NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners';
import { proxiedListenerOf } from '@/common/proxy-handler';
import { NTQQPacketApi } from './apis/packet';
export * from './wrapper';
export * from './entities';
export * from './services';
@@ -84,6 +85,7 @@ export class NapCatCore {
FileApi: new NTQQFileApi(this.context, this),
SystemApi: new NTQQSystemApi(this.context, this),
CollectionApi: new NTQQCollectionApi(this.context, this),
PacketApi: new NTQQPacketApi(this.context, this),
WebApi: new NTQQWebApi(this.context, this),
FriendApi: new NTQQFriendApi(this.context, this),
MsgApi: new NTQQMsgApi(this.context, this),
@@ -322,6 +324,7 @@ export interface InstanceContext {
export interface StableNTApiWrapper {
FileApi: NTQQFileApi,
SystemApi: NTQQSystemApi,
PacketApi: NTQQPacketApi,
CollectionApi: NTQQCollectionApi,
WebApi: NTQQWebApi,
FriendApi: NTQQFriendApi,

View File

@@ -0,0 +1,21 @@
import { MessageType, ScalarType } from "@protobuf-ts/runtime";
import { OidbSvcTrpcTcpBase } from "./Poke";
export const OidbSvcTrpcTcp0XFE1_2 = new MessageType("oidb_svc_trpctcp_0xfe1_2", [
{ no: 1, name: "uin", kind: "scalar", T: ScalarType.UINT32 },
{ no: 3, name: "key", kind: "scalar", T: ScalarType.BYTES, opt: true }
]);
export function encode_packet_0xfe1_2(PeerUin: string) {
let Body = OidbSvcTrpcTcp0XFE1_2.toBinary
({
uin: parseInt(PeerUin),
key: new Uint8Array([0x00, 0x00, 0x00, 0x00])
});
return OidbSvcTrpcTcpBase.toBinary
({
command: 0xfe1,
subcommand: 2,
body: Body,
isreserved: 1
});
}

View File

@@ -3,7 +3,8 @@ import { MessageType, ScalarType, BinaryWriter } from '@protobuf-ts/runtime';
export const OidbSvcTrpcTcpBase = new MessageType("oidb_svc_trpctcp_base", [
{ no: 1, name: "command", kind: "scalar", T: ScalarType.UINT32 },
{ no: 2, name: "subcommand", kind: "scalar", T: ScalarType.UINT32, opt: true },
{ no: 4, name: "body", kind: "scalar", T: ScalarType.BYTES, opt: true }
{ no: 4, name: "body", kind: "scalar", T: ScalarType.BYTES, opt: true },
{ no: 12, name: "isreserved", kind: "scalar", T: ScalarType.INT32, opt: true }
]);
export const OidbSvcTrpcTcp0XED3_1 = new MessageType("oidb_svc_trpctcp_0xed3_1", [