From ed2f554d4e12dfd87fc6fa384800d9280ffe6edd Mon Sep 17 00:00:00 2001 From: idranme Date: Sat, 28 Sep 2024 21:54:25 +0800 Subject: [PATCH 1/5] refactor --- src/main/main.ts | 4 +- src/main/store.ts | 33 ++- src/ntqqapi/core.ts | 52 ++-- src/ntqqapi/entities.ts | 4 +- src/ntqqapi/types/notify.ts | 18 +- .../action/go-cqhttp/UploadGroupFile.ts | 4 +- .../action/go-cqhttp/UploadPrivateFile.ts | 4 +- src/onebot11/action/msg/GetMsg.ts | 2 +- src/onebot11/action/types.ts | 2 +- src/onebot11/adapter.ts | 245 +++++++----------- src/onebot11/entities.ts | 1 - src/onebot11/helper/createMessage.ts | 34 +-- 12 files changed, 195 insertions(+), 208 deletions(-) diff --git a/src/main/main.ts b/src/main/main.ts index 5ce2a50..6079a29 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -191,7 +191,9 @@ function onLoad() { ctx.plugin(SQLiteDriver, { path: path.join(dbDir, `${selfInfo.uin}.db`) }) - ctx.plugin(Store) + ctx.plugin(Store, { + msgCacheExpire: config.msgCacheExpire! * 1000 + }) ctx.start() ipcMain.on(CHANNEL_SET_CONFIG_CONFIRMED, (event, config: LLOBConfig) => { ctx.parallel('llonebot/config-updated', config) diff --git a/src/main/store.ts b/src/main/store.ts index 3b1a6ee..aa8d7c9 100644 --- a/src/main/store.ts +++ b/src/main/store.ts @@ -1,4 +1,4 @@ -import { Peer } from '@/ntqqapi/types' +import { Peer, RawMessage } from '@/ntqqapi/types' import { createHash } from 'node:crypto' import { LimitedHashTable } from '@/common/utils/table' import { FileCacheV2 } from '@/common/types' @@ -24,13 +24,15 @@ interface MsgInfo { peer: Peer } -export default class Store extends Service { +class Store extends Service { static inject = ['database', 'model'] private cache: LimitedHashTable + private messages: Map - constructor(protected ctx: Context) { + constructor(protected ctx: Context, public config: Store.Config) { super(ctx, 'store', true) this.cache = new LimitedHashTable(1000) + this.messages = new Map() this.initDatabase() } @@ -123,4 +125,29 @@ export default class Store extends Service { getFileCacheById(fileUuid: string) { return this.ctx.database.get('file_v2', { fileUuid }) } + + async addMsgCache(msg: RawMessage) { + const expire = this.config.msgCacheExpire + if (expire === 0) { + return + } + const id = msg.msgId + this.messages.set(id, msg) + setTimeout(() => { + this.messages.delete(id) + }, expire) + } + + getMsgCache(msgId: string) { + return this.messages.get(msgId) + } } + +namespace Store { + export interface Config { + /** 单位为毫秒 */ + msgCacheExpire: number + } +} + +export default Store diff --git a/src/ntqqapi/core.ts b/src/ntqqapi/core.ts index 4a703f7..a5496fa 100644 --- a/src/ntqqapi/core.ts +++ b/src/ntqqapi/core.ts @@ -2,7 +2,6 @@ import { unlink } from 'node:fs/promises' import { Service, Context } from 'cordis' import { registerCallHook, registerReceiveHook, ReceiveCmdS } from './hook' import { Config as LLOBConfig } from '../common/types' -import { llonebotError } from '../common/globalVars' import { isNumeric } from '../common/utils/misc' import { NTMethod } from './ntcall' import { @@ -13,7 +12,8 @@ import { GroupMember, CategoryFriend, SimpleInfo, - ChatType + ChatType, + BuddyReqType } from './types' import { selfInfo } from '../common/globalVars' import { version } from '../version' @@ -24,25 +24,26 @@ declare module 'cordis' { app: Core } interface Events { - 'nt/message-created': (input: RawMessage[]) => void + 'nt/message-created': (input: RawMessage) => void 'nt/message-deleted': (input: RawMessage) => void 'nt/message-sent': (input: RawMessage) => void - 'nt/group-notify': (input: GroupNotify[]) => void - 'nt/friend-request': (input: FriendRequest[]) => void + 'nt/group-notify': (input: GroupNotify) => void + 'nt/friend-request': (input: FriendRequest) => void 'nt/group-member-info-updated': (input: { groupCode: string, members: GroupMember[] }) => void 'nt/system-message-created': (input: Uint8Array) => void } } class Core extends Service { - static inject = ['ntMsgApi', 'ntFriendApi', 'ntGroupApi'] + static inject = ['ntMsgApi', 'ntFriendApi', 'ntGroupApi', 'store'] + public startTime = 0 constructor(protected ctx: Context, public config: Core.Config) { super(ctx, 'app', true) } public start() { - llonebotError.otherError = '' + this.startTime = Date.now() this.registerListener() this.ctx.logger.info(`LLOneBot/${version}`) this.ctx.on('llonebot/config-updated', input => { @@ -121,7 +122,7 @@ class Core extends Service { this.ctx.ntMsgApi.getMsgHistory(peer, '', 20).then(({ msgList }) => { const lastTempMsg = msgList.at(-1) if (Date.now() / 1000 - Number(lastTempMsg?.msgTime) < 5) { - this.ctx.parallel('nt/message-created', [lastTempMsg!]) + this.ctx.parallel('nt/message-created', lastTempMsg!) } }) }) @@ -161,7 +162,16 @@ class Core extends Service { }) registerReceiveHook<{ msgList: RawMessage[] }>([ReceiveCmdS.NEW_MSG, ReceiveCmdS.NEW_ACTIVE_MSG], payload => { - this.ctx.parallel('nt/message-created', payload.msgList) + for (const message of payload.msgList) { + // 过滤启动之前的消息 + if (parseInt(message.msgTime) < this.startTime / 1000) { + continue + } + if (message.senderUin && message.senderUin !== '0') { + this.ctx.store.addMsgCache(message) + } + this.ctx.parallel('nt/message-created', message) + } }) const sentMsgIds = new Map() @@ -199,20 +209,28 @@ class Core extends Service { } catch (e) { return } - const list = notifies.filter(v => { - const flag = v.group.groupCode + '|' + v.seq + '|' + v.type - if (groupNotifyFlags.includes(flag)) { - return false + for (const notify of notifies) { + const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type + const notifyTime = parseInt(notify.seq) / 1000 + if (groupNotifyFlags.includes(flag) || notifyTime < this.startTime) { + continue } groupNotifyFlags.push(flag) - return true - }) - this.ctx.parallel('nt/group-notify', list) + this.ctx.parallel('nt/group-notify', notify) + } } }) registerReceiveHook(ReceiveCmdS.FRIEND_REQUEST, payload => { - this.ctx.parallel('nt/friend-request', payload.data.buddyReqs) + for (const req of payload.data.buddyReqs) { + if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.MeInitiatorWaitPeerConfirm)) { + continue + } + if (+req.reqTime < this.startTime / 1000) { + continue + } + this.ctx.parallel('nt/friend-request', req) + } }) invoke('nodeIKernelMsgListener/onRecvSysMsg', [], { registerEvent: true }) diff --git a/src/ntqqapi/entities.ts b/src/ntqqapi/entities.ts index ef0671b..64d0bd2 100644 --- a/src/ntqqapi/entities.ts +++ b/src/ntqqapi/entities.ts @@ -23,9 +23,7 @@ import { encodeSilk } from '../common/utils/audio' import { Context } from 'cordis' import { isNullable } from 'cosmokit' -//export const mFaceCache = new Map() // emojiId -> faceName - -export namespace SendElementEntities { +export namespace SendElement { export function text(content: string): SendTextElement { return { elementType: ElementType.Text, diff --git a/src/ntqqapi/types/notify.ts b/src/ntqqapi/types/notify.ts index 3d8362f..cdf5189 100644 --- a/src/ntqqapi/types/notify.ts +++ b/src/ntqqapi/types/notify.ts @@ -56,20 +56,8 @@ export enum GroupRequestOperateTypes { } export enum BuddyReqType { - KMEINITIATOR, - KPEERINITIATOR, - KMEAGREED, - KMEAGREEDANDADDED, - KPEERAGREED, - KPEERAGREEDANDADDED, - KPEERREFUSED, - KMEREFUSED, - KMEIGNORED, - KMEAGREEANYONE, - KMESETQUESTION, - KMEAGREEANDADDFAILED, - KMSGINFO, - KMEINITIATORWAITPEERCONFIRM + MsgInfo = 12, + MeInitiatorWaitPeerConfirm = 13, } export interface FriendRequest { @@ -128,4 +116,4 @@ export interface GroupExtParam { memberIcon: number memberInfoSeq: number } -} \ No newline at end of file +} diff --git a/src/onebot11/action/go-cqhttp/UploadGroupFile.ts b/src/onebot11/action/go-cqhttp/UploadGroupFile.ts index 10ba0e5..72cc3ad 100644 --- a/src/onebot11/action/go-cqhttp/UploadGroupFile.ts +++ b/src/onebot11/action/go-cqhttp/UploadGroupFile.ts @@ -1,6 +1,6 @@ import { BaseAction, Schema } from '../BaseAction' import { ActionName } from '../types' -import { SendElementEntities } from '@/ntqqapi/entities' +import { SendElement } from '@/ntqqapi/entities' import { uri2local } from '@/common/utils' import { sendMsg, createPeer, CreatePeerMode } from '../../helper/createMessage' @@ -27,7 +27,7 @@ export class UploadGroupFile extends BaseAction { if (!success) { throw new Error(errMsg) } - const file = await SendElementEntities.file(this.ctx, path, payload.name || fileName, payload.folder ?? payload.folder_id) + const file = await SendElement.file(this.ctx, path, payload.name || fileName, payload.folder ?? payload.folder_id) const peer = await createPeer(this.ctx, payload, CreatePeerMode.Group) await sendMsg(this.ctx, peer, [file], []) return null diff --git a/src/onebot11/action/go-cqhttp/UploadPrivateFile.ts b/src/onebot11/action/go-cqhttp/UploadPrivateFile.ts index 667b791..7c1c07c 100644 --- a/src/onebot11/action/go-cqhttp/UploadPrivateFile.ts +++ b/src/onebot11/action/go-cqhttp/UploadPrivateFile.ts @@ -1,6 +1,6 @@ import { BaseAction, Schema } from '../BaseAction' import { ActionName } from '../types' -import { SendElementEntities } from '@/ntqqapi/entities' +import { SendElement } from '@/ntqqapi/entities' import { uri2local } from '@/common/utils' import { sendMsg, createPeer, CreatePeerMode } from '../../helper/createMessage' @@ -23,7 +23,7 @@ export class UploadPrivateFile extends BaseAction { peerUid: msgInfo.peer.peerUid, chatType: msgInfo.peer.chatType } - const msg = this.adapter.getMsgCache(msgInfo.msgId) ?? (await this.ctx.ntMsgApi.getMsgsByMsgId(peer, [msgInfo.msgId])).msgList[0] + const msg = this.ctx.store.getMsgCache(msgInfo.msgId) ?? (await this.ctx.ntMsgApi.getMsgsByMsgId(peer, [msgInfo.msgId])).msgList[0] const retMsg = await OB11Entities.message(this.ctx, msg) if (!retMsg) { throw new Error('消息为空') diff --git a/src/onebot11/action/types.ts b/src/onebot11/action/types.ts index 2cb911f..6a5321b 100644 --- a/src/onebot11/action/types.ts +++ b/src/onebot11/action/types.ts @@ -58,7 +58,7 @@ export enum ActionName { GetCookies = 'get_cookies', ForwardFriendSingleMsg = 'forward_friend_single_msg', ForwardGroupSingleMsg = 'forward_group_single_msg', - // 以下为go-cqhttp api + // go-cqhttp GoCQHTTP_SendGroupForwardMsg = 'send_group_forward_msg', GoCQHTTP_SendPrivateForwardMsg = 'send_private_forward_msg', GoCQHTTP_GetStrangerInfo = 'get_stranger_info', diff --git a/src/onebot11/adapter.ts b/src/onebot11/adapter.ts index 5e2b38c..1c60dcd 100644 --- a/src/onebot11/adapter.ts +++ b/src/onebot11/adapter.ts @@ -37,8 +37,6 @@ declare module 'cordis' { class OneBot11Adapter extends Service { static inject = ['ntMsgApi', 'ntFileApi', 'ntFileCacheApi', 'ntFriendApi', 'ntGroupApi', 'ntUserApi', 'ntWindowApi', 'ntWebApi', 'store'] - public messages: Map = new Map() - public startTime = 0 private ob11WebSocket: OB11WebSocket private ob11WebSocketReverseManager: OB11WebSocketReverseManager private ob11Http: OB11Http @@ -74,24 +72,6 @@ class OneBot11Adapter extends Service { }) } - /** 缓存近期消息内容 */ - public async addMsgCache(msg: RawMessage) { - const expire = this.config.msgCacheExpire * 1000 - if (expire === 0) { - return - } - const id = msg.msgId - this.messages.set(id, msg) - setTimeout(() => { - this.messages.delete(id) - }, expire) - } - - /** 获取近期消息内容 */ - public getMsgCache(msgId: string) { - return this.messages.get(msgId) - } - public dispatch(event: OB11BaseEvent | OB11Message) { if (this.config.enableWs) { this.ob11WebSocket.emitEvent(event) @@ -108,115 +88,99 @@ class OneBot11Adapter extends Service { } } - private async handleGroupNotify(notifies: GroupNotify[]) { - for (const notify of notifies) { - try { - const notifyTime = parseInt(notify.seq) / 1000 - if (notifyTime < this.startTime) { - continue - } - const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type - if ([GroupNotifyType.MEMBER_LEAVE_NOTIFY_ADMIN, GroupNotifyType.KICK_MEMBER_NOTIFY_ADMIN].includes(notify.type)) { - this.ctx.logger.info('有成员退出通知', notify) - const member1Uin = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid) - let operatorId = member1Uin - let subType: GroupDecreaseSubType = 'leave' - if (notify.user2.uid) { - // 是被踢的 - const member2Uin = await this.ctx.ntUserApi.getUinByUid(notify.user2.uid) - if (member2Uin) { - operatorId = member2Uin - } - subType = 'kick' + private async handleGroupNotify(notify: GroupNotify) { + try { + const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type + if ([GroupNotifyType.MEMBER_LEAVE_NOTIFY_ADMIN, GroupNotifyType.KICK_MEMBER_NOTIFY_ADMIN].includes(notify.type)) { + this.ctx.logger.info('有成员退出通知', notify) + const member1Uin = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid) + let operatorId = member1Uin + let subType: GroupDecreaseSubType = 'leave' + if (notify.user2.uid) { + // 是被踢的 + const member2Uin = await this.ctx.ntUserApi.getUinByUid(notify.user2.uid) + if (member2Uin) { + operatorId = member2Uin } - const event = new OB11GroupDecreaseEvent( - parseInt(notify.group.groupCode), - parseInt(member1Uin), - parseInt(operatorId), - subType, - ) - this.dispatch(event) + subType = 'kick' } - else if (notify.type === GroupNotifyType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS && notify.status === GroupNotifyStatus.KUNHANDLE) { - this.ctx.logger.info('有加群请求') - const requestUin = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid) - const event = new OB11GroupRequestEvent( - parseInt(notify.group.groupCode), - parseInt(requestUin) || 0, - flag, - notify.postscript, - ) - this.dispatch(event) - } - else if (notify.type === GroupNotifyType.INVITED_BY_MEMBER && notify.status === GroupNotifyStatus.KUNHANDLE) { - this.ctx.logger.info('收到邀请我加群通知') - const userId = await this.ctx.ntUserApi.getUinByUid(notify.user2.uid) - const event = new OB11GroupRequestEvent( - parseInt(notify.group.groupCode), - parseInt(userId) || 0, - flag, - notify.postscript, - undefined, - 'invite' - ) - this.dispatch(event) - } - else if (notify.type === GroupNotifyType.INVITED_NEED_ADMINI_STRATOR_PASS && notify.status === GroupNotifyStatus.KUNHANDLE) { - this.ctx.logger.info('收到群员邀请加群通知') - const userId = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid) - const event = new OB11GroupRequestEvent( - parseInt(notify.group.groupCode), - parseInt(userId) || 0, - flag, - notify.postscript - ) - this.dispatch(event) - } - } catch (e) { - this.ctx.logger.error('解析群通知失败', (e as Error).stack) + const event = new OB11GroupDecreaseEvent( + parseInt(notify.group.groupCode), + parseInt(member1Uin), + parseInt(operatorId), + subType, + ) + this.dispatch(event) } + else if (notify.type === GroupNotifyType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS && notify.status === GroupNotifyStatus.KUNHANDLE) { + this.ctx.logger.info('有加群请求') + const requestUin = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid) + const event = new OB11GroupRequestEvent( + parseInt(notify.group.groupCode), + parseInt(requestUin) || 0, + flag, + notify.postscript, + ) + this.dispatch(event) + } + else if (notify.type === GroupNotifyType.INVITED_BY_MEMBER && notify.status === GroupNotifyStatus.KUNHANDLE) { + this.ctx.logger.info('收到邀请我加群通知') + const userId = await this.ctx.ntUserApi.getUinByUid(notify.user2.uid) + const event = new OB11GroupRequestEvent( + parseInt(notify.group.groupCode), + parseInt(userId) || 0, + flag, + notify.postscript, + undefined, + 'invite' + ) + this.dispatch(event) + } + else if (notify.type === GroupNotifyType.INVITED_NEED_ADMINI_STRATOR_PASS && notify.status === GroupNotifyStatus.KUNHANDLE) { + this.ctx.logger.info('收到群员邀请加群通知') + const userId = await this.ctx.ntUserApi.getUinByUid(notify.user1.uid) + const event = new OB11GroupRequestEvent( + parseInt(notify.group.groupCode), + parseInt(userId) || 0, + flag, + notify.postscript + ) + this.dispatch(event) + } + } catch (e) { + this.ctx.logger.error('解析群通知失败', (e as Error).stack) } } - private handleMsg(msgList: RawMessage[]) { - for (const message of msgList) { - // 过滤启动之前的消息 - if (parseInt(message.msgTime) < this.startTime / 1000) { - continue + private handleMsg(message: RawMessage) { + OB11Entities.message(this.ctx, message).then(msg => { + if (!msg) { + return } - this.addMsgCache(message) + if (!this.config.debug && msg.message.length === 0) { + return + } + const isSelfMsg = msg.user_id.toString() === selfInfo.uin + if (isSelfMsg && !this.config.reportSelfMessage) { + return + } + if (isSelfMsg) { + msg.target_id = parseInt(message.peerUin) + } + this.dispatch(msg) + }).catch(e => this.ctx.logger.error('constructMessage error: ', e.stack.toString())) - OB11Entities.message(this.ctx, message) - .then((msg) => { - if (!msg) { - return - } - if (!this.config.debug && msg.message.length === 0) { - return - } - const isSelfMsg = msg.user_id.toString() === selfInfo.uin - if (isSelfMsg && !this.config.reportSelfMessage) { - return - } - if (isSelfMsg) { - msg.target_id = parseInt(message.peerUin) - } - this.dispatch(msg) - }) - .catch((e) => this.ctx.logger.error('constructMessage error: ', e.stack.toString())) + OB11Entities.groupEvent(this.ctx, message).then(groupEvent => { + if (groupEvent) { + this.dispatch(groupEvent) + } + }) - OB11Entities.groupEvent(this.ctx, message).then((groupEvent) => { - if (groupEvent) { - this.dispatch(groupEvent) - } - }) - - OB11Entities.privateEvent(this.ctx, message).then((privateEvent) => { - if (privateEvent) { - this.dispatch(privateEvent) - } - }) - } + OB11Entities.privateEvent(this.ctx, message).then(privateEvent => { + if (privateEvent) { + this.dispatch(privateEvent) + } + }) } private handleRecallMsg(message: RawMessage) { @@ -235,30 +199,22 @@ class OneBot11Adapter extends Service { }) } - private async handleFriendRequest(buddyReqs: FriendRequest[]) { - for (const req of buddyReqs) { - if (!!req.isInitiator || (req.isDecide && req.reqType !== BuddyReqType.KMEINITIATORWAITPEERCONFIRM)) { - continue - } - if (+req.reqTime < this.startTime / 1000) { - continue - } - let userId = 0 - try { - const requesterUin = await this.ctx.ntUserApi.getUinByUid(req.friendUid) - userId = parseInt(requesterUin) - } catch (e) { - this.ctx.logger.error('获取加好友者QQ号失败', e) - } - const flag = req.friendUid + '|' + req.reqTime - const comment = req.extWords - const friendRequestEvent = new OB11FriendRequestEvent( - userId, - comment, - flag - ) - this.dispatch(friendRequestEvent) + private async handleFriendRequest(req: FriendRequest) { + let userId = 0 + try { + const requesterUin = await this.ctx.ntUserApi.getUinByUid(req.friendUid) + userId = parseInt(requesterUin) + } catch (e) { + this.ctx.logger.error('获取加好友者QQ号失败', e) } + const flag = req.friendUid + '|' + req.reqTime + const comment = req.extWords + const friendRequestEvent = new OB11FriendRequestEvent( + userId, + comment, + flag + ) + this.dispatch(friendRequestEvent) } private async handleConfigUpdated(config: LLOBConfig) { @@ -374,7 +330,6 @@ class OneBot11Adapter extends Service { } public start() { - this.startTime = Date.now() if (this.config.enableWs) { this.ob11WebSocket.start() } @@ -397,7 +352,7 @@ class OneBot11Adapter extends Service { this.handleRecallMsg(input) }) this.ctx.on('nt/message-sent', input => { - this.handleMsg([input]) + this.handleMsg(input) }) this.ctx.on('nt/group-notify', input => { this.handleGroupNotify(input) diff --git a/src/onebot11/entities.ts b/src/onebot11/entities.ts index daffca6..499ca76 100644 --- a/src/onebot11/entities.ts +++ b/src/onebot11/entities.ts @@ -338,7 +338,6 @@ export namespace OB11Entities { key: marketFaceElement.key } } - //mFaceCache.set(emojiId, element.marketFaceElement.faceName!) } else if (element.markdownElement) { const { markdownElement } = element diff --git a/src/onebot11/helper/createMessage.ts b/src/onebot11/helper/createMessage.ts index b8b08c6..7bbb6a3 100644 --- a/src/onebot11/helper/createMessage.ts +++ b/src/onebot11/helper/createMessage.ts @@ -15,7 +15,7 @@ import { } from '../types' import { decodeCQCode } from '../cqcode' import { Peer } from '@/ntqqapi/types/msg' -import { SendElementEntities } from '@/ntqqapi/entities' +import { SendElement } from '@/ntqqapi/entities' import { selfInfo } from '@/common/globalVars' import { uri2local } from '@/common/utils' import { Context } from 'cordis' @@ -36,7 +36,7 @@ export async function createSendElements( case OB11MessageDataType.text: { const text = sendMsg.data?.text if (text) { - sendElements.push(SendElementEntities.text(sendMsg.data!.text)) + sendElements.push(SendElement.text(sendMsg.data!.text)) } } break @@ -62,7 +62,7 @@ export async function createSendElements( } } if (isAdmin && remainAtAllCount > 0) { - sendElements.push(SendElementEntities.at(atQQ, atQQ, AtType.All, '@全体成员')) + sendElements.push(SendElement.at(atQQ, atQQ, AtType.All, '@全体成员')) } } else if (peer.chatType === ChatType.Group) { @@ -70,14 +70,14 @@ export async function createSendElements( if (atMember) { const display = `@${atMember.cardName || atMember.nick}` sendElements.push( - SendElementEntities.at(atQQ, atMember.uid, AtType.One, display), + SendElement.at(atQQ, atMember.uid, AtType.One, display), ) } else { const atNmae = sendMsg.data?.name const uid = await ctx.ntUserApi.getUidByUin(atQQ) || '' const display = atNmae ? `@${atNmae}` : '' sendElements.push( - SendElementEntities.at(atQQ, uid, AtType.One, display), + SendElement.at(atQQ, uid, AtType.One, display), ) } } @@ -97,7 +97,7 @@ export async function createSendElements( )).msgList[0] if (replyMsg) { sendElements.push( - SendElementEntities.reply( + SendElement.reply( replyMsg.msgSeq, replyMsg.msgId, replyMsg.senderUin!, @@ -111,13 +111,13 @@ export async function createSendElements( case OB11MessageDataType.face: { const faceId = sendMsg.data?.id if (faceId) { - sendElements.push(SendElementEntities.face(parseInt(faceId))) + sendElements.push(SendElement.face(parseInt(faceId))) } } break case OB11MessageDataType.mface: { sendElements.push( - SendElementEntities.mface( + SendElement.mface( +sendMsg.data.emoji_package_id, sendMsg.data.emoji_id, sendMsg.data.key, @@ -127,7 +127,7 @@ export async function createSendElements( } break case OB11MessageDataType.image: { - const res = await SendElementEntities.pic( + const res = await SendElement.pic( ctx, (await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles })).path, sendMsg.data.summary || '', @@ -140,7 +140,7 @@ export async function createSendElements( break case OB11MessageDataType.file: { const { path, fileName } = await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles }) - sendElements.push(await SendElementEntities.file(ctx, path, fileName)) + sendElements.push(await SendElement.file(ctx, path, fileName)) } break case OB11MessageDataType.video: { @@ -150,38 +150,38 @@ export async function createSendElements( const uri2LocalRes = await uri2local(thumb) if (uri2LocalRes.success) thumb = uri2LocalRes.path } - const res = await SendElementEntities.video(ctx, path, fileName, thumb) + const res = await SendElement.video(ctx, path, fileName, thumb) deleteAfterSentFiles.push(res.videoElement.filePath) sendElements.push(res) } break case OB11MessageDataType.voice: { const { path } = await handleOb11FileLikeMessage(ctx, sendMsg, { deleteAfterSentFiles }) - sendElements.push(await SendElementEntities.ptt(ctx, path)) + sendElements.push(await SendElement.ptt(ctx, path)) } break case OB11MessageDataType.json: { - sendElements.push(SendElementEntities.ark(sendMsg.data.data)) + sendElements.push(SendElement.ark(sendMsg.data.data)) } break case OB11MessageDataType.dice: { const resultId = sendMsg.data?.result - sendElements.push(SendElementEntities.dice(resultId)) + sendElements.push(SendElement.dice(resultId)) } break case OB11MessageDataType.RPS: { const resultId = sendMsg.data?.result - sendElements.push(SendElementEntities.rps(resultId)) + sendElements.push(SendElement.rps(resultId)) } break case OB11MessageDataType.contact: { const { type, id } = sendMsg.data const data = type === 'qq' ? ctx.ntFriendApi.getBuddyRecommendContact(id) : ctx.ntGroupApi.getGroupRecommendContact(id) - sendElements.push(SendElementEntities.ark(await data)) + sendElements.push(SendElement.ark(await data)) } break case OB11MessageDataType.shake: { - sendElements.push(SendElementEntities.shake()) + sendElements.push(SendElement.shake()) } break } From 496d56f2974db8bf8fbe7c508ad5d0bb2a507a44 Mon Sep 17 00:00:00 2001 From: idranme Date: Mon, 30 Sep 2024 00:49:58 +0800 Subject: [PATCH 2/5] feat --- src/ntqqapi/api/msg.ts | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/ntqqapi/api/msg.ts b/src/ntqqapi/api/msg.ts index e119444..dfae31f 100644 --- a/src/ntqqapi/api/msg.ts +++ b/src/ntqqapi/api/msg.ts @@ -61,7 +61,7 @@ export class NTQQMsgApi extends Service { return await invoke('nodeIKernelMsgService/getAioFirstViewLatestMsgs', [{ peer, cnt }, null]) } - async getMsgsByMsgId(peer: Peer | undefined, msgIds: string[] | undefined) { + async getMsgsByMsgId(peer: Peer, msgIds: string[]) { if (!peer) throw new Error('peer is not allowed') if (!msgIds) throw new Error('msgIds is not allowed') const session = getSession() @@ -275,4 +275,27 @@ export class NTQQMsgApi extends Service { return `${Date.now()}${random}` } } + + async queryMsgsById(chatType: ChatType, msgId: string) { + const msgTime = this.getMsgTimeFromId(msgId) + return await invoke('nodeIKernelMsgService/queryMsgsWithFilterEx', [{ + msgId, + msgTime: '0', + msgSeq: '0', + params: { + chatInfo: { + peerUid: '', + chatType + }, + filterMsgToTime: msgTime, + filterMsgFromTime: msgTime, + isIncludeCurrent: true, + pageLimit: 1, + } + }, null]) + } + + getMsgTimeFromId(msgId: string) { + return String(BigInt(msgId) >> 32n) + } } From a7d86f8fe06d961837b0af6cd61748f8951c5d75 Mon Sep 17 00:00:00 2001 From: idranme Date: Tue, 1 Oct 2024 21:09:27 +0800 Subject: [PATCH 3/5] refactor --- src/common/utils/legacyLog.ts | 22 +--- src/ntqqapi/api/file.ts | 44 +++---- src/ntqqapi/api/friend.ts | 12 +- src/ntqqapi/api/msg.ts | 121 ++++++++--------- src/ntqqapi/api/user.ts | 123 ++++++++++-------- src/ntqqapi/helper/rkey.ts | 24 +--- src/ntqqapi/hook.ts | 3 +- src/ntqqapi/ntcall.ts | 5 +- src/ntqqapi/services/NodeIKernelMsgService.ts | 6 +- .../services/NodeIKernelRobotService.ts | 13 ++ src/ntqqapi/services/index.ts | 1 + src/ntqqapi/types/user.ts | 7 +- src/onebot11/action/msg/ForwardSingleMsg.ts | 4 +- src/onebot11/action/msg/GetMsg.ts | 3 - .../event/notice/OB11MsgEmojiLikeEvent.ts | 4 +- 15 files changed, 178 insertions(+), 214 deletions(-) create mode 100644 src/ntqqapi/services/NodeIKernelRobotService.ts diff --git a/src/common/utils/legacyLog.ts b/src/common/utils/legacyLog.ts index 217c9e8..0010d2f 100644 --- a/src/common/utils/legacyLog.ts +++ b/src/common/utils/legacyLog.ts @@ -2,24 +2,7 @@ import fs from 'fs' import path from 'node:path' import { getConfigUtil } from '../config' import { LOG_DIR } from '../globalVars' -import { Dict } from 'cosmokit' - -function truncateString(obj: Dict | null, maxLength = 500) { - if (obj !== null && typeof obj === 'object') { - Object.keys(obj).forEach((key) => { - if (typeof obj[key] === 'string') { - // 如果是字符串且超过指定长度,则截断 - if (obj[key].length > maxLength) { - obj[key] = obj[key].substring(0, maxLength) + '...' - } - } else if (typeof obj[key] === 'object') { - // 如果是对象或数组,则递归调用 - truncateString(obj[key], maxLength) - } - }) - } - return obj -} +import { inspect } from 'node:util' export const logFileName = `llonebot-${new Date().toLocaleString('zh-CN')}.log`.replace(/\//g, '-').replace(/:/g, '-') @@ -29,9 +12,8 @@ export function log(...msg: unknown[]) { } let logMsg = '' for (const msgItem of msg) { - // 判断是否是对象 if (typeof msgItem === 'object') { - logMsg += JSON.stringify(truncateString(msgItem)) + ' ' + logMsg += inspect(msgItem, { depth: 10, compact: true, breakLength: Infinity }) + ' ' continue } logMsg += msgItem + ' ' diff --git a/src/ntqqapi/api/file.ts b/src/ntqqapi/api/file.ts index d2ff5af..5ec5d8e 100644 --- a/src/ntqqapi/api/file.ts +++ b/src/ntqqapi/api/file.ts @@ -16,7 +16,6 @@ import path from 'node:path' import { existsSync } from 'node:fs' import { ReceiveCmdS } from '../hook' import { RkeyManager } from '@/ntqqapi/helper/rkey' -import { getSession } from '@/ntqqapi/wrapper' import { OnRichMediaDownloadCompleteParams, Peer } from '@/ntqqapi/types/msg' import { calculateFileMD5 } from '@/common/utils/file' import { fileTypeFromFile } from 'file-type' @@ -39,39 +38,28 @@ export class NTQQFileApi extends Service { this.rkeyManager = new RkeyManager(ctx, 'https://llob.linyuchen.net/rkey') } - async getVideoUrl(peer: Peer, msgId: string, elementId: string) { - const session = getSession() - if (session) { - return (await session.getRichMediaService().getVideoPlayUrlV2( - peer, - msgId, - elementId, - 0, - { downSourceType: 1, triggerType: 1 } - )).urlResult.domainUrl[0]?.url - } else { - const data = await invoke('nodeIKernelRichMediaService/getVideoPlayUrlV2', [{ - peer, - msgId, - elemId: elementId, - videoCodecFormat: 0, - exParams: { - downSourceType: 1, - triggerType: 1 - }, - }, null]) - if (data.result !== 0) { - this.ctx.logger.warn('getVideoUrl', data) + async getVideoUrl(peer: Peer, msgId: string, elementId: string): Promise { + const data = await invoke('nodeIKernelRichMediaService/getVideoPlayUrlV2', [{ + peer, + msgId, + elemId: elementId, + videoCodecFormat: 0, + params: { + downSourceType: 1, + triggerType: 1 } - return data.urlResult.domainUrl[0]?.url + }]) + if (data.result !== 0) { + this.ctx.logger.warn('getVideoUrl', data) } + return data.urlResult.domainUrl[0]?.url } async getFileType(filePath: string) { return fileTypeFromFile(filePath) } - // 上传文件到QQ的文件夹 + /** 上传文件到 QQ 的文件夹 */ async uploadFile(filePath: string, elementType = ElementType.Pic, elementSubType = 0) { const fileMd5 = await calculateFileMD5(filePath) let fileName = path.basename(filePath) @@ -174,8 +162,8 @@ export class NTQQFileApi extends Service { if (url) { const parsedUrl = new URL(IMAGE_HTTP_HOST + url) //临时解析拼接 const imageAppid = parsedUrl.searchParams.get('appid') - const isNewPic = imageAppid && ['1406', '1407'].includes(imageAppid) - if (isNewPic) { + const isNTPic = imageAppid && ['1406', '1407'].includes(imageAppid) + if (isNTPic) { let rkey = parsedUrl.searchParams.get('rkey') if (rkey) { return IMAGE_HTTP_HOST_NT + url diff --git a/src/ntqqapi/api/friend.ts b/src/ntqqapi/api/friend.ts index fd65f5a..3c92ae3 100644 --- a/src/ntqqapi/api/friend.ts +++ b/src/ntqqapi/api/friend.ts @@ -1,8 +1,7 @@ -import { Friend, FriendV2, SimpleInfo, CategoryFriend, BuddyListReqType } from '../types' +import { Friend, SimpleInfo, CategoryFriend } from '../types' import { ReceiveCmdS } from '../hook' import { invoke, NTMethod, NTClass } from '../ntcall' import { getSession } from '@/ntqqapi/wrapper' -import { Dict, pick } from 'cosmokit' import { Service, Context } from 'cordis' declare module 'cordis' { @@ -60,7 +59,7 @@ export class NTQQFriendApi extends Service { } } - async getBuddyV2(refresh = false): Promise { + async getBuddyV2(refresh = false): Promise { const data = await invoke<{ buddyCategory: CategoryFriend[] userSimpleInfos: Record @@ -118,12 +117,7 @@ export class NTQQFriendApi extends Service { } async isBuddy(uid: string): Promise { - const session = getSession() - if (session) { - return session.getBuddyService().isBuddy(uid) - } else { - return await invoke('nodeIKernelBuddyService/isBuddy', [{ uid }, null]) - } + return await invoke('nodeIKernelBuddyService/isBuddy', [{ uid }]) } async getBuddyRecommendContact(uin: string) { diff --git a/src/ntqqapi/api/msg.ts b/src/ntqqapi/api/msg.ts index dfae31f..4207a10 100644 --- a/src/ntqqapi/api/msg.ts +++ b/src/ntqqapi/api/msg.ts @@ -27,17 +27,12 @@ export class NTQQMsgApi extends Service { } } - async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, setEmoji: boolean = true) { + async setEmojiLike(peer: Peer, msgSeq: string, emojiId: string, setEmoji: boolean) { // nt_qq//global//nt_data//Emoji//emoji-resource//sysface_res/apng/ 下可以看到所有QQ表情预览 // nt_qq\global\nt_data\Emoji\emoji-resource\face_config.json 里面有所有表情的id, 自带表情id是QSid, 标准emoji表情id是QCid // 其实以官方文档为准是最好的,https://bot.q.qq.com/wiki/develop/api-v2/openapi/emoji/model.html#EmojiType - const session = getSession() const emojiType = emojiId.length > 3 ? '2' : '1' - if (session) { - return session.getMsgService().setMsgEmojiLikes(peer, msgSeq, emojiId, emojiType, setEmoji) - } else { - return await invoke(NTMethod.EMOJI_LIKE, [{ peer, msgSeq, emojiId, emojiType, setEmoji }, null]) - } + return await invoke(NTMethod.EMOJI_LIKE, [{ peer, msgSeq, emojiId, emojiType, setEmoji }]) } async getMultiMsg(peer: Peer, rootMsgId: string, parentMsgId: string) { @@ -58,59 +53,41 @@ export class NTQQMsgApi extends Service { } async getAioFirstViewLatestMsgs(peer: Peer, cnt: number) { - return await invoke('nodeIKernelMsgService/getAioFirstViewLatestMsgs', [{ peer, cnt }, null]) + return await invoke('nodeIKernelMsgService/getAioFirstViewLatestMsgs', [{ peer, cnt }]) } async getMsgsByMsgId(peer: Peer, msgIds: string[]) { if (!peer) throw new Error('peer is not allowed') if (!msgIds) throw new Error('msgIds is not allowed') - const session = getSession() - if (session) { - return session.getMsgService().getMsgsByMsgId(peer, msgIds) - } else { - return await invoke('nodeIKernelMsgService/getMsgsByMsgId', [{ peer, msgIds }, null]) - } + return await invoke('nodeIKernelMsgService/getMsgsByMsgId', [{ peer, msgIds }]) } async getMsgHistory(peer: Peer, msgId: string, cnt: number, isReverseOrder: boolean = false) { - const session = getSession() // 消息时间从旧到新 - if (session) { - return session.getMsgService().getMsgsIncludeSelf(peer, msgId, cnt, isReverseOrder) - } else { - return await invoke(NTMethod.HISTORY_MSG, [{ peer, msgId, cnt, queryOrder: isReverseOrder }, null]) - } + return await invoke(NTMethod.HISTORY_MSG, [{ peer, msgId, cnt, queryOrder: isReverseOrder }]) } async recallMsg(peer: Peer, msgIds: string[]) { - const session = getSession() - if (session) { - return session.getMsgService().recallMsg(peer, msgIds) - } else { - return await invoke(NTMethod.RECALL_MSG, [{ peer, msgIds }, null]) - } + return await invoke(NTMethod.RECALL_MSG, [{ peer, msgIds }]) } async sendMsg(peer: Peer, msgElements: SendMessageElement[], timeout = 10000) { - const msgId = await this.generateMsgUniqueId(peer.chatType) - peer.guildId = msgId + const uniqueId = await this.generateMsgUniqueId(peer.chatType) + peer.guildId = uniqueId const data = await invoke<{ msgList: RawMessage[] }>( 'nodeIKernelMsgService/sendMsg', - [ - { - msgId: '0', - peer, - msgElements, - msgAttributeInfos: new Map() - }, - null - ], + [{ + msgId: '0', + peer, + msgElements, + msgAttributeInfos: new Map() + }], { cbCmd: 'nodeIKernelMsgListener/onMsgInfoListUpdate', afterFirstCmd: false, cmdCB: payload => { for (const msgRecord of payload.msgList) { - if (msgRecord.guildId === msgId && msgRecord.sendStatus === 2) { + if (msgRecord.guildId === uniqueId && msgRecord.sendStatus === 2) { return true } } @@ -119,22 +96,35 @@ export class NTQQMsgApi extends Service { timeout } ) - return data.msgList.find(msgRecord => msgRecord.guildId === msgId) + return data.msgList.find(msgRecord => msgRecord.guildId === uniqueId) } async forwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]) { - const session = getSession() - if (session) { - return session.getMsgService().forwardMsg(msgIds, srcPeer, [destPeer], []) - } else { - return await invoke(NTMethod.FORWARD_MSG, [{ + const uniqueId = await this.generateMsgUniqueId(destPeer.chatType) + destPeer.guildId = uniqueId + const data = await invoke<{ msgList: RawMessage[] }>( + 'nodeIKernelMsgService/forwardMsg', + [{ msgIds, srcContact: srcPeer, dstContacts: [destPeer], commentElements: [], msgAttributeInfos: new Map(), - }, null]) - } + }], + { + cbCmd: 'nodeIKernelMsgListener/onMsgInfoListUpdate', + afterFirstCmd: false, + cmdCB: payload => { + for (const msgRecord of payload.msgList) { + if (msgRecord.guildId === uniqueId && msgRecord.sendStatus === 2) { + return true + } + } + return false + } + } + ) + return data.msgList.filter(msgRecord => msgRecord.guildId === uniqueId) } async multiForwardMsg(srcPeer: Peer, destPeer: Peer, msgIds: string[]): Promise { @@ -145,27 +135,24 @@ export class NTQQMsgApi extends Service { const selfUid = selfInfo.uid const data = await invoke<{ msgList: RawMessage[] }>( 'nodeIKernelMsgService/multiForwardMsgWithComment', - [ - { - msgInfos, - srcContact: srcPeer, - dstContact: destPeer, - commentElements: [], - msgAttributeInfos: new Map(), - }, - null, - ], + [{ + msgInfos, + srcContact: srcPeer, + dstContact: destPeer, + commentElements: [], + msgAttributeInfos: new Map(), + }], { cbCmd: 'nodeIKernelMsgListener/onMsgInfoListUpdate', afterFirstCmd: false, cmdCB: payload => { for (const msgRecord of payload.msgList) { - if (msgRecord.peerUid == destPeer.peerUid && msgRecord.senderUid == selfUid) { + if (msgRecord.peerUid === destPeer.peerUid && msgRecord.senderUid === selfUid) { return true } } return false - }, + } } ) for (const msg of data.msgList) { @@ -174,10 +161,10 @@ export class NTQQMsgApi extends Service { continue } const forwardData = JSON.parse(arkElement.arkElement!.bytesData) - if (forwardData.app != 'com.tencent.multimsg') { + if (forwardData.app !== 'com.tencent.multimsg') { continue } - if (msg.peerUid == destPeer.peerUid && msg.senderUid == selfUid) { + if (msg.peerUid === destPeer.peerUid && msg.senderUid === selfUid) { return msg } } @@ -240,7 +227,7 @@ export class NTQQMsgApi extends Service { isIncludeCurrent: true, pageLimit: 1, } - }, null]) + }]) } async setMsgRead(peer: Peer) { @@ -254,7 +241,7 @@ export class NTQQMsgApi extends Service { emojiId, emojiType, cnt: count - }, null]) + }]) } async fetchFavEmojiList(count: number) { @@ -267,7 +254,8 @@ export class NTQQMsgApi extends Service { } async generateMsgUniqueId(chatType: number) { - const uniqueId = await invoke('nodeIKernelMsgService/generateMsgUniqueId', [{ chatType }]) + const time = await this.getServerTime() + const uniqueId = await invoke('nodeIKernelMsgService/generateMsgUniqueId', [{ chatType, time }]) if (typeof uniqueId === 'string') { return uniqueId } else { @@ -292,10 +280,15 @@ export class NTQQMsgApi extends Service { isIncludeCurrent: true, pageLimit: 1, } - }, null]) + }]) } getMsgTimeFromId(msgId: string) { + // 小概率相差1毫秒 return String(BigInt(msgId) >> 32n) } + + async getServerTime() { + return await invoke('nodeIKernelMSFService/getServerTime', [null]) + } } diff --git a/src/ntqqapi/api/user.ts b/src/ntqqapi/api/user.ts index ccea3ca..22cce9f 100644 --- a/src/ntqqapi/api/user.ts +++ b/src/ntqqapi/api/user.ts @@ -1,9 +1,9 @@ -import { User, UserDetailInfoByUin, UserDetailInfoByUinV2, UserDetailInfoListenerArg, UserDetailSource, ProfileBizType } from '../types' +import { User, UserDetailInfoByUin, UserDetailInfoByUinV2, UserDetailInfo, UserDetailSource, ProfileBizType, SimpleInfo } from '../types' import { invoke } from '../ntcall' import { getBuildVersion } from '@/common/utils' import { getSession } from '@/ntqqapi/wrapper' import { RequestUtil } from '@/common/utils/request' -import { Time } from 'cosmokit' +import { isNullable, Time } from 'cosmokit' import { Service, Context } from 'cordis' import { selfInfo } from '@/common/globalVars' @@ -34,17 +34,14 @@ export class NTQQUserApi extends Service { } async fetchUserDetailInfo(uid: string) { - const result = await invoke<{ info: UserDetailInfoListenerArg }>( + const result = await invoke<{ info: UserDetailInfo }>( 'nodeIKernelProfileService/fetchUserDetailInfo', - [ - { - callFrom: 'BuddyProfileStore', - uid: [uid], - source: UserDetailSource.KSERVER, - bizList: [ProfileBizType.KALL] - }, - null - ], + [{ + callFrom: 'BuddyProfileStore', + uid: [uid], + source: UserDetailSource.KSERVER, + bizList: [ProfileBizType.KALL] + }], { cbCmd: 'nodeIKernelProfileListener/onUserDetailInfoChanged', afterFirstCmd: false, @@ -70,13 +67,10 @@ export class NTQQUserApi extends Service { } const result = await invoke<{ info: User }>( 'nodeIKernelProfileService/getUserDetailInfoWithBizInfo', - [ - { - uid, - bizList: [0] - }, - null, - ], + [{ + uid, + bizList: [0] + }], { cbCmd: 'nodeIKernelProfileListener/onProfileDetailInfoChanged', afterFirstCmd: false, @@ -129,9 +123,7 @@ export class NTQQUserApi extends Service { } async getUidByUinV1(uin: string) { - const session = getSession() - // 通用转换开始尝试 - let uid = (await session?.getUixConvertService().getUid([uin]))?.uidInfo.get(uin) + let uid = (await invoke('nodeIKernelUixConvertService/getUid', [{ uins: [uin] }])).uidInfo.get(uin) if (!uid) { for (const membersList of this.ctx.ntGroupApi.groupMembers.values()) { //从群友列表转 for (const member of membersList.values()) { @@ -181,20 +173,14 @@ export class NTQQUserApi extends Service { async getUserDetailInfoByUinV2(uin: string) { return await invoke( 'nodeIKernelProfileService/getUserDetailInfoByUin', - [ - { uin }, - null, - ], + [{ uin }] ) } async getUserDetailInfoByUin(uin: string) { return await invoke( 'nodeIKernelProfileService/getUserDetailInfoByUin', - [ - { uin }, - null, - ], + [{ uin }] ) } @@ -202,31 +188,21 @@ export class NTQQUserApi extends Service { const ret = await invoke('nodeIKernelUixConvertService/getUin', [{ uids: [uid] }]) let uin = ret.uinInfo.get(uid) if (!uin) { - uin = (await this.getUserDetailInfo(uid)).uin //从QQ Native 转换 + uin = (await this.getUserDetailInfo(uid)).uin } return uin } async getUinByUidV2(uid: string) { - const session = getSession() - if (session) { - let uin = (await session.getGroupService().getUinByUids([uid])).uins.get(uid) - if (uin) return uin - uin = (await session.getProfileService().getUinByUid('FriendsServiceImpl', [uid])).get(uid) - if (uin) return uin - uin = (await session.getUixConvertService().getUin([uid])).uinInfo.get(uid) - if (uin) return uin - } else { - let uin = (await invoke('nodeIKernelGroupService/getUinByUids', [{ uid: [uid] }])).uins.get(uid) - if (uin) return uin - uin = (await invoke('nodeIKernelProfileService/getUinByUid', [{ callFrom: 'FriendsServiceImpl', uid: [uid] }])).get(uid) - if (uin) return uin - uin = (await invoke('nodeIKernelUixConvertService/getUin', [{ uids: [uid] }])).uinInfo.get(uid) - if (uin) return uin - } - let uin = (await this.ctx.ntFriendApi.getBuddyIdMap(true)).get(uid) + let uin = (await invoke('nodeIKernelGroupService/getUinByUids', [{ uid: [uid] }])).uins.get(uid) if (uin) return uin - uin = (await this.getUserDetailInfo(uid)).uin //从QQ Native 转换 + uin = (await invoke('nodeIKernelProfileService/getUinByUid', [{ callFrom: 'FriendsServiceImpl', uid: [uid] }])).get(uid) + if (uin) return uin + uin = (await invoke('nodeIKernelUixConvertService/getUin', [{ uids: [uid] }])).uinInfo.get(uid) + if (uin) return uin + uin = (await this.ctx.ntFriendApi.getBuddyIdMap(true)).get(uid) + if (uin) return uin + uin = (await this.getUserDetailInfo(uid)).uin return uin } @@ -246,13 +222,10 @@ export class NTQQUserApi extends Service { } } - async getSelfNick(refresh = false) { + async getSelfNick(refresh = true) { if ((refresh || !selfInfo.nick) && selfInfo.uid) { - const userInfo = await this.getUserDetailInfo(selfInfo.uid) - if (userInfo) { - Object.assign(selfInfo, { nick: userInfo.nick }) - return userInfo.nick - } + const { profiles } = await this.getUserSimpleInfo(selfInfo.uid) + selfInfo.nick = profiles[selfInfo.uid].coreInfo.nick } return selfInfo.nick } @@ -281,4 +254,44 @@ export class NTQQUserApi extends Service { } }, null]) } + + async getUserSimpleInfo(uid: string, force = true) { + return await invoke<{ profiles: Record }>( + 'nodeIKernelProfileService/getUserSimpleInfo', + [{ + uids: [uid], + force + }], + { + cbCmd: 'onProfileSimpleChanged', + afterFirstCmd: false, + cmdCB: payload => !isNullable(payload.profiles[uid]), + } + ) + } + + async getCoreAndBaseInfo(uids: string[]) { + return await invoke( + 'nodeIKernelProfileService/getCoreAndBaseInfo', + [{ + uids, + callFrom: 'nodeStore' + }] + ) + } + + async getRobotUinRange() { + const data = await invoke( + 'nodeIKernelRobotService/getRobotUinRange', + [{ + req: { + justFetchMsgConfig: '1', + type: 1, + version: 0, + aioKeywordVersion: 0 + } + }] + ) + return data.response.robotUinRanges + } } diff --git a/src/ntqqapi/helper/rkey.ts b/src/ntqqapi/helper/rkey.ts index afd091b..726a009 100644 --- a/src/ntqqapi/helper/rkey.ts +++ b/src/ntqqapi/helper/rkey.ts @@ -31,30 +31,18 @@ export class RkeyManager { isExpired(): boolean { const now = new Date().getTime() / 1000 - // console.log(`now: ${now}, expired_time: ${this.rkeyData.expired_time}`) return now > this.rkeyData.expired_time } async refreshRkey() { - //刷新rkey this.rkeyData = await this.fetchServerRkey() } - async fetchServerRkey() { - return new Promise((resolve, reject) => { - fetch(this.serverUrl) - .then(response => { - if (!response.ok) { - return reject(response.statusText) // 请求失败,返回错误信息 - } - return response.json() // 解析 JSON 格式的响应体 - }) - .then(data => { - resolve(data) - }) - .catch(error => { - reject(error) - }) - }) + async fetchServerRkey(): Promise { + const response = await fetch(this.serverUrl) + if (!response.ok) { + throw new Error(response.statusText) + } + return response.json() } } diff --git a/src/ntqqapi/hook.ts b/src/ntqqapi/hook.ts index f973183..9e7ddea 100644 --- a/src/ntqqapi/hook.ts +++ b/src/ntqqapi/hook.ts @@ -26,7 +26,6 @@ export enum ReceiveCmdS { SELF_STATUS = 'nodeIKernelProfileListener/onSelfStatusChanged', CACHE_SCAN_FINISH = 'nodeIKernelStorageCleanListener/onFinishScan', MEDIA_UPLOAD_COMPLETE = 'nodeIKernelMsgListener/onRichMediaUploadComplete', - SKEY_UPDATE = 'onSkeyUpdate', } type NTReturnData = [ @@ -96,7 +95,7 @@ export function hookNTQQApiCall(window: BrowserWindow, onlyLog: boolean) { const isLogger = args[3]?.[0]?.eventName?.startsWith('ns-LoggerApi') if (!isLogger) { try { - logHook && log('call NTQQ api', thisArg, args) + logHook && log('call NTQQ api', args) } catch (e) { } if (!onlyLog) { try { diff --git a/src/ntqqapi/ntcall.ts b/src/ntqqapi/ntcall.ts index 5ed6ad3..c7197e7 100644 --- a/src/ntqqapi/ntcall.ts +++ b/src/ntqqapi/ntcall.ts @@ -13,7 +13,8 @@ import { NodeIKernelUixConvertService, NodeIKernelRichMediaService, NodeIKernelTicketService, - NodeIKernelTipOffService + NodeIKernelTipOffService, + NodeIKernelRobotService } from './services' export enum NTClass { @@ -39,7 +40,6 @@ export enum NTMethod { MEDIA_FILE_PATH = 'nodeIKernelMsgService/getRichMediaFilePathForGuild', RECALL_MSG = 'nodeIKernelMsgService/recallMsg', EMOJI_LIKE = 'nodeIKernelMsgService/setMsgEmojiLikes', - FORWARD_MSG = 'nodeIKernelMsgService/forwardMsgWithComment', SELF_INFO = 'fetchAuthData', FILE_TYPE = 'getFileType', @@ -93,6 +93,7 @@ interface NTService { nodeIKernelRichMediaService: NodeIKernelRichMediaService nodeIKernelTicketService: NodeIKernelTicketService nodeIKernelTipOffService: NodeIKernelTipOffService + nodeIKernelRobotService: NodeIKernelRobotService } interface InvokeOptions { diff --git a/src/ntqqapi/services/NodeIKernelMsgService.ts b/src/ntqqapi/services/NodeIKernelMsgService.ts index 449b8e1..af6276a 100644 --- a/src/ntqqapi/services/NodeIKernelMsgService.ts +++ b/src/ntqqapi/services/NodeIKernelMsgService.ts @@ -10,7 +10,9 @@ export interface NodeIKernelMsgService { setStatus(args: { status: number, extStatus: number, batteryStatus: number }): Promise - forwardMsg(msgIds: string[], srcContact: Peer, dstContacts: Peer[], commentElements: MessageElement[]): Promise + forwardMsg(msgIds: string[], srcContact: Peer, dstContacts: Peer[], commentElements: MessageElement[]): Promise + }> forwardMsgWithComment(...args: unknown[]): Promise @@ -71,7 +73,7 @@ export interface NodeIKernelMsgService { downloadRichMedia(...args: unknown[]): unknown - setMsgEmojiLikes(...args: unknown[]): unknown + setMsgEmojiLikes(...args: unknown[]): Promise getMsgEmojiLikesList(peer: Peer, msgSeq: string, emojiId: string, emojiType: string, cookie: string, bForward: boolean, number: number): Promise<{ result: number diff --git a/src/ntqqapi/services/NodeIKernelRobotService.ts b/src/ntqqapi/services/NodeIKernelRobotService.ts new file mode 100644 index 0000000..4dca0a3 --- /dev/null +++ b/src/ntqqapi/services/NodeIKernelRobotService.ts @@ -0,0 +1,13 @@ +import { GeneralCallResult } from './common' + +export interface NodeIKernelRobotService { + getRobotUinRange(req: unknown): Promise +} diff --git a/src/ntqqapi/services/index.ts b/src/ntqqapi/services/index.ts index 2ce5f5a..f233136 100644 --- a/src/ntqqapi/services/index.ts +++ b/src/ntqqapi/services/index.ts @@ -9,3 +9,4 @@ export * from './NodeIKernelUixConvertService' export * from './NodeIKernelRichMediaService' export * from './NodeIKernelTicketService' export * from './NodeIKernelTipOffService' +export * from './NodeIKernelRobotService' diff --git a/src/ntqqapi/types/user.ts b/src/ntqqapi/types/user.ts index 09054d7..99f8d8d 100644 --- a/src/ntqqapi/types/user.ts +++ b/src/ntqqapi/types/user.ts @@ -221,11 +221,6 @@ interface RelationFlags { isHidePrivilegeIcon: number } -export interface FriendV2 extends SimpleInfo { - categoryId?: number - categroyName?: string -} - interface CommonExt { constellation: number shengXiao: number @@ -255,7 +250,7 @@ interface PhotoWall { picList: Pic[] } -export interface UserDetailInfoListenerArg { +export interface UserDetailInfo { uid: string uin: string simpleInfo: SimpleInfo diff --git a/src/onebot11/action/msg/ForwardSingleMsg.ts b/src/onebot11/action/msg/ForwardSingleMsg.ts index 18098c3..b11d1e0 100644 --- a/src/onebot11/action/msg/ForwardSingleMsg.ts +++ b/src/onebot11/action/msg/ForwardSingleMsg.ts @@ -19,8 +19,8 @@ abstract class ForwardSingleMsg extends BaseAction { } const peer = await createPeer(this.ctx, payload) const ret = await this.ctx.ntMsgApi.forwardMsg(msg.peer, peer, [msg.msgId]) - if (ret.result !== 0) { - throw new Error(`转发消息失败 ${ret.errMsg}`) + if (ret.length === 0) { + throw new Error(`转发消息失败`) } return null } diff --git a/src/onebot11/action/msg/GetMsg.ts b/src/onebot11/action/msg/GetMsg.ts index 6c742e2..27de7ca 100644 --- a/src/onebot11/action/msg/GetMsg.ts +++ b/src/onebot11/action/msg/GetMsg.ts @@ -30,9 +30,6 @@ class GetMsg extends BaseAction { if (!retMsg) { throw new Error('消息为空') } - retMsg.message_id = this.ctx.store.createMsgShortId(peer, msg.msgId) - retMsg.message_seq = retMsg.message_id - retMsg.real_id = retMsg.message_id return retMsg } } diff --git a/src/onebot11/event/notice/OB11MsgEmojiLikeEvent.ts b/src/onebot11/event/notice/OB11MsgEmojiLikeEvent.ts index 9173aa0..b33d045 100644 --- a/src/onebot11/event/notice/OB11MsgEmojiLikeEvent.ts +++ b/src/onebot11/event/notice/OB11MsgEmojiLikeEvent.ts @@ -8,17 +8,15 @@ export interface MsgEmojiLike { export class OB11GroupMsgEmojiLikeEvent extends OB11GroupNoticeEvent { notice_type = 'group_msg_emoji_like' message_id: number - sub_type?: 'ban' | 'lift_ban' likes: MsgEmojiLike[] group_id: number user_id: number - constructor(groupId: number, userId: number, messageId: number, likes: MsgEmojiLike[], sub_type?: 'ban' | 'lift_ban') { + constructor(groupId: number, userId: number, messageId: number, likes: MsgEmojiLike[]) { super() this.group_id = groupId this.user_id = userId // 可为空,表示是对别人的消息操作,如果是对bot自己的消息则不为空 this.message_id = messageId this.likes = likes - this.sub_type = sub_type } } From e313b2b3e6432032561628817008d890af1b6d99 Mon Sep 17 00:00:00 2001 From: idranme Date: Tue, 1 Oct 2024 21:16:39 +0800 Subject: [PATCH 4/5] feat --- src/onebot11/action/index.ts | 6 ++++-- .../action/llonebot/GetRobotUinRange.ts | 11 ++++++++++ .../{msg => llonebot}/SetMsgEmojiLike.ts | 0 src/onebot11/action/types.ts | 3 ++- src/onebot11/entities.ts | 21 +++++++++++-------- 5 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 src/onebot11/action/llonebot/GetRobotUinRange.ts rename src/onebot11/action/{msg => llonebot}/SetMsgEmojiLike.ts (100%) diff --git a/src/onebot11/action/index.ts b/src/onebot11/action/index.ts index 1558639..a439a2c 100644 --- a/src/onebot11/action/index.ts +++ b/src/onebot11/action/index.ts @@ -45,7 +45,7 @@ import { GetGroupMsgHistory } from './go-cqhttp/GetGroupMsgHistory' import GetFile from './file/GetFile' import { GetForwardMsg } from './go-cqhttp/GetForwardMsg' import { GetCookies } from './user/GetCookie' -import { SetMsgEmojiLike } from './msg/SetMsgEmojiLike' +import { SetMsgEmojiLike } from './llonebot/SetMsgEmojiLike' import { ForwardFriendSingleMsg, ForwardGroupSingleMsg } from './msg/ForwardSingleMsg' import { GetEssenceMsgList } from './go-cqhttp/GetGroupEssence' import { GetGroupHonorInfo } from './group/GetGroupHonorInfo' @@ -71,6 +71,7 @@ import { UploadGroupFile } from './go-cqhttp/UploadGroupFile' import { UploadPrivateFile } from './go-cqhttp/UploadPrivateFile' import { GetGroupFileUrl } from './go-cqhttp/GetGroupFileUrl' import { GetGroupNotice } from './go-cqhttp/GetGroupNotice' +import { GetRobotUinRange } from './llonebot/GetRobotUinRange' export function initActionMap(adapter: Adapter) { const actionHandlers = [ @@ -87,6 +88,8 @@ export function initActionMap(adapter: Adapter) { new GetFriendMsgHistory(adapter), new FetchEmojiLike(adapter), new FetchCustomFace(adapter), + new SetMsgEmojiLike(adapter), + new GetRobotUinRange(adapter), // onebot11 new SendLike(adapter), new GetMsg(adapter), @@ -117,7 +120,6 @@ export function initActionMap(adapter: Adapter) { new GetRecord(adapter), new CleanCache(adapter), new GetCookies(adapter), - new SetMsgEmojiLike(adapter), new ForwardFriendSingleMsg(adapter), new ForwardGroupSingleMsg(adapter), // go-cqhttp diff --git a/src/onebot11/action/llonebot/GetRobotUinRange.ts b/src/onebot11/action/llonebot/GetRobotUinRange.ts new file mode 100644 index 0000000..151dc51 --- /dev/null +++ b/src/onebot11/action/llonebot/GetRobotUinRange.ts @@ -0,0 +1,11 @@ +import { BaseAction } from '../BaseAction' +import { ActionName } from '../types' +import { Dict } from 'cosmokit' + +export class GetRobotUinRange extends BaseAction { + actionName = ActionName.GetRobotUinRange + + async _handle() { + return await this.ctx.ntUserApi.getRobotUinRange() + } +} diff --git a/src/onebot11/action/msg/SetMsgEmojiLike.ts b/src/onebot11/action/llonebot/SetMsgEmojiLike.ts similarity index 100% rename from src/onebot11/action/msg/SetMsgEmojiLike.ts rename to src/onebot11/action/llonebot/SetMsgEmojiLike.ts diff --git a/src/onebot11/action/types.ts b/src/onebot11/action/types.ts index 6a5321b..7b9edb6 100644 --- a/src/onebot11/action/types.ts +++ b/src/onebot11/action/types.ts @@ -25,6 +25,8 @@ export enum ActionName { FetchCustomFace = 'fetch_custom_face', GetFriendMsgHistory = 'get_friend_msg_history', SendForwardMsg = 'send_forward_msg', + SetMsgEmojiLike = 'set_msg_emoji_like', + GetRobotUinRange = 'get_robot_uin_range', // onebot 11 SendLike = 'send_like', GetLoginInfo = 'get_login_info', @@ -38,7 +40,6 @@ export enum ActionName { SendGroupMsg = 'send_group_msg', SendPrivateMsg = 'send_private_msg', DeleteMsg = 'delete_msg', - SetMsgEmojiLike = 'set_msg_emoji_like', SetGroupAddRequest = 'set_group_add_request', SetFriendAddRequest = 'set_friend_add_request', SetGroupLeave = 'set_group_leave', diff --git a/src/onebot11/entities.ts b/src/onebot11/entities.ts index 499ca76..8912e8b 100644 --- a/src/onebot11/entities.ts +++ b/src/onebot11/entities.ts @@ -20,7 +20,7 @@ import { Sex, TipGroupElementType, User, - FriendV2 + SimpleInfo } from '../ntqqapi/types' import { EventType } from './event/OB11BaseEvent' import { encodeCQCode } from './cqcode' @@ -518,11 +518,11 @@ export namespace OB11Entities { if (!replyMsgList?.length) { return } - const shortId = ctx.store.getShortIdByMsgInfo(peer, replyMsgList[0].msgId) + const shortId = ctx.store.createMsgShortId(peer, replyMsgList[0].msgId) return new OB11GroupMsgEmojiLikeEvent( parseInt(msg.peerUid), parseInt(senderUin), - shortId!, + shortId, [{ emoji_id: emojiId, count: 1, @@ -568,8 +568,7 @@ export namespace OB11Entities { pokedetail ) } - } - if (grayTipElement.jsonGrayTipElement?.busiId === '2401' && json.items[2]) { + } else if (grayTipElement.jsonGrayTipElement?.busiId === '2401' && json.items[2]) { ctx.logger.info('收到群精华消息', json) const searchParams = new URL(json.items[2].jp).searchParams const msgSeq = searchParams.get('seq') @@ -591,8 +590,7 @@ export namespace OB11Entities { parseInt(essence.items[0]?.msgSenderUin ?? sourceMsg.senderUin), parseInt(essence.items[0]?.opUin ?? '0'), ) - } - if (grayTipElement.jsonGrayTipElement?.busiId === '2407') { + } else if (grayTipElement.jsonGrayTipElement?.busiId === '2407') { const memberUin = json.items[1].param[0] const title = json.items[3].txt ctx.logger.info('收到群成员新头衔消息', json) @@ -602,6 +600,11 @@ export namespace OB11Entities { } }) return new OB11GroupTitleEvent(parseInt(msg.peerUid), parseInt(memberUin), title) + } else if (grayTipElement.jsonGrayTipElement?.busiId === '19217') { + ctx.logger.info('收到新人被邀请进群消息', grayTipElement) + const userId = new URL(json.items[2].jp).searchParams.get('robot_uin') + const operatorId = new URL(json.items[0].jp).searchParams.get('uin') + return new OB11GroupIncreaseEvent(Number(msg.peerUid), Number(userId), Number(operatorId), 'invite') } } } @@ -648,7 +651,7 @@ export namespace OB11Entities { return friends.map(friend) } - export function friendV2(raw: FriendV2): OB11User { + export function friendV2(raw: SimpleInfo): OB11User { return { ...omit(raw.baseInfo, ['richBuffer', 'phoneNum']), ...omit(raw.coreInfo, ['nick']), @@ -660,7 +663,7 @@ export namespace OB11Entities { } } - export function friendsV2(raw: FriendV2[]): OB11User[] { + export function friendsV2(raw: SimpleInfo[]): OB11User[] { return raw.map(friendV2) } From a2f9128623f711ec5a0c6f2ee23614abf4ae55bc Mon Sep 17 00:00:00 2001 From: idranme Date: Tue, 1 Oct 2024 21:19:08 +0800 Subject: [PATCH 5/5] chore: v3.34.0 --- manifest.json | 2 +- src/version.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/manifest.json b/manifest.json index 65a7b54..a186757 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "LLOneBot", "slug": "LLOneBot", "description": "实现 OneBot 11 协议,用于 QQ 机器人开发", - "version": "3.33.10", + "version": "3.34.0", "icon": "./icon.webp", "authors": [ { diff --git a/src/version.ts b/src/version.ts index 3dd42de..29e963a 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const version = '3.33.10' +export const version = '3.34.0'