Compare commits

..

7 Commits

Author SHA1 Message Date
手瓜一十雪
64beeba2d0 Revert "release: v4.9.1"
This reverts commit 7e9d852617.
2025-10-30 15:17:26 +08:00
手瓜一十雪
b3b946e91d Revert "Comment out packet logging in napcat and shell"
This reverts commit 5da1586811.
2025-10-30 15:17:20 +08:00
Mlikiowa
7e9d852617 release: v4.9.1 2025-10-30 05:10:56 +00:00
手瓜一十雪
5da1586811 Comment out packet logging in napcat and shell
Disabled the console logging of all packets in both napcat.ts and base.ts by commenting out the nativePacketHandler.onAll debug statements. This reduces console noise during normal operation.
2025-10-30 13:10:35 +08:00
Mlikiowa
743de940df release: v4.9.0 2025-10-30 04:52:41 +00:00
手瓜一十雪
e37fa59b8c feat: 4.9.0 New Capability (#1331)
* fix: get_group_info ownerUin is "0"

* Add native module loading and improve logging

Loaded a native module in NTQQGroupApi and added test calls to sendSsoCmdReqByContend with different parameter types. Changed fileLog default to true in config. Enhanced NativePacketClient with detailed send/receive logging. Updated NodeIKernelMsgService to accept unknown type for sendSsoCmdReqByContend param. Added process PID logging in napcat shell.

* feat: new napcat-4.9.0-beta

* feat: Add FFmpeg native addon and TypeScript definitions

Introduced FFmpeg Node.js native addon binaries for multiple platforms (darwin-arm64, linux-arm64, linux-x64, win-x64) and added TypeScript type definitions for the addon interface, including video info extraction, duration detection, audio conversion, and PCM decoding.

* Remove baseClient and simplify packet client selection

Deleted baseClient.ts and moved its logic into nativeClient.ts, making NativePacketClient a standalone class. Refactored PacketClientContext to always use NativePacketClient, removing support for multiple packet backends and related selection logic. Updated binary for napi2native.win32.x64.node.

* Remove debug log for process PID in napcat.ts

Eliminated an unnecessary console.log statement that printed the process PID. This cleans up the output and removes leftover debugging code.

* fix: getQQBuildStr

* fix: 简化代码

* refactor: 大幅度调整send

* feat: ffmpeg enhance for native node addon

* Remove baseClient.ts from packet client module

Deleted the src/core/packet/client/baseClient.ts file, which contained the PacketClient class and related interfaces. This may be part of a refactor or cleanup to remove unused or redundant code.

* Remove 'bmp24' argument from getVideoInfo call

Updated the extractThumbnail method to call addon.getVideoInfo without the 'bmp24' argument, aligning with the updated addon API.

* refactor: 重构目录删除旧支持

* feat: raw包能力增强完成

* refactor: 规范化

* feat: packet能力增强

* feat: 9.9.22-40824 & 9.9.22-40768

* Refactor addon path resolution and rename Windows addon

Simplifies the logic for resolving the ffmpeg addon path by dynamically constructing the filename from process.platform and process.arch. Also renames the Windows x64 addon file to ffmpegAddon.win32.x64.node for consistency.

* Add mappings for 3.2.20 versions in napi2native.json

Added send and recv address mappings for 3.2.20-x64 and 3.2.20-arm64 builds to support additional versions in napi2native.json.

---------

Co-authored-by: Clansty <i@gao4.pw>
2025-10-30 12:50:45 +08:00
凌莞~(=^▽^=)
18d0f11320 fix: get_group_info ownerUin is "0" (#1303) 2025-10-29 21:36:30 +08:00
33 changed files with 89 additions and 273 deletions

Binary file not shown.

View File

@@ -1,9 +1,9 @@
{
"name": "qq-chat",
"verHash": "2c9d3f6c",
"version": "9.9.22-40990",
"linuxVersion": "3.2.20-40990",
"linuxVerHash": "ec800879",
"verHash": "c50d6326",
"version": "9.9.22-40768",
"linuxVersion": "3.2.20-40768",
"linuxVerHash": "ab90fdfa",
"private": true,
"description": "QQ",
"productName": "QQ",
@@ -17,7 +17,7 @@
"qd": "externals/devtools/cli/index.js"
},
"main": "./loadNapCat.js",
"buildVersion": "40990",
"buildVersion": "40768",
"isPureShell": true,
"isByteCodeShell": true,
"platform": "win32",

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ",
"slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现",
"version": "4.9.20",
"version": "4.9.0",
"icon": "./logo.png",
"authors": [
{

View File

@@ -2,7 +2,7 @@
"name": "napcat",
"private": true,
"type": "module",
"version": "4.9.20",
"version": "4.9.0",
"scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1",

View File

@@ -30,8 +30,7 @@ async function handleWavFile(
): Promise<{ input: Buffer; sampleRate: number }> {
const { fmt } = getWavFileInfo(file);
if (!ALLOW_SAMPLE_RATE.includes(fmt.sampleRate)) {
const result = await FFmpegService.convert(filePath, pcmPath);
return { input: await fsPromise.readFile(pcmPath), sampleRate: result.sampleRate };
return { input: await FFmpegService.convert(filePath, pcmPath), sampleRate: 24000 };
}
return { input: file, sampleRate: fmt.sampleRate };
}
@@ -43,18 +42,9 @@ export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: Log
if (!isSilk(file)) {
logger.log(`语音文件${filePath}需要转换成silk`);
const pcmPath = `${pttPath}.pcm`;
// const { input, sampleRate } = isWav(file) ? await handleWavFile(file, filePath, pcmPath): { input: await FFmpegService.convert(filePath, pcmPath) ? await fsPromise.readFile(pcmPath) : Buffer.alloc(0), sampleRate: 24000 };
let input: Buffer;
let sampleRate: number;
if (isWav(file)) {
const result = await handleWavFile(file, filePath, pcmPath);
input = result.input;
sampleRate = result.sampleRate;
} else {
const result = await FFmpegService.convert(filePath, pcmPath);
input = await fsPromise.readFile(pcmPath);
sampleRate = result.sampleRate;
}
const { input, sampleRate } = isWav(file)
? await handleWavFile(file, filePath, pcmPath)
: { input: await FFmpegService.convert(filePath, pcmPath), sampleRate: 24000 };
const silk = await runTask<EncodeArgs, EncodeResult>(getWorkerPath(), { input: input, sampleRate: sampleRate });
fsPromise.unlink(pcmPath).catch((e) => logger.logError('删除临时文件失败', pcmPath, e));
await fsPromise.writeFile(pttPath, Buffer.from(silk.data));
@@ -79,7 +69,7 @@ export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: Log
};
}
} catch (error: unknown) {
logger.logError('convert silk failed', error);
logger.logError('convert silk failed', (error as Error).stack);
return {};
}
}

View File

@@ -49,7 +49,7 @@ export interface IFFmpegAdapter {
* @param pcmPath 输出 PCM 文件路径
* @returns PCM 数据 Buffer
*/
convertToPCM(filePath: string, pcmPath: string): Promise<{ result: boolean, sampleRate: number }>;
convertToPCM(filePath: string, pcmPath: string): Promise<Buffer>;
/**
* 转换音频文件

View File

@@ -20,9 +20,9 @@ function getAddonPath(binaryPath: string): string {
const archName = arch();
let addonFileName: string = process.platform + '.' + process.arch;
let addonPath = path.join(binaryPath, "./native/ffmpeg/", `ffmpegAddon.${addonFileName}.node`);
if (!existsSync(addonPath)) {
throw new Error(`Unsupported platform: ${platformName} ${archName} - Addon not found at ${addonPath}`);
let addonPath = path.join(binaryPath, "./native/ffmpeg/", `${addonFileName}.node`);
if (existsSync(addonPath)) {
throw new Error(`Unsupported platform: ${platformName} ${archName}`);
}
return addonPath;
}
@@ -44,12 +44,16 @@ export class FFmpegAddonAdapter implements IFFmpegAdapter {
*/
async isAvailable(): Promise<boolean> {
try {
const addonPath = getAddonPath(this.binaryPath);
if (!existsSync(addonPath)) {
return false;
}
let temp_addon = { exports: {} };
dlopen(temp_addon, getAddonPath(this.binaryPath));
dlopen(temp_addon, addonPath);
this.addon = temp_addon.exports as FFmpeg;
return this.addon !== null;
} catch (error) {
console.log('[FFmpegAddonAdapter] Failed to load addon:', error);
console.error('[FFmpegAddonAdapter] Failed to load addon:', error);
return false;
}
}
@@ -88,11 +92,14 @@ export class FFmpegAddonAdapter implements IFFmpegAdapter {
/**
* 转换为 PCM
*/
async convertToPCM(filePath: string, pcmPath: string): Promise<{ result: boolean, sampleRate: number }> {
async convertToPCM(filePath: string, pcmPath: string): Promise<Buffer> {
const addon = this.ensureAddon();
const result = await addon.decodeAudioToPCM(filePath, pcmPath, 24000);
const result = await addon.decodeAudioToPCM(filePath);
return result;
// 写入文件
await writeFile(pcmPath, result.pcm);
return result.pcm;
}
/**

View File

@@ -67,5 +67,5 @@ export interface FFmpeg {
/**
* Decode audio file to raw PCM data
*/
decodeAudioToPCM(filePath: string, pcmPath: string, sampleRate?: number): Promise<{ result: boolean, sampleRate: number }>;
decodeAudioToPCM(filePath: string): Promise<AudioPCMResult>;
}

View File

@@ -157,7 +157,7 @@ export class FFmpegExecAdapter implements IFFmpegAdapter {
/**
* 转换为 PCM
*/
async convertToPCM(filePath: string, pcmPath: string): Promise<{ result: boolean, sampleRate: number }> {
async convertToPCM(filePath: string, pcmPath: string): Promise<Buffer> {
try {
ensureDirExists(pcmPath);
@@ -174,7 +174,7 @@ export class FFmpegExecAdapter implements IFFmpegAdapter {
throw new Error('转换PCM失败输出文件不存在');
}
return { result: true, sampleRate: 24000 };
return readFileSync(pcmPath);
} catch (error: any) {
throw new Error(`FFmpeg处理转换出错: ${error.message}`);
}

View File

@@ -93,7 +93,7 @@ export class FFmpegService {
/**
* 转换为 PCM 格式
*/
public static async convert(filePath: string, pcmPath: string): Promise<{ result: boolean, sampleRate: number }> {
public static async convert(filePath: string, pcmPath: string): Promise<Buffer> {
const adapter = await this.getAdapter();
return adapter.convertToPCM(filePath, pcmPath);
}

View File

@@ -1 +1 @@
export const napCatVersion = '4.9.20';
export const napCatVersion = '4.9.0';

View File

@@ -64,7 +64,7 @@ export class NTQQFileApi {
}
}
async getFileUrl(chatType: ChatType, peer: string, fileUUID?: string, file10MMd5?: string | undefined, timeout: number = 5000) {
async getFileUrl(chatType: ChatType, peer: string, fileUUID?: string, file10MMd5?: string | undefined,timeout: number = 5000) {
if (this.core.apis.PacketApi.packetStatus) {
try {
if (chatType === ChatType.KCHATTYPEGROUP && fileUUID) {
@@ -79,7 +79,7 @@ export class NTQQFileApi {
throw new Error('fileUUID or file10MMd5 is undefined');
}
async getPttUrl(peer: string, fileUUID?: string, timeout: number = 5000) {
async getPttUrl(peer: string, fileUUID?: string,timeout: number = 5000) {
if (this.core.apis.PacketApi.packetStatus && fileUUID) {
let appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
try {
@@ -107,7 +107,7 @@ export class NTQQFileApi {
throw new Error('packet cant get ptt url');
}
async getVideoUrlPacket(peer: string, fileUUID?: string, timeout: number = 5000) {
async getVideoUrlPacket(peer: string, fileUUID?: string,timeout: number = 5000) {
if (this.core.apis.PacketApi.packetStatus && fileUUID) {
let appid = new NapProtoMsg(FileId).decode(Buffer.from(fileUUID.replaceAll('-', '+').replaceAll('_', '/'), 'base64')).appid;
try {
@@ -333,10 +333,6 @@ export class NTQQFileApi {
fileSubId: '',
playState: 1,
autoConvertText: 0,
storeID: 0,
otherBusinessInfo: {
aiVoiceType: 0
}
},
};
}

View File

@@ -426,21 +426,5 @@
"3.2.20-40990": {
"appid": 537319891,
"qua": "V1_LNX_NQ_3.2.20_40990_GW_B"
},
"9.9.23-41679": {
"appid": 537320110,
"qua": "V1_WIN_NQ_9.9.23_41679_GW_B"
},
"6.8.83-41679": {
"appid": 537320135,
"qua": "V1_MAC_NQ_6.9.83_41679_GW_B"
},
"9.9.23-41785": {
"appid": 537320110,
"qua": "V1_WIN_NQ_9.9.23_41785_GW_B"
},
"6.8.83-41785": {
"appid": 537320135,
"qua": "V1_MAC_NQ_6.9.83_41785_GW_B"
}
}

View File

@@ -12,15 +12,15 @@
"recv": "1D8CA9D"
},
"3.2.20-40768-x64": {
"send": "2A1B840",
"send": "2CC8120",
"recv": "2D28F20"
},
"3.2.20-40824-x64": {
"send": "2A1B840",
"send": "2CC8120",
"recv": "2D28F20"
},
"3.2.20-40990-x64": {
"send": "2A1B840",
"send": "2CC8120",
"recv": "2D28F20"
},
"3.2.20-40990-arm64": {
@@ -34,33 +34,5 @@
"3.2.20-40768-arm64": {
"send": "157C0E8",
"recv": "1546658"
},
"9.9.23-41679-x64": {
"send": "09FF0F4",
"recv": "1D1A039"
},
"6.9.82-40824-arm64": {
"send": "05FA930",
"recv": "0B41B90"
},
"6.9.82-40768-arm64": {
"send": "05FA930",
"recv": "0B41B90"
},
"6.9.82-40990-arm64": {
"send": "05FA930",
"recv": "0B41B90"
},
"6.9.83-41679-arm64": {
"send": "237D114",
"recv": "0957648"
},
"6.9.83-41785-arm64": {
"send": "23B0BF0",
"recv": "095567C"
},
"9.9.23-41785-x64": {
"send": "09FF0A4",
"recv": "1D19FF9"
}
}

View File

@@ -558,21 +558,5 @@
"6.9.82-40990-arm64": {
"send": "202A198",
"recv": "202B718"
},
"9.9.23-41679-x64": {
"send": "2C94520",
"recv": "2C97AA0"
},
"6.9.83-41679-arm64": {
"send": "3D718F8",
"recv": "3D74208"
},
"9.9.23-41785-x64": {
"send": "2C944A0",
"recv": "2C97A20"
},
"6.8.83-41785-arm64": {
"send": "3D6DA28",
"recv": "3D70338"
}
}

View File

@@ -74,36 +74,6 @@ export const GroupChange = {
field7: ProtoField(7, ScalarType.BYTES, true),
};
// Group Reaction Related
export const GroupReactionDataInnerDataTarget = {
seq: ProtoField(1, ScalarType.UINT64, true),
};
export const GroupReactionDataContent = {
code: ProtoField(1, ScalarType.STRING, true),
count: ProtoField(3, ScalarType.UINT32, true),
operatorUid: ProtoField(4, ScalarType.STRING, true),
type: ProtoField(5, ScalarType.UINT32, true),
};
export const GroupReactionDataInnerData = {
groupReactionTarget: ProtoField(2, () => GroupReactionDataInnerDataTarget, true),
groupReactionDataContent: ProtoField(3, () => GroupReactionDataContent, true),
};
export const GroupReactionDataInner = {
data: ProtoField(1, () => GroupReactionDataInnerData, true),
};
export const GroupReactionData = {
data: ProtoField(1, () => GroupReactionDataInner, true),
};
export const GroupReactNotify = {
groupUin: ProtoField(4, ScalarType.UINT64, true),
field13: ProtoField(13, ScalarType.UINT32, true),
groupReactionData: ProtoField(44, () => GroupReactionData, true),
};
// Group Invite Related
export const GroupInvite = {
groupUin: ProtoField(1, ScalarType.UINT32),
field2: ProtoField(2, ScalarType.UINT32),

View File

@@ -311,10 +311,6 @@ export interface PttElement {
voiceType: number;
waveAmplitudes: number[];
autoConvertText: number;
storeID: number;
otherBusinessInfo: {
aiVoiceType: number;
};
}
export type SendRecommendedMsgElement = SendElementBase<ElementType.RECOMMENDEDMSG> & ElementBase<'recommendedMsgElement'>;
@@ -353,7 +349,7 @@ export type SendPicElement = SendElementBase<ElementType.PIC> & ElementBase<'pic
export type SendPttElement = SendElementBase<ElementType.PTT> & ElementBase<'pttElement', {
pttElement: ['fileName', 'filePath', 'md5HexStr', 'fileSize', 'duration', 'formatType', 'voiceType',
'voiceChangeType', 'canConvert2Text', 'waveAmplitudes', 'fileSubId', 'playState', 'autoConvertText', 'storeID', 'otherBusinessInfo']
'voiceChangeType', 'canConvert2Text', 'waveAmplitudes', 'fileSubId', 'playState', 'autoConvertText']
}>;
export type SendFileElement = SendElementBase<ElementType.FILE> & ElementBase<'fileElement'>;

View File

@@ -139,20 +139,8 @@ export interface NodeQQNTWrapperUtil {
isNull(): unknown;
}
export interface NodeIQQNTStartupSessionWrapper {
create(): NodeIQQNTStartupSessionWrapper;
stop(): void;
start(): void;
createWithModuleList(uk: unknown): unknown;
getSessionIdList(): unknown;
}
export interface NodeIQQNTWrapperSession {
getNTWrapperSession(str: string): NodeIQQNTWrapperSession;
get(): NodeIQQNTWrapperSession;
new(): NodeIQQNTWrapperSession;
create(): NodeIQQNTWrapperSession;
init(
@@ -281,7 +269,6 @@ export interface WrapperNodeApi {
NodeIO3MiscService: NodeIO3MiscService;
NodeQQNTWrapperUtil: NodeQQNTWrapperUtil;
NodeIQQNTWrapperSession: NodeIQQNTWrapperSession;
NodeIQQNTStartupSessionWrapper: NodeIQQNTStartupSessionWrapper
NodeIQQNTWrapperEngine: NodeIQQNTWrapperEngine;
NodeIKernelLoginService: NodeIKernelLoginService;

View File

@@ -39,9 +39,9 @@ export async function NCoreInitFramework(
const basicInfoWrapper = new QQBasicInfoWrapper({ logger });
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVersion());
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
// nativePacketHandler.onAll((packet) => {
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
// });
nativePacketHandler.onAll((packet) => {
console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
});
await nativePacketHandler.init(basicInfoWrapper.getFullQQVersion());
// 在 init 之后注册监听器

View File

@@ -28,10 +28,7 @@ export class GetGroupEssence extends OneBotAction<Payload, unknown> {
}
async _handle(payload: Payload, _adapter: string, config: NetworkAdapterConfig) {
const msglist = (await this.core.apis.WebApi.getGroupEssenceMsgAll(payload.group_id.toString()))
.flatMap((e) => e?.data?.msg_list)
// 在群精华回空的时候会出现[null]的情况~ https://github.com/NapNeko/NapCatQQ/issues/1334
.filter(Boolean);
const msglist = (await this.core.apis.WebApi.getGroupEssenceMsgAll(payload.group_id.toString())).flatMap((e) => e.data.msg_list);
if (!msglist) {
throw new Error('获取失败');
}

View File

@@ -24,8 +24,6 @@ import { OB11GroupUploadNoticeEvent } from '../event/notice/OB11GroupUploadNotic
import { OB11GroupNameEvent } from '../event/notice/OB11GroupNameEvent';
import { FileNapCatOneBotUUID } from '@/common/file-uuid';
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
import { NapProtoMsg } from '@napneko/nap-proto-core';
import { GroupReactNotify, PushMsg } from '@/core/packet/transformer/proto';
export class OneBotGroupApi {
obContext: NapCatOneBot11Adapter;
@@ -70,10 +68,6 @@ export class OneBotGroupApi {
groupCode: string,
grayTipElement: GrayTipElement
) {
if (this.core.apis.PacketApi.packetStatus === true) {
return;
//Raw包解析支持时禁用NT解析
}
const emojiLikeData = new fastXmlParser.XMLParser({
ignoreAttributes: false,
attributeNamePrefix: '',
@@ -82,7 +76,7 @@ export class OneBotGroupApi {
const senderUin = emojiLikeData.gtip.qq.jp;
const msgSeq = emojiLikeData.gtip.url.msgseq;
const emojiId = emojiLikeData.gtip.face.id;
return await this.createGroupEmojiLikeEvent(groupCode, senderUin, msgSeq, emojiId, true, 1);
return await this.createGroupEmojiLikeEvent(groupCode, senderUin, msgSeq, emojiId);
}
async createGroupEmojiLikeEvent(
@@ -90,8 +84,6 @@ export class OneBotGroupApi {
senderUin: string,
msgSeq: string,
emojiId: string,
isAdd: boolean = true,
count: number = 1,
) {
const peer = {
chatType: ChatType.KCHATTYPEGROUP,
@@ -111,13 +103,11 @@ export class OneBotGroupApi {
this.core,
parseInt(groupCode),
parseInt(senderUin),
MessageUnique.createUniqueMsgId({ chatType: ChatType.KCHATTYPEGROUP, guildId: '', peerUid: groupCode }, replyMsg.msgId),
MessageUnique.getShortIdByMsgId(replyMsg.msgId)!,
[{
emoji_id: emojiId,
count: count,
count: 1,
}],
isAdd,
msgSeq
);
}
@@ -136,40 +126,7 @@ export class OneBotGroupApi {
}
return undefined;
}
async registerParseGroupReactEvent() {
this.obContext.core.context.packetHandler.onCmd('trpc.msg.olpush.OlPushService.MsgPush', async (packet) => {
let data = new NapProtoMsg(PushMsg).decode(Buffer.from(packet.hex_data, 'hex'));
if (data.message.contentHead.type === 732 && data.message.contentHead.subType === 16) {
let pbNotify = data.message.body?.msgContent?.slice(7);
if (!pbNotify) {
return;
}
// 开始解析Notify
const notify = new NapProtoMsg(GroupReactNotify).decode(pbNotify);
if ((notify.field13 ?? 0) === 35) {
// Group React Notify
const groupCode = notify.groupUin?.toString() ?? '';
const operatorUid = notify.groupReactionData?.data?.data?.groupReactionDataContent?.operatorUid ?? '';
const type = notify.groupReactionData?.data?.data?.groupReactionDataContent?.type ?? 0;
const seq = notify.groupReactionData?.data?.data?.groupReactionTarget?.seq?.toString() ?? '';
const code = notify.groupReactionData?.data?.data?.groupReactionDataContent?.code ?? '';
//const count = notify.groupReactionData?.data?.data?.groupReactionDataContent?.count ?? 0;
const senderUin = await this.core.apis.UserApi.getUinByUidV2(operatorUid);
const event = await this.createGroupEmojiLikeEvent(
groupCode,
senderUin,
seq,
code,
type === 1,
1
);
if (event) {
this.obContext.networkManager.emitEvent(event);
}
}
}
});
}
async parsePaiYiPai(msg: RawMessage, jsonStr: string) {
const json = JSON.parse(jsonStr);
//判断业务类型

View File

@@ -1182,7 +1182,7 @@ export class OneBotMsgApi {
}, returnMsg.msgId);
return returnMsg;
} catch (error) {
throw error;
throw new Error((error as Error).message);
} finally {
cleanTaskQueue.addFiles(deleteAfterSentFiles, timeout);
// setTimeout(async () => {

View File

@@ -10,15 +10,12 @@ export class OB11GroupMsgEmojiLikeEvent extends OB11GroupNoticeEvent {
notice_type = 'group_msg_emoji_like';
message_id: number;
likes: MsgEmojiLike[];
is_add: boolean;
message_seq?: string;
constructor(core: NapCatCore, groupId: number, userId: number, messageId: number, likes: MsgEmojiLike[], isAdd: boolean, messageSeq?: string) {
constructor(core: NapCatCore, groupId: number, userId: number, messageId: number, likes: MsgEmojiLike[]) {
super(core, groupId, userId);
this.group_id = groupId;
this.user_id = userId; // 可为空表示是对别人的消息操作如果是对bot自己的消息则不为空
this.message_id = messageId;
this.likes = likes;
this.is_add = isAdd;
}
}

View File

@@ -66,7 +66,7 @@ export class NapCatOneBot11Adapter {
actions: ActionMap;
private readonly bootTime = Date.now() / 1000;
recallEventCache = new Map<string, NodeJS.Timeout>();
constructor(core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) {
constructor (core: NapCatCore, context: InstanceContext, pathWrapper: NapCatPathWrapper) {
this.core = core;
this.context = context;
this.configLoader = new OB11ConfigLoader(core, pathWrapper.configPath, OneBotConfigSchema);
@@ -80,7 +80,7 @@ export class NapCatOneBot11Adapter {
this.actions = createActionMap(this, core);
this.networkManager = new OB11NetworkManager();
}
async creatOneBotLog(ob11Config: OneBotConfig) {
async creatOneBotLog (ob11Config: OneBotConfig) {
let log = '[network] 配置加载\n';
for (const key of ob11Config.network.httpServers) {
log += `HTTP服务: ${key.host}:${key.port}, : ${key.enable ? '已启动' : '未启动'}\n`;
@@ -99,7 +99,7 @@ export class NapCatOneBot11Adapter {
}
return log;
}
async InitOneBot() {
async InitOneBot () {
const selfInfo = this.core.selfInfo;
const ob11Config = this.configLoader.configData;
this.core.apis.UserApi.getUserDetailInfo(selfInfo.uid, false)
@@ -118,9 +118,9 @@ export class NapCatOneBot11Adapter {
elementId: '',
textElement: {
content:
'[NapCat] 温馨提示:\n' +
'WebUI密码为默认密码已进行强制修改\n' +
'新密码: ' + pendingTokenToSend,
'[NapCat] 温馨提示:\n'+
'WebUI密码为默认密码已进行强制修改\n'+
'新密码: ' +pendingTokenToSend,
atType: NTMsgAtType.ATTYPEUNKNOWN,
atUid: '',
atTinyId: '',
@@ -216,13 +216,10 @@ export class NapCatOneBot11Adapter {
//this.context.logger.log(`OneBot11 配置更改:${JSON.stringify(prev)} -> ${JSON.stringify(newConfig)}`);
await this.reloadNetwork(prev, newConfig);
});
this.apis.GroupApi.registerParseGroupReactEvent().catch(e =>
this.context.logger.logError('注册群消息反应表情失败', e)
);
}
private async reloadNetwork(prev: OneBotConfig, now: OneBotConfig): Promise<void> {
private async reloadNetwork (prev: OneBotConfig, now: OneBotConfig): Promise<void> {
const prevLog = await this.creatOneBotLog(prev);
const newLog = await this.creatOneBotLog(now);
this.context.logger.log(`[Notice] [OneBot11] 配置变更前:\n${prevLog}`);
@@ -235,7 +232,7 @@ export class NapCatOneBot11Adapter {
await this.handleConfigChange(prev.network.websocketClients, now.network.websocketClients, OB11WebSocketClientAdapter);
}
private async handleConfigChange<CT extends NetworkAdapterConfig>(
private async handleConfigChange<CT extends NetworkAdapterConfig> (
prevConfig: NetworkAdapterConfig[],
nowConfig: NetworkAdapterConfig[],
adapterClass: new (
@@ -267,7 +264,7 @@ export class NapCatOneBot11Adapter {
}
}
private initMsgListener() {
private initMsgListener () {
const msgListener = new NodeIKernelMsgListener();
msgListener.onRecvSysMsg = (msg) => {
this.apis.MsgApi.parseSysMessage(msg)
@@ -381,7 +378,7 @@ export class NapCatOneBot11Adapter {
this.context.session.getMsgService().addKernelMsgListener(proxiedListenerOf(msgListener, this.context.logger));
}
private initBuddyListener() {
private initBuddyListener () {
const buddyListener = new NodeIKernelBuddyListener();
buddyListener.onBuddyReqChange = async (reqs) => {
@@ -412,7 +409,7 @@ export class NapCatOneBot11Adapter {
.addKernelBuddyListener(proxiedListenerOf(buddyListener, this.context.logger));
}
private initGroupListener() {
private initGroupListener () {
const groupListener = new NodeIKernelGroupListener();
groupListener.onGroupNotifiesUpdated = async (_, notifies) => {
@@ -505,7 +502,7 @@ export class NapCatOneBot11Adapter {
.addKernelGroupListener(proxiedListenerOf(groupListener, this.context.logger));
}
private async emitMsg(message: RawMessage) {
private async emitMsg (message: RawMessage) {
const network = await this.networkManager.getAllConfig();
this.context.logger.logDebug('收到新消息 RawMessage', message);
await Promise.allSettled([
@@ -514,7 +511,7 @@ export class NapCatOneBot11Adapter {
]);
}
private async handleMsg(message: RawMessage, network: Array<NetworkAdapterConfig>) {
private async handleMsg (message: RawMessage, network: Array<NetworkAdapterConfig>) {
// 过滤无效消息
if (message.msgType === NTMsgType.KMSGTYPENULL) {
return;
@@ -535,7 +532,7 @@ export class NapCatOneBot11Adapter {
}
}
private isSelfMessage(ob11Msg: {
private isSelfMessage (ob11Msg: {
stringMsg: OB11Message
arrayMsg: OB11Message
}): boolean {
@@ -543,7 +540,7 @@ export class NapCatOneBot11Adapter {
ob11Msg.arrayMsg.user_id.toString() == this.core.selfInfo.uin;
}
private createMsgMap(network: Array<NetworkAdapterConfig>, ob11Msg: {
private createMsgMap (network: Array<NetworkAdapterConfig>, ob11Msg: {
stringMsg: OB11Message
arrayMsg: OB11Message
}, isSelfMsg: boolean, message: RawMessage): Map<string, OB11Message> {
@@ -563,7 +560,7 @@ export class NapCatOneBot11Adapter {
return msgMap;
}
private handleDebugNetwork(network: Array<NetworkAdapterConfig>, msgMap: Map<string, OB11Message>, message: RawMessage) {
private handleDebugNetwork (network: Array<NetworkAdapterConfig>, msgMap: Map<string, OB11Message>, message: RawMessage) {
const debugNetwork = network.filter(e => e.enable && e.debug);
if (debugNetwork.length > 0) {
debugNetwork.forEach(adapter => {
@@ -577,7 +574,7 @@ export class NapCatOneBot11Adapter {
}
}
private handleNotReportSelfNetwork(network: Array<NetworkAdapterConfig>, msgMap: Map<string, OB11Message>, isSelfMsg: boolean) {
private handleNotReportSelfNetwork (network: Array<NetworkAdapterConfig>, msgMap: Map<string, OB11Message>, isSelfMsg: boolean) {
if (isSelfMsg) {
const notReportSelfNetwork = network.filter(e => e.enable && (('reportSelfMessage' in e && !e.reportSelfMessage) || !('reportSelfMessage' in e)));
notReportSelfNetwork.forEach(adapter => {
@@ -586,7 +583,7 @@ export class NapCatOneBot11Adapter {
}
}
private async handleGroupEvent(message: RawMessage) {
private async handleGroupEvent (message: RawMessage) {
try {
// 群名片修改事件解析 任何都该判断
if (message.senderUin && message.senderUin !== '0') {
@@ -619,7 +616,7 @@ export class NapCatOneBot11Adapter {
}
}
private async handlePrivateMsgEvent(message: RawMessage) {
private async handlePrivateMsgEvent (message: RawMessage) {
try {
if (message.msgType === NTMsgType.KMSGTYPEGRAYTIPS) {
// 灰条为单元素消息
@@ -637,7 +634,7 @@ export class NapCatOneBot11Adapter {
}
}
private async emitRecallMsg(message: RawMessage, element: MessageElement) {
private async emitRecallMsg (message: RawMessage, element: MessageElement) {
const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' };
const oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId) ?? MessageUnique.createUniqueMsgId(peer, message.msgId);
if (message.chatType == ChatType.KCHATTYPEC2C) {
@@ -648,7 +645,7 @@ export class NapCatOneBot11Adapter {
return;
}
private async emitFriendRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) {
private async emitFriendRecallMsg (message: RawMessage, oriMessageId: number, element: MessageElement) {
const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
if (!operatorUid) return undefined;
return new OB11FriendRecallNoticeEvent(
@@ -658,7 +655,7 @@ export class NapCatOneBot11Adapter {
);
}
private async emitGroupRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) {
private async emitGroupRecallMsg (message: RawMessage, oriMessageId: number, element: MessageElement) {
const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
if (!operatorUid) return undefined;
const operatorId = await this.core.apis.UserApi.getUinByUidV2(operatorUid);

View File

@@ -10,7 +10,6 @@ import {
loadQQWrapper,
NapCatCore,
NapCatCoreWorkingEnv,
NodeIQQNTStartupSessionWrapper,
NodeIQQNTWrapperEngine,
NodeIQQNTWrapperSession,
PlatformType,
@@ -243,8 +242,7 @@ async function handleLoginInner(context: { isLogined: boolean }, logger: LogWrap
async function initializeSession(
session: NodeIQQNTWrapperSession,
sessionConfig: WrapperSessionInitConfig,
startupSession: NodeIQQNTStartupSessionWrapper | null
sessionConfig: WrapperSessionInitConfig
) {
return new Promise<void>((resolve, reject) => {
const sessionListener = new NodeIKernelSessionListener();
@@ -261,9 +259,6 @@ async function initializeSession(
new NodeIDispatcherAdapter(),
sessionListener,
);
if (startupSession) {
startupSession.start();
} else {
try {
session.startNT(0);
} catch {
@@ -273,7 +268,6 @@ async function initializeSession(
reject(new Error('init failed ' + (e as Error).message));
}
}
}
});
}
async function handleProxy(session: NodeIQQNTWrapperSession, logger: LogWrapper) {
@@ -328,9 +322,9 @@ export async function NCoreInitShell() {
const wrapper = loadQQWrapper(basicInfoWrapper.getFullQQVersion());
const nativePacketHandler = new NativePacketHandler({ logger }); // 初始化 NativePacketHandler 用于后续使用
// nativePacketHandler.onAll((packet) => {
// console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
// });
nativePacketHandler.onAll((packet) => {
console.log('[Packet]', packet.uin, packet.cmd, packet.hex_data);
});
await nativePacketHandler.init(basicInfoWrapper.getFullQQVersion());
const o3Service = wrapper.NodeIO3MiscService.get();
@@ -341,20 +335,8 @@ export async function NCoreInitShell() {
const engine = wrapper.NodeIQQNTWrapperEngine.get();
const loginService = wrapper.NodeIKernelLoginService.get();
let session: NodeIQQNTWrapperSession;
let startupSession: NodeIQQNTStartupSessionWrapper | null = null;
try {
startupSession = wrapper.NodeIQQNTStartupSessionWrapper.create();
session = wrapper.NodeIQQNTWrapperSession.getNTWrapperSession('nt_1');
} catch (e: unknown) {
try {
session = wrapper.NodeIQQNTWrapperSession.create();
} catch (error) {
logger.logError('创建 StartupSession 失败', e);
logger.logError('创建 Session 失败', error);
throw error;
}
}
const session = wrapper.NodeIQQNTWrapperSession.create();
const [dataPath, dataPathGlobal] = getDataPaths(wrapper);
const systemPlatform = getPlatformType();
@@ -388,7 +370,7 @@ export async function NCoreInitShell() {
dataPath,
);
await initializeSession(session, sessionConfig, startupSession);
await initializeSession(session, sessionConfig);
const accountDataPath = path.resolve(dataPath, './NapCat/data');
//判断dataPath是否为根目录 或者 D:/ 之类的盘目录