import BaseAction from '@/onebot11/action/BaseAction'; import { OB11MessageData, OB11MessageDataType, OB11MessageMixType, OB11MessageNode, OB11PostSendMsg } from '@/onebot11/types'; import { ActionName, BaseCheckResult } from '@/onebot11/action/types'; import { getFriend, getGroup } from '@/core/data'; import { dbUtil } from '@/common/utils/db'; import { ChatType, ElementType, Group, NTQQMsgApi, NTQQUserApi, Peer, SendMessageElement, } from '@/core'; import fs from 'node:fs'; import { logDebug, logError } from '@/common/utils/log'; import { decodeCQCode } from '@/onebot11/cqcode'; import createSendElements from './create-send-elements'; import { handleForwardNode } from '@/onebot11/action/msg/SendMsg/handle-forward-node'; const ALLOW_SEND_TEMP_MSG = false; export interface ReturnDataType { message_id: number; } // Normalizes a mixed type (CQCode/a single segment/segment array) into a segment array. export function normalize(message: OB11MessageMixType, autoEscape = false): OB11MessageData[] { return typeof message === 'string' ? ( autoEscape ? [{ type: OB11MessageDataType.text, data: { text: message } }] : decodeCQCode(message) ) : Array.isArray(message) ? message : [message]; } export { createSendElements }; export async function sendMsg(peer: Peer, sendElements: SendMessageElement[], deleteAfterSentFiles: string[], waitComplete = true) { if (!sendElements.length) { throw ('消息体无法解析, 请检查是否发送了不支持的消息类型'); } let totalSize = 0; let timeout = 5000; try { for (const fileElement of sendElements) { if (fileElement.elementType === ElementType.PTT) { totalSize += fs.statSync(fileElement.pttElement.filePath).size; } if (fileElement.elementType === ElementType.FILE) { totalSize += fs.statSync(fileElement.fileElement.filePath).size; } if (fileElement.elementType === ElementType.VIDEO) { totalSize += fs.statSync(fileElement.videoElement.filePath).size; } if (fileElement.elementType === ElementType.PIC) { totalSize += fs.statSync(fileElement.picElement.sourcePath).size; } } //且 PredictTime ((totalSize / 1024 / 512) * 1000)不等于Nan const PredictTime = totalSize / 1024 / 256 * 1000; if (!Number.isNaN(PredictTime)) { timeout += PredictTime;// 5S Basic Timeout + PredictTime( For File 512kb/s ) } } catch (e) { logError('发送消息计算预计时间异常', e); } const returnMsg = await NTQQMsgApi.sendMsg(peer, sendElements, waitComplete, timeout); try { returnMsg.id = await dbUtil.addMsg(returnMsg, false); } catch (e: any) { logDebug('发送消息id获取失败', e); returnMsg.id = 0; } deleteAfterSentFiles.map(f => fs.unlinkSync(f)); return returnMsg; } async function createContext(payload: OB11PostSendMsg): Promise<{ peer: Peer, group?: Group }> { // This function determines the type of message by the existence of user_id / group_id, // not message_type. // This redundant design of Ob11 here should be blamed. if (payload.group_id) { // take this as a group message const group = (await getGroup(payload.group_id))!; // checked before return { peer: { chatType: ChatType.group, peerUid: group.groupCode }, group: group, }; } else if (payload.user_id) { // take this as a private message const friend = await getFriend(payload.user_id.toString()); if (!friend) { if (ALLOW_SEND_TEMP_MSG) { //const tempUid = getUidByUin(payload.user_id.toString()); const tempUid = await NTQQUserApi.getUidByUin(payload.user_id.toString()); if (tempUid) return { peer: { chatType: ChatType.temp, peerUid: tempUid }, }; } throw `找不到私聊对象 ${payload.user_id}`; } return { peer: { chatType: ChatType.friend, peerUid: friend.uid }, }; } throw '请指定 group_id 或 user_id'; } function getSpecialMsgNum(payload: OB11PostSendMsg, msgType: OB11MessageDataType): number { if (Array.isArray(payload.message)) { return payload.message.filter(msg => msg.type == msgType).length; } return 0; } export class SendMsg extends BaseAction { actionName = ActionName.SendMsg; protected async check(payload: OB11PostSendMsg): Promise { const messages = normalize(payload.message); const nodeElementLength = getSpecialMsgNum(payload, OB11MessageDataType.node); if (nodeElementLength > 0 && nodeElementLength != messages.length) { return { valid: false, message: '转发消息不能和普通消息混在一起发送,转发需要保证message只有type为node的元素' }; } if (payload.message_type !== 'private' && payload.group_id && !(await getGroup(payload.group_id))) { return { valid: false, message: `群${payload.group_id}不存在` }; } if (payload.user_id && payload.message_type !== 'group') { if (!(await getFriend(payload.user_id))) { if ( !ALLOW_SEND_TEMP_MSG && !(await dbUtil.getUidByTempUin(payload.user_id)) ) { return { valid: false, message: '不能发送临时消息' }; } } } return { valid: true, }; } protected async _handle(payload: OB11PostSendMsg): Promise<{ message_id: number }> { const { peer, group } = await createContext(payload); const messages = normalize( payload.message, payload.auto_escape === true || payload.auto_escape === 'true' ); if (getSpecialMsgNum(payload, OB11MessageDataType.node)) { const returnMsg = await handleForwardNode(peer, messages as OB11MessageNode[], group); if (returnMsg) { const msgShortId = await dbUtil.addMsg(returnMsg!, false); return { message_id: msgShortId }; } else { throw Error('发送转发消息失败'); } } else { // if (getSpecialMsgNum(payload, OB11MessageDataType.music)) { // const music: OB11MessageCustomMusic = messages[0] as OB11MessageCustomMusic; // if (music) { // } // } } // log("send msg:", peer, sendElements) const { sendElements, deleteAfterSentFiles } = await createSendElements(messages, group); const returnMsg = await sendMsg(peer, sendElements, deleteAfterSentFiles); return { message_id: returnMsg.id! }; } } export default SendMsg;