const path = require("path"); const fs = require("fs"); const socket = require("./socket.js"); const db = require("./db.js"); const { bot, getDisplayName, getGroupMsgSpeaker, getGroupMsgContent, getNickName, } = require("./bot.js"); const { composeMessageID, composeMediaFilePath, composeFilename, composeCacheFilePath, fileMD5, ensureMediaDir, } = require("./util"); async function processMessage(msg) { if ([50, 51, 52, 53, 9999].includes(msg.MsgType)) { return; } const peerContact = bot.contacts[msg.getPeerUserName()]; const fromContact = bot.contacts[msg["FromUserName"]]; const toContact = bot.contacts[msg["ToUserName"]]; if (!peerContact || !fromContact || !toContact) { // TODO: notify self return; } if ( bot.Contact.isPublicContact(peerContact) || (bot.Contact.isSpContact(peerContact) && peerContact.UserName !== "filehelper") ) { return; } ensureMediaDir(msg); const msgID = msg["MsgId"]; const msgType = msg["MsgType"]; const m = { messageID: msgID, composedID: composeMessageID(msg), createTimestamp: msg["CreateTime"], messageType: msgType, fromID: msg["FromUserName"], toID: msg["ToUserName"], fromName: getDisplayName(fromContact), fromNickName: getNickName(fromContact), toName: getDisplayName(toContact), toNickName: getNickName(toContact), type: "unknown", content: null, group: bot.Contact.isRoomContact(peerContact) ? peerContact : null, speakingGroupMember: null, filename: null, displayFilename: msg["FileName"] || "", fileSize: msg.FileSize | 0, isGroupMessage: bot.Contact.isRoomContact(peerContact), isSentBySelf: msg.isSendBySelf, }; if (m.isGroupMessage) { if (!msg.isSendBySelf) { m.speakingGroupMember = getGroupMsgSpeaker(msg); } } switch (msgType) { case bot.CONF.MSGTYPE_TEXT: // 文本消息 m.type = "text"; m.content = m.isGroupMessage ? getGroupMsgContent(msg) : msg["Content"]; break; case bot.CONF.MSGTYPE_EMOTICON: case bot.CONF.MSGTYPE_IMAGE: { const suffix = msgType === bot.CONF.MSGTYPE_IMAGE ? ".jpg" : ".gif"; m.type = "img"; m.content = `[${msgType === bot.CONF.MSGTYPE_IMAGE ? "图片" : "表情"}]` + (msg["FileName"] || ""); if (msg["HasProductId"]) { m.type = "unknown"; m.content = "[表情贴图]请在手机上查看"; break; } const cachePath = composeCacheFilePath(msg, suffix); // ENHANCE: 提前判断表情是否存在,节约带宽 const res = await bot.getMsgImg(msg.MsgId); fs.writeFileSync(cachePath, res.data); const md5 = await fileMD5(cachePath); m.filename = md5 + suffix; const filename = composeMediaFilePath(msg, m.filename); if (fs.existsSync(filename)) { fs.rmSync(cachePath); } else { fs.renameSync(cachePath, filename); } break; } case bot.CONF.MSGTYPE_VOICE: { m.type = "voice"; m.content = "[语音消息]"; m.filename = composeFilename(msg, ".mp3"); const { data } = await bot.getVoice(msg.MsgId); fs.writeFileSync(composeMediaFilePath(msg, m.filename), data); break; } case bot.CONF.MSGTYPE_VIDEO: case bot.CONF.MSGTYPE_MICROVIDEO: { m.type = "video"; m.content = "[视频]"; const suffix = ".mp4"; const cachePath = composeCacheFilePath(msg, suffix); const res = await bot.getVideo(msg.MsgId); fs.writeFileSync(cachePath, res.data); const md5 = await fileMD5(cachePath); m.filename = md5 + suffix; const filename = composeMediaFilePath(msg, m.filename); if (fs.existsSync(filename)) { fs.rmSync(cachePath); } else { fs.renameSync(cachePath, filename); } break; } case bot.CONF.MSGTYPE_APP: if (msg.AppMsgType === 5) { m.type = "text"; m.content = "[链接]" + msg["FileName"] + ": " + msg["Url"]; break; } else if (msg.AppMsgType == 6) { m.type = "file"; m.content = "[文件]" + m.displayFilename; if (!(msg.FileSize < 10 * 1024 * 1024)) { // 忽略10M以上的大文件 m.type = "unknown"; m.filename = null; break; } const suffix = path.extname(m.displayFilename); const cachePath = composeCacheFilePath(msg, suffix); const res = await bot.getDoc( msg.FromUserName, msg.MediaId, msg.FileName ); fs.writeFileSync(cachePath, res.data); const md5 = await fileMD5(cachePath); m.filename = md5 + suffix; const filename = composeMediaFilePath(msg, m.filename); if (fs.existsSync(filename)) { fs.rmSync(cachePath); } else { fs.renameSync(cachePath, filename); } break; } break; default: break; } if (m.type === "unknown") { m.content ||= "未知类型的消息,请在手机上查看"; } const dbMsg = { messageID: m.composedID, createTimestamp: m.createTimestamp, type: m.type, messageType: m.messageType, fromName: m.fromName, fromNickName: m.fromNickName, toName: m.toName, toNickName: m.toNickName, isGroupMessage: m.isGroupMessage, speakingGroupMemberName: (m.speakingGroupMember && getDisplayName(m.speakingGroupMember)) || null, content: m.content, filename: m.filename, fileSize: m.fileSize, displayFilename: m.displayFilename, isSentBySelf: m.isSentBySelf, }; const clientMsg = { messageID: m.composedID, createTimestamp: m.createTimestamp, type: m.type, fromID: m.fromID, fromName: m.fromName, fromNickName: m.fromNickName, toID: m.toID, toName: m.toName, toNickName: m.toNickName, isGroupMessage: m.isGroupMessage, isSentBySelf: m.isSentBySelf, speakingGroupMemberID: m.speakingGroupMember && m.speakingGroupMember.UserName, speakingGroupMemberName: (m.speakingGroupMember && getDisplayName(m.speakingGroupMember)) || null, content: m.content, filename: m.filename, fileSize: m.fileSize, displayFilename: m.displayFilename, }; if (!bot.Contact.isSpContact(peerContact)) { await db.pushMessage(dbMsg); } socket.pushMessage(clientMsg); } module.exports = { processMessage, };