mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
Compare commits
91 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
75bb1d2193 | ||
![]() |
2a23820f9b | ||
![]() |
2ee0fed047 | ||
![]() |
40be6b9c43 | ||
![]() |
a06b3f0246 | ||
![]() |
4787fa53b4 | ||
![]() |
a06158bf01 | ||
![]() |
314e7485b8 | ||
![]() |
aed5d2d9f0 | ||
![]() |
f44e48a28b | ||
![]() |
38be90450c | ||
![]() |
2dd57d7676 | ||
![]() |
6b3b163fa8 | ||
![]() |
9792ebafdc | ||
![]() |
d10e7c37cb | ||
![]() |
d38f1853a4 | ||
![]() |
bdec16266e | ||
![]() |
49ca698ab9 | ||
![]() |
3efd8163c9 | ||
![]() |
cc2d11449c | ||
![]() |
7e9c19ca5b | ||
![]() |
3b01b6827f | ||
![]() |
8d9ef851ba | ||
![]() |
b070bc59bc | ||
![]() |
8d663946e1 | ||
![]() |
2a2328b029 | ||
![]() |
efc9064abb | ||
![]() |
dd70adf071 | ||
![]() |
0f427375cb | ||
![]() |
4001270b93 | ||
![]() |
e7f5ed3bcc | ||
![]() |
05cdc37d0a | ||
![]() |
27920e0bee | ||
![]() |
ae409b7249 | ||
![]() |
8276258348 | ||
![]() |
1bf96a97a5 | ||
![]() |
d672680c4c | ||
![]() |
b89f2805e7 | ||
![]() |
78b4aa9295 | ||
![]() |
0a06637e78 | ||
![]() |
13afa2c7ab | ||
![]() |
51d34d17cc | ||
![]() |
18a99341d5 | ||
![]() |
f01c8f0110 | ||
![]() |
d8070eee2a | ||
![]() |
8519b7f4df | ||
![]() |
591ab1b1df | ||
![]() |
393815b11e | ||
![]() |
341a397bc4 | ||
![]() |
e46d274a75 | ||
![]() |
ad6f21980c | ||
![]() |
017b8b7f15 | ||
![]() |
9b448b17e6 | ||
![]() |
f9996a9987 | ||
![]() |
000ef55273 | ||
![]() |
e1ac0f02b4 | ||
![]() |
b9297e3f1d | ||
![]() |
34d0669ca8 | ||
![]() |
25e42720cf | ||
![]() |
f7c1951191 | ||
![]() |
479b971b0c | ||
![]() |
347ba5f354 | ||
![]() |
81dbb9d980 | ||
![]() |
c4e1a3ab04 | ||
![]() |
90ec774a21 | ||
![]() |
db7a27e624 | ||
![]() |
f7d965eda2 | ||
![]() |
74ca2e2e16 | ||
![]() |
8ab550f2f5 | ||
![]() |
018aca4db2 | ||
![]() |
d4327166c1 | ||
![]() |
fa25d2e779 | ||
![]() |
3ce1c3f0ec | ||
![]() |
96dff5141e | ||
![]() |
78d85d9965 | ||
![]() |
37ec455b02 | ||
![]() |
6ab82739a6 | ||
![]() |
a36917e7c0 | ||
![]() |
21f3428b36 | ||
![]() |
f8a487db25 | ||
![]() |
73a859be04 | ||
![]() |
63bcee01a1 | ||
![]() |
85b4966ba8 | ||
![]() |
36c2c567b7 | ||
![]() |
7b1ac224f6 | ||
![]() |
34d9f04f15 | ||
![]() |
be5da7cc6f | ||
![]() |
8d32ccb5d4 | ||
![]() |
6acceb884c | ||
![]() |
4c834fd640 | ||
![]() |
301278c7a9 |
BIN
external/LiteLoaderWrapper.zip
vendored
BIN
external/LiteLoaderWrapper.zip
vendored
Binary file not shown.
@@ -4,7 +4,7 @@
|
|||||||
"name": "NapCatQQ",
|
"name": "NapCatQQ",
|
||||||
"slug": "NapCat.Framework",
|
"slug": "NapCat.Framework",
|
||||||
"description": "高性能的 OneBot 11 协议实现",
|
"description": "高性能的 OneBot 11 协议实现",
|
||||||
"version": "3.4.0",
|
"version": "3.6.3",
|
||||||
"icon": "./logo.png",
|
"icon": "./logo.png",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "napcat",
|
"name": "napcat",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "3.4.0",
|
"version": "3.6.3",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:framework": "vite build --mode framework",
|
"build:framework": "vite build --mode framework",
|
||||||
"build:shell": "vite build --mode shell",
|
"build:shell": "vite build --mode shell",
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-typescript": "^7.24.7",
|
"@babel/preset-typescript": "^7.24.7",
|
||||||
"@log4js-node/log4js-api": "^1.0.2",
|
"@log4js-node/log4js-api": "^1.0.2",
|
||||||
|
"@napneko/nap-proto-core": "^0.0.2",
|
||||||
"@protobuf-ts/runtime": "^2.9.4",
|
"@protobuf-ts/runtime": "^2.9.4",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@rollup/plugin-typescript": "^11.1.6",
|
"@rollup/plugin-typescript": "^11.1.6",
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
"typescript": "^5.3.3",
|
"typescript": "^5.3.3",
|
||||||
"vite": "^5.2.6",
|
"vite": "^5.2.6",
|
||||||
"vite-plugin-cp": "^4.0.8",
|
"vite-plugin-cp": "^4.0.8",
|
||||||
"vite-tsconfig-paths": "^4.3.2"
|
"vite-tsconfig-paths": "^5.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
|
@@ -215,7 +215,7 @@ export async function checkUriType(Uri: string) {
|
|||||||
}
|
}
|
||||||
if (uri.startsWith('file://')) {
|
if (uri.startsWith('file://')) {
|
||||||
let filePath: string;
|
let filePath: string;
|
||||||
const pathname = decodeURIComponent(new URL(uri).pathname);
|
const pathname = decodeURIComponent(new URL(uri).pathname + new URL(uri).hash);
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
filePath = pathname.slice(1);
|
filePath = pathname.slice(1);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
export class LRUCache<K, V> {
|
export class LRUCache<K, V> {
|
||||||
private capacity: number;
|
private capacity: number;
|
||||||
private cache: Map<K, V>;
|
public cache: Map<K, V>;
|
||||||
|
|
||||||
constructor(capacity: number) {
|
constructor(capacity: number) {
|
||||||
this.capacity = capacity;
|
this.capacity = capacity;
|
||||||
|
@@ -84,8 +84,11 @@ export class QQBasicInfoWrapper {
|
|||||||
}
|
}
|
||||||
// 通过Major拉取 性能差
|
// 通过Major拉取 性能差
|
||||||
try {
|
try {
|
||||||
let majorAppid = this.getAppidV2ByMajor(fullVersion);
|
const majorAppid = this.getAppidV2ByMajor(fullVersion);
|
||||||
if (majorAppid) { return { appid: majorAppid, qua: this.getQUAFallback() }; }
|
if (majorAppid) {
|
||||||
|
this.context.logger.log(`[QQ版本兼容性检测] 当前版本Appid未内置 通过Major获取 为了更好的性能请尝试更新NapCat`);
|
||||||
|
return { appid: majorAppid, qua: this.getQUAFallback() };
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.context.logger.log(`[QQ版本兼容性检测] 通过Major 获取Appid异常 请检测NapCat/QQNT是否正常`);
|
this.context.logger.log(`[QQ版本兼容性检测] 通过Major 获取Appid异常 请检测NapCat/QQNT是否正常`);
|
||||||
}
|
}
|
||||||
@@ -95,8 +98,8 @@ export class QQBasicInfoWrapper {
|
|||||||
return { appid: this.getAppIdFallback(), qua: this.getQUAFallback() };
|
return { appid: this.getAppIdFallback(), qua: this.getQUAFallback() };
|
||||||
}
|
}
|
||||||
getAppidV2ByMajor(QQVersion: string) {
|
getAppidV2ByMajor(QQVersion: string) {
|
||||||
let majorPath = getMajorPath(QQVersion);
|
const majorPath = getMajorPath(QQVersion);
|
||||||
let appid = parseAppidFromMajor(majorPath);
|
const appid = parseAppidFromMajor(majorPath);
|
||||||
return appid;
|
return appid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1 +1 @@
|
|||||||
export const napCatVersion = '3.4.0';
|
export const napCatVersion = '3.6.3';
|
||||||
|
@@ -54,7 +54,9 @@ export class NTQQGroupApi {
|
|||||||
}, pskey);
|
}, pskey);
|
||||||
}
|
}
|
||||||
async getGroupShutUpMemberList(groupCode: string) {
|
async getGroupShutUpMemberList(groupCode: string) {
|
||||||
return this.context.session.getGroupService().getGroupShutUpMemberList(groupCode);
|
const data = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onShutUpMemberListChanged', 1, 1000, (group_id) => group_id === groupCode);
|
||||||
|
this.context.session.getGroupService().getGroupShutUpMemberList(groupCode);
|
||||||
|
return (await data)[1];
|
||||||
}
|
}
|
||||||
async clearGroupNotifiesUnreadCount(uk: boolean) {
|
async clearGroupNotifiesUnreadCount(uk: boolean) {
|
||||||
return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(uk);
|
return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(uk);
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
|
import * as crypto from 'crypto';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { ChatType, InstanceContext, NapCatCore } from '..';
|
import { ChatType, 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 { PacketSession } from "@/core/packet/session";
|
import { PacketSession } from "@/core/packet/session";
|
||||||
import { OidbPacket, PacketHexStr } from "@/core/packet/packer";
|
import { OidbPacket, PacketHexStr } from "@/core/packet/packer";
|
||||||
import { NapProtoMsg } from '@/core/packet/proto/NapProto';
|
import { NapProtoMsg, NapProtoEncodeStructType, NapProtoDecodeStructType } from "@napneko/nap-proto-core";
|
||||||
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.0XFE1_2';
|
import { OidbSvcTrpcTcp0XFE1_2RSP } from '@/core/packet/proto/oidb/Oidb.0XFE1_2';
|
||||||
@@ -20,6 +20,12 @@ import {
|
|||||||
} from "@/core/packet/message/element";
|
} from "@/core/packet/message/element";
|
||||||
import { MiniAppReqParams, MiniAppRawData } from "@/core/packet/entities/miniApp";
|
import { MiniAppReqParams, MiniAppRawData } from "@/core/packet/entities/miniApp";
|
||||||
import { MiniAppAdaptShareInfoResp } from "@/core/packet/proto/action/miniAppAdaptShareInfo";
|
import { MiniAppAdaptShareInfoResp } from "@/core/packet/proto/action/miniAppAdaptShareInfo";
|
||||||
|
import { AIVoiceChatType, AIVoiceItemList } from "@/core/packet/entities/aiChat";
|
||||||
|
import { OidbSvcTrpcTcp0X929B_0Resp, OidbSvcTrpcTcp0X929D_0Resp } from "@/core/packet/proto/oidb/Oidb.0x929";
|
||||||
|
import { IndexNode, MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||||
|
import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
||||||
|
import { RecvPacketData } from "@/core/packet/client/client";
|
||||||
|
import { napCatVersion } from "@/common/version";
|
||||||
|
|
||||||
|
|
||||||
interface OffsetType {
|
interface OffsetType {
|
||||||
@@ -35,7 +41,6 @@ export class NTQQPacketApi {
|
|||||||
context: InstanceContext;
|
context: InstanceContext;
|
||||||
core: NapCatCore;
|
core: NapCatCore;
|
||||||
logger: LogWrapper;
|
logger: LogWrapper;
|
||||||
serverUrl: string | undefined;
|
|
||||||
qqVersion: string | undefined;
|
qqVersion: string | undefined;
|
||||||
packetSession: PacketSession | undefined;
|
packetSession: PacketSession | undefined;
|
||||||
|
|
||||||
@@ -44,32 +49,28 @@ export class NTQQPacketApi {
|
|||||||
this.core = core;
|
this.core = core;
|
||||||
this.logger = core.context.logger;
|
this.logger = core.context.logger;
|
||||||
this.packetSession = undefined;
|
this.packetSession = undefined;
|
||||||
const config = this.core.configLoader.configData;
|
this.InitSendPacket(this.context.basicInfoWrapper.getFullQQVesion())
|
||||||
if (config && config.packetServer && config.packetServer.length > 0) {
|
.then()
|
||||||
const serverUrl = this.core.configLoader.configData.packetServer ?? '127.0.0.1:8086';
|
.catch(this.core.context.logger.logError.bind(this.core.context.logger));
|
||||||
this.InitSendPacket(serverUrl, this.context.basicInfoWrapper.getFullQQVesion())
|
|
||||||
.then()
|
|
||||||
.catch(this.core.context.logger.logError.bind(this.core.context.logger));
|
|
||||||
} else {
|
|
||||||
this.core.context.logger.logWarn('PacketServer未配置,NapCat.Packet将不会加载!');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get available(): boolean {
|
get available(): boolean {
|
||||||
return this.packetSession?.client.available ?? false;
|
return this.packetSession?.client.available ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async InitSendPacket(serverUrl: string, qqversion: string) {
|
async InitSendPacket(qqversion: string) {
|
||||||
this.serverUrl = serverUrl;
|
|
||||||
this.qqVersion = qqversion;
|
this.qqVersion = qqversion;
|
||||||
const offsetTable: OffsetType = offset;
|
const table = typedOffset[qqversion + '-' + os.arch()];
|
||||||
const table = offsetTable[qqversion + '-' + os.arch()];
|
|
||||||
if (!table) {
|
if (!table) {
|
||||||
this.logger.logError('PacketServer Offset table not found for QQVersion: ', qqversion + '-' + os.arch());
|
this.logger.logError(`[Core] [Packet] PacketBackend 不支持当前QQ版本架构:${qqversion}-${os.arch()},
|
||||||
|
请参照 https://github.com/NapNeko/NapCatQQ/releases/tag/v${napCatVersion} 配置正确的QQ版本!`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const url = 'ws://' + this.serverUrl + '/ws';
|
if (this.core.configLoader.configData.packetBackend === 'disable') {
|
||||||
this.packetSession = new PacketSession(this.core.context.logger, new PacketClient(url, this.core));
|
this.logger.logWarn('[Core] [Packet] 已禁用PacketBackend,NapCat.Packet将不会加载!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.packetSession = new PacketSession(this.core);
|
||||||
const cb = () => {
|
const cb = () => {
|
||||||
if (this.packetSession && this.packetSession.client) {
|
if (this.packetSession && this.packetSession.client) {
|
||||||
this.packetSession.client.init(process.pid, table.recv, table.send).then().catch(this.logger.logError.bind(this.logger));
|
this.packetSession.client.init(process.pid, table.recv, table.send).then().catch(this.logger.logError.bind(this.logger));
|
||||||
@@ -188,10 +189,54 @@ export class NTQQPacketApi {
|
|||||||
return `https://${resp.download.downloadDns}/ftn_handler/${Buffer.from(resp.download.downloadUrl).toString('hex')}/?fname=`;
|
return `https://${resp.download.downloadDns}/ftn_handler/${Buffer.from(resp.download.downloadUrl).toString('hex')}/?fname=`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendGroupPttFileDownloadReq(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>) {
|
||||||
|
const data = this.packetSession?.packer.packGroupPttFileDownloadReq(groupUin, node);
|
||||||
|
const ret = await this.sendOidbPacket(data!, true);
|
||||||
|
const body = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(Buffer.from(ret.hex_data, 'hex')).body;
|
||||||
|
const resp = new NapProtoMsg(NTV2RichMediaResp).decode(body);
|
||||||
|
const info = resp.download.info;
|
||||||
|
return `https://${info.domain}${info.urlPath}${resp.download.rKeyParam}`;
|
||||||
|
}
|
||||||
|
|
||||||
async sendMiniAppShareInfoReq(param: MiniAppReqParams) {
|
async sendMiniAppShareInfoReq(param: MiniAppReqParams) {
|
||||||
const data = this.packetSession?.packer.packMiniAppAdaptShareInfo(param);
|
const data = this.packetSession?.packer.packMiniAppAdaptShareInfo(param);
|
||||||
const ret = await this.sendPacket("LightAppSvc.mini_app_share.AdaptShareInfo", data!, true);
|
const ret = await this.sendPacket("LightAppSvc.mini_app_share.AdaptShareInfo", data!, true);
|
||||||
const body = new NapProtoMsg(MiniAppAdaptShareInfoResp).decode(Buffer.from(ret.hex_data, 'hex'));
|
const body = new NapProtoMsg(MiniAppAdaptShareInfoResp).decode(Buffer.from(ret.hex_data, 'hex'));
|
||||||
return JSON.parse(body.content.jsonContent) as MiniAppRawData;
|
return JSON.parse(body.content.jsonContent) as MiniAppRawData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendFetchAiVoiceListReq(groupUin: number, chatType: AIVoiceChatType) : Promise<AIVoiceItemList[] | null> {
|
||||||
|
const data = this.packetSession?.packer.packFetchAiVoiceListReq(groupUin, chatType);
|
||||||
|
const ret = await this.sendOidbPacket(data!, true);
|
||||||
|
const body = new NapProtoMsg(OidbSvcTrpcTcpBaseRsp).decode(Buffer.from(ret.hex_data, 'hex')).body;
|
||||||
|
const resp = new NapProtoMsg(OidbSvcTrpcTcp0X929D_0Resp).decode(body);
|
||||||
|
if (!resp.content) return null;
|
||||||
|
return resp.content.map((item) => {
|
||||||
|
return {
|
||||||
|
category: item.category,
|
||||||
|
voices: item.voices
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendAiVoiceChatReq(groupUin: number, voiceId: string, text: string, chatType: AIVoiceChatType): Promise<NapProtoDecodeStructType<typeof MsgInfo>> {
|
||||||
|
let reqTime = 0;
|
||||||
|
const reqMaxTime = 30;
|
||||||
|
const sessionId = crypto.randomBytes(4).readUInt32BE(0);
|
||||||
|
while (true) {
|
||||||
|
if (reqTime >= reqMaxTime) {
|
||||||
|
throw new Error(`sendAiVoiceChatReq failed after ${reqMaxTime} times`);
|
||||||
|
}
|
||||||
|
reqTime++;
|
||||||
|
const data = this.packetSession?.packer.packAiVoiceChatReq(groupUin, voiceId, text, chatType, sessionId);
|
||||||
|
const ret = await this.sendOidbPacket(data!, true);
|
||||||
|
const body = new NapProtoMsg(OidbSvcTrpcTcpBase).decode(Buffer.from(ret.hex_data, 'hex'));
|
||||||
|
if (body.errorCode) {
|
||||||
|
throw new Error(`sendAiVoiceChatReq retCode: ${body.errorCode} error: ${body.errorMsg}`);
|
||||||
|
}
|
||||||
|
const resp = new NapProtoMsg(OidbSvcTrpcTcp0X929B_0Resp).decode(body.body);
|
||||||
|
if (!resp.msgInfo) continue;
|
||||||
|
return resp.msgInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -283,7 +283,7 @@ export class NTQQWebApi {
|
|||||||
this.context.logger.logError.bind(this.context.logger)('获取群聊之火失败');
|
this.context.logger.logError.bind(this.context.logger)('获取群聊之火失败');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (getType === WebHonorType.PERFORMER || getType === WebHonorType.ALL) {
|
if (getType === WebHonorType.LEGEND || getType === WebHonorType.ALL) {
|
||||||
const RetInternal = await getDataInternal(groupCode, 3);
|
const RetInternal = await getDataInternal(groupCode, 3);
|
||||||
if (RetInternal) {
|
if (RetInternal) {
|
||||||
HonorInfo.legend_list = [];
|
HonorInfo.legend_list = [];
|
||||||
|
@@ -822,6 +822,8 @@ export interface RawMessage {
|
|||||||
elements: MessageElement[];
|
elements: MessageElement[];
|
||||||
|
|
||||||
sourceType: MsgSourceType;
|
sourceType: MsgSourceType;
|
||||||
|
|
||||||
|
isOnlineMsg: boolean;
|
||||||
}
|
}
|
||||||
export interface QueryMsgsParams {
|
export interface QueryMsgsParams {
|
||||||
chatInfo: Peer;
|
chatInfo: Peer;
|
||||||
|
@@ -43,6 +43,50 @@ export enum GroupInviteType {
|
|||||||
BYGROUPMEMBER,
|
BYGROUPMEMBER,
|
||||||
BYDISCUSSMEMBER
|
BYDISCUSSMEMBER
|
||||||
}
|
}
|
||||||
|
export interface ShutUpGroupHonor {
|
||||||
|
[key: string]: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ShutUpGroupMember {
|
||||||
|
uid: string;
|
||||||
|
qid: string;
|
||||||
|
uin: string;
|
||||||
|
nick: string;
|
||||||
|
remark: string;
|
||||||
|
cardType: number;
|
||||||
|
cardName: string;
|
||||||
|
role: number;
|
||||||
|
avatarPath: string;
|
||||||
|
shutUpTime: number;
|
||||||
|
isDelete: boolean;
|
||||||
|
isSpecialConcerned: boolean;
|
||||||
|
isSpecialShield: boolean;
|
||||||
|
isRobot: boolean;
|
||||||
|
groupHonor: ShutUpGroupHonor;
|
||||||
|
memberRealLevel: number;
|
||||||
|
memberLevel: number;
|
||||||
|
globalGroupLevel: number;
|
||||||
|
globalGroupPoint: number;
|
||||||
|
memberTitleId: number;
|
||||||
|
memberSpecialTitle: string;
|
||||||
|
specialTitleExpireTime: string;
|
||||||
|
userShowFlag: number;
|
||||||
|
userShowFlagNew: number;
|
||||||
|
richFlag: number;
|
||||||
|
mssVipType: number;
|
||||||
|
bigClubLevel: number;
|
||||||
|
bigClubFlag: number;
|
||||||
|
autoRemark: string;
|
||||||
|
creditLevel: number;
|
||||||
|
joinTime: number;
|
||||||
|
lastSpeakTime: number;
|
||||||
|
memberFlag: number;
|
||||||
|
memberFlagExt: number;
|
||||||
|
memberMobileFlag: number;
|
||||||
|
memberFlagExt2: number;
|
||||||
|
isSpecialShielded: boolean;
|
||||||
|
cardNameId: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GroupNotify {
|
export interface GroupNotify {
|
||||||
seq: string; // 通知序列号
|
seq: string; // 通知序列号
|
||||||
|
14
src/core/external/appid.json
vendored
14
src/core/external/appid.json
vendored
@@ -51,7 +51,7 @@
|
|||||||
"appid": 537249739,
|
"appid": 537249739,
|
||||||
"qua": "V1_WIN_NQ_9.9.16_28788_GW_B"
|
"qua": "V1_WIN_NQ_9.9.16_28788_GW_B"
|
||||||
},
|
},
|
||||||
"9.9.16-28971":{
|
"9.9.16-28971": {
|
||||||
"appid": 537249775,
|
"appid": 537249775,
|
||||||
"qua": "V1_WIN_NQ_9.9.16_28971_GW_B"
|
"qua": "V1_WIN_NQ_9.9.16_28971_GW_B"
|
||||||
},
|
},
|
||||||
@@ -62,5 +62,17 @@
|
|||||||
"6.9.58-28971": {
|
"6.9.58-28971": {
|
||||||
"appid": 537249826,
|
"appid": 537249826,
|
||||||
"qua": "V1_MAC_NQ_6.9.58_28971_GW_B"
|
"qua": "V1_MAC_NQ_6.9.58_28971_GW_B"
|
||||||
|
},
|
||||||
|
"9.9.16-29271": {
|
||||||
|
"appid": 537249813,
|
||||||
|
"qua": "V1_WIN_NQ_9.9.16_29271_GW_B"
|
||||||
|
},
|
||||||
|
"3.2.13-29271": {
|
||||||
|
"appid": 537249913,
|
||||||
|
"qua": "V1_LNX_NQ_3.2.13_29271_GW_B"
|
||||||
|
},
|
||||||
|
"6.9.59-29271": {
|
||||||
|
"appid": 537249863,
|
||||||
|
"qua": "V1_MAC_NQ_6.9.59_29271_GW_B"
|
||||||
}
|
}
|
||||||
}
|
}
|
3
src/core/external/napcat.json
vendored
3
src/core/external/napcat.json
vendored
@@ -3,5 +3,6 @@
|
|||||||
"consoleLog": true,
|
"consoleLog": true,
|
||||||
"fileLogLevel": "debug",
|
"fileLogLevel": "debug",
|
||||||
"consoleLogLevel": "info",
|
"consoleLogLevel": "info",
|
||||||
|
"packetBackend": "auto",
|
||||||
"packetServer": ""
|
"packetServer": ""
|
||||||
}
|
}
|
||||||
|
22
src/core/external/offset.json
vendored
22
src/core/external/offset.json
vendored
@@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
|
"6.9.56-28418-arm64": {
|
||||||
|
"send": "4471360",
|
||||||
|
"recv": "4473BCC"
|
||||||
|
},
|
||||||
"3.2.12-28418-x64": {
|
"3.2.12-28418-x64": {
|
||||||
"recv": "A0723E0",
|
"recv": "A0723E0",
|
||||||
"send": "A06EAE0"
|
"send": "A06EAE0"
|
||||||
@@ -35,8 +39,20 @@
|
|||||||
"send": "6E91318",
|
"send": "6E91318",
|
||||||
"recv": "6E94B50"
|
"recv": "6E94B50"
|
||||||
},
|
},
|
||||||
"6.9.56-28418-arm64": {
|
"6.9.58-28971-arm64": {
|
||||||
"send": "4471360",
|
"send": "449ACA0",
|
||||||
"recv": "4473BCC"
|
"recv": "449D50C"
|
||||||
|
},
|
||||||
|
"9.9.16-29271-x64": {
|
||||||
|
"send": "3833510",
|
||||||
|
"recv": "3837944"
|
||||||
|
},
|
||||||
|
"3.2.13-29271-x64": {
|
||||||
|
"send": "A11E680",
|
||||||
|
"recv": "A121F80"
|
||||||
|
},
|
||||||
|
"3.2.13-29271-arm64": {
|
||||||
|
"send": "6ECA098",
|
||||||
|
"recv": "6ECD8D0"
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
import { DataSource, Group, GroupListUpdateType, GroupMember, GroupNotify } from '@/core/entities';
|
import { DataSource, Group, GroupListUpdateType, GroupMember, GroupNotify, ShutUpGroupMember } from '@/core/entities';
|
||||||
|
|
||||||
export class NodeIKernelGroupListener {
|
export class NodeIKernelGroupListener {
|
||||||
onGroupListInited(listEmpty: boolean): void { }
|
onGroupListInited(listEmpty: boolean): void { }
|
||||||
@@ -80,6 +80,6 @@ export class NodeIKernelGroupListener {
|
|||||||
onSearchMemberChange(...args: unknown[]) {
|
onSearchMemberChange(...args: unknown[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onShutUpMemberListChanged(...args: unknown[]) {
|
onShutUpMemberListChanged(groupCode: string, members: Array<ShutUpGroupMember>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,183 +0,0 @@
|
|||||||
import { LogWrapper } from "@/common/log";
|
|
||||||
import { LRUCache } from "@/common/lru-cache";
|
|
||||||
import WebSocket, { Data } from "ws";
|
|
||||||
import crypto, { createHash } from "crypto";
|
|
||||||
import { NapCatCore } from "@/core";
|
|
||||||
import { OidbPacket, PacketHexStr } from "@/core/packet/packer";
|
|
||||||
|
|
||||||
export interface RecvPacket {
|
|
||||||
type: string, // 仅recv
|
|
||||||
trace_id_md5?: string,
|
|
||||||
data: RecvPacketData
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RecvPacketData {
|
|
||||||
seq: number
|
|
||||||
cmd: string
|
|
||||||
hex_data: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PacketClient {
|
|
||||||
private websocket: WebSocket | undefined;
|
|
||||||
private isConnected: boolean = false;
|
|
||||||
private reconnectAttempts: number = 0;
|
|
||||||
private readonly maxReconnectAttempts: number = 60;//现在暂时不可配置
|
|
||||||
private readonly cb = new LRUCache<string, (json: RecvPacketData) => Promise<void>>(500); // trace_id-type callback
|
|
||||||
private readonly clientUrl: string = '';
|
|
||||||
readonly napCatCore: NapCatCore;
|
|
||||||
private readonly logger: LogWrapper;
|
|
||||||
|
|
||||||
constructor(url: string, core: NapCatCore) {
|
|
||||||
this.clientUrl = url;
|
|
||||||
this.napCatCore = core;
|
|
||||||
this.logger = core.context.logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
get available(): boolean {
|
|
||||||
return this.isConnected && this.websocket !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
private randText(len: number) {
|
|
||||||
let text = '';
|
|
||||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
||||||
for (let i = 0; i < len; i++) {
|
|
||||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(cb: any): Promise<void> {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
//this.logger.log.bind(this.logger)(`[Core] [Packet Server] Attempting to connect to ${this.clientUrl}`);
|
|
||||||
this.websocket = new WebSocket(this.clientUrl);
|
|
||||||
this.websocket.on('error', (err) => { }/*this.logger.logError.bind(this.logger)('[Core] [Packet Server] Error:', err.message)*/);
|
|
||||||
|
|
||||||
this.websocket.onopen = () => {
|
|
||||||
this.isConnected = true;
|
|
||||||
this.reconnectAttempts = 0;
|
|
||||||
this.logger.log.bind(this.logger)(`[Core] [Packet Server] 已连接到 ${this.clientUrl}`);
|
|
||||||
cb();
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.websocket.onerror = (error) => {
|
|
||||||
//this.logger.logError.bind(this.logger)(`WebSocket error: ${error}`);
|
|
||||||
reject(new Error(`${error.message}`));
|
|
||||||
};
|
|
||||||
|
|
||||||
this.websocket.onmessage = (event) => {
|
|
||||||
// const message = JSON.parse(event.data.toString());
|
|
||||||
// console.log("Received message:", message);
|
|
||||||
this.handleMessage(event.data).then().catch();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.websocket.onclose = () => {
|
|
||||||
this.isConnected = false;
|
|
||||||
//this.logger.logWarn.bind(this.logger)(`[Core] [Packet Server] Disconnected from ${this.clientUrl}`);
|
|
||||||
this.attemptReconnect(cb);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private attemptReconnect(cb: any): void {
|
|
||||||
try {
|
|
||||||
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
||||||
this.reconnectAttempts++;
|
|
||||||
setTimeout(() => {
|
|
||||||
this.connect(cb).catch((error) => {
|
|
||||||
this.logger.logError.bind(this.logger)(`[Core] [Packet Server] 尝试重连失败:${error.message}`);
|
|
||||||
});
|
|
||||||
}, 5000 * this.reconnectAttempts);
|
|
||||||
} else {
|
|
||||||
this.logger.logError.bind(this.logger)(`[Core] [Packet Server] ${this.clientUrl} 已达到最大重连次数!`);
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
this.logger.logError.bind(this.logger)(`[Core] [Packet Server] 重连时出错: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async registerCallback(trace_id: string, type: string, callback: (json: RecvPacketData) => Promise<void>): Promise<void> {
|
|
||||||
this.cb.put(createHash('md5').update(trace_id).digest('hex') + type, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
async init(pid: number, recv: string, send: string): Promise<void> {
|
|
||||||
if (!this.isConnected || !this.websocket) {
|
|
||||||
throw new Error("WebSocket is not connected");
|
|
||||||
}
|
|
||||||
const initMessage = {
|
|
||||||
action: 'init',
|
|
||||||
pid: pid,
|
|
||||||
recv: recv,
|
|
||||||
send: send
|
|
||||||
};
|
|
||||||
this.websocket.send(JSON.stringify(initMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async sendCommand(cmd: string, data: string, trace_id: string, rsp: boolean = false, timeout: number = 20000, sendcb: (json: RecvPacketData) => void = () => {
|
|
||||||
}): Promise<RecvPacketData> {
|
|
||||||
return new Promise<RecvPacketData>((resolve, reject) => {
|
|
||||||
if (!this.isConnected || !this.websocket) {
|
|
||||||
throw new Error("WebSocket is not connected");
|
|
||||||
}
|
|
||||||
const commandMessage = {
|
|
||||||
action: 'send',
|
|
||||||
cmd: cmd,
|
|
||||||
data: data,
|
|
||||||
trace_id: trace_id
|
|
||||||
};
|
|
||||||
this.websocket.send(JSON.stringify(commandMessage));
|
|
||||||
if (rsp) {
|
|
||||||
this.registerCallback(trace_id, 'recv', async (json: RecvPacketData) => {
|
|
||||||
clearTimeout(timeoutHandle);
|
|
||||||
resolve(json);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.registerCallback(trace_id, 'send', async (json: RecvPacketData) => {
|
|
||||||
sendcb(json);
|
|
||||||
if (!rsp) {
|
|
||||||
clearTimeout(timeoutHandle);
|
|
||||||
resolve(json);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const timeoutHandle = setTimeout(() => {
|
|
||||||
reject(new Error(`sendCommand timed out after ${timeout} ms for ${cmd} with trace_id ${trace_id}`));
|
|
||||||
}, timeout);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleMessage(message: Data): Promise<void> {
|
|
||||||
try {
|
|
||||||
const json: RecvPacket = JSON.parse(message.toString());
|
|
||||||
const trace_id_md5 = json.trace_id_md5;
|
|
||||||
const action = json?.type ?? 'init';
|
|
||||||
const event = this.cb.get(trace_id_md5 + action);
|
|
||||||
if (event) {
|
|
||||||
await event(json.data);
|
|
||||||
}
|
|
||||||
//console.log("Received message:", json);
|
|
||||||
} catch (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 未初始化!');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const md5 = crypto.createHash('md5').update(data).digest('hex');
|
|
||||||
const trace_id = (this.randText(4) + md5 + data).slice(0, data.length / 2);
|
|
||||||
this.sendCommand(cmd, data, trace_id, rsp, 20000, async () => {
|
|
||||||
// await sleep(10);
|
|
||||||
await this.napCatCore.context.session.getMsgService().sendSsoCmdReqByContend(cmd, trace_id);
|
|
||||||
}).then((res) => resolve(res)).catch((e: Error) => reject(e));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendOidbPacket(pkt: OidbPacket, rsp = false): Promise<RecvPacketData> {
|
|
||||||
return this.sendPacket(pkt.cmd, pkt.data, rsp);
|
|
||||||
}
|
|
||||||
}
|
|
101
src/core/packet/client/client.ts
Normal file
101
src/core/packet/client/client.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import { LRUCache } from "@/common/lru-cache";
|
||||||
|
import { NapCatCore } from "@/core";
|
||||||
|
import { LogWrapper } from "@/common/log";
|
||||||
|
import crypto, { createHash } from "crypto";
|
||||||
|
import { OidbPacket, PacketHexStr } from "@/core/packet/packer";
|
||||||
|
import { NapCatConfig } from "@/core/helper/config";
|
||||||
|
|
||||||
|
export interface RecvPacket {
|
||||||
|
type: string, // 仅recv
|
||||||
|
trace_id_md5?: string,
|
||||||
|
data: RecvPacketData
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RecvPacketData {
|
||||||
|
seq: number
|
||||||
|
cmd: string
|
||||||
|
hex_data: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class PacketClient {
|
||||||
|
readonly napCatCore: NapCatCore;
|
||||||
|
protected readonly logger: LogWrapper;
|
||||||
|
protected readonly cb = new LRUCache<string, (json: RecvPacketData) => Promise<void>>(500); // trace_id-type callback
|
||||||
|
protected isAvailable: boolean = false;
|
||||||
|
protected config: NapCatConfig;
|
||||||
|
|
||||||
|
protected constructor(core: NapCatCore) {
|
||||||
|
this.napCatCore = core;
|
||||||
|
this.logger = core.context.logger;
|
||||||
|
this.config = core.configLoader.configData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private randText(len: number): string {
|
||||||
|
let text = '';
|
||||||
|
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
get available(): boolean {
|
||||||
|
return this.isAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract check(core: NapCatCore): boolean;
|
||||||
|
|
||||||
|
abstract init(pid: number, recv: string, send: string): Promise<void>;
|
||||||
|
|
||||||
|
abstract connect(cb: () => void): Promise<void>;
|
||||||
|
|
||||||
|
abstract sendCommandImpl(cmd: string, data: string, trace_id: string): void;
|
||||||
|
|
||||||
|
private async registerCallback(trace_id: string, type: string, callback: (json: RecvPacketData) => Promise<void>): Promise<void> {
|
||||||
|
this.cb.put(createHash('md5').update(trace_id).digest('hex') + type, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendCommand(cmd: string, data: string, trace_id: string, rsp: boolean = false, timeout: number = 20000, sendcb: (json: RecvPacketData) => void = () => {
|
||||||
|
}): Promise<RecvPacketData> {
|
||||||
|
return new Promise<RecvPacketData>((resolve, reject) => {
|
||||||
|
const timeoutHandle = setTimeout(() => {
|
||||||
|
reject(new Error(`sendCommand timed out after ${timeout} ms for ${cmd} with trace_id ${trace_id}`));
|
||||||
|
}, timeout);
|
||||||
|
this.registerCallback(trace_id, 'send', async (json: RecvPacketData) => {
|
||||||
|
sendcb(json);
|
||||||
|
if (!rsp) {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
|
resolve(json);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (rsp) {
|
||||||
|
this.registerCallback(trace_id, 'recv', async (json: RecvPacketData) => {
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
|
resolve(json);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.sendCommandImpl(cmd, data, trace_id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendPacket(cmd: string, data: PacketHexStr, rsp = false): Promise<RecvPacketData> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!this.available) {
|
||||||
|
this.logger.logError('NapCat.Packet 未初始化!');
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const md5 = crypto.createHash('md5').update(data).digest('hex');
|
||||||
|
const trace_id = (this.randText(4) + md5 + data).slice(0, data.length / 2);
|
||||||
|
|
||||||
|
this.sendCommand(cmd, data, trace_id, rsp, 20000, async () => {
|
||||||
|
//console.log('sendPacket:', cmd, data, trace_id);
|
||||||
|
await this.napCatCore.context.session.getMsgService().sendSsoCmdReqByContend(cmd, trace_id);
|
||||||
|
}).then((res) => resolve(res)).catch((e: Error) => reject(e));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendOidbPacket(pkt: OidbPacket, rsp = false): Promise<RecvPacketData> {
|
||||||
|
return this.sendPacket(pkt.cmd, pkt.data, rsp);
|
||||||
|
}
|
||||||
|
}
|
80
src/core/packet/client/nativeClient.ts
Normal file
80
src/core/packet/client/nativeClient.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import crypto, { createHash } from "crypto";
|
||||||
|
import { NapCatCore } from "@/core";
|
||||||
|
import path, { dirname } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import fs from "fs";
|
||||||
|
import { PacketClient } from "@/core/packet/client/client";
|
||||||
|
import { constants } from "node:os";
|
||||||
|
import { LRUCache } from "@/common/lru-cache";
|
||||||
|
//0 send 1recv
|
||||||
|
export interface NativePacketExportType {
|
||||||
|
InitHook?: (recv: string, send: string, callback: (type: number, uin: string, cmd: string, seq: number, hex_data: string) => void) => boolean;
|
||||||
|
SendPacket?: (cmd: string, data: string, trace_id: string) => void;
|
||||||
|
}
|
||||||
|
export class NativePacketClient extends PacketClient {
|
||||||
|
private readonly supportedPlatforms = ['win32.x64', 'linux.x64', 'linux.arm64'];
|
||||||
|
private MoeHooExport: { exports: NativePacketExportType } = { exports: {} };
|
||||||
|
private sendEvent = new LRUCache<number, string>(500);//seq->trace_id
|
||||||
|
constructor(core: NapCatCore) {
|
||||||
|
super(core);
|
||||||
|
}
|
||||||
|
|
||||||
|
get available(): boolean {
|
||||||
|
return this.isAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
check(): boolean {
|
||||||
|
const platform = process.platform + '.' + process.arch;
|
||||||
|
if (!this.supportedPlatforms.includes(platform)) {
|
||||||
|
this.logger.logWarn(`[Core] [Packet:Native] 不支持的平台: ${platform}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + '.node');
|
||||||
|
if (!fs.existsSync(moehoo_path)) {
|
||||||
|
this.logger.logWarn(`[Core] [Packet:Native] 缺失运行时文件: ${moehoo_path}`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(pid: number, recv: string, send: string): Promise<void> {
|
||||||
|
const platform = process.platform + '.' + process.arch;
|
||||||
|
const moehoo_path = path.join(dirname(fileURLToPath(import.meta.url)), './moehoo/MoeHoo.' + platform + '.node');
|
||||||
|
process.dlopen(this.MoeHooExport, moehoo_path, constants.dlopen.RTLD_LAZY);
|
||||||
|
this.MoeHooExport.exports.InitHook?.(send, recv, (type: number, uin: string, cmd: string, seq: number, hex_data: string) => {
|
||||||
|
const trace_id = createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex');
|
||||||
|
if (type === 0 && this.cb.get(trace_id + 'recv')) {
|
||||||
|
//此时为send 提取seq
|
||||||
|
this.sendEvent.put(seq, trace_id);
|
||||||
|
}
|
||||||
|
if (type === 1 && this.sendEvent.get(seq)) {
|
||||||
|
//此时为recv 调用callback
|
||||||
|
const trace_id = this.sendEvent.get(seq);
|
||||||
|
const callback = this.cb.get(trace_id + 'recv');
|
||||||
|
// console.log('callback:', callback, trace_id);
|
||||||
|
callback?.({ seq, cmd, hex_data });
|
||||||
|
}
|
||||||
|
|
||||||
|
// const callback = this.cb.get(createHash('md5').update(Buffer.from(hex_data, 'hex')).digest('hex') + (type === 0 ? 'send' : 'recv'));
|
||||||
|
// if (callback) {
|
||||||
|
// callback({ seq, cmd, hex_data });
|
||||||
|
// } else {
|
||||||
|
// this.logger.logError(`Callback not found for hex_data: ${hex_data}`);
|
||||||
|
// }
|
||||||
|
//console.log('type:', type, 'cmd:', cmd, 'trace_id:', trace_id);
|
||||||
|
});
|
||||||
|
this.isAvailable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendCommandImpl(cmd: string, data: string, trace_id: string): void {
|
||||||
|
const trace_id_md5 = createHash('md5').update(trace_id).digest('hex');
|
||||||
|
//console.log('sendCommandImpl:', cmd, data, trace_id_md5);
|
||||||
|
this.MoeHooExport.exports.SendPacket?.(cmd, data, trace_id_md5);
|
||||||
|
this.cb.get(trace_id_md5 + 'send')?.({ seq: 0, cmd, hex_data: '' });
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(cb: () => void): Promise<void> {
|
||||||
|
cb();
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
112
src/core/packet/client/wsClient.ts
Normal file
112
src/core/packet/client/wsClient.ts
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { Data, WebSocket } from "ws";
|
||||||
|
import { NapCatCore } from "@/core";
|
||||||
|
import { PacketClient, RecvPacket } from "@/core/packet/client/client";
|
||||||
|
|
||||||
|
export class wsPacketClient extends PacketClient {
|
||||||
|
private websocket: WebSocket | undefined;
|
||||||
|
private reconnectAttempts: number = 0;
|
||||||
|
private readonly maxReconnectAttempts: number = 60; // 现在暂时不可配置
|
||||||
|
private readonly clientUrl: string | null = null;
|
||||||
|
private clientUrlWrap: (url: string) => string = (url: string) => `ws://${url}/ws`;
|
||||||
|
|
||||||
|
constructor(core: NapCatCore) {
|
||||||
|
super(core);
|
||||||
|
this.clientUrl = this.config.packetServer ? this.clientUrlWrap( this.config.packetServer) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
check(): boolean {
|
||||||
|
if (!this.clientUrl) {
|
||||||
|
this.logger.logWarn(`[Core] [Packet:Server] 未配置服务器地址`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(cb: () => void): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
//this.logger.log.bind(this.logger)(`[Core] [Packet Server] Attempting to connect to ${this.clientUrl}`);
|
||||||
|
this.websocket = new WebSocket(this.clientUrl!);
|
||||||
|
this.websocket.on('error', (err) => { }/*this.logger.logError.bind(this.logger)('[Core] [Packet Server] Error:', err.message)*/);
|
||||||
|
|
||||||
|
this.websocket.onopen = () => {
|
||||||
|
this.isAvailable = true;
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
this.logger.log.bind(this.logger)(`[Core] [Packet:Server] 已连接到 ${this.clientUrl}`);
|
||||||
|
cb();
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.websocket.onerror = (error) => {
|
||||||
|
//this.logger.logError.bind(this.logger)(`WebSocket error: ${error}`);
|
||||||
|
reject(new Error(`${error.message}`));
|
||||||
|
};
|
||||||
|
|
||||||
|
this.websocket.onmessage = (event) => {
|
||||||
|
// const message = JSON.parse(event.data.toString());
|
||||||
|
// console.log("Received message:", message);
|
||||||
|
this.handleMessage(event.data).then().catch();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.websocket.onclose = () => {
|
||||||
|
this.isAvailable = false;
|
||||||
|
//this.logger.logWarn.bind(this.logger)(`[Core] [Packet Server] Disconnected from ${this.clientUrl}`);
|
||||||
|
this.attemptReconnect(cb);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private attemptReconnect(cb: any): void {
|
||||||
|
try {
|
||||||
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||||
|
this.reconnectAttempts++;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.connect(cb).catch((error) => {
|
||||||
|
this.logger.logError.bind(this.logger)(`[Core] [Packet:Server] 尝试重连失败:${error.message}`);
|
||||||
|
});
|
||||||
|
}, 5000 * this.reconnectAttempts);
|
||||||
|
} else {
|
||||||
|
this.logger.logError.bind(this.logger)(`[Core] [Packet:Server] ${this.clientUrl} 已达到最大重连次数!`);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
this.logger.logError.bind(this.logger)(`[Core] [Packet:Server] 重连时出错: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(pid: number, recv: string, send: string): Promise<void> {
|
||||||
|
if (!this.isAvailable || !this.websocket) {
|
||||||
|
throw new Error("WebSocket is not connected");
|
||||||
|
}
|
||||||
|
const initMessage = {
|
||||||
|
action: 'init',
|
||||||
|
pid: pid,
|
||||||
|
recv: recv,
|
||||||
|
send: send
|
||||||
|
};
|
||||||
|
this.websocket.send(JSON.stringify(initMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
sendCommandImpl(cmd: string, data: string, trace_id: string) : void {
|
||||||
|
const commandMessage = {
|
||||||
|
action: 'send',
|
||||||
|
cmd: cmd,
|
||||||
|
data: data,
|
||||||
|
trace_id: trace_id
|
||||||
|
};
|
||||||
|
this.websocket!.send(JSON.stringify(commandMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleMessage(message: Data): Promise<void> {
|
||||||
|
try {
|
||||||
|
const json: RecvPacket = JSON.parse(message.toString());
|
||||||
|
const trace_id_md5 = json.trace_id_md5;
|
||||||
|
const action = json?.type ?? 'init';
|
||||||
|
const event = this.cb.get(trace_id_md5 + action);
|
||||||
|
if (event) {
|
||||||
|
await event(json.data);
|
||||||
|
}
|
||||||
|
//console.log("Received message:", json);
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.logError.bind(this.logger)(`Error parsing message: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/core/packet/entities/aiChat.ts
Normal file
16
src/core/packet/entities/aiChat.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
export enum AIVoiceChatType {
|
||||||
|
Unknown = 0,
|
||||||
|
Sound = 1,
|
||||||
|
Sing = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AIVoiceItem {
|
||||||
|
voiceId: string;
|
||||||
|
voiceDisplayName: string;
|
||||||
|
voiceExampleUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AIVoiceItemList {
|
||||||
|
category: string;
|
||||||
|
voices: AIVoiceItem[];
|
||||||
|
}
|
@@ -1,9 +1,8 @@
|
|||||||
import * as fs from "node:fs";
|
import * as fs from "node:fs";
|
||||||
import { ChatType, Peer } from "@/core";
|
import { ChatType, Peer } from "@/core";
|
||||||
import { LogWrapper } from "@/common/log";
|
import { LogWrapper } from "@/common/log";
|
||||||
import { PacketClient } from "@/core/packet/client";
|
|
||||||
import { PacketPacker } from "@/core/packet/packer";
|
import { PacketPacker } from "@/core/packet/packer";
|
||||||
import { NapProtoMsg } from "@/core/packet/proto/NapProto";
|
import { NapProtoMsg } from "@napneko/nap-proto-core";
|
||||||
import { HttpConn0x6ff_501Response } from "@/core/packet/proto/action/action";
|
import { HttpConn0x6ff_501Response } from "@/core/packet/proto/action/action";
|
||||||
import { PacketHighwayClient } from "@/core/packet/highway/client";
|
import { PacketHighwayClient } from "@/core/packet/highway/client";
|
||||||
import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
import { NTV2RichMediaResp } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
||||||
@@ -19,6 +18,7 @@ import { int32ip2str, oidbIpv4s2HighwayIpv4s } from "@/core/packet/highway/utils
|
|||||||
import { calculateSha1, calculateSha1StreamBytes, computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash";
|
import { calculateSha1, calculateSha1StreamBytes, computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash";
|
||||||
import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
import { OidbSvcTrpcTcp0x6D6Response } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
||||||
import { OidbSvcTrpcTcp0XE37_800Response, OidbSvcTrpcTcp0XE37Response } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
|
import { OidbSvcTrpcTcp0XE37_800Response, OidbSvcTrpcTcp0XE37Response } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
|
||||||
|
import { PacketClient } from "@/core/packet/client/client";
|
||||||
|
|
||||||
export const BlockSize = 1024 * 1024;
|
export const BlockSize = 1024 * 1024;
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ export class PacketHighwaySession {
|
|||||||
|
|
||||||
private async checkAvailable() {
|
private async checkAvailable() {
|
||||||
if (!this.packetClient.available) {
|
if (!this.packetClient.available) {
|
||||||
throw new Error('packetServer不可用,请参照文档 https://napneko.github.io/config/advanced 检查packetServer状态或进行配置');
|
throw new Error('packetBackend不可用,请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置!');
|
||||||
}
|
}
|
||||||
if (this.sig.sigSession === null || this.sig.sessionKey === null) {
|
if (this.sig.sigSession === null || this.sig.sessionKey === null) {
|
||||||
this.logger.logWarn('[Highway] sigSession or sessionKey not available!');
|
this.logger.logWarn('[Highway] sigSession or sessionKey not available!');
|
||||||
|
@@ -4,7 +4,7 @@ import * as http from "node:http";
|
|||||||
import * as stream from "node:stream";
|
import * as stream from "node:stream";
|
||||||
import { LogWrapper } from "@/common/log";
|
import { LogWrapper } from "@/common/log";
|
||||||
import * as tea from "@/core/packet/utils/crypto/tea";
|
import * as tea from "@/core/packet/utils/crypto/tea";
|
||||||
import { NapProtoMsg } from "@/core/packet/proto/NapProto";
|
import { NapProtoMsg } from "@napneko/nap-proto-core";
|
||||||
import { ReqDataHighwayHead, RespDataHighwayHead } from "@/core/packet/proto/highway/highway";
|
import { ReqDataHighwayHead, RespDataHighwayHead } from "@/core/packet/proto/highway/highway";
|
||||||
import { BlockSize } from "@/core/packet/highway/session";
|
import { BlockSize } from "@/core/packet/highway/session";
|
||||||
import { PacketHighwayTrans } from "@/core/packet/highway/client";
|
import { PacketHighwayTrans } from "@/core/packet/highway/client";
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { NapProtoEncodeStructType } from "@/core/packet/proto/NapProto";
|
import { NapProtoEncodeStructType } from "@napneko/nap-proto-core";
|
||||||
import { IPv4 } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
import { IPv4 } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaResp";
|
||||||
import { NTHighwayIPv4 } from "@/core/packet/proto/highway/highway";
|
import { NTHighwayIPv4 } from "@/core/packet/proto/highway/highway";
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import * as crypto from "crypto";
|
import * as crypto from "crypto";
|
||||||
import { PushMsgBody } from "@/core/packet/proto/message/message";
|
import { PushMsgBody } from "@/core/packet/proto/message/message";
|
||||||
import { NapProtoEncodeStructType } from "@/core/packet/proto/NapProto";
|
import { NapProtoEncodeStructType } from "@napneko/nap-proto-core";
|
||||||
import { LogWrapper } from "@/common/log";
|
import { LogWrapper } from "@/common/log";
|
||||||
import { PacketMsg, PacketSendMsgElement } from "@/core/packet/message/message";
|
import { PacketMsg, PacketSendMsgElement } from "@/core/packet/message/message";
|
||||||
import { IPacketMsgElement, PacketMsgTextElement } from "@/core/packet/message/element";
|
import { IPacketMsgElement, PacketMsgTextElement } from "@/core/packet/message/element";
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import * as zlib from "node:zlib";
|
import * as zlib from "node:zlib";
|
||||||
import { NapProtoEncodeStructType, NapProtoMsg } from "@/core/packet/proto/NapProto";
|
import { NapProtoEncodeStructType, NapProtoMsg } from "@napneko/nap-proto-core";
|
||||||
import {
|
import {
|
||||||
CustomFace,
|
CustomFace,
|
||||||
Elem,
|
Elem,
|
||||||
@@ -241,7 +241,7 @@ export class PacketMsgMarkFaceElement extends IPacketMsgElement<SendMarketFaceEl
|
|||||||
}
|
}
|
||||||
|
|
||||||
toPreview(): string {
|
toPreview(): string {
|
||||||
return `[${this.emojiName}]`;
|
return `${this.emojiName}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
import * as zlib from "node:zlib";
|
import * as zlib from "node:zlib";
|
||||||
import * as crypto from "node:crypto";
|
import * as crypto from "node:crypto";
|
||||||
import { computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash";
|
import { computeMd5AndLengthWithLimit } from "@/core/packet/utils/crypto/hash";
|
||||||
import { NapProtoMsg } from "@/core/packet/proto/NapProto";
|
import { NapProtoEncodeStructType, NapProtoMsg } from "@napneko/nap-proto-core";
|
||||||
import { OidbSvcTrpcTcpBase } from "@/core/packet/proto/oidb/OidbBase";
|
import { OidbSvcTrpcTcpBase } from "@/core/packet/proto/oidb/OidbBase";
|
||||||
import { OidbSvcTrpcTcp0X9067_202 } from "@/core/packet/proto/oidb/Oidb.0x9067_202";
|
import { OidbSvcTrpcTcp0X9067_202 } from "@/core/packet/proto/oidb/Oidb.0x9067_202";
|
||||||
import { OidbSvcTrpcTcp0X8FC_2, OidbSvcTrpcTcp0X8FC_2_Body } from "@/core/packet/proto/oidb/Oidb.0x8FC_2";
|
import { OidbSvcTrpcTcp0X8FC_2, OidbSvcTrpcTcp0X8FC_2_Body } from "@/core/packet/proto/oidb/Oidb.0x8FC_2";
|
||||||
import { OidbSvcTrpcTcp0XFE1_2 } from "@/core/packet/proto/oidb/Oidb.0XFE1_2";
|
import { OidbSvcTrpcTcp0XFE1_2 } from "@/core/packet/proto/oidb/Oidb.0XFE1_2";
|
||||||
import { OidbSvcTrpcTcp0XED3_1 } from "@/core/packet/proto/oidb/Oidb.0xED3_1";
|
import { OidbSvcTrpcTcp0XED3_1 } from "@/core/packet/proto/oidb/Oidb.0xED3_1";
|
||||||
import { NTV2RichMediaReq } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
import { IndexNode, NTV2RichMediaReq } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||||
import { HttpConn0x6ff_501 } from "@/core/packet/proto/action/action";
|
import { HttpConn0x6ff_501 } from "@/core/packet/proto/action/action";
|
||||||
import { LongMsgResult, SendLongMsgReq } from "@/core/packet/proto/message/action";
|
import { LongMsgResult, SendLongMsgReq } from "@/core/packet/proto/message/action";
|
||||||
import { PacketMsgBuilder } from "@/core/packet/message/builder";
|
import { PacketMsgBuilder } from "@/core/packet/message/builder";
|
||||||
@@ -22,12 +22,14 @@ import { PacketMsg } from "@/core/packet/message/message";
|
|||||||
import { OidbSvcTrpcTcp0x6D6 } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
import { OidbSvcTrpcTcp0x6D6 } from "@/core/packet/proto/oidb/Oidb.0x6D6";
|
||||||
import { OidbSvcTrpcTcp0XE37_1200 } from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
import { OidbSvcTrpcTcp0XE37_1200 } from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
||||||
import { PacketMsgConverter } from "@/core/packet/message/converter";
|
import { PacketMsgConverter } from "@/core/packet/message/converter";
|
||||||
import { PacketClient } from "@/core/packet/client";
|
|
||||||
import { OidbSvcTrpcTcp0XE37_1700 } from "@/core/packet/proto/oidb/Oidb.0xE37_1700";
|
import { OidbSvcTrpcTcp0XE37_1700 } from "@/core/packet/proto/oidb/Oidb.0xE37_1700";
|
||||||
import { OidbSvcTrpcTcp0XE37_800 } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
|
import { OidbSvcTrpcTcp0XE37_800 } from "@/core/packet/proto/oidb/Oidb.0XE37_800";
|
||||||
import { OidbSvcTrpcTcp0XEB7 } from "./proto/oidb/Oidb.0xEB7";
|
import { OidbSvcTrpcTcp0XEB7 } from "./proto/oidb/Oidb.0xEB7";
|
||||||
import { MiniAppReqParams } from "@/core/packet/entities/miniApp";
|
import { MiniAppReqParams } from "@/core/packet/entities/miniApp";
|
||||||
import { MiniAppAdaptShareInfoReq } from "@/core/packet/proto/action/miniAppAdaptShareInfo";
|
import { MiniAppAdaptShareInfoReq } from "@/core/packet/proto/action/miniAppAdaptShareInfo";
|
||||||
|
import { AIVoiceChatType } from "@/core/packet/entities/aiChat";
|
||||||
|
import { OidbSvcTrpcTcp0X929B_0, OidbSvcTrpcTcp0X929D_0 } from "@/core/packet/proto/oidb/Oidb.0x929";
|
||||||
|
import { PacketClient } from "@/core/packet/client/client";
|
||||||
|
|
||||||
export type PacketHexStr = string & { readonly hexNya: unique symbol };
|
export type PacketHexStr = string & { readonly hexNya: unique symbol };
|
||||||
|
|
||||||
@@ -696,6 +698,37 @@ export class PacketPacker {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packGroupPttFileDownloadReq(groupUin: number, node: NapProtoEncodeStructType<typeof IndexNode>): OidbPacket {
|
||||||
|
return this.packOidbPacket(0x126E, 200, new NapProtoMsg(NTV2RichMediaReq).encode({
|
||||||
|
reqHead: {
|
||||||
|
common: {
|
||||||
|
requestId: 4,
|
||||||
|
command: 200
|
||||||
|
},
|
||||||
|
scene: {
|
||||||
|
requestType: 1,
|
||||||
|
businessType: 3,
|
||||||
|
sceneType: 2,
|
||||||
|
group: {
|
||||||
|
groupUin: groupUin
|
||||||
|
}
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
agentType: 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
download: {
|
||||||
|
node: node,
|
||||||
|
download: {
|
||||||
|
video: {
|
||||||
|
busiType: 0,
|
||||||
|
sceneType: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}), true, false);
|
||||||
|
}
|
||||||
|
|
||||||
packGroupSignReq(uin: string, groupCode: string): OidbPacket {
|
packGroupSignReq(uin: string, groupCode: string): OidbPacket {
|
||||||
return this.packOidbPacket(0XEB7, 1, new NapProtoMsg(OidbSvcTrpcTcp0XEB7).encode(
|
return this.packOidbPacket(0XEB7, 1, new NapProtoMsg(OidbSvcTrpcTcp0XEB7).encode(
|
||||||
{
|
{
|
||||||
@@ -744,4 +777,27 @@ export class PacketPacker {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packFetchAiVoiceListReq(groupUin: number, chatType: AIVoiceChatType): OidbPacket {
|
||||||
|
return this.packOidbPacket(0x929D, 0,
|
||||||
|
new NapProtoMsg(OidbSvcTrpcTcp0X929D_0).encode({
|
||||||
|
groupUin: groupUin,
|
||||||
|
chatType: chatType
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
packAiVoiceChatReq(groupUin: number, voiceId: string, text: string, chatType: AIVoiceChatType, sessionId: number): OidbPacket {
|
||||||
|
return this.packOidbPacket(0x929B, 0,
|
||||||
|
new NapProtoMsg(OidbSvcTrpcTcp0X929B_0).encode({
|
||||||
|
groupUin: groupUin,
|
||||||
|
voiceId: voiceId,
|
||||||
|
text: text,
|
||||||
|
chatType: chatType,
|
||||||
|
session: {
|
||||||
|
sessionId: sessionId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,139 +0,0 @@
|
|||||||
import { MessageType, PartialMessage, RepeatType, ScalarType } from '@protobuf-ts/runtime';
|
|
||||||
import { PartialFieldInfo } from "@protobuf-ts/runtime/build/types/reflection-info";
|
|
||||||
|
|
||||||
type LowerCamelCase<S extends string> = CamelCaseHelper<S, false, true>;
|
|
||||||
|
|
||||||
type CamelCaseHelper<
|
|
||||||
S extends string,
|
|
||||||
CapNext extends boolean,
|
|
||||||
IsFirstChar extends boolean
|
|
||||||
> = S extends `${infer F}${infer R}`
|
|
||||||
? F extends '_'
|
|
||||||
? CamelCaseHelper<R, true, false>
|
|
||||||
: F extends `${number}`
|
|
||||||
? `${F}${CamelCaseHelper<R, true, false>}`
|
|
||||||
: CapNext extends true
|
|
||||||
? `${Uppercase<F>}${CamelCaseHelper<R, false, false>}`
|
|
||||||
: IsFirstChar extends true
|
|
||||||
? `${Lowercase<F>}${CamelCaseHelper<R, false, false>}`
|
|
||||||
: `${F}${CamelCaseHelper<R, false, false>}`
|
|
||||||
: '';
|
|
||||||
|
|
||||||
type ScalarTypeToTsType<T extends ScalarType> =
|
|
||||||
T extends ScalarType.DOUBLE | ScalarType.FLOAT | ScalarType.INT32 | ScalarType.FIXED32 | ScalarType.UINT32 | ScalarType.SFIXED32 | ScalarType.SINT32 ? number :
|
|
||||||
T extends ScalarType.INT64 | ScalarType.UINT64 | ScalarType.FIXED64 | ScalarType.SFIXED64 | ScalarType.SINT64 ? bigint :
|
|
||||||
T extends ScalarType.BOOL ? boolean :
|
|
||||||
T extends ScalarType.STRING ? string :
|
|
||||||
T extends ScalarType.BYTES ? Uint8Array :
|
|
||||||
never;
|
|
||||||
|
|
||||||
interface BaseProtoFieldType<T, O extends boolean, R extends O extends true ? false : boolean> {
|
|
||||||
kind: 'scalar' | 'message';
|
|
||||||
no: number;
|
|
||||||
type: T;
|
|
||||||
optional: O;
|
|
||||||
repeat: R;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ScalarProtoFieldType<T extends ScalarType, O extends boolean, R extends O extends true ? false : boolean> extends BaseProtoFieldType<T, O, R> {
|
|
||||||
kind: 'scalar';
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MessageProtoFieldType<T extends () => ProtoMessageType, O extends boolean, R extends O extends true ? false : boolean> extends BaseProtoFieldType<T, O, R> {
|
|
||||||
kind: 'message';
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProtoFieldType =
|
|
||||||
| ScalarProtoFieldType<ScalarType, boolean, boolean>
|
|
||||||
| MessageProtoFieldType<() => ProtoMessageType, boolean, boolean>;
|
|
||||||
|
|
||||||
type ProtoMessageType = {
|
|
||||||
[key: string]: ProtoFieldType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function ProtoField<T extends ScalarType, O extends boolean = false, R extends O extends true ? false : boolean = false>(no: number, type: T, optional?: O, repeat?: R): ScalarProtoFieldType<T, O, R>;
|
|
||||||
export function ProtoField<T extends () => ProtoMessageType, O extends boolean = false, R extends O extends true ? false : boolean = false>(no: number, type: T, optional?: O, repeat?: R): MessageProtoFieldType<T, O, R>;
|
|
||||||
export function ProtoField(no: number, type: ScalarType | (() => ProtoMessageType), optional?: boolean, repeat?: boolean): ProtoFieldType {
|
|
||||||
if (typeof type === 'function') {
|
|
||||||
return { kind: 'message', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false };
|
|
||||||
} else {
|
|
||||||
return { kind: 'scalar', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProtoFieldReturnType<T, E extends boolean> = NonNullable<T> extends ScalarProtoFieldType<infer S, infer O, infer R>
|
|
||||||
? ScalarTypeToTsType<S>
|
|
||||||
: T extends NonNullable<MessageProtoFieldType<infer S, infer O, infer R>>
|
|
||||||
? NonNullable<NapProtoStructType<ReturnType<S>, E>>
|
|
||||||
: never;
|
|
||||||
|
|
||||||
type RequiredFieldsBaseType<T, E extends boolean> = {
|
|
||||||
[K in keyof T as T[K] extends { optional: true } ? never : LowerCamelCase<K & string>]:
|
|
||||||
T[K] extends { repeat: true }
|
|
||||||
? ProtoFieldReturnType<T[K], E>[]
|
|
||||||
: ProtoFieldReturnType<T[K], E>
|
|
||||||
}
|
|
||||||
|
|
||||||
type OptionalFieldsBaseType<T, E extends boolean> = {
|
|
||||||
[K in keyof T as T[K] extends { optional: true } ? LowerCamelCase<K & string> : never]?:
|
|
||||||
T[K] extends { repeat: true }
|
|
||||||
? ProtoFieldReturnType<T[K], E>[]
|
|
||||||
: ProtoFieldReturnType<T[K], E>
|
|
||||||
}
|
|
||||||
|
|
||||||
type RequiredFieldsType<T, E extends boolean> = E extends true ? Partial<RequiredFieldsBaseType<T, E>> : RequiredFieldsBaseType<T, E>;
|
|
||||||
|
|
||||||
type OptionalFieldsType<T, E extends boolean> = E extends true ? Partial<OptionalFieldsBaseType<T, E>> : OptionalFieldsBaseType<T, E>;
|
|
||||||
|
|
||||||
type NapProtoStructType<T, E extends boolean> = RequiredFieldsType<T, E> & OptionalFieldsType<T, E>;
|
|
||||||
|
|
||||||
export type NapProtoEncodeStructType<T> = NapProtoStructType<T, true>;
|
|
||||||
|
|
||||||
export type NapProtoDecodeStructType<T> = NapProtoStructType<T, false>;
|
|
||||||
|
|
||||||
const NapProtoMsgCache = new Map<ProtoMessageType, MessageType<NapProtoStructType<ProtoMessageType, boolean>>>();
|
|
||||||
|
|
||||||
export class NapProtoMsg<T extends ProtoMessageType> {
|
|
||||||
private readonly _msg: T;
|
|
||||||
private readonly _field: PartialFieldInfo[];
|
|
||||||
private readonly _proto_msg: MessageType<NapProtoStructType<T, boolean>>;
|
|
||||||
|
|
||||||
constructor(fields: T) {
|
|
||||||
this._msg = fields;
|
|
||||||
this._field = Object.keys(fields).map(key => {
|
|
||||||
const field = fields[key];
|
|
||||||
if (field.kind === 'scalar') {
|
|
||||||
const repeatType = field.repeat
|
|
||||||
? [ScalarType.STRING, ScalarType.BYTES].includes(field.type)
|
|
||||||
? RepeatType.UNPACKED
|
|
||||||
: RepeatType.PACKED
|
|
||||||
: RepeatType.NO;
|
|
||||||
return {
|
|
||||||
no: field.no,
|
|
||||||
name: key,
|
|
||||||
kind: 'scalar',
|
|
||||||
T: field.type,
|
|
||||||
opt: field.optional,
|
|
||||||
repeat: repeatType,
|
|
||||||
};
|
|
||||||
} else if (field.kind === 'message') {
|
|
||||||
return {
|
|
||||||
no: field.no,
|
|
||||||
name: key,
|
|
||||||
kind: 'message',
|
|
||||||
repeat: field.repeat ? RepeatType.PACKED : RepeatType.NO,
|
|
||||||
T: () => new NapProtoMsg(field.type())._proto_msg,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}) as PartialFieldInfo[];
|
|
||||||
this._proto_msg = new MessageType<NapProtoStructType<T, boolean>>('nya', this._field);
|
|
||||||
}
|
|
||||||
|
|
||||||
encode(data: NapProtoEncodeStructType<T>): Uint8Array {
|
|
||||||
return this._proto_msg.toBinary(this._proto_msg.create(data as PartialMessage<NapProtoEncodeStructType<T>>));
|
|
||||||
}
|
|
||||||
|
|
||||||
decode(data: Uint8Array): NapProtoDecodeStructType<T> {
|
|
||||||
return this._proto_msg.fromBinary(data) as NapProtoDecodeStructType<T>;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
import { ContentHead, MessageBody, MessageControl, RoutingHead } from "@/core/packet/proto/message/message";
|
import { ContentHead, MessageBody, MessageControl, RoutingHead } from "@/core/packet/proto/message/message";
|
||||||
|
|
||||||
export const FaceRoamRequest = {
|
export const FaceRoamRequest = {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const MiniAppAdaptShareInfoReq = {
|
export const MiniAppAdaptShareInfoReq = {
|
||||||
appId: ProtoField(2, ScalarType.STRING),
|
appId: ProtoField(2, ScalarType.STRING),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
import { MsgInfo, MsgInfoBody } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
import { MsgInfo, MsgInfoBody } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||||
|
|
||||||
export const DataHighwayHead = {
|
export const DataHighwayHead = {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
import { PushMsgBody } from "@/core/packet/proto/message/message";
|
import { PushMsgBody } from "@/core/packet/proto/message/message";
|
||||||
|
|
||||||
export const LongMsgResult = {
|
export const LongMsgResult = {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const C2C = {
|
export const C2C = {
|
||||||
uin: ProtoField(1, ScalarType.UINT32, true),
|
uin: ProtoField(1, ScalarType.UINT32, true),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
import { Elem } from "@/core/packet/proto/message/element";
|
import { Elem } from "@/core/packet/proto/message/element";
|
||||||
|
|
||||||
export const Attr = {
|
export const Attr = {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const Elem = {
|
export const Elem = {
|
||||||
text: ProtoField(1, () => Text, true),
|
text: ProtoField(1, () => Text, true),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const GroupRecallMsg = {
|
export const GroupRecallMsg = {
|
||||||
type: ProtoField(1, ScalarType.UINT32),
|
type: ProtoField(1, ScalarType.UINT32),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
import { ForwardHead, Grp, GrpTmp, ResponseForward, ResponseGrp, Trans0X211, WPATmp } from "@/core/packet/proto/message/routing";
|
import { ForwardHead, Grp, GrpTmp, ResponseForward, ResponseGrp, Trans0X211, WPATmp } from "@/core/packet/proto/message/routing";
|
||||||
import { RichText } from "@/core/packet/proto/message/component";
|
import { RichText } from "@/core/packet/proto/message/component";
|
||||||
import { C2C } from "@/core/packet/proto/message/c2c";
|
import { C2C } from "@/core/packet/proto/message/c2c";
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const FriendRecall = {
|
export const FriendRecall = {
|
||||||
info: ProtoField(1, () => FriendRecallInfo),
|
info: ProtoField(1, () => FriendRecallInfo),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const ForwardHead = {
|
export const ForwardHead = {
|
||||||
field1: ProtoField(1, ScalarType.UINT32, true),
|
field1: ProtoField(1, ScalarType.UINT32, true),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
import { OidbSvcTrpcTcp0XE37_800_1200Metadata } from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
import { OidbSvcTrpcTcp0XE37_800_1200Metadata } from "@/core/packet/proto/oidb/Oidb.0xE37_1200";
|
||||||
|
|
||||||
export const OidbSvcTrpcTcp0XE37_800 = {
|
export const OidbSvcTrpcTcp0XE37_800 = {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const OidbSvcTrpcTcp0XFE1_2 = {
|
export const OidbSvcTrpcTcp0XFE1_2 = {
|
||||||
uin: ProtoField(1, ScalarType.UINT32),
|
uin: ProtoField(1, ScalarType.UINT32),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const OidbSvcTrpcTcp0x6D6 = {
|
export const OidbSvcTrpcTcp0x6D6 = {
|
||||||
file: ProtoField(1, () => OidbSvcTrpcTcp0x6D6Upload, true),
|
file: ProtoField(1, () => OidbSvcTrpcTcp0x6D6Upload, true),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
|
|
||||||
//设置群头衔 OidbSvcTrpcTcp.0x8fc_2
|
//设置群头衔 OidbSvcTrpcTcp.0x8fc_2
|
||||||
@@ -13,4 +13,4 @@ export const OidbSvcTrpcTcp0X8FC_2_Body = {
|
|||||||
export const OidbSvcTrpcTcp0X8FC_2 = {
|
export const OidbSvcTrpcTcp0X8FC_2 = {
|
||||||
groupUin: ProtoField(1, ScalarType.UINT32),
|
groupUin: ProtoField(1, ScalarType.UINT32),
|
||||||
body: ProtoField(3, ScalarType.BYTES),
|
body: ProtoField(3, ScalarType.BYTES),
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
import { MultiMediaReqHead } from "./common/Ntv2.RichMediaReq";
|
import { MultiMediaReqHead } from "./common/Ntv2.RichMediaReq";
|
||||||
|
|
||||||
//Req
|
//Req
|
||||||
|
42
src/core/packet/proto/oidb/Oidb.0x929.ts
Normal file
42
src/core/packet/proto/oidb/Oidb.0x929.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
import { MsgInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0X929D_0 = {
|
||||||
|
groupUin: ProtoField(1, ScalarType.UINT32),
|
||||||
|
chatType: ProtoField(2, ScalarType.UINT32),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0X929D_0Resp = {
|
||||||
|
content: ProtoField(1, () => OidbSvcTrpcTcp0X929D_0RespContent, false, true),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0X929D_0RespContent = {
|
||||||
|
category: ProtoField(1, ScalarType.STRING),
|
||||||
|
voices: ProtoField(2, () => OidbSvcTrpcTcp0X929D_0RespContentVoice, false, true),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0X929D_0RespContentVoice = {
|
||||||
|
voiceId: ProtoField(1, ScalarType.STRING),
|
||||||
|
voiceDisplayName: ProtoField(2, ScalarType.STRING),
|
||||||
|
voiceExampleUrl: ProtoField(3, ScalarType.STRING),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0X929B_0 = {
|
||||||
|
groupUin: ProtoField(1, ScalarType.UINT32),
|
||||||
|
voiceId: ProtoField(2, ScalarType.STRING),
|
||||||
|
text: ProtoField(3, ScalarType.STRING),
|
||||||
|
chatType: ProtoField(4, ScalarType.UINT32),
|
||||||
|
session: ProtoField(5, () => OidbSvcTrpcTcp0X929B_0_Session),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0X929B_0_Session = {
|
||||||
|
sessionId: ProtoField(1, ScalarType.UINT32),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OidbSvcTrpcTcp0X929B_0Resp = {
|
||||||
|
statusCode: ProtoField(1, ScalarType.UINT32),
|
||||||
|
field2: ProtoField(2, ScalarType.UINT32, true),
|
||||||
|
field3: ProtoField(3, ScalarType.UINT32),
|
||||||
|
msgInfo: ProtoField(4, () => MsgInfo, true),
|
||||||
|
};
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const OidbSvcTrpcTcp0XE37_1200 = {
|
export const OidbSvcTrpcTcp0XE37_1200 = {
|
||||||
subCommand: ProtoField(1, ScalarType.UINT32, true),
|
subCommand: ProtoField(1, ScalarType.UINT32, true),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const OidbSvcTrpcTcp0XE37_1700 = {
|
export const OidbSvcTrpcTcp0XE37_1700 = {
|
||||||
command: ProtoField(1, ScalarType.UINT32, true),
|
command: ProtoField(1, ScalarType.UINT32, true),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const OidbSvcTrpcTcp0XEB7_Body = {
|
export const OidbSvcTrpcTcp0XEB7_Body = {
|
||||||
uin: ProtoField(1, ScalarType.STRING),
|
uin: ProtoField(1, ScalarType.STRING),
|
||||||
@@ -9,4 +9,4 @@ export const OidbSvcTrpcTcp0XEB7_Body = {
|
|||||||
|
|
||||||
export const OidbSvcTrpcTcp0XEB7 = {
|
export const OidbSvcTrpcTcp0XEB7 = {
|
||||||
body: ProtoField(2, () => OidbSvcTrpcTcp0XEB7_Body),
|
body: ProtoField(2, () => OidbSvcTrpcTcp0XEB7_Body),
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
// Send Poke
|
// Send Poke
|
||||||
export const OidbSvcTrpcTcp0XED3_1 = {
|
export const OidbSvcTrpcTcp0XED3_1 = {
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const OidbSvcTrpcTcpBase = {
|
export const OidbSvcTrpcTcpBase = {
|
||||||
command: ProtoField(1, ScalarType.UINT32),
|
command: ProtoField(1, ScalarType.UINT32),
|
||||||
subCommand: ProtoField(2, ScalarType.UINT32),
|
subCommand: ProtoField(2, ScalarType.UINT32),
|
||||||
|
errorCode: ProtoField(3, ScalarType.UINT32),
|
||||||
body: ProtoField(4, ScalarType.BYTES),
|
body: ProtoField(4, ScalarType.BYTES),
|
||||||
errorMsg: ProtoField(5, ScalarType.STRING, true),
|
errorMsg: ProtoField(5, ScalarType.STRING, true),
|
||||||
isReserved: ProtoField(12, ScalarType.UINT32)
|
isReserved: ProtoField(12, ScalarType.UINT32)
|
||||||
};
|
};
|
||||||
export const OidbSvcTrpcTcpBaseRsp = {
|
export const OidbSvcTrpcTcpBaseRsp = {
|
||||||
body: ProtoField(4, ScalarType.BYTES)
|
body: ProtoField(4, ScalarType.BYTES)
|
||||||
};
|
};
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
|
|
||||||
export const NTV2RichMediaReq = {
|
export const NTV2RichMediaReq = {
|
||||||
ReqHead: ProtoField(1, () => MultiMediaReqHead),
|
ReqHead: ProtoField(1, () => MultiMediaReqHead),
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { ScalarType } from "@protobuf-ts/runtime";
|
import { ScalarType } from "@protobuf-ts/runtime";
|
||||||
import { ProtoField } from "../../NapProto";
|
import { ProtoField } from "@napneko/nap-proto-core";
|
||||||
import { CommonHead, MsgInfo, PicUrlExtInfo, VideoExtInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
import { CommonHead, MsgInfo, PicUrlExtInfo, VideoExtInfo } from "@/core/packet/proto/oidb/common/Ntv2.RichMediaReq";
|
||||||
|
|
||||||
export const NTV2RichMediaResp = {
|
export const NTV2RichMediaResp = {
|
||||||
|
@@ -1,18 +1,73 @@
|
|||||||
import { PacketClient } from "@/core/packet/client";
|
|
||||||
import { PacketHighwaySession } from "@/core/packet/highway/session";
|
import { PacketHighwaySession } from "@/core/packet/highway/session";
|
||||||
import { LogWrapper } from "@/common/log";
|
import { LogWrapper } from "@/common/log";
|
||||||
import { PacketPacker } from "@/core/packet/packer";
|
import { PacketPacker } from "@/core/packet/packer";
|
||||||
|
import { PacketClient } from "@/core/packet/client/client";
|
||||||
|
import { NativePacketClient } from "@/core/packet/client/nativeClient";
|
||||||
|
import { wsPacketClient } from "@/core/packet/client/wsClient";
|
||||||
|
import { NapCatCore } from "@/core";
|
||||||
|
|
||||||
|
type clientPriority = {
|
||||||
|
[key: number]: (core: NapCatCore) => PacketClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientPriority: clientPriority = {
|
||||||
|
10: (core: NapCatCore) => new NativePacketClient(core),
|
||||||
|
1: (core: NapCatCore) => new wsPacketClient(core),
|
||||||
|
};
|
||||||
|
|
||||||
export class PacketSession {
|
export class PacketSession {
|
||||||
readonly logger: LogWrapper;
|
readonly logger: LogWrapper;
|
||||||
readonly client: PacketClient;
|
readonly client: PacketClient ;
|
||||||
readonly packer: PacketPacker;
|
readonly packer: PacketPacker;
|
||||||
readonly highwaySession: PacketHighwaySession;
|
readonly highwaySession: PacketHighwaySession;
|
||||||
|
|
||||||
constructor(logger: LogWrapper, client: PacketClient) {
|
constructor(core: NapCatCore) {
|
||||||
this.logger = logger;
|
this.logger = core.context.logger;
|
||||||
this.client = client;
|
this.client = this.newClient(core);
|
||||||
this.packer = new PacketPacker(this.logger, this.client);
|
this.packer = new PacketPacker(this.logger, this.client);
|
||||||
this.highwaySession = new PacketHighwaySession(this.logger, this.client, this.packer);
|
this.highwaySession = new PacketHighwaySession(this.logger, this.client, this.packer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private newClient(core: NapCatCore): PacketClient {
|
||||||
|
const prefer = core.configLoader.configData.packetBackend;
|
||||||
|
let client: PacketClient | null;
|
||||||
|
switch (prefer) {
|
||||||
|
case "native":
|
||||||
|
this.logger.log("[Core] [Packet] 使用指定的 NativePacketClient 作为后端");
|
||||||
|
client = new NativePacketClient(core);
|
||||||
|
break;
|
||||||
|
case "frida":
|
||||||
|
this.logger.log("[Core] [Packet] 使用指定的 FridaPacketClient 作为后端");
|
||||||
|
client = new wsPacketClient(core);
|
||||||
|
break;
|
||||||
|
case "auto":
|
||||||
|
case undefined:
|
||||||
|
client = this.judgeClient(core);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.logger.logError(`[Core] [Packet] 未知的PacketBackend ${prefer},请检查配置文件!`);
|
||||||
|
client = null;
|
||||||
|
}
|
||||||
|
if (!(client && client.check(core))) {
|
||||||
|
throw new Error("[Core] [Packet] 无可用的后端,NapCat.Packet将不会加载!");
|
||||||
|
}
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private judgeClient(core: NapCatCore): PacketClient {
|
||||||
|
const sortedClients = Object.entries(clientPriority)
|
||||||
|
.map(([priority, clientFactory]) => {
|
||||||
|
const client = clientFactory(core);
|
||||||
|
const score = +priority * +client.check(core);
|
||||||
|
return { client, score };
|
||||||
|
})
|
||||||
|
.filter(({ score }) => score > 0)
|
||||||
|
.sort((a, b) => b.score - a.score);
|
||||||
|
const selectedClient = sortedClients[0]?.client;
|
||||||
|
if (!selectedClient) {
|
||||||
|
throw new Error("[Core] [Packet] 无可用的后端,NapCat.Packet将不会加载!");
|
||||||
|
}
|
||||||
|
this.logger.log(`[Core] [Packet] 自动选择 ${selectedClient.constructor.name} 作为后端`);
|
||||||
|
return selectedClient;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
src/native/packet/MoeHoo.linux.arm64.node
Normal file
BIN
src/native/packet/MoeHoo.linux.arm64.node
Normal file
Binary file not shown.
BIN
src/native/packet/MoeHoo.linux.x64.node
Normal file
BIN
src/native/packet/MoeHoo.linux.x64.node
Normal file
Binary file not shown.
BIN
src/native/packet/MoeHoo.win32.x64.node
Normal file
BIN
src/native/packet/MoeHoo.win32.x64.node
Normal file
Binary file not shown.
41
src/onebot/action/extends/GetAiCharacters.ts
Normal file
41
src/onebot/action/extends/GetAiCharacters.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
|
||||||
|
import { AIVoiceChatType } from "@/core/packet/entities/aiChat";
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
chat_type: { type: ['number', 'string'] },
|
||||||
|
},
|
||||||
|
required: ['group_id'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
interface GetAiCharactersResponse {
|
||||||
|
type: string;
|
||||||
|
characters: {
|
||||||
|
character_id: string;
|
||||||
|
character_name: string;
|
||||||
|
preview_url: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GetAiCharacters extends GetPacketStatusDepends<Payload, GetAiCharactersResponse[]> {
|
||||||
|
actionName = ActionName.GetAiCharacters;
|
||||||
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
|
async _handle(payload: Payload) {
|
||||||
|
const rawList = await this.core.apis.PacketApi.sendFetchAiVoiceListReq(+payload.group_id, +(payload.chat_type ?? 1) as AIVoiceChatType);
|
||||||
|
return rawList?.map((item) => ({
|
||||||
|
type: item.category,
|
||||||
|
characters: item.voices.map((voice) => ({
|
||||||
|
character_id: voice.voiceId,
|
||||||
|
character_name: voice.voiceDisplayName,
|
||||||
|
preview_url: voice.voiceExampleUrl,
|
||||||
|
})),
|
||||||
|
})) ?? [];
|
||||||
|
}
|
||||||
|
}
|
@@ -5,7 +5,7 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
|||||||
const SchemaData = {
|
const SchemaData = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
group_id: { type: 'string' },
|
group_id: { type: ['string', 'number'] },
|
||||||
},
|
},
|
||||||
required: ['group_id'],
|
required: ['group_id'],
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
@@ -17,6 +17,9 @@ export class SetGroupSign extends BaseAction<Payload, any> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
return await this.core.apis.PacketApi.sendGroupSignPacket(payload.group_id);
|
return await this.core.apis.PacketApi.sendGroupSignPacket(payload.group_id.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export class SendGroupSign extends SetGroupSign {
|
||||||
|
actionName = ActionName.SendGroupSign;
|
||||||
|
}
|
@@ -73,16 +73,14 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction<Payload, any> {
|
|||||||
|
|
||||||
const singleMsg = data.msgList[0];
|
const singleMsg = data.msgList[0];
|
||||||
const resMsg = await this.obContext.apis.MsgApi.parseMessage(singleMsg, 'array');//强制array 以便处理
|
const resMsg = await this.obContext.apis.MsgApi.parseMessage(singleMsg, 'array');//强制array 以便处理
|
||||||
if (!resMsg) {
|
if (!(resMsg?.message?.[0] as OB11MessageForward)?.data?.content) {
|
||||||
throw new Error('找不到相关的聊天记录');
|
throw new Error('找不到相关的聊天记录');
|
||||||
}
|
}
|
||||||
//if (this.obContext.configLoader.configData.messagePostFormat === 'array') {
|
return {
|
||||||
//提取
|
messages: (resMsg?.message?.[0] as OB11MessageForward)?.data?.content
|
||||||
const realmsg = ((await this.parseForward([resMsg]))[0].data.message as OB11MessageNode[])[0].data.message;
|
};
|
||||||
//里面都是offline消息 id都是0 没得说话
|
|
||||||
return { message: realmsg };
|
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// return { message: resMsg };
|
// return { message: resMsg };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,10 +6,15 @@ const SchemaData = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
friend_id: { type: ['string', 'number'] },
|
friend_id: { type: ['string', 'number'] },
|
||||||
|
user_id: { type: ['string', 'number'] },
|
||||||
temp_block: { type: 'boolean' },
|
temp_block: { type: 'boolean' },
|
||||||
temp_both_del: { type: 'boolean' },
|
temp_both_del: { type: 'boolean' },
|
||||||
},
|
},
|
||||||
required: ['friend_id'],
|
oneOf: [
|
||||||
|
{ required: ['friend_id'] },
|
||||||
|
{ required: ['user_id'] },
|
||||||
|
],
|
||||||
|
|
||||||
} as const satisfies JSONSchema;
|
} as const satisfies JSONSchema;
|
||||||
type Payload = FromSchema<typeof SchemaData>;
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
@@ -18,7 +23,8 @@ export class GoCQHTTPDeleteFriend extends BaseAction<Payload, any> {
|
|||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
async _handle(payload: Payload) {
|
async _handle(payload: Payload) {
|
||||||
const uid = await this.core.apis.UserApi.getUidByUinV2(payload.friend_id.toString());
|
const uin = payload.friend_id ?? payload.user_id ?? '';
|
||||||
|
const uid = await this.core.apis.UserApi.getUidByUinV2(uin.toString());
|
||||||
|
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
return {
|
return {
|
||||||
|
26
src/onebot/action/group/GetAiRecord.ts
Normal file
26
src/onebot/action/group/GetAiRecord.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
|
||||||
|
import { AIVoiceChatType } from "@/core/packet/entities/aiChat";
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
character: { type: ['string'] },
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
text: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['character', 'group_id', 'text'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class GetAiRecord extends GetPacketStatusDepends<Payload, string> {
|
||||||
|
actionName = ActionName.GetAiRecord;
|
||||||
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
|
async _handle(payload: Payload) {
|
||||||
|
const rawRsp = await this.core.apis.PacketApi.sendAiVoiceChatReq(+payload.group_id, payload.character, payload.text, AIVoiceChatType.Sound);
|
||||||
|
return await this.core.apis.PacketApi.sendGroupPttFileDownloadReq(+payload.group_id, rawRsp.msgInfoBody[0].index);
|
||||||
|
}
|
||||||
|
}
|
@@ -13,7 +13,7 @@ const SchemaData = {
|
|||||||
|
|
||||||
type Payload = FromSchema<typeof SchemaData>;
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
export class GetGroupShutList extends BaseAction<Payload, OB11Group> {
|
export class GetGroupShutList extends BaseAction<Payload, any> {
|
||||||
actionName = ActionName.GetGroupShutList;
|
actionName = ActionName.GetGroupShutList;
|
||||||
payloadSchema = SchemaData;
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
|
38
src/onebot/action/group/SendGroupAiRecord.ts
Normal file
38
src/onebot/action/group/SendGroupAiRecord.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { ActionName } from '../types';
|
||||||
|
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
|
||||||
|
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
|
||||||
|
import { AIVoiceChatType } from "@/core/packet/entities/aiChat";
|
||||||
|
import { uri2local } from "@/common/file";
|
||||||
|
import { ChatType, Peer } from "@/core";
|
||||||
|
|
||||||
|
const SchemaData = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
character: { type: ['string'] },
|
||||||
|
group_id: { type: ['number', 'string'] },
|
||||||
|
text: { type: 'string' },
|
||||||
|
},
|
||||||
|
required: ['character', 'group_id', 'text'],
|
||||||
|
} as const satisfies JSONSchema;
|
||||||
|
|
||||||
|
type Payload = FromSchema<typeof SchemaData>;
|
||||||
|
|
||||||
|
export class SendGroupAiRecord extends GetPacketStatusDepends<Payload, {
|
||||||
|
message_id: number
|
||||||
|
}> {
|
||||||
|
actionName = ActionName.SendGroupAiRecord;
|
||||||
|
payloadSchema = SchemaData;
|
||||||
|
|
||||||
|
async _handle(payload: Payload) {
|
||||||
|
const rawRsp = await this.core.apis.PacketApi.sendAiVoiceChatReq(+payload.group_id, payload.character, payload.text, AIVoiceChatType.Sound);
|
||||||
|
const url = await this.core.apis.PacketApi.sendGroupPttFileDownloadReq(+payload.group_id, rawRsp.msgInfoBody[0].index);
|
||||||
|
const { path, fileName, errMsg, success } = (await uri2local(this.core.NapCatTempPath, url));
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
const peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString() } as Peer;
|
||||||
|
const element = await this.core.apis.FileApi.createValidSendPttElement(path);
|
||||||
|
const sendRes = await this.obContext.apis.MsgApi.sendMsgWithOb11UniqueId(peer, [element], [path]);
|
||||||
|
return { message_id: sendRes.id ?? -1 };
|
||||||
|
}
|
||||||
|
}
|
@@ -92,13 +92,16 @@ import { GetGroupFileUrl } from "@/onebot/action/file/GetGroupFileUrl";
|
|||||||
import { GetPacketStatus } from "@/onebot/action/packet/GetPacketStatus";
|
import { GetPacketStatus } from "@/onebot/action/packet/GetPacketStatus";
|
||||||
import { FriendPoke } from "@/onebot/action/user/FriendPoke";
|
import { FriendPoke } from "@/onebot/action/user/FriendPoke";
|
||||||
import { GetCredentials } from './system/GetCredentials';
|
import { GetCredentials } from './system/GetCredentials';
|
||||||
import { SetGroupSign } from './extends/SetGroupSign';
|
import { SendGroupSign, SetGroupSign } from './extends/SetGroupSign';
|
||||||
import { GoCQHTTPGetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain';
|
import { GoCQHTTPGetGroupAtAllRemain } from './go-cqhttp/GetGroupAtAllRemain';
|
||||||
import { GoCQHTTPCheckUrlSafely } from './go-cqhttp/GoCQHTTPCheckUrlSafely';
|
import { GoCQHTTPCheckUrlSafely } from './go-cqhttp/GoCQHTTPCheckUrlSafely';
|
||||||
import { GoCQHTTPGetModelShow } from './go-cqhttp/GoCQHTTPGetModelShow';
|
import { GoCQHTTPGetModelShow } from './go-cqhttp/GoCQHTTPGetModelShow';
|
||||||
import { GoCQHTTPSetModelShow } from './go-cqhttp/GoCQHTTPSetModelShow';
|
import { GoCQHTTPSetModelShow } from './go-cqhttp/GoCQHTTPSetModelShow';
|
||||||
import { GoCQHTTPDeleteFriend } from './go-cqhttp/GoCQHTTPDeleteFriend';
|
import { GoCQHTTPDeleteFriend } from './go-cqhttp/GoCQHTTPDeleteFriend';
|
||||||
import { GetMiniAppArk } from "@/onebot/action/extends/GetMiniAppArk";
|
import { GetMiniAppArk } from "@/onebot/action/extends/GetMiniAppArk";
|
||||||
|
import { GetAiRecord } from "@/onebot/action/group/GetAiRecord";
|
||||||
|
import { SendGroupAiRecord } from "@/onebot/action/group/SendGroupAiRecord";
|
||||||
|
import { GetAiCharacters } from "@/onebot/action/extends/GetAiCharacters";
|
||||||
|
|
||||||
|
|
||||||
export type ActionMap = Map<string, BaseAction<any, any>>;
|
export type ActionMap = Map<string, BaseAction<any, any>>;
|
||||||
@@ -122,6 +125,7 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
|
|||||||
new TranslateEnWordToZn(obContext, core),
|
new TranslateEnWordToZn(obContext, core),
|
||||||
new GetGroupRootFiles(obContext, core),
|
new GetGroupRootFiles(obContext, core),
|
||||||
new SetGroupSign(obContext, core),
|
new SetGroupSign(obContext, core),
|
||||||
|
new SendGroupSign(obContext, core),
|
||||||
// onebot11
|
// onebot11
|
||||||
new SendLike(obContext, core),
|
new SendLike(obContext, core),
|
||||||
new GetMsg(obContext, core),
|
new GetMsg(obContext, core),
|
||||||
@@ -212,6 +216,9 @@ export function createActionMap(obContext: NapCatOneBot11Adapter, core: NapCatCo
|
|||||||
new GetGroupShutList(obContext, core),
|
new GetGroupShutList(obContext, core),
|
||||||
new GetGroupFileUrl(obContext, core),
|
new GetGroupFileUrl(obContext, core),
|
||||||
new GetMiniAppArk(obContext, core),
|
new GetMiniAppArk(obContext, core),
|
||||||
|
new GetAiRecord(obContext, core),
|
||||||
|
new SendGroupAiRecord(obContext, core),
|
||||||
|
new GetAiCharacters(obContext, core),
|
||||||
];
|
];
|
||||||
const actionMap = new Map();
|
const actionMap = new Map();
|
||||||
for (const action of actionHandlers) {
|
for (const action of actionHandlers) {
|
||||||
|
@@ -9,7 +9,7 @@ export abstract class GetPacketStatusDepends<PT, RT> extends BaseAction<PT, RT>
|
|||||||
if (!this.core.apis.PacketApi.available) {
|
if (!this.core.apis.PacketApi.available) {
|
||||||
return {
|
return {
|
||||||
valid: false,
|
valid: false,
|
||||||
message: "packetServer不可用,请参照文档 https://napneko.github.io/config/advanced 检查packetServer状态或进行配置!",
|
message: "packetBackend不可用,请参照文档 https://napneko.github.io/config/advanced 和启动日志检查packetBackend状态或进行配置!",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return await super.check(payload);
|
return await super.check(payload);
|
||||||
|
@@ -136,6 +136,11 @@ export enum ActionName {
|
|||||||
GetGroupIgnoredNotifies = 'get_group_ignored_notifies',
|
GetGroupIgnoredNotifies = 'get_group_ignored_notifies',
|
||||||
|
|
||||||
SetGroupSign = "set_group_sign",
|
SetGroupSign = "set_group_sign",
|
||||||
|
SendGroupSign = "send_group_sign",
|
||||||
|
|
||||||
GetMiniAppArk = "get_mini_app_ark",
|
GetMiniAppArk = "get_mini_app_ark",
|
||||||
// UploadForwardMsg = "upload_forward_msg",
|
// UploadForwardMsg = "upload_forward_msg",
|
||||||
|
GetAiRecord = "get_ai_record",
|
||||||
|
GetAiCharacters = "get_ai_characters",
|
||||||
|
SendGroupAiRecord = "send_group_ai_record",
|
||||||
}
|
}
|
||||||
|
@@ -311,15 +311,19 @@ export class NapCatOneBot11Adapter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const msgIdSend = new LRUCache<string, boolean>(100);
|
const msgIdSend = new LRUCache<string, number>(100);
|
||||||
const recallMsgs = new LRUCache<string, boolean>(100);
|
const recallMsgs = new LRUCache<string, boolean>(100);
|
||||||
|
msgListener.onAddSendMsg = async msg => {
|
||||||
|
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) {
|
||||||
|
msgIdSend.put(msg.msgId, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
msgListener.onMsgInfoListUpdate = async msgList => {
|
msgListener.onMsgInfoListUpdate = async msgList => {
|
||||||
this.emitRecallMsg(msgList, recallMsgs)
|
this.emitRecallMsg(msgList, recallMsgs)
|
||||||
.catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e));
|
.catch(e => this.context.logger.logError.bind(this.context.logger)('处理消息失败', e));
|
||||||
|
|
||||||
for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) {
|
for (const msg of msgList.filter(e => e.senderUin == this.core.selfInfo.uin)) {
|
||||||
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && !msgIdSend.get(msg.msgId)) {
|
if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && msgIdSend.get(msg.msgId) == 0) {
|
||||||
msgIdSend.put(msg.msgId, true);
|
msgIdSend.put(msg.msgId, 1);
|
||||||
// 完成后再post
|
// 完成后再post
|
||||||
this.apis.MsgApi.parseMessage(msg)
|
this.apis.MsgApi.parseMessage(msg)
|
||||||
.then((ob11Msg) => {
|
.then((ob11Msg) => {
|
||||||
@@ -523,7 +527,7 @@ export class NapCatOneBot11Adapter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async emitMsg(message: RawMessage) {
|
private async emitMsg(message: RawMessage, parseEvent: boolean = true) {
|
||||||
const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData;
|
const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData;
|
||||||
this.context.logger.logDebug('收到新消息 RawMessage', message);
|
this.context.logger.logDebug('收到新消息 RawMessage', message);
|
||||||
this.apis.MsgApi.parseMessage(message, messagePostFormat).then((ob11Msg) => {
|
this.apis.MsgApi.parseMessage(message, messagePostFormat).then((ob11Msg) => {
|
||||||
|
@@ -143,7 +143,7 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter {
|
|||||||
private registerHeartBeat() {
|
private registerHeartBeat() {
|
||||||
this.heartbeatIntervalId = setInterval(() => {
|
this.heartbeatIntervalId = setInterval(() => {
|
||||||
this.wsClientsMutex.runExclusive(async () => {
|
this.wsClientsMutex.runExclusive(async () => {
|
||||||
this.wsClients.forEach((wsClient) => {
|
this.wsClientWithEvent.forEach((wsClient) => {
|
||||||
if (wsClient.readyState === WebSocket.OPEN) {
|
if (wsClient.readyState === WebSocket.OPEN) {
|
||||||
wsClient.send(JSON.stringify(new OB11HeartbeatEvent(this.core, this.heartbeatInterval, this.core.selfInfo.online, true)));
|
wsClient.send(JSON.stringify(new OB11HeartbeatEvent(this.core, this.heartbeatInterval, this.core.selfInfo.online, true)));
|
||||||
}
|
}
|
||||||
|
@@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) {
|
|||||||
SettingItem(
|
SettingItem(
|
||||||
'<span id="napcat-update-title">Napcat</span>',
|
'<span id="napcat-update-title">Napcat</span>',
|
||||||
void 0,
|
void 0,
|
||||||
SettingButton("V3.4.0", "napcat-update-button", "secondary")
|
SettingButton("V3.6.3", "napcat-update-button", "secondary")
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
SettingList([
|
SettingList([
|
||||||
|
@@ -10,6 +10,20 @@
|
|||||||
<link rel="stylesheet" href="./assets/webcomponents.css" />
|
<link rel="stylesheet" href="./assets/webcomponents.css" />
|
||||||
<link rel="stylesheet" href="./assets/style.css" />
|
<link rel="stylesheet" href="./assets/style.css" />
|
||||||
<link rel="stylesheet" href="./assets/color.css" />
|
<link rel="stylesheet" href="./assets/color.css" />
|
||||||
|
<style>
|
||||||
|
body > div {
|
||||||
|
padding: 12px;
|
||||||
|
|
||||||
|
@media screen and (min-width: 900px) {
|
||||||
|
width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ob-setting-select {
|
||||||
|
width: 210px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<!-- 脚手架 -->
|
<!-- 脚手架 -->
|
||||||
<!-- 渲染脚本 -->
|
<!-- 渲染脚本 -->
|
||||||
<script>
|
<script>
|
||||||
|
@@ -26,6 +26,7 @@ const FrameworkBaseConfigPlugin: PluginOption[] = [
|
|||||||
targets: [
|
targets: [
|
||||||
{ src: './manifest.json', dest: 'dist' },
|
{ src: './manifest.json', dest: 'dist' },
|
||||||
{ src: './src/core/external/napcat.json', dest: 'dist/config/' },
|
{ src: './src/core/external/napcat.json', dest: 'dist/config/' },
|
||||||
|
{ src: './src/native/packet', dest: 'dist/moehoo', flatten: false },
|
||||||
{ src: './static/', dest: 'dist/static/', flatten: false },
|
{ src: './static/', dest: 'dist/static/', flatten: false },
|
||||||
{ src: './src/onebot/config/onebot11.json', dest: 'dist/config/' },
|
{ src: './src/onebot/config/onebot11.json', dest: 'dist/config/' },
|
||||||
{ src: './src/framework/liteloader.cjs', dest: 'dist' },
|
{ src: './src/framework/liteloader.cjs', dest: 'dist' },
|
||||||
@@ -42,6 +43,7 @@ const ShellBaseConfigPlugin: PluginOption[] = [
|
|||||||
cp({
|
cp({
|
||||||
targets: [
|
targets: [
|
||||||
{ src: './src/native/external', dest: 'dist/native', flatten: false },
|
{ src: './src/native/external', dest: 'dist/native', flatten: false },
|
||||||
|
{ src: './src/native/packet', dest: 'dist/moehoo', flatten: false },
|
||||||
{ src: './static/', dest: 'dist/static/', flatten: false },
|
{ src: './static/', dest: 'dist/static/', flatten: false },
|
||||||
{ src: './src/core/external/napcat.json', dest: 'dist/config/' },
|
{ src: './src/core/external/napcat.json', dest: 'dist/config/' },
|
||||||
{ src: './src/onebot/config/onebot11.json', dest: 'dist/config/' },
|
{ src: './src/onebot/config/onebot11.json', dest: 'dist/config/' },
|
||||||
|
Reference in New Issue
Block a user