diff --git a/baileys-server/index.js b/baileys-server/index.js index 2eafb6d..a774e43 100644 --- a/baileys-server/index.js +++ b/baileys-server/index.js @@ -1,13 +1,13 @@ /* eslint-disable no-undef */ const { makeWASocket, - WAProto, + Browsers, DisconnectReason, fetchLatestBaileysVersion, makeCacheableSignalKeyStore, makeInMemoryStore, useMultiFileAuthState, - downloadMediaMessage + downloadMediaMessage, } = require('@whiskeysockets/baileys'); const { writeFile } = require('fs/promises'); @@ -31,163 +31,256 @@ setInterval(() => { store?.writeToFile('./baileys_store_multi.json') }, 10_000) -// start a connection -const startSock = async () => { +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 channelId = '创建时赋值,唯一标识' - const phone = '手机号' - const createTimestamp = '创建时间戳' - const status = 'close, open, connecting, online' - const waVersion = 'v2.3000.1017531287, latest(out)' + const formatPhoneNumber = number => { + if (number === null || number === undefined) return ''; - const sendTextMessage = (whatsAppNo, content) => { - sock.sendMessage(whatsAppNo + '@s.whatsapp.net', { text: content }) - } + if (number.indexOf('@g.us') > -1) { + return number; + } else if (number.indexOf('@broadcast') > -1) { + return number; + } else { + return number + '@s.whatsapp.net'; + } + }; - const sendImageMessage = (whatsAppNo, imageUrl) => { - sock.sendMessage(whatsAppNo + '@s.whatsapp.net', { - image: { url: imageUrl}, - }) - } + const parsePhoneNumber = number => { + if (number === null || number === undefined) return ''; - 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, - }) - - sock.ev.on('connection.update', (update) => { - const { connection, lastDisconnect, qr } = update - if(connection === 'close') { - console.info('链接断了') - } else if(connection === 'open') { - console.info('扫码成功') - } else if(connection === 'connecting') { - console.info('二维码:', qr) + if (number.indexOf('@s.whatsapp.net') > -1) { + return number.split('@')[0]; + } else { + return number; } - }) - - 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) - } + // 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 ''; - if (msg.message?.conversation || msg.message?.extendedTextMessage?.text) { - const text = msg.message?.conversation || msg.message?.extendedTextMessage?.text + const datetime = new Date(timestamp * 1000); - if (text.indexOf('图片') > -1){ - sendImageMessage('8617607730395', 'https://images.asiahighlights.com/allpicture/2022/07/8a7d9ced5936463bb904c82a_cut_750x850_349.webp') - } else if (text.indexOf('文本') > -1){ - sendTextMessage('8617607730395', '文本消息' + new Date().toString()) - } - } - } - } - }) + 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; - // 不绑定不会影响扫码登录 - store?.bind(sock.ev) + 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, + }); - // 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 + waSocket.ev.on('connection.update', async update => { + console.log('connection update: ', update); + const { connection, lastDisconnect, qr } = update; if (connection === 'close') { - console.log('链接断开:', lastDisconnect) + console.log('链接断开:', lastDisconnect); if (lastDisconnect?.error?.output?.statusCode !== DisconnectReason.loggedOut) { - startSock() + start(); } 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.') + 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); } + }); - // 扫码成功,可以发送消息 - if (update.connection === 'open') { - await sock.sendMessage('8617607730395' + '@s.whatsapp.net', { text: 'OPEN: ' + new Date().toString() }) - } + waSocket.ev.on('messages.upsert', async upsert => { + console.log('收到消息:', JSON.stringify(upsert, undefined, 2)); - // WebSocket 创建成功等待扫码,如果没有扫码会更新 qr - if (update.connection === 'connecting') { - // qr - } + if (upsert.type === 'notify') { + for (const msg of upsert.messages) { + const messageType = Object.keys(msg.message)[0]; - console.log('connection update', update) - } + 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); - // credentials updated -- save them - if (events['creds.update']) { - await saveCreds() - } + console.log('writeFile', messageType); + } - // 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}`) - } + const fromWhatsAppNo = parsePhoneNumber(msg.key.remoteJid); - // received a new message - if (events['messages.upsert']) { - const upsert = events['messages.upsert'] - console.log('收到消息:', JSON.stringify(upsert, undefined, 2)) + 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 (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) + 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 sock -} + return { + createTimestamp: Date.now(), + status: 'offline', + version: waVersion, + channelId: channelId, + phone: phone, + start, + sendTextMessage, + sendImageMessage, + }; +}; -startSock() +createWhatsApp().then(wa => wa.start()) \ No newline at end of file diff --git a/baileys-server/package-lock.json b/baileys-server/package-lock.json index fd83feb..f7f18f5 100644 --- a/baileys-server/package-lock.json +++ b/baileys-server/package-lock.json @@ -5,7 +5,8 @@ "packages": { "": { "dependencies": { - "@whiskeysockets/baileys": "^6.7.9" + "@whiskeysockets/baileys": "^6.7.9", + "uuid": "3.3.2" } }, "node_modules/@adiwajshing/keyed-db": { @@ -618,6 +619,19 @@ } } }, + "node_modules/@whiskeysockets/baileys/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@whiskeysockets/eslint-config": { "version": "1.0.0", "resolved": "git+ssh://git@github.com/whiskeysockets/eslint-config.git#326b55f2842668f4e11f471451c4e39819a0e1bf", @@ -2633,16 +2647,13 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "bin/uuid" } }, "node_modules/which": { diff --git a/baileys-server/package.json b/baileys-server/package.json index f3e7dcd..fa32170 100644 --- a/baileys-server/package.json +++ b/baileys-server/package.json @@ -1,5 +1,6 @@ { "dependencies": { - "@whiskeysockets/baileys": "^6.7.9" + "@whiskeysockets/baileys": "^6.7.9", + "uuid": "3.3.2" } }