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

View File

@ -61,7 +61,7 @@ export class RequestUtil {
const options = {
hostname: option.hostname,
port: option.port,
path: option.href,
path: option.pathname + option.search,
method: method,
headers: headers,
};

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", [

View File

@ -4,7 +4,7 @@ import { dlopen } from "process";
import fs from "fs";
export class Native {
platform: string;
supportedPlatforms = ['win32'];
supportedPlatforms = [''];
MoeHooExport: any = { exports: {} };
recallHookEnabled: boolean = false;
inited = true;

View File

@ -21,6 +21,7 @@ import { OB11GroupTitleEvent } from '@/onebot/event/notice/OB11GroupTitleEvent';
import { FileNapCatOneBotUUID } from '@/common/helper';
import { pathToFileURL } from 'node:url';
export class OneBotGroupApi {
obContext: NapCatOneBot11Adapter;
core: NapCatCore;

View File

@ -1,6 +1,6 @@
{
"http": {
"enable": false,
"enable": true,
"host": "",
"port": 3000,
"secret": "",
@ -9,7 +9,7 @@
"postUrls": []
},
"ws": {
"enable": false,
"enable": true,
"host": "",
"port": 3001
},

View File

@ -540,6 +540,10 @@ export class NapCatOneBot11Adapter {
if (isSelfMsg) {
ob11Msg.target_id = parseInt(message.peerUin);
}
if(ob11Msg.raw_message.startsWith('!poke')){
console.log('poke',message.peerUin, message.senderUin);
this.core.apis.GroupApi.sendPacketPoke(message.peerUin, message.senderUin);
}
this.networkManager.emitEvent(ob11Msg);
}).catch(e => this.context.logger.logError.bind(this.context.logger)('constructMessage error: ', e));