import { makeWASocket, delay, WAProto, DisconnectReason, fetchLatestBaileysVersion, getAggregateVotesInPollMessage, makeCacheableSignalKeyStore, makeInMemoryStore, useMultiFileAuthState, } from '@whiskeysockets/baileys' import NodeCache from 'node-cache' import P from '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 startSock = async () => { const { state, saveCreds } = await useMultiFileAuthState('baileys_auth_info') // fetch latest version of WA Web const { version, isLatest } = await fetchLatestBaileysVersion() console.log(`using WA v${version.join('.')}, isLatest: ${isLatest}`) 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, // ignore all broadcast messages -- to receive the same // comment the line below out // shouldIgnoreJid: jid => isJidBroadcast(jid), // implement to handle retries & poll updates getMessage, }) // 不绑定不会影响扫码登录 // 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) => { // something about the connection changed // maybe it closed, or we received all offline message or connection opened if (events['connection.update']) { const update = events['connection.update'] const { connection, lastDisconnect, qr } = update if (connection === 'close') { console.log('链接断开:', lastDisconnect) if (lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut) { startSock() } 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.') } } // 扫码成功,可以发送消息 if (update.connection === 'open') { await sock.sendMessage('8617607730395' + '@s.whatsapp.net', { text: 'oh ' + new Date().toDateString() }) } // WebSocket 创建成功等待扫码,如果没有扫码会更新 qr if (update.connection === 'connecting') { // qr } console.log('connection update', update) } // credentials updated -- save them if (events['creds.update']) { await saveCreds() } if (events['labels.association']) { console.log(events['labels.association']) } if (events['labels.edit']) { console.log(events['labels.edit']) } if (events.call) { console.log('recv call event', events.call) } // history received if (events['messaging-history.set']) { const { chats, contacts, messages, isLatest, progress, syncType } = events['messaging-history.set'] if (syncType === WAProto.HistorySync.HistorySyncType.ON_DEMAND) { console.log('received on-demand history sync, messages=', messages) } console.log(`recv ${chats.length} chats, ${contacts.length} contacts, ${messages.length} msgs (is latest: ${isLatest}, progress: ${progress}%), type: ${syncType}`) } // received a new message if (events['messages.upsert']) { const upsert = events['messages.upsert'] console.log('收到消息:', JSON.stringify(upsert, undefined, 2)) if (upsert.type === 'notify') { for (const msg of upsert.messages) { if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) { const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text console.log('收到 notify:', text) } } } } // messages updated like status delivered, message deleted etc. if (events['messages.update']) { console.log('messages.update: ', JSON.stringify(events['messages.update'], undefined, 2)) for (const { key, update } of events['messages.update']) { if (update.pollUpdates) { const pollCreation = await getMessage(key) if (pollCreation) { console.log( 'got poll update, aggregation: ', getAggregateVotesInPollMessage({ message: pollCreation, pollUpdates: update.pollUpdates, }), ) } } } } if (events['message-receipt.update']) { console.log('message-receipt.update: ', events['message-receipt.update']) } if (events['messages.reaction']) { console.log('message-receipt.update: ', events['messages.reaction']) } if (events['presence.update']) { console.log('presence.update: ', events['presence.update']) } if (events['chats.update']) { console.log('chats.update: ', events['chats.update']) } if (events['contacts.update']) { for (const contact of events['contacts.update']) { if (typeof contact.imgUrl !== 'undefined') { const newUrl = contact.imgUrl === null ? null : await sock.profilePictureUrl(contact.id).catch(() => null) console.log(`contact ${contact.id} has a new profile pic: ${newUrl}`) } } } if (events['chats.delete']) { console.log('chats deleted ', events['chats.delete']) } }, ) return sock async function getMessage(key) { if (store) { const msg = await store.loadMessage(key.remoteJid, key.id) return msg?.message || undefined } // only if store is present return WAProto.Message.fromObject({}) } } startSock()