From f7a0fb22b41e3030dfc1cbc916ec07c6059d55e7 Mon Sep 17 00:00:00 2001 From: Seijo Cecilia Date: Sun, 25 Aug 2024 19:45:14 +0800 Subject: [PATCH] refactor: make SendMsg a single file again --- .../msg/{SendMsg/index.ts => SendMsg.ts} | 2 +- .../action/msg/SendMsg/check-send-message.ts | 36 --- .../msg/SendMsg/create-send-elements.ts | 262 ------------------ .../action/msg/SendMsg/handle-forward-node.ts | 132 --------- 4 files changed, 1 insertion(+), 431 deletions(-) rename src/onebot/action/msg/{SendMsg/index.ts => SendMsg.ts} (99%) delete mode 100644 src/onebot/action/msg/SendMsg/check-send-message.ts delete mode 100644 src/onebot/action/msg/SendMsg/create-send-elements.ts delete mode 100644 src/onebot/action/msg/SendMsg/handle-forward-node.ts diff --git a/src/onebot/action/msg/SendMsg/index.ts b/src/onebot/action/msg/SendMsg.ts similarity index 99% rename from src/onebot/action/msg/SendMsg/index.ts rename to src/onebot/action/msg/SendMsg.ts index 48366c53..e4d4a4e5 100644 --- a/src/onebot/action/msg/SendMsg/index.ts +++ b/src/onebot/action/msg/SendMsg.ts @@ -11,7 +11,7 @@ import fsPromise from 'node:fs/promises'; import { decodeCQCode } from '@/onebot/helper/cqcode'; import { MessageUnique } from '@/common/utils/MessageUnique'; import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendMessageElement } from '@/core'; -import BaseAction from '../../BaseAction'; +import BaseAction from '../BaseAction'; export interface ReturnDataType { message_id: number; diff --git a/src/onebot/action/msg/SendMsg/check-send-message.ts b/src/onebot/action/msg/SendMsg/check-send-message.ts deleted file mode 100644 index ddbc0a2b..00000000 --- a/src/onebot/action/msg/SendMsg/check-send-message.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { OB11MessageData } from '@/onebot/types'; - -function checkSendMessage(sendMsgList: OB11MessageData[]) { - function checkUri(uri: string): boolean { - const pattern = /^(file:\/\/|http:\/\/|https:\/\/|base64:\/\/)/; - return pattern.test(uri); - } - - for (const msg of sendMsgList) { - if (msg['type'] && msg['data']) { - const type = msg['type']; - const data = msg['data']; - if (type === 'text' && !data['text']) { - return 400; - } else if (['image', 'voice', 'record'].includes(type)) { - if (!data['file']) { - return 400; - } else { - if (checkUri(data['file'])) { - return 200; - } else { - return 400; - } - } - - } else if (type === 'at' && !data['qq']) { - return 400; - } else if (type === 'reply' && !data['id']) { - return 400; - } - } else { - return 400; - } - } - return 200; -} diff --git a/src/onebot/action/msg/SendMsg/create-send-elements.ts b/src/onebot/action/msg/SendMsg/create-send-elements.ts deleted file mode 100644 index ab7f983a..00000000 --- a/src/onebot/action/msg/SendMsg/create-send-elements.ts +++ /dev/null @@ -1,262 +0,0 @@ -import { OB11MessageData, OB11MessageDataType, OB11MessageFileBase } from '@/onebot/types'; -import { uri2local } from '@/common/utils/file'; -import { RequestUtil } from '@/common/utils/request'; -import { MessageUnique } from '@/common/utils/MessageUnique'; -import { AtType, ChatType, CustomMusicSignPostData, IdMusicSignPostData, NapCatCore, Peer, SendMessageElement } from '@/core'; -import { SendMsgElementConstructor } from '@/onebot/helper/genMessage'; -import { NapCatOneBot11Adapter } from '@/onebot'; - -export type MessageContext = { - deleteAfterSentFiles: string[], - peer: Peer -} - -async function handleOb11FileLikeMessage( - coreContext: NapCatCore, - obContext: NapCatOneBot11Adapter, - { data: inputdata }: OB11MessageFileBase, - { deleteAfterSentFiles }: MessageContext, -) { - //inputdata?.url || inputdata.file - const isBlankUrl = !inputdata.url || inputdata.url === ''; - const isBlankFile = !inputdata.file || inputdata.file === ''; - if (isBlankUrl && isBlankFile) { - coreContext.context.logger.logError('文件消息缺少参数', inputdata); - throw Error('文件消息缺少参数'); - } - const fileOrUrl = (isBlankUrl ? inputdata.file : inputdata.url) || ""; - const { - path, - isLocal, - fileName, - errMsg, - success, - } = (await uri2local(coreContext.NapCatTempPath, fileOrUrl)); - - if (!success) { - coreContext.context.logger.logError('文件下载失败', errMsg); - throw Error('文件下载失败' + errMsg); - } - - if (!isLocal) { // 只删除http和base64转过来的文件 - deleteAfterSentFiles.push(path); - } - - return { path, fileName: inputdata.name || fileName }; -} - -const _handlers: { - [Key in OB11MessageDataType]: ( - CoreContext: NapCatCore, - obContext: NapCatOneBot11Adapter, - sendMsg: Extract, - // This picks the correct message type out - // How great the type system of TypeScript is! - context: MessageContext, - ) => Promise -} = { - [OB11MessageDataType.text]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { text } }) => SendMsgElementConstructor.text(coreContext, text), - - [OB11MessageDataType.at]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { qq: atQQ } }, context) => { - if (!context.peer || context.peer.chatType == ChatType.KCHATTYPEC2C) return undefined; - if (atQQ === 'all') return SendMsgElementConstructor.at(coreContext, atQQ, atQQ, AtType.atAll, '全体成员'); - const NTQQGroupApi = coreContext.apis.GroupApi; - const NTQQUserApi = coreContext.apis.UserApi; - const atMember = await NTQQGroupApi.getGroupMember(context.peer.peerUid, atQQ); - if (atMember) { - return SendMsgElementConstructor.at(coreContext, atQQ, atMember.uid, AtType.atUser, atMember.nick || atMember.cardName); - } - const uid = await NTQQUserApi.getUidByUinV2(`${atQQ}`); - if (!uid) throw new Error('Get Uid Error'); - const info = await NTQQUserApi.getUserDetailInfo(uid); - return SendMsgElementConstructor.at(coreContext, atQQ, uid, AtType.atUser, info.nick || ''); - }, - [OB11MessageDataType.reply]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { id } }) => { - const replyMsgM = MessageUnique.getMsgIdAndPeerByShortId(parseInt(id)); - if (!replyMsgM) { - coreContext.context.logger.logWarn('回复消息不存在', id); - return undefined; - } - const NTQQMsgApi = coreContext.apis.MsgApi; - const replyMsg = (await NTQQMsgApi.getMsgsByMsgId( - replyMsgM.Peer, [replyMsgM.MsgId!])).msgList[0]; - return replyMsg ? - SendMsgElementConstructor.reply(coreContext, replyMsg.msgSeq, replyMsg.msgId, replyMsg.senderUin!, replyMsg.senderUin!) : - undefined; - }, - - [OB11MessageDataType.face]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { id } }) => SendMsgElementConstructor.face(coreContext, parseInt(id)), - - [OB11MessageDataType.mface]: async (coreContext, obContext: NapCatOneBot11Adapter, { - data: { - emoji_package_id, emoji_id, key, summary, - }, - }) => SendMsgElementConstructor.mface(coreContext, emoji_package_id, emoji_id, key, summary), - - // File service - [OB11MessageDataType.image]: async (coreContext, obContext: NapCatOneBot11Adapter, sendMsg, context) => { - const PicEle = await SendMsgElementConstructor.pic( - coreContext, - (await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context)).path, - sendMsg.data.summary || '', - sendMsg.data.sub_type || 0, - ); - context.deleteAfterSentFiles.push(PicEle.picElement.sourcePath); - return PicEle; - }, // currently not supported - [OB11MessageDataType.file]: async (coreContext, obContext: NapCatOneBot11Adapter, sendMsg, context) => { - const { path, fileName } = await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context); - //logDebug('发送文件', path, fileName); - const FileEle = await SendMsgElementConstructor.file(coreContext, path, fileName); - // 清除Upload的应该 - // context.deleteAfterSentFiles.push(fileName || FileEle.fileElement.filePath); - return FileEle; - }, - - [OB11MessageDataType.video]: async (coreContext, obContext, sendMsg, context) => { - const { path, fileName } = await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context); - - //logDebug('发送视频', path, fileName); - let thumb = sendMsg.data.thumb; - if (thumb) { - const uri2LocalRes = await uri2local(coreContext.NapCatTempPath, thumb); - if (uri2LocalRes.success) thumb = uri2LocalRes.path; - } - const videoEle = await SendMsgElementConstructor.video(coreContext, path, fileName, thumb); - //未测试 - context.deleteAfterSentFiles.push(videoEle.videoElement.filePath); - return videoEle; - }, - - [OB11MessageDataType.voice]: async (coreContext, obContext: NapCatOneBot11Adapter, sendMsg, context) => SendMsgElementConstructor.ptt(coreContext, (await handleOb11FileLikeMessage(coreContext, obContext, sendMsg, context)).path), - - [OB11MessageDataType.json]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { data } }) => SendMsgElementConstructor.ark(coreContext, data), - - [OB11MessageDataType.dice]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { result } }) => SendMsgElementConstructor.dice(coreContext, result), - - [OB11MessageDataType.RPS]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { result } }) => SendMsgElementConstructor.rps(coreContext, result), - - [OB11MessageDataType.markdown]: async (coreContext, obContext: NapCatOneBot11Adapter, { data: { content } }) => SendMsgElementConstructor.markdown(coreContext, content), - - [OB11MessageDataType.music]: async (coreContext, obContext: NapCatOneBot11Adapter, { data }) => { - // 保留, 直到...找到更好的解决方案 - if (data.type === 'custom') { - if (!data.url) { - coreContext.context.logger.logError('自定义音卡缺少参数url'); - return undefined; - } - if (!data.audio) { - coreContext.context.logger.logError('自定义音卡缺少参数audio'); - return undefined; - } - if (!data.title) { - coreContext.context.logger.logError('自定义音卡缺少参数title'); - return undefined; - } - } else { - if (!['qq', '163'].includes(data.type)) { - coreContext.context.logger.logError('音乐卡片type错误, 只支持qq、163、custom,当前type:', data.type); - return undefined; - } - if (!data.id) { - coreContext.context.logger.logError('音乐卡片缺少参数id'); - return undefined; - } - } - - let postData: IdMusicSignPostData | CustomMusicSignPostData; - if (data.type === 'custom' && data.content) { - const { content, ...others } = data; - postData = { singer: content, ...others }; - } else { - postData = data; - } - // Mlikiowa V2.2.7 Refactor Todo - const signUrl = obContext.configLoader.configData.musicSignUrl; - if (!signUrl) { - if (data.type === 'qq') { - //const musicJson = (await SignMusicWrapper(data.id.toString())).data.arkResult.slice(0, -1); - //return SendMsgElementConstructor.ark(musicJson); - } - throw Error('音乐消息签名地址未配置'); - } - try { - const musicJson = await RequestUtil.HttpGetJson(signUrl, 'POST', postData); - return SendMsgElementConstructor.ark(coreContext, musicJson); - } catch (e) { - coreContext.context.logger.logError('生成音乐消息失败', e); - } - }, - - [OB11MessageDataType.node]: async (coreContext, obContext: NapCatOneBot11Adapter) => undefined, - - [OB11MessageDataType.forward]: async (coreContext, obContext: NapCatOneBot11Adapter) => undefined, - - [OB11MessageDataType.xml]: async (coreContext, obContext: NapCatOneBot11Adapter) => undefined, - - [OB11MessageDataType.poke]: async (coreContext, obContext: NapCatOneBot11Adapter) => undefined, - - [OB11MessageDataType.Location]: async (coreContext, obContext: NapCatOneBot11Adapter) => { - return SendMsgElementConstructor.location(coreContext); - }, - [OB11MessageDataType.miniapp]: function (CoreContext: NapCatCore, obContext: NapCatOneBot11Adapter, sendMsg: never, context: MessageContext): Promise { - throw new Error('Function not implemented.'); - }, -}; - -const handlers = <{ - [Key in OB11MessageDataType]: ( - coreContext: NapCatCore, - obContext: NapCatOneBot11Adapter, - sendMsg: OB11MessageData, - context: MessageContext, - ) => Promise -}>_handlers; - -export default async function createSendElements( - CoreContext: NapCatCore, - obContext: NapCatOneBot11Adapter, - messageData: OB11MessageData[], - peer: Peer, - ignoreTypes: OB11MessageDataType[] = [], -) { - const deleteAfterSentFiles: string[] = []; - const callResultList: Array> = []; - for (const sendMsg of messageData) { - if (ignoreTypes.includes(sendMsg.type)) { - continue; - } - const callResult = handlers[sendMsg.type]( - CoreContext, - obContext, - sendMsg, - { peer, deleteAfterSentFiles }, - )?.catch(undefined); - callResultList.push(callResult); - } - const ret = await Promise.all(callResultList); - const sendElements: SendMessageElement[] = ret.filter(ele => !!ele); - return { sendElements, deleteAfterSentFiles }; -} - -export async function createSendElementsParallel( - CoreContext: NapCatCore, - obContext: NapCatOneBot11Adapter, - messageData: OB11MessageData[], - peer: Peer, - ignoreTypes: OB11MessageDataType[] = [], -) { - const deleteAfterSentFiles: string[] = []; - const sendElements = ( - await Promise.all( - messageData.map(async sendMsg => ignoreTypes.includes(sendMsg.type) ? - undefined : - handlers[sendMsg.type](CoreContext, obContext, sendMsg, { peer, deleteAfterSentFiles })), - ).then( - results => results.filter( - element => element !== undefined, - ), - ) - ); - return { sendElements, deleteAfterSentFiles }; -} diff --git a/src/onebot/action/msg/SendMsg/handle-forward-node.ts b/src/onebot/action/msg/SendMsg/handle-forward-node.ts deleted file mode 100644 index a2668de7..00000000 --- a/src/onebot/action/msg/SendMsg/handle-forward-node.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { ChatType, ElementType, NapCatCore, Peer, RawMessage, SendMessageElement } from '@/core'; -import { MessageUnique } from '@/common/utils/MessageUnique'; -import { OB11MessageDataType, OB11MessageNode } from '@/onebot/types'; -import createSendElements from './create-send-elements'; -import { normalize, sendMsg } from '../SendMsg/index'; -import { NapCatOneBot11Adapter } from '@/onebot'; - -async function cloneMsg(coreContext: NapCatCore, msg: RawMessage): Promise { - const selfPeer = { - chatType: ChatType.KCHATTYPEC2C, - peerUid: coreContext.selfInfo.uid, - }; - const logger = coreContext.context.logger; - const NTQQMsgApi = coreContext.apis.MsgApi; - //logDebug('克隆的目标消息', msg); - - const sendElements: SendMessageElement[] = []; - - for (const element of msg.elements) { - sendElements.push(element as SendMessageElement); - } - - if (sendElements.length === 0) { - logger.logDebug('需要clone的消息无法解析,将会忽略掉', msg); - } - try { - const nodeMsg = await NTQQMsgApi.sendMsg(selfPeer, sendElements, true); - return nodeMsg; - } catch (e) { - logger.logError(e, '克隆转发消息失败,将忽略本条消息', msg); - } -} - -export async function handleForwardNode(coreContext: NapCatCore, obContext: NapCatOneBot11Adapter, destPeer: Peer, messageNodes: OB11MessageNode[]): Promise { - const NTQQMsgApi = coreContext.apis.MsgApi; - const selfPeer = { - chatType: ChatType.KCHATTYPEC2C, - peerUid: coreContext.selfInfo.uid, - }; - let nodeMsgIds: string[] = []; - const logger = coreContext.context.logger; - for (const messageNode of messageNodes) { - const nodeId = messageNode.data.id; - if (nodeId) { - //对Mgsid和OB11ID混用情况兜底 - const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(nodeId)) || MessageUnique.getPeerByMsgId(nodeId); - if (!nodeMsg) { - logger.logError('转发消息失败,未找到消息', nodeId); - continue; - } - nodeMsgIds.push(nodeMsg.MsgId); - } else { - // 自定义的消息 - try { - const OB11Data = normalize(messageNode.data.content); - //筛选node消息 - const isNodeMsg = OB11Data.filter(e => e.type === OB11MessageDataType.node).length;//找到子转发消息 - if (isNodeMsg !== 0) { - if (isNodeMsg !== OB11Data.length) { - logger.logError('子消息中包含非node消息 跳过不合法部分'); - continue; - } - const nodeMsg = await handleForwardNode(coreContext, obContext, selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node)); - if (nodeMsg) { - nodeMsgIds.push(nodeMsg.msgId); - MessageUnique.createMsg(selfPeer, nodeMsg.msgId); - } - //完成子卡片生成跳过后续 - continue; - } - const { sendElements } = await createSendElements(coreContext, obContext, 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 AllElement: SendMessageElement[][] = [MixElement, ...SingleElement].filter(e => e !== undefined && e.length !== 0); - const MsgNodeList: Promise[] = []; - for (const sendElementsSplitElement of AllElement) { - MsgNodeList.push(sendMsg(coreContext, selfPeer, sendElementsSplitElement, [], true).catch(e => new Promise((resolve, reject) => { - resolve(undefined); - }))); - } - (await Promise.allSettled(MsgNodeList)).map((result) => { - if (result.status === 'fulfilled' && result.value) { - nodeMsgIds.push(result.value.msgId); - MessageUnique.createMsg(selfPeer, result.value.msgId); - } - }); - } catch (e) { - logger.logDebug('生成转发消息节点失败', e); - } - } - } - const nodeMsgArray: Array = []; - let srcPeer: Peer | undefined = undefined; - let needSendSelf = false; - //检测是否处于同一个Peer 不在同一个peer则全部消息由自身发送 - for (const msgId of nodeMsgIds) { - const nodeMsgPeer = MessageUnique.getPeerByMsgId(msgId); - if (!nodeMsgPeer) { - logger.logError('转发消息失败,未找到消息', msgId); - continue; - } - const nodeMsg = (await NTQQMsgApi.getMsgsByMsgId(nodeMsgPeer.Peer, [msgId])).msgList[0]; - srcPeer = srcPeer ?? { chatType: nodeMsg.chatType, peerUid: nodeMsg.peerUid }; - if (srcPeer.peerUid !== nodeMsg.peerUid) { - needSendSelf = true; - } - nodeMsgArray.push(nodeMsg); - } - nodeMsgIds = nodeMsgArray.map(msg => msg.msgId); - let retMsgIds: string[] = []; - if (needSendSelf) { - for (const [, msg] of nodeMsgArray.entries()) { - if (msg.peerUid === coreContext.selfInfo.uid){ - retMsgIds.push(msg.msgId); - continue; - } - const ClonedMsg = await cloneMsg(coreContext, msg); - if (ClonedMsg) retMsgIds.push(ClonedMsg.msgId); - } - } else { - retMsgIds = nodeMsgIds; - } - if (retMsgIds.length === 0) throw Error('转发消息失败,生成节点为空'); - try { - logger.logDebug('开发转发', srcPeer, destPeer, retMsgIds); - return await NTQQMsgApi.multiForwardMsg(srcPeer!, destPeer, retMsgIds); - } catch (e) { - logger.logError('forward failed', e); - return null; - } -}