From 7849e188a850769c1c0631f8d9db05e3f87cd685 Mon Sep 17 00:00:00 2001 From: LiaoYijun Date: Mon, 6 Jan 2025 13:51:41 +0800 Subject: [PATCH 1/2] =?UTF-8?q?perf:=20WA=20=E5=9B=BE=E7=89=87=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E4=B8=8B=E8=BD=BD=E5=9B=BE=E7=89=87=E5=88=B0=E4=B8=B4?= =?UTF-8?q?=E6=97=B6=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wai-server/core/baileys/helper.js | 18 ++ wai-server/core/baileys/index.js | 332 +++++++++++++++++------------- 2 files changed, 202 insertions(+), 148 deletions(-) diff --git a/wai-server/core/baileys/helper.js b/wai-server/core/baileys/helper.js index 4b208bd..c4e1fd9 100644 --- a/wai-server/core/baileys/helper.js +++ b/wai-server/core/baileys/helper.js @@ -50,7 +50,25 @@ const formatTimestamp = timestamp => { return `${datetime.getFullYear()}-${datetime.getMonth() + 1}-${datetime.getDate()} ${datetime.getHours()}:${datetime.getMinutes()}:${datetime.getSeconds()}`; }; +const getFileExtension = mimeType => { + switch (mimeType) { + case 'image/jpeg': + return '.jpg'; + case 'image/png': + return '.png'; + case 'image/gif': + return '.gif'; + case 'image/bmp': + return '.bmp'; + case 'image/webp': + return '.webp'; + default: + return 'unknown'; + } +} + module.exports = { + getFileExtension, formatTimestamp, formatStatus, encodeJid, diff --git a/wai-server/core/baileys/index.js b/wai-server/core/baileys/index.js index 28b9ec8..94b1270 100644 --- a/wai-server/core/baileys/index.js +++ b/wai-server/core/baileys/index.js @@ -15,7 +15,7 @@ const { writeFile } = require('fs/promises'); const waEmitter = require('../emitter'); const serverConfig = require('../../config').server; -const { encodeJid, decodeJid, formatStatus, formatTimestamp } = require('./helper'); +const { encodeJid, decodeJid, formatStatus, formatTimestamp, getFileExtension } = require('./helper'); const generateId = require('../../utils/generateId.util'); const NodeCache = require('node-cache'); const P = require('pino'); @@ -43,152 +43,6 @@ const createWhatsApp = async phone => { const { version, isLatest } = await fetchLatestBaileysVersion(); const waVersion = version.join('.') + ', ' + (isLatest ? 'latest' : 'out'); - const handleMessagesUpsert = async upsert => { - console.info('messages.upsert: ', JSON.stringify(upsert, undefined, 2)); - - if (upsert.type === 'notify') { - for (const msg of upsert.messages) { - - // 没有类型的消息,先忽略 - if (!msg.message) { - continue; - } - - const messageType = Object.keys(msg.message)[0]; - console.log('messageType', messageType); - // TODO: 待处理图片消息 - - const fromWhatsAppNo = decodeJid(msg.key.remoteJid); - - if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) { - - const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text; - const externalId = externalIdCache.get(msg.key.id); - - if (msg.key.fromMe) { - waEmitter.emit('message:updated', { - id: msg.key.id, - externalId, - status: formatStatus(msg.status), - direction: 'outbound', - from: whatsAppNo, - to: fromWhatsAppNo, - type: 'text', - text: { - body: text, - }, - conversation: { - type: isJidUser(msg.key.remoteJid) ? 'individual' : 'group', - }, - customerProfile: { - id: decodeJid(msg.key.participant), - name: msg.pushName, - }, - whatsAppNo, - fromMe: msg.key.fromMe, - eventSource: serverConfig.name + '.messages.upsert.notify', - updateTime: formatTimestamp(msg.messageTimestamp), - }); - } else { - waEmitter.emit('message:received', { - id: msg.key.id, - externalId, - status: '', - direction: 'inbound', - from: fromWhatsAppNo, - to: whatsAppNo, - type: 'text', - text: { - body: text, - }, - conversation: { - type: isJidUser(msg.key.remoteJid) ? 'individual' : 'group', - }, - customerProfile: { - id: decodeJid(msg.key.participant), - name: msg.pushName, - }, - whatsAppNo, - fromMe: msg.key.fromMe, - eventSource: serverConfig.name + '.messages.upsert.notify', - createTime: formatTimestamp(msg.messageTimestamp), - }); - } - } - } - } else if (upsert.type === 'append') { - for (const msg of upsert.messages) { - if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) { - const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text; - const fromWhatsAppNo = decodeJid(msg.key.remoteJid); - - const externalId = externalIdCache.get(msg.key.id); - if (msg.key.fromMe) { - waEmitter.emit('message:updated', { - id: msg.key.id, - externalId, - status: formatStatus(msg.status), - direction: 'outbound', - from: whatsAppNo, - to: fromWhatsAppNo, - type: 'text', - text: { - body: text, - }, - conversation: { - type: isJidUser(msg.key.remoteJid) ? 'individual' : 'group', - }, - customerProfile: { - id: decodeJid(msg.participant), - name: msg.pushName, - }, - whatsAppNo, - fromMe: msg.key.fromMe, - eventSource: serverConfig.name + '.messages.upsert.append', - updateTime: formatTimestamp(msg.messageTimestamp), - }); - } - } - } - } - } - - const handleMessagesUpdate = async messageUpdate => { - console.info('messages.update: ', JSON.stringify(messageUpdate, undefined, 2)); - - for (const msg of messageUpdate) { - - // 没有明确标识状态的更新,忽略 - const ignore = msg.update === undefined || msg.update.status === undefined; - - if (ignore) continue; - - const externalId = externalIdCache.get(msg.key.id); - waEmitter.emit('message:updated', { - id: msg.key.id, - externalId, - status: formatStatus(msg.update?.status), - direction: msg.key.fromMe ? 'outbound' : 'inbound', - from: msg.key.fromMe ? whatsAppNo : decodeJid(msg.key.remoteJid), - to: msg.key.fromMe ? decodeJid(msg.key.remoteJid) : whatsAppNo, - conversation: { - type: isJidUser(msg.key.remoteJid) ? 'individual' : 'group', - }, - customerProfile: { - id: decodeJid(msg.key.participant), - name: msg.pushName, - }, - whatsAppNo, - fromMe: msg.key.fromMe, - eventSource: serverConfig.name + '.messages.updated', - updateTime: formatTimestamp(new Date().getTime() / 1000), - }); - } - } - - const handleCredsUpdate = async () => { - await saveCreds(); - } const start = () => { @@ -213,6 +67,188 @@ const createWhatsApp = async phone => { store?.bind(waSocket.ev); + const handleMessagesUpsert = async upsert => { + console.info('messages.upsert: ', JSON.stringify(upsert, undefined, 2)); + + if (upsert.type === 'notify') { + for (const msg of upsert.messages) { + + // 没有类型的消息,先忽略 + if (!msg.message) { + continue; + } + + const messageType = Object.keys(msg.message)[0]; + const remoteNo = decodeJid(msg.key.remoteJid); + const externalId = externalIdCache.get(msg.key.id); + + if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) { + + const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text; + + if (msg.key.fromMe) { + waEmitter.emit('message:updated', { + id: msg.key.id, + externalId, + status: formatStatus(msg.status), + direction: 'outbound', + from: whatsAppNo, + to: remoteNo, + type: 'text', + text: { + body: text, + }, + conversation: { + type: isJidUser(msg.key.remoteJid) ? 'individual' : 'group', + }, + customerProfile: { + id: decodeJid(msg.key.participant), + name: msg.pushName, + }, + whatsAppNo, + fromMe: msg.key.fromMe, + eventSource: serverConfig.name + '.messages.upsert.notify', + updateTime: formatTimestamp(msg.messageTimestamp), + }); + } else { + waEmitter.emit('message:received', { + id: msg.key.id, + externalId, + status: '', + direction: 'inbound', + from: remoteNo, + to: whatsAppNo, + type: 'text', + text: { + body: text, + }, + conversation: { + type: isJidUser(msg.key.remoteJid) ? 'individual' : 'group', + }, + customerProfile: { + id: decodeJid(msg.key.participant), + name: msg.pushName, + }, + whatsAppNo, + fromMe: msg.key.fromMe, + eventSource: serverConfig.name + '.messages.upsert.notify', + createTime: formatTimestamp(msg.messageTimestamp), + }); + } + } else if (messageType === 'imageMessage') { + console.info('image message: ', msg) + const imageMessage = msg.message.imageMessage; + const fileExtension = getFileExtension(imageMessage.mimetype); + const imageBuffer = await downloadMediaMessage( + msg, 'buffer', {}, { logger, reuploadRequest: waSocket.updateMediaMessage, }, + ); + const imageFilename = './temp/image_' + whatsAppNo + '_' + msg.key.id + fileExtension; + await writeFile(imageFilename, imageBuffer); + if (msg.key.fromMe) { + waEmitter.emit('message:updated', { + id: msg.key.id, + externalId, + status: formatStatus(msg.status), + direction: 'outbound', + from: whatsAppNo, + to: remoteNo, + type: 'text', + image: { + mimetype: imageMessage.mimetype, + sha256: imageMessage.fileSha256, + caption: '', // 暂时为空 + filename: imageFilename, + link_original: imageMessage.url, + }, + conversation: { + type: isJidUser(msg.key.remoteJid) ? 'individual' : 'group', + }, + customerProfile: { + id: decodeJid(msg.key.participant), + name: msg.pushName, + }, + whatsAppNo, + fromMe: msg.key.fromMe, + eventSource: serverConfig.name + '.messages.upsert.notify', + updateTime: formatTimestamp(msg.messageTimestamp), + }); + } + } + } + } else if (upsert.type === 'append') { + for (const msg of upsert.messages) { + if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) { + const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text; + const fromWhatsAppNo = decodeJid(msg.key.remoteJid); + + const externalId = externalIdCache.get(msg.key.id); + if (msg.key.fromMe) { + waEmitter.emit('message:updated', { + id: msg.key.id, + externalId, + status: formatStatus(msg.status), + direction: 'outbound', + from: whatsAppNo, + to: fromWhatsAppNo, + type: 'text', + text: { + body: text, + }, + conversation: { + type: isJidUser(msg.key.remoteJid) ? 'individual' : 'group', + }, + customerProfile: { + id: decodeJid(msg.participant), + name: msg.pushName, + }, + whatsAppNo, + fromMe: msg.key.fromMe, + eventSource: serverConfig.name + '.messages.upsert.append', + updateTime: formatTimestamp(msg.messageTimestamp), + }); + } + } + } + } + } + + const handleMessagesUpdate = async messageUpdate => { + console.info('messages.update: ', JSON.stringify(messageUpdate, undefined, 2)); + + for (const msg of messageUpdate) { + + // 没有明确标识状态的更新,忽略 + const ignore = msg.update === undefined || msg.update.status === undefined; + + if (ignore) continue; + + const externalId = externalIdCache.get(msg.key.id); + waEmitter.emit('message:updated', { + id: msg.key.id, + externalId, + status: formatStatus(msg.update?.status), + direction: msg.key.fromMe ? 'outbound' : 'inbound', + from: msg.key.fromMe ? whatsAppNo : decodeJid(msg.key.remoteJid), + to: msg.key.fromMe ? decodeJid(msg.key.remoteJid) : whatsAppNo, + conversation: { + type: isJidUser(msg.key.remoteJid) ? 'individual' : 'group', + }, + customerProfile: { + id: decodeJid(msg.key.participant), + name: msg.pushName, + }, + whatsAppNo, + fromMe: msg.key.fromMe, + eventSource: serverConfig.name + '.messages.updated', + updateTime: formatTimestamp(new Date().getTime() / 1000), + }); + } + } + + const handleCredsUpdate = async () => { + await saveCreds(); + } + const sendMessageHandler = (event) => { const { to: number, externalId, ...content } = event; const jid = encodeJid(number); @@ -264,7 +300,7 @@ const createWhatsApp = async phone => { eventSource: serverConfig.name + '.connection.update.open', }); - waEmitter.on('request.' + whatsAppNo + '.send.text', sendMessageHandler); + waEmitter.on('request.' + whatsAppNo + '.send.message', sendMessageHandler); } else if (qr !== undefined) { // WebSocket 创建成功等待扫码,如果没有扫码会更新 qr // 第一次一分钟,后面是 20 秒更新一次 From f008392f8b4719c09d2280a50f4b6e40178a3c5c Mon Sep 17 00:00:00 2001 From: LiaoYijun Date: Mon, 6 Jan 2025 14:31:00 +0800 Subject: [PATCH 2/2] =?UTF-8?q?perf:=20=E5=9B=BE=E7=89=87=20sha256=20?= =?UTF-8?q?=E8=BD=AC=E4=B8=BA=20base64=20=E5=AD=97=E7=AC=A6=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wai-server/core/baileys/helper.js | 6 ++++++ wai-server/core/baileys/index.js | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/wai-server/core/baileys/helper.js b/wai-server/core/baileys/helper.js index c4e1fd9..ad684ea 100644 --- a/wai-server/core/baileys/helper.js +++ b/wai-server/core/baileys/helper.js @@ -67,7 +67,13 @@ const getFileExtension = mimeType => { } } +const uint8ArrayToBase64 = uint8Array => { + const binaryString = String.fromCharCode(...uint8Array); + return btoa(binaryString); +} + module.exports = { + uint8ArrayToBase64, getFileExtension, formatTimestamp, formatStatus, diff --git a/wai-server/core/baileys/index.js b/wai-server/core/baileys/index.js index 94b1270..9e33ab7 100644 --- a/wai-server/core/baileys/index.js +++ b/wai-server/core/baileys/index.js @@ -15,7 +15,7 @@ const { writeFile } = require('fs/promises'); const waEmitter = require('../emitter'); const serverConfig = require('../../config').server; -const { encodeJid, decodeJid, formatStatus, formatTimestamp, getFileExtension } = require('./helper'); +const { encodeJid, decodeJid, formatStatus, formatTimestamp, getFileExtension, uint8ArrayToBase64 } = require('./helper'); const generateId = require('../../utils/generateId.util'); const NodeCache = require('node-cache'); const P = require('pino'); @@ -152,10 +152,10 @@ const createWhatsApp = async phone => { direction: 'outbound', from: whatsAppNo, to: remoteNo, - type: 'text', + type: 'image', image: { mimetype: imageMessage.mimetype, - sha256: imageMessage.fileSha256, + sha256: uint8ArrayToBase64(imageMessage.fileSha256), caption: '', // 暂时为空 filename: imageFilename, link_original: imageMessage.url,