/* eslint-disable no-undef */ const { makeWASocket, Browsers, 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) const createWhatsApp = async phone => { const channelId = phone; const whatsAppNo = phone; const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info/' + phone); // fetch latest version of WA Web const { version, isLatest } = await fetchLatestBaileysVersion(); const waVersion = version.join('.') + ', ' + (isLatest ? 'latest' : 'out'); const formatPhoneNumber = number => { if (number === null || number === undefined) return ''; if (number.indexOf('@g.us') > -1) { return number; } else if (number.indexOf('@broadcast') > -1) { return number; } else { return number + '@s.whatsapp.net'; } }; const parsePhoneNumber = number => { if (number === null || number === undefined) return ''; if (number.indexOf('@s.whatsapp.net') > -1) { return number.split('@')[0]; } else { return number; } }; // status: sent read delivered failed // 2 sent, 3 delivered, 4 read, 0 error // Time: 2008-07-07 15:37:07 const formatStatus = number => { if (number === 2) return 'sent'; else if (number === 3) return 'delivered'; else if (number === 4) return 'read'; else if (number === 0) return 'error'; else return 'played'; }; const formatTimestamp = timestamp => { if (timestamp === null) return ''; const datetime = new Date(timestamp * 1000); return datetime.getFullYear() + '-' + (datetime.getMonth() + 1) + '-' + datetime.getDay() + ' ' + datetime.getHours() + ':' + datetime.getMinutes() + ':' + datetime.getSeconds(); }; const sendTextMessage = (whatsAppNo, content) => { const number = formatPhoneNumber(whatsAppNo); console.info('formatPhoneNumber: ', number); waSocket.sendMessage(number, { text: content }); }; const sendImageMessage = (whatsAppNo, imageUrl) => { const number = formatPhoneNumber(whatsAppNo); waSocket.sendMessage(number, { image: { url: imageUrl }, }); }; let waSocket = null; const start = () => { return new Promise((resolve, reject) => { waSocket = makeWASocket({ version, logger, auth: { creds: state.creds, /** caching makes the store faster to send/recv messages */ keys: makeCacheableSignalKeyStore(state.keys, logger), }, // https://github.com/WhiskeySockets/Baileys/blob/31bc8ab/src/Utils/generics.ts#L21 browser: Browsers.macOS('Desktop'), msgRetryCounterCache, generateHighQualityLinkPreview: false, syncFullHistory: false, }); // something about the connection changed // maybe it closed, or we received all offline message or connection opened waSocket.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) { start(); } else { waSocket.end(error => console.error('end.error: ', error)); waSocket.logout(msg => console.error('logout.msg: ', msg)); console.log('Connection closed. You are logged out.'); } } else if (connection === 'open') { console.info('链接成功'); waEmitter.emit('connection.open', waSocket); } else if (qr !== undefined) { // WebSocket 创建成功等待扫码,如果没有扫码会更新 qr resolve(qr); } }); waSocket.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: waSocket.updateMediaMessage, }, ); // save to file await writeFile('d:/my-download.jpeg', buffer); console.log('writeFile', messageType); } const fromWhatsAppNo = parsePhoneNumber(msg.key.remoteJid); if (msg.key.remoteJid.indexOf('@g.us') > -1) { const metadata = await waSocket.groupMetadata(msg.key.remoteJid); console.log('群: ' + metadata.subject + ', description: ' + metadata.desc); try { const ppUrl = await waSocket.profilePictureUrl(msg.key.remoteJid); console.log('群头像: ' + ppUrl); } catch (ex) { console.error('群头像出错了: ', ex); } } else { console.log('remoteJid: ', msg.key.remoteJid); try { const ppUrl = await waSocket.profilePictureUrl(msg.key.remoteJid); console.log('个人头像: ' + ppUrl); } catch (ex) { console.error('个人头像出错了: ', ex); } } 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()); } else if (text.indexOf('发群') > -1) { sendTextMessage('120363335516526642@g.us', '这是群消息:' + new Date().toString()); } if (msg.key.fromMe) { waEmitter.emit('message:updated', { id: msg.key.id, status: formatStatus(msg.status), from: whatsAppNo, to: fromWhatsAppNo, type: 'text', text: { body: text, }, participant: { id: parsePhoneNumber(msg.key.participant), name: msg.pushName, }, updateTime: formatTimestamp(msg.messageTimestamp), }); } else { waEmitter.emit('message:received', { id: msg.key.id, status: '', from: fromWhatsAppNo, to: whatsAppNo, type: 'text', text: { body: text, }, participant: { id: parsePhoneNumber(msg.key.participant), name: msg.pushName, }, createTime: formatTimestamp(msg.messageTimestamp), }); } } } } }); waSocket.ev.on('messages.update', async messageUpdate => { console.info('messages.update: ', messageUpdate); for (const msg of messageUpdate) { waEmitter.emit('message:updated', { id: msg.key.id, status: formatStatus(msg.update.status), from: msg.key.fromMe ? whatsAppNo : parsePhoneNumber(msg.key.remoteJid), to: msg.key.fromMe ? parsePhoneNumber(msg.key.remoteJid) : whatsAppNo, participant: { id: parsePhoneNumber(msg.key.participant), name: msg.pushName, }, updateTime: formatTimestamp(new Date().getTime() / 1000), }); } }); waSocket.ev.on('group-participants.update', async GroupMetadata => { console.info('group-participants.update: ', GroupMetadata); }); // 不绑定不会影响扫码登录 store?.bind(waSocket.ev); // the process function lets you process all events that just occurred // efficiently in a batch waSocket.ev.process( // events is a map for event name => event data async events => { // credentials updated -- save them if (events['creds.update']) { await saveCreds(); } }, ); }); }; return { createTimestamp: Date.now(), status: 'offline', version: waVersion, channelId: channelId, phone: phone, start, sendTextMessage, sendImageMessage, }; }; createWhatsApp().then(wa => wa.start())