From ccfaf25c0a099eae792bb1d1dd2699c522318345 Mon Sep 17 00:00:00 2001 From: LiaoYijun Date: Mon, 23 Dec 2024 10:17:24 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E8=BF=81=E7=A7=BB=20Baileys=20?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- wai-server/api/channels/channel.controller.js | 3 +- wai-server/core/baileys/index.js | 160 ++++++++++++++++++ wai-server/core/index.js | 159 +---------------- wai-server/index.js | 5 - 4 files changed, 163 insertions(+), 164 deletions(-) create mode 100644 wai-server/core/baileys/index.js diff --git a/wai-server/api/channels/channel.controller.js b/wai-server/api/channels/channel.controller.js index 7d0c095..4a9a654 100644 --- a/wai-server/api/channels/channel.controller.js +++ b/wai-server/api/channels/channel.controller.js @@ -1,4 +1,5 @@ -const { websocketService, websocketManager, createWhatsApp } = require('../../core'); // Import from core/index.js +const { websocketService, websocketManager } = require('../../core'); // Import from core/index.js +const { createWhatsApp } = require('../../core/baileys'); // Import from core/index.js const generateId = require('../../utils/generateId.util'); // const { createConnection, getConnection } = require('../../core/whatsapp/connection'); // const { getAvailableConnection } = require('../../core/whatsapp/sessionStore'); diff --git a/wai-server/core/baileys/index.js b/wai-server/core/baileys/index.js new file mode 100644 index 0000000..712b671 --- /dev/null +++ b/wai-server/core/baileys/index.js @@ -0,0 +1,160 @@ +const { + makeWASocket, + WAProto, + DisconnectReason, + fetchLatestBaileysVersion, + makeCacheableSignalKeyStore, + makeInMemoryStore, + useMultiFileAuthState, + downloadMediaMessage +} = require('@whiskeysockets/baileys'); + +const { writeFile } = require('fs/promises'); + +const NodeCache = require('node-cache'); +const P = require('pino'); + +const logger = P({ timestamp: () => `,"time":"${new Date().toJSON()}"` }, P.destination('./wa-logs.txt')) +logger.level = 'trace' + +// external map to store retry counts of messages when decryption/encryption fails +// keep this out of the socket itself, so as to prevent a message decryption/encryption loop across socket restarts +const msgRetryCounterCache = new NodeCache() + +// the store maintains the data of the WA connection in memory +// can be written out to a file & read from it +const store = makeInMemoryStore({ logger }) +store?.readFromFile('./baileys_store_multi.json') +// save every 10s +setInterval(() => { + store?.writeToFile('./baileys_store_multi.json') +}, 10_000) + +// start a connection +const createWhatsApp = async (channelId, phone) => { + + const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info_' + channelId + '_' + phone) + // fetch latest version of WA Web + const { version, isLatest } = await fetchLatestBaileysVersion() + const waVersion = version.join('.') + ', ' + (isLatest ? 'latest' : 'out') + + return new Promise((resolve, reject) => { + + const sendTextMessage = (whatsAppNo, content) => { + sock.sendMessage(whatsAppNo + '@s.whatsapp.net', { text: content }) + } + + const sendImageMessage = (whatsAppNo, imageUrl) => { + sock.sendMessage(whatsAppNo + '@s.whatsapp.net', { + image: { url: imageUrl}, + }) + } + + const sock = makeWASocket({ + version, + logger, + auth: { + creds: state.creds, + /** caching makes the store faster to send/recv messages */ + keys: makeCacheableSignalKeyStore(state.keys, logger), + }, + msgRetryCounterCache, + generateHighQualityLinkPreview: true, + }) + + // something about the connection changed + // maybe it closed, or we received all offline message or connection opened + sock.ev.on('connection.update', async (update) => { + console.log('connection update: ', update) + const { connection, lastDisconnect, qr } = update + if(connection === 'close') { + console.log('链接断开:', lastDisconnect) + if (lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut) { + createWhatsApp(channelId, phone) + } else { + sock.end((error) => console.error('end.error: ', error)) + sock.logout((msg) => console.error('logout.msg: ', msg)) + console.log('Connection closed. You are logged out.') + reject('Connection closed. You are logged out.') + } + } else if(connection === 'open') { + console.info('链接成功') + await sock.sendMessage('8617607730395' + '@s.whatsapp.net', { text: 'OPEN: ' + new Date().toString() }) + } else if(qr !== undefined) { + // WebSocket 创建成功等待扫码,如果没有扫码会更新 qr + resolve({ + qrCode: qr, + version: waVersion, + createTimestamp: Date.now(), + status: 'connecting', + channelId : channelId, + phone: phone, + sendTextMessage, + sendImageMessage + }) + } + }) + + sock.ev.on('messages.upsert', async (upsert) => { + console.log('收到消息:', JSON.stringify(upsert, undefined, 2)) + + if (upsert.type === 'notify') { + for (const msg of upsert.messages) { + + const messageType = Object.keys(msg.message)[0] + + console.log('messageType', messageType) + if (messageType === 'imageMessage') { + // download the message + const buffer = await downloadMediaMessage( + msg, + 'buffer', + { }, + { + logger, + // pass this so that baileys can request a reupload of media + // that has been deleted + reuploadRequest: sock.updateMediaMessage + } + ) + // save to file + await writeFile('d:/my-download.jpeg', buffer) + + console.log('writeFile', messageType) + } + + const fromWhatsAppNo = msg.key.remoteJid.split('@')[0] + + if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) { + const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text + + if (text.indexOf('图片') > -1){ + sendImageMessage(fromWhatsAppNo, 'https://images.asiahighlights.com/allpicture/2022/07/8a7d9ced5936463bb904c82a_cut_750x850_349.webp') + } else if (text.indexOf('文本') > -1){ + sendTextMessage(fromWhatsAppNo, '文本消息' + new Date().toString()) + } + } + } + } + }) + + // 不绑定不会影响扫码登录 + store?.bind(sock.ev) + + // the process function lets you process all events that just occurred + // efficiently in a batch + sock.ev.process( + // events is a map for event name => event data + async (events) => { + // credentials updated -- save them + if (events['creds.update']) { + await saveCreds() + } + }, + ) + }) +} + +module.exports = { + createWhatsApp, +}; diff --git a/wai-server/core/index.js b/wai-server/core/index.js index fbdabc9..2272204 100644 --- a/wai-server/core/index.js +++ b/wai-server/core/index.js @@ -5,164 +5,7 @@ const websocketConnectionI = require('./websocket/connection'); // Create the instances here const websocketService = websocketServicesI(); const websocketManager = websocketConnectionI(websocketService); - - -const { - makeWASocket, - WAProto, - DisconnectReason, - fetchLatestBaileysVersion, - makeCacheableSignalKeyStore, - makeInMemoryStore, - useMultiFileAuthState, - downloadMediaMessage -} = require('@whiskeysockets/baileys'); - -const { writeFile } = require('fs/promises'); - -const NodeCache = require('node-cache'); -const P = require('pino'); - -const logger = P({ timestamp: () => `,"time":"${new Date().toJSON()}"` }, P.destination('./wa-logs.txt')) -logger.level = 'trace' - -// external map to store retry counts of messages when decryption/encryption fails -// keep this out of the socket itself, so as to prevent a message decryption/encryption loop across socket restarts -const msgRetryCounterCache = new NodeCache() - -// the store maintains the data of the WA connection in memory -// can be written out to a file & read from it -const store = makeInMemoryStore({ logger }) -store?.readFromFile('./baileys_store_multi.json') -// save every 10s -setInterval(() => { - store?.writeToFile('./baileys_store_multi.json') -}, 10_000) - -// start a connection -const createWhatsApp = async (channelId, phone) => { - - const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info_' + channelId + '_' + phone) - // fetch latest version of WA Web - const { version, isLatest } = await fetchLatestBaileysVersion() - const waVersion = version.join('.') + ', ' + (isLatest ? 'latest' : 'out') - - return new Promise((resolve, reject) => { - - const sendTextMessage = (whatsAppNo, content) => { - sock.sendMessage(whatsAppNo + '@s.whatsapp.net', { text: content }) - } - - const sendImageMessage = (whatsAppNo, imageUrl) => { - sock.sendMessage(whatsAppNo + '@s.whatsapp.net', { - image: { url: imageUrl}, - }) - } - - const sock = makeWASocket({ - version, - logger, - auth: { - creds: state.creds, - /** caching makes the store faster to send/recv messages */ - keys: makeCacheableSignalKeyStore(state.keys, logger), - }, - msgRetryCounterCache, - generateHighQualityLinkPreview: true, - }) - - // something about the connection changed - // maybe it closed, or we received all offline message or connection opened - sock.ev.on('connection.update', async (update) => { - console.log('connection update: ', update) - const { connection, lastDisconnect, qr } = update - if(connection === 'close') { - console.log('链接断开:', lastDisconnect) - if (lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut) { - createWhatsApp(channelId, phone) - } else { - sock.end((error) => console.error('end.error: ', error)) - sock.logout((msg) => console.error('logout.msg: ', msg)) - console.log('Connection closed. You are logged out.') - reject('Connection closed. You are logged out.') - } - } else if(connection === 'open') { - console.info('链接成功') - await sock.sendMessage('8617607730395' + '@s.whatsapp.net', { text: 'OPEN: ' + new Date().toString() }) - } else if(qr !== undefined) { - // WebSocket 创建成功等待扫码,如果没有扫码会更新 qr - resolve({ - qrCode: qr, - version: waVersion, - createTimestamp: Date.now(), - status: 'connecting', - channelId : channelId, - phone: phone, - sendTextMessage, - sendImageMessage - }) - } - }) - - sock.ev.on('messages.upsert', async (upsert) => { - console.log('收到消息:', JSON.stringify(upsert, undefined, 2)) - - if (upsert.type === 'notify') { - for (const msg of upsert.messages) { - - const messageType = Object.keys(msg.message)[0] - - console.log('messageType', messageType) - if (messageType === 'imageMessage') { - // download the message - const buffer = await downloadMediaMessage( - msg, - 'buffer', - { }, - { - logger, - // pass this so that baileys can request a reupload of media - // that has been deleted - reuploadRequest: sock.updateMediaMessage - } - ) - // save to file - await writeFile('d:/my-download.jpeg', buffer) - - console.log('writeFile', messageType) - } - - const fromWhatsAppNo = msg.key.remoteJid.split('@')[0] - - if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) { - const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text - - if (text.indexOf('图片') > -1){ - sendImageMessage(fromWhatsAppNo, 'https://images.asiahighlights.com/allpicture/2022/07/8a7d9ced5936463bb904c82a_cut_750x850_349.webp') - } else if (text.indexOf('文本') > -1){ - sendTextMessage(fromWhatsAppNo, '文本消息' + new Date().toString()) - } - } - } - } - }) - - // 不绑定不会影响扫码登录 - store?.bind(sock.ev) - - // the process function lets you process all events that just occurred - // efficiently in a batch - sock.ev.process( - // events is a map for event name => event data - async (events) => { - // credentials updated -- save them - if (events['creds.update']) { - await saveCreds() - } - }, - ) - }) -} +const createWhatsApp = require('./baileys/index'); module.exports = { websocketService, diff --git a/wai-server/index.js b/wai-server/index.js index 87a0dfb..80779a8 100644 --- a/wai-server/index.js +++ b/wai-server/index.js @@ -4,13 +4,8 @@ const http = require('http'); const server = require('./server'); const { port } = require('./config').server; -const { createWhatsApp } = require('./core'); async function bootstrap() { - // const whatsApp1 = await createWhatsApp('channel_001', '8618777396951'); - // console.info(whatsApp1); - // const whatsApp2 = await createWhatsApp('channel_002', '8613557032060'); - // console.info(whatsApp2); /** * Add external services init as async operations (db, redis, etc...) * e.g.