From b51ce24d0cb76976d78e62b36cc14eddf80dc915 Mon Sep 17 00:00:00 2001 From: linyuchen Date: Sat, 18 May 2024 12:50:11 +0800 Subject: [PATCH] fix: #219 --- src/ntqqapi/constructor.ts | 10 +- src/onebot11/action/msg/SendMsg.ts | 369 ++++++++++++++++------------- 2 files changed, 207 insertions(+), 172 deletions(-) diff --git a/src/ntqqapi/constructor.ts b/src/ntqqapi/constructor.ts index 969f6ca..06bb2d2 100644 --- a/src/ntqqapi/constructor.ts +++ b/src/ntqqapi/constructor.ts @@ -76,8 +76,9 @@ export class SendMsgElementConstructor { if (fileSize === 0) { throw '文件异常,大小为0' } - if (fileSize > 1000 * 1000 * 7){ // todo: 没有采用1024计算,因为具体上限数值未知,大概是7MB - throw `图片过大,最大支持7MB,当前文件大小${fileSize}B` + const maxMB = 30; + if (fileSize > 1024 * 1024 * 30){ + throw `图片过大,最大支持${maxMB}MB,当前文件大小${fileSize}B` } const imageSize = await NTQQFileApi.getImageSize(picPath) const picElement = { @@ -134,8 +135,9 @@ export class SendMsgElementConstructor { if (fileSize === 0) { throw '文件异常,大小为0' } - if (fileSize > 1000 * 1000 * 100) { // todo: 没有采用1024计算,因为具体上限数值未知,大概是100MB - throw `视频过大,最大支持100MB,当前文件大小${fileSize}B` + const maxMB = 100; + if (fileSize > 1024 * 1024 * maxMB) { + throw `视频过大,最大支持${maxMB}MB,当前文件大小${fileSize}B` } const pathLib = require('path') let thumbDir = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`) diff --git a/src/onebot11/action/msg/SendMsg.ts b/src/onebot11/action/msg/SendMsg.ts index a0a769b..1bda604 100644 --- a/src/onebot11/action/msg/SendMsg.ts +++ b/src/onebot11/action/msg/SendMsg.ts @@ -50,22 +50,28 @@ function checkSendMessage(sendMsgList: OB11MessageData[]) { let data = msg['data'] if (type === 'text' && !data['text']) { return 400 - } else if (['image', 'voice', 'record'].includes(type)) { + } + else if (['image', 'voice', 'record'].includes(type)) { if (!data['file']) { return 400 - } else { + } + else { if (checkUri(data['file'])) { return 200 - } else { + } + else { return 400 } } - } else if (type === 'at' && !data['qq']) { - return 400 - } else if (type === 'reply' && !data['id']) { + } + else if (type === 'at' && !data['qq']) { return 400 } - } else { + else if (type === 'reply' && !data['id']) { + return 400 + } + } + else { return 400 } } @@ -87,10 +93,12 @@ export function convertMessage2List(message: OB11MessageMixType, autoEscape = fa }, }, ] - } else { + } + else { message = decodeCQCode(message.toString()) } - } else if (!Array.isArray(message)) { + } + else if (!Array.isArray(message)) { message = [message] } return message @@ -108,179 +116,177 @@ export async function createSendElements( continue } switch (sendMsg.type) { - case OB11MessageDataType.text: - { - const text = sendMsg.data?.text - if (text) { - sendElements.push(SendMsgElementConstructor.text(sendMsg.data!.text)) - } + case OB11MessageDataType.text: { + const text = sendMsg.data?.text + if (text) { + sendElements.push(SendMsgElementConstructor.text(sendMsg.data!.text)) } + } break - case OB11MessageDataType.at: - { - if (!target) { - continue + case OB11MessageDataType.at: { + if (!target) { + continue + } + let atQQ = sendMsg.data?.qq + if (atQQ) { + atQQ = atQQ.toString() + if (atQQ === 'all') { + // todo:查询剩余的at全体次数 + const groupCode = (target as Group)?.groupCode + let remainAtAllCount = 1 + let isAdmin: boolean = true + if (groupCode) { + try { + remainAtAllCount = (await NTQQGroupApi.getGroupAtAllRemainCount(groupCode)).atInfo + .RemainAtAllCountForUin + log(`群${groupCode}剩余at全体次数`, remainAtAllCount) + const self = await getGroupMember((target as Group)?.groupCode, selfInfo.uin) + isAdmin = self.role === GroupMemberRole.admin || self.role === GroupMemberRole.owner + } catch (e) { + } + } + if (isAdmin && remainAtAllCount > 0) { + sendElements.push(SendMsgElementConstructor.at(atQQ, atQQ, AtType.atAll, '全体成员')) + } } - let atQQ = sendMsg.data?.qq - if (atQQ) { - atQQ = atQQ.toString() - if (atQQ === 'all') { - // todo:查询剩余的at全体次数 - const groupCode = (target as Group)?.groupCode - let remainAtAllCount = 1 - let isAdmin: boolean = true - if (groupCode) { - try { - remainAtAllCount = (await NTQQGroupApi.getGroupAtAllRemainCount(groupCode)).atInfo - .RemainAtAllCountForUin - log(`群${groupCode}剩余at全体次数`, remainAtAllCount) - const self = await getGroupMember((target as Group)?.groupCode, selfInfo.uin) - isAdmin = self.role === GroupMemberRole.admin || self.role === GroupMemberRole.owner - } catch (e) {} - } - if (isAdmin && remainAtAllCount > 0) { - sendElements.push(SendMsgElementConstructor.at(atQQ, atQQ, AtType.atAll, '全体成员')) - } - } else { - // const atMember = group?.members.find(m => m.uin == atQQ) - const atMember = await getGroupMember((target as Group)?.groupCode, atQQ) - if (atMember) { - sendElements.push( - SendMsgElementConstructor.at(atQQ, atMember.uid, AtType.atUser, atMember.cardName || atMember.nick), - ) - } + else { + // const atMember = group?.members.find(m => m.uin == atQQ) + const atMember = await getGroupMember((target as Group)?.groupCode, atQQ) + if (atMember) { + sendElements.push( + SendMsgElementConstructor.at(atQQ, atMember.uid, AtType.atUser, atMember.cardName || atMember.nick), + ) } } } + } break - case OB11MessageDataType.reply: - { - let replyMsgId = sendMsg.data.id - if (replyMsgId) { - const replyMsg = await dbUtil.getMsgByShortId(parseInt(replyMsgId)) - if (replyMsg) { + case OB11MessageDataType.reply: { + let replyMsgId = sendMsg.data.id + if (replyMsgId) { + const replyMsg = await dbUtil.getMsgByShortId(parseInt(replyMsgId)) + if (replyMsg) { + sendElements.push( + SendMsgElementConstructor.reply( + replyMsg.msgSeq, + replyMsg.msgId, + replyMsg.senderUin, + replyMsg.senderUin, + ), + ) + } + } + } + break + case OB11MessageDataType.face: { + const faceId = sendMsg.data?.id + if (faceId) { + sendElements.push(SendMsgElementConstructor.face(parseInt(faceId))) + } + } + break + case OB11MessageDataType.mface: { + sendElements.push( + SendMsgElementConstructor.mface( + sendMsg.data.emoji_package_id, + sendMsg.data.emoji_id, + sendMsg.data.key, + sendMsg.data.summary, + ), + ) + } + break + case OB11MessageDataType.image: + case OB11MessageDataType.file: + case OB11MessageDataType.video: + case OB11MessageDataType.voice: { + const data = (sendMsg as OB11MessageFile).data + let file = data.file + const payloadFileName = data?.name + if (file) { + const cache = await dbUtil.getFileCache(file) + if (cache) { + if (fs.existsSync(cache.filePath)) { + file = 'file://' + cache.filePath + } + else if (cache.downloadFunc) { + await cache.downloadFunc() + file = cache.filePath + } + else if (cache.url) { + file = cache.url + } + log('找到文件缓存', file) + } + const { path, isLocal, fileName, errMsg } = await uri2local(file) + if (errMsg) { + throw errMsg + } + if (path) { + if (!isLocal) { + // 只删除http和base64转过来的文件 + deleteAfterSentFiles.push(path) + } + if (sendMsg.type === OB11MessageDataType.file) { + log('发送文件', path, payloadFileName || fileName) + sendElements.push(await SendMsgElementConstructor.file(path, payloadFileName || fileName)) + } + else if (sendMsg.type === OB11MessageDataType.video) { + log('发送视频', path, payloadFileName || fileName) + let thumb = sendMsg.data?.thumb + if (thumb) { + let uri2LocalRes = await uri2local(thumb) + if (uri2LocalRes.success) { + thumb = uri2LocalRes.path + } + } + sendElements.push(await SendMsgElementConstructor.video(path, payloadFileName || fileName, thumb)) + } + else if (sendMsg.type === OB11MessageDataType.voice) { + sendElements.push(await SendMsgElementConstructor.ptt(path)) + } + else if (sendMsg.type === OB11MessageDataType.image) { sendElements.push( - SendMsgElementConstructor.reply( - replyMsg.msgSeq, - replyMsg.msgId, - replyMsg.senderUin, - replyMsg.senderUin, + await SendMsgElementConstructor.pic( + path, + sendMsg.data.summary || '', + parseInt(sendMsg.data?.subType?.toString()) || 0, ), ) } } } + } break - case OB11MessageDataType.face: - { - const faceId = sendMsg.data?.id - if (faceId) { - sendElements.push(SendMsgElementConstructor.face(parseInt(faceId))) + case OB11MessageDataType.json: { + sendElements.push(SendMsgElementConstructor.ark(sendMsg.data.data)) + } + break + case OB11MessageDataType.poke: { + let qq = sendMsg.data?.qq || sendMsg.data?.id + if (qq) { + if ('groupCode' in target) { + crychic.sendGroupPoke(target.groupCode, qq.toString()) } - } - break - case OB11MessageDataType.mface: - { - sendElements.push( - SendMsgElementConstructor.mface( - sendMsg.data.emoji_package_id, - sendMsg.data.emoji_id, - sendMsg.data.key, - sendMsg.data.summary, - ), - ) - } - break - case OB11MessageDataType.image: - case OB11MessageDataType.file: - case OB11MessageDataType.video: - case OB11MessageDataType.voice: - { - const data = (sendMsg as OB11MessageFile).data - let file = data.file - const payloadFileName = data?.name - if (file) { - const cache = await dbUtil.getFileCache(file) - if (cache) { - if (fs.existsSync(cache.filePath)) { - file = 'file://' + cache.filePath - } else if (cache.downloadFunc) { - await cache.downloadFunc() - file = cache.filePath - } else if (cache.url) { - file = cache.url - } - log('找到文件缓存', file) - } - const { path, isLocal, fileName, errMsg } = await uri2local(file) - if (errMsg) { - throw errMsg - } - if (path) { - if (!isLocal) { - // 只删除http和base64转过来的文件 - deleteAfterSentFiles.push(path) - } - if (sendMsg.type === OB11MessageDataType.file) { - log('发送文件', path, payloadFileName || fileName) - sendElements.push(await SendMsgElementConstructor.file(path, payloadFileName || fileName)) - } else if (sendMsg.type === OB11MessageDataType.video) { - log('发送视频', path, payloadFileName || fileName) - let thumb = sendMsg.data?.thumb - if (thumb) { - let uri2LocalRes = await uri2local(thumb) - if (uri2LocalRes.success) { - thumb = uri2LocalRes.path - } - } - sendElements.push(await SendMsgElementConstructor.video(path, payloadFileName || fileName, thumb)) - } else if (sendMsg.type === OB11MessageDataType.voice) { - sendElements.push(await SendMsgElementConstructor.ptt(path)) - } else if (sendMsg.type === OB11MessageDataType.image) { - sendElements.push( - await SendMsgElementConstructor.pic( - path, - sendMsg.data.summary || '', - parseInt(sendMsg.data?.subType?.toString()) || 0, - ), - ) - } + else { + if (!qq) { + qq = parseInt(target.uin) } + crychic.sendFriendPoke(qq.toString()) } + sendElements.push(SendMsgElementConstructor.poke('', '')) } + } break - case OB11MessageDataType.json: - { - sendElements.push(SendMsgElementConstructor.ark(sendMsg.data.data)) - } + case OB11MessageDataType.dice: { + const resultId = sendMsg.data?.result + sendElements.push(SendMsgElementConstructor.dice(resultId)) + } break - case OB11MessageDataType.poke: - { - let qq = sendMsg.data?.qq || sendMsg.data?.id - if (qq) { - if ('groupCode' in target) { - crychic.sendGroupPoke(target.groupCode, qq.toString()) - } else { - if (!qq) { - qq = parseInt(target.uin) - } - crychic.sendFriendPoke(qq.toString()) - } - sendElements.push(SendMsgElementConstructor.poke('', '')) - } - } - break - case OB11MessageDataType.dice: - { - const resultId = sendMsg.data?.result - sendElements.push(SendMsgElementConstructor.dice(resultId)) - } - break - case OB11MessageDataType.RPS: - { - const resultId = sendMsg.data?.result - sendElements.push(SendMsgElementConstructor.rps(resultId)) - } + case OB11MessageDataType.RPS: { + const resultId = sendMsg.data?.result + sendElements.push(SendMsgElementConstructor.rps(resultId)) + } break } } @@ -300,10 +306,34 @@ export async function sendMsg( if (!sendElements.length) { throw '消息体无法解析,请检查是否发送了不支持的消息类型' } - const returnMsg = await NTQQMsgApi.sendMsg(peer, sendElements, waitComplete, 20000) + // 计算发送的文件大小 + let totalSize = 0 + for (const fileElement of sendElements) { + try { + 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 + } + } catch (e) { + log('文件大小计算失败', e, fileElement) + } + } + log('发送消息总大小', totalSize, 'bytes') + let timeout = ((totalSize / 1024 / 512) * 1000) || 5000 // 512kb/s + log('设置消息超时时间', timeout) + const returnMsg = await NTQQMsgApi.sendMsg(peer, sendElements, waitComplete, timeout) log('消息发送结果', returnMsg) returnMsg.msgShortId = await dbUtil.addMsg(returnMsg) - deleteAfterSentFiles.map((f) => fs.unlink(f, () => {})) + deleteAfterSentFiles.map((f) => fs.unlink(f, () => { + })) return returnMsg } @@ -402,7 +432,8 @@ export class SendMsg extends BaseAction { } catch (e) { throw '发送转发消息失败 ' + e.toString() } - } else if (this.getSpecialMsgNum(messages, OB11MessageDataType.music)) { + } + else if (this.getSpecialMsgNum(messages, OB11MessageDataType.music)) { const music = messages[0] as OB11MessageMusic if (music) { const { musicSignUrl } = getConfigUtil().getConfig() @@ -439,7 +470,7 @@ export class SendMsg extends BaseAction { let jsonContent: string try { jsonContent = await new MusicSign(musicSignUrl).sign(postData) - if (!jsonContent){ + if (!jsonContent) { throw '音乐消息生成失败,提交内容有误或者签名服务器签名失败' } } catch (e) { @@ -459,7 +490,8 @@ export class SendMsg extends BaseAction { } } const returnMsg = await sendMsg(peer, sendElements, deleteAfterSentFiles) - deleteAfterSentFiles.map((f) => fs.unlink(f, () => {})) + deleteAfterSentFiles.map((f) => fs.unlink(f, () => { + })) return { message_id: returnMsg.msgShortId } } @@ -562,7 +594,8 @@ export class SendMsg extends BaseAction { await sleep(500) log('转发节点生成成功', nodeMsg.msgId) } - deleteAfterSentFiles.map((f) => fs.unlink(f, () => {})) + deleteAfterSentFiles.map((f) => fs.unlink(f, () => { + })) } catch (e) { log('生成转发消息节点失败', e) }