diff --git a/README.md b/README.md index d14613e8..ab25c145 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,12 @@ NapCatQQ (aka 猫猫框架) 是现代化的基于 NTQQ 的 Bot 协议端实现。 ## 猫猫技能 +- [x] **高性能**:1K+ 群聊数目、20 线程并行发送消息毫无压力 - [x] **多种启动方式**:支持以无头、LiteLoader 插件、仅 QQ GUI 三种方式启动 - [x] **低占用**:无头模式占用资源极低,适合在服务器上运行 -- [x] **超多接口**:在实现大部分Onebot接口上扩展了一套私有API +- [x] **超多接口**:实现大部分 OneBot 和 go-cqhttp 接口,超多扩展 API - [x] **WebUI**:自带 WebUI 支持,远程管理更加便捷 +- [x] **低故障率**:快速适配最新版本,日常保证 0 Issue ## 使用猫猫 diff --git a/manifest.json b/manifest.json index e199eaf5..703bb7a5 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "2.2.38", + "version": "2.2.43", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index 97b81d86..d10847ea 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "2.2.38", + "version": "2.2.43", "scripts": { "build:framework": "vite build --mode framework", "build:shell": "vite build --mode shell", diff --git a/src/common/version.ts b/src/common/version.ts index c2b87813..2297647c 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '2.2.38'; +export const napCatVersion = '2.2.43'; diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index dacb96cb..e051f061 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -314,9 +314,6 @@ export class NTQQGroupApi { return this.context.session.getRichMediaService().batchGetGroupFileCount(Gids); } - async getGroupIgnoreNotifies() { - } - async getArkJsonGroupShare(GroupCode: string) { const ret = await this.core.eventWrapper.callNoListenerEvent( 'NodeIKernelGroupService/getGroupRecommendContactArkJson', diff --git a/src/core/apis/msg.ts b/src/core/apis/msg.ts index c4e98c28..1fd0c98a 100644 --- a/src/core/apis/msg.ts +++ b/src/core/apis/msg.ts @@ -93,6 +93,18 @@ export class NTQQMsgApi { pageLimit: 1, }); } + async queryFirstMsgBySeq(peer: Peer, msgSeq: string) { + return await this.context.session.getMsgService().queryMsgsWithFilterEx('0', '0', msgSeq, { + chatInfo: peer, + filterMsgType: [], + filterSendersUid: [], + filterMsgToTime: '0', + filterMsgFromTime: '0', + isReverseOrder: true, + isIncludeCurrent: true, + pageLimit: 1, + }); + } async getMsgsBySeqAndCount(peer: Peer, seq: string, count: number, desc: boolean, z: boolean) { return await this.context.session.getMsgService().getMsgsBySeqAndCount(peer, seq, count, desc, z); } diff --git a/src/core/index.ts b/src/core/index.ts index 198e4992..adc5e917 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -24,7 +24,7 @@ import path from 'node:path'; import fs from 'node:fs'; import { getMachineId, hostname, systemName, systemVersion } from '@/common/system'; import { NTEventWrapper } from '@/common/event'; -import { DataSource, GroupMember, SelfInfo } from '@/core/entities'; +import { DataSource, GroupMember, KickedOffLineInfo, SelfInfo, SelfStatusInfo } from '@/core/entities'; import { NapCatConfigLoader } from '@/core/helper/config'; import os from 'node:os'; import { NodeIKernelGroupListener, NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners'; @@ -113,6 +113,11 @@ export class NapCatCore { // Renamed from 'InitDataListener' async initNapCatCoreListeners() { const msgListener = new NodeIKernelMsgListener(); + msgListener.onKickedOffLine = (Info: KickedOffLineInfo) => { + // 下线通知 + this.context.logger.logError('[KickedOffLine] [' + Info.tipsTitle + '] ' + Info.tipsDesc); + this.selfInfo.online = false; + }; msgListener.onRecvMsg = (msgs) => { msgs.forEach(msg => this.context.logger.logMessage(msg, this.selfInfo)); }; @@ -130,10 +135,12 @@ export class NapCatCore { Object.assign(this.selfInfo, profile); } }; - profileListener.onSelfStatusChanged = (/* Info: SelfStatusInfo */) => { - // if (Info.status == 20) { - // log("账号状态变更为离线") - // } + profileListener.onSelfStatusChanged = (Info: SelfStatusInfo) => { + if (Info.status == 20) { + this.selfInfo.online = false; + this.context.logger.log("账号状态变更为离线"); + } + this.selfInfo.online = true; }; this.context.session.getProfileService().addKernelProfileListener( proxiedListenerOf(profileListener, this.context.logger), diff --git a/src/onebot/action/BaseAction.ts b/src/onebot/action/BaseAction.ts index c6d696d8..8c4aa543 100644 --- a/src/onebot/action/BaseAction.ts +++ b/src/onebot/action/BaseAction.ts @@ -54,7 +54,7 @@ abstract class BaseAction { public async websocketHandle(payload: PayloadType, echo: any): Promise> { const result = await this.check(payload); if (!result.valid) { - return OB11Response.error(result.message, 1400); + return OB11Response.error(result.message, 1400, echo); } try { const resData = await this._handle(payload); diff --git a/src/onebot/action/msg/SendMsg.ts b/src/onebot/action/msg/SendMsg.ts index bec021a4..c3c80509 100644 --- a/src/onebot/action/msg/SendMsg.ts +++ b/src/onebot/action/msg/SendMsg.ts @@ -173,9 +173,18 @@ export class SendMsg extends BaseAction { } const { sendElements } = await this.obContext.apis.MsgApi .createSendElements(OB11Data, destPeer); + //拆分消息 - const MixElement = sendElements.filter(element => element.elementType !== ElementType.FILE && element.elementType !== ElementType.VIDEO); - const SingleElement = sendElements.filter(element => element.elementType === ElementType.FILE || element.elementType === ElementType.VIDEO).map(e => [e]); + + const MixElement = sendElements.filter( + element => + element.elementType !== ElementType.FILE && element.elementType !== ElementType.VIDEO && element.elementType !== ElementType.ARK + ); + const SingleElement = sendElements.filter( + element => + element.elementType === ElementType.FILE || element.elementType === ElementType.VIDEO || element.elementType === ElementType.ARK + ).map(e => [e]); + const AllElement: SendMessageElement[][] = [MixElement, ...SingleElement].filter(e => e !== undefined && e.length !== 0); const MsgNodeList: Promise[] = []; for (const sendElementsSplitElement of AllElement) { diff --git a/src/onebot/api/group.ts b/src/onebot/api/group.ts index bf112201..40f0380e 100644 --- a/src/onebot/api/group.ts +++ b/src/onebot/api/group.ts @@ -271,14 +271,11 @@ export class OneBotGroupApi { guildId: '', peerUid: groupCode, }; - const replyMsgList = (await this.core.apis.MsgApi.getMsgExBySeq(peer, msgSeq)).msgList; + const replyMsgList = (await this.core.apis.MsgApi.queryFirstMsgBySeq(peer, msgSeq)).msgList; if (replyMsgList.length < 1) { return; } - const replyMsg = replyMsgList - .filter(e => e.msgSeq == msgSeq) - .sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0]; - //console.log("表情回应消息长度检测", msgSeq, replyMsg.elements); + const replyMsg = replyMsgList[0]; if (!replyMsg) { this.core.context.logger.logError('解析表情回应消息失败: 未找到回应消息'); return undefined; diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index 8e4a13b4..85689dba 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -709,6 +709,9 @@ export class OneBotMsgApi { message_format: messagePostFormat === 'string' ? 'string' : 'array', post_type: this.core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE, }; + if (this.core.selfInfo.uin == msg.senderUin) { + resMsg.message_sent_type = 'self'; + } if (msg.chatType == ChatType.KCHATTYPEGROUP) { resMsg.sub_type = 'normal'; // 这里go-cqhttp是group,而onebot11标准是normal, 蛋疼 resMsg.group_id = parseInt(msg.peerUin); diff --git a/src/onebot/network/active-websocket.ts b/src/onebot/network/active-websocket.ts index ec6c3002..cf7ec602 100644 --- a/src/onebot/network/active-websocket.ts +++ b/src/onebot/network/active-websocket.ts @@ -131,23 +131,24 @@ export class OB11ActiveWebSocketAdapter implements IOB11NetworkAdapter { private async handleMessage(message: any) { let receiveData: { action: ActionName, params?: any, echo?: any } = { action: ActionName.Unknown, params: {} }; let echo = undefined; + try { - try { - receiveData = JSON.parse(message.toString()); - echo = receiveData.echo; - this.logger.logDebug('[OneBot] [WebSocket Client] 收到正向Websocket消息', receiveData); - } catch (e) { - this.checkStateAndReply(OB11Response.error('json解析失败,请检查数据格式', 1400, echo)); - return; - } - receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 - const retdata = await this.actions.get(receiveData.action) - ?.websocketHandle(receiveData.params, echo ?? ''); - const packet = Object.assign({}, retdata); - this.checkStateAndReply(packet); + receiveData = JSON.parse(message.toString()); + echo = receiveData.echo; + this.logger.logDebug('[OneBot] [WebSocket Client] 收到正向Websocket消息', receiveData); } catch (e) { - this.logger.logError('[OneBot] [WebSocket Client] 发生错误', e); - this.checkStateAndReply(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo)); + this.checkStateAndReply(OB11Response.error('json解析失败,请检查数据格式', 1400, echo)); + return; } + receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 + const action = this.actions.get(receiveData.action); + if (!action) { + this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的api ' + receiveData.action); + this.checkStateAndReply(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo)); + return; + } + const retdata = await action.websocketHandle(receiveData.params, echo ?? ''); + const packet = Object.assign({}, retdata); + this.checkStateAndReply(packet); } } diff --git a/src/onebot/network/passive-websocket.ts b/src/onebot/network/passive-websocket.ts index fe197f4f..70b2f371 100644 --- a/src/onebot/network/passive-websocket.ts +++ b/src/onebot/network/passive-websocket.ts @@ -146,22 +146,23 @@ export class OB11PassiveWebSocketAdapter implements IOB11NetworkAdapter { let receiveData: { action: ActionName, params?: any, echo?: any } = { action: ActionName.Unknown, params: {} }; let echo = undefined; try { - try { - receiveData = JSON.parse(message.toString()); - echo = receiveData.echo; - //this.logger.logDebug('收到正向Websocket消息', receiveData); - } catch (e) { - this.checkStateAndReply(OB11Response.error('json解析失败,请检查数据格式', 1400, echo), wsClient); - return; - } - receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 - const retdata = await this.actions.get(receiveData.action)?.websocketHandle(receiveData.params, echo ?? ''); - const packet = Object.assign({}, retdata); - this.checkStateAndReply(packet, wsClient); + receiveData = JSON.parse(message.toString()); + echo = receiveData.echo; + //this.logger.logDebug('收到正向Websocket消息', receiveData); } catch (e) { - this.checkStateAndReply(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo), wsClient); + this.checkStateAndReply(OB11Response.error('json解析失败,请检查数据格式', 1400, echo), wsClient); + return; } + receiveData.params = (receiveData?.params) ? receiveData.params : {};//兼容类型验证 + const action = this.actions.get(receiveData.action); + if (!action) { + this.logger.logError('[OneBot] [WebSocket Client] 发生错误', '不支持的api ' + receiveData.action); + this.checkStateAndReply(OB11Response.error('不支持的api ' + receiveData.action, 1404, echo), wsClient); + return; + } + const retdata = await action.websocketHandle(receiveData.params, echo ?? ''); + const packet = Object.assign({}, retdata); + this.checkStateAndReply(packet, wsClient); } - } diff --git a/src/onebot/types/message.ts b/src/onebot/types/message.ts index 001c0199..2a340bab 100644 --- a/src/onebot/types/message.ts +++ b/src/onebot/types/message.ts @@ -8,6 +8,7 @@ export enum OB11MessageType { } export interface OB11Message { + message_sent_type?: string; target_id?: number; // 自己发送的消息才有此字段 self_id?: number, time: number, diff --git a/src/webui/ui/NapCat.ts b/src/webui/ui/NapCat.ts index 8f80b3c6..3bc0b62f 100644 --- a/src/webui/ui/NapCat.ts +++ b/src/webui/ui/NapCat.ts @@ -30,7 +30,7 @@ async function onSettingWindowCreated(view: Element) { SettingItem( 'Napcat', undefined, - SettingButton('V2.2.38', 'napcat-update-button', 'secondary'), + SettingButton('V2.2.43', 'napcat-update-button', 'secondary'), ), ]), SettingList([ diff --git a/static/assets/renderer.js b/static/assets/renderer.js index c82de3ff..84aec789 100644 --- a/static/assets/renderer.js +++ b/static/assets/renderer.js @@ -164,7 +164,7 @@ async function onSettingWindowCreated(view) { SettingItem( 'Napcat', void 0, - SettingButton("V2.2.38", "napcat-update-button", "secondary") + SettingButton("V2.2.43", "napcat-update-button", "secondary") ) ]), SettingList([