diff --git a/packages/provider/package.json b/packages/provider/package.json index 42aa0f2..3262cdc 100644 --- a/packages/provider/package.json +++ b/packages/provider/package.json @@ -17,6 +17,7 @@ "./mock": "./lib/mock/index.cjs", "./twilio": "./lib/twilio/index.cjs", "./web-whatsapp": "./lib/web-whatsapp/index.cjs", - "./venom": "./lib/venom/index.cjs" + "./venom": "./lib/venom/index.cjs", + "./baileys": "./lib/baileys/index.cjs" } } diff --git a/packages/provider/rollup-provider.config.js b/packages/provider/rollup-provider.config.js index 2afef9f..03aa368 100644 --- a/packages/provider/rollup-provider.config.js +++ b/packages/provider/rollup-provider.config.js @@ -39,4 +39,13 @@ module.exports = [ }, plugins: [commonjs()], }, + { + input: join(__dirname, 'src', 'baileys', 'index.js'), + output: { + banner: banner['banner.output'].join(''), + file: join(__dirname, 'lib', 'baileys', 'index.cjs'), + format: 'cjs', + }, + plugins: [commonjs()], + }, ] diff --git a/packages/provider/src/baileys/index.js b/packages/provider/src/baileys/index.js index 16d3e75..e985e4a 100644 --- a/packages/provider/src/baileys/index.js +++ b/packages/provider/src/baileys/index.js @@ -1,79 +1,171 @@ const { ProviderClass } = require('@bot-whatsapp/bot') -const PINO = require('pino') -const makeWASocket = require('@adiwajshing/baileys').default -const { useMultiFileAuthState } = require('@adiwajshing/baileys') +const pino = require('pino') +const { + default: makeWASocket, + useMultiFileAuthState, +} = require('@adiwajshing/baileys') +const { + baileyGenerateImage, + baileyCleanNumber, + baileyIsValidNumber, +} = require('./utils') -class Baileys extends ProviderClass { +/** + * ⚙️ BaileysProvider: Es una clase tipo adaptor + * que extiende clases de ProviderClass (la cual es como interfaz para sber que funciones rqueridas) + * https://github.com/adiwajshing/Baileys + */ +class BaileysProvider extends ProviderClass { + vendor constructor() { super() - this.sock + this.initBailey().then(() => this.initBusEvents()) } - async baileys() { - const { state, saveCreds } = await useMultiFileAuthState( - 'baileys_auth_whatsapp' - ) + /** + * Iniciar todo Bailey + */ + async initBailey() { + const { state, saveCreds } = await useMultiFileAuthState('sessions') - this.sock = await makeWASocket({ - printQRInTerminal: true, - auth: state, - logger: PINO({ level: 'error' }), - }) + try { + this.vendor = makeWASocket({ + printQRInTerminal: false, + auth: state, + logger: pino({ level: 'error' }), + }) + + this.vendor.ev.on( + 'connection.update', + async ({ qr, connection, lastDisconnect }) => { + if (qr) baileyGenerateImage(qr) + if (connection === 'open') this.emit('ready', true) + if (lastDisconnect?.error) { + saveCreds() + this.initBailey() + } + } + ) + } catch (e) { + this.emit('error', e) + } + } + + /** + * Mapeamos los eventos nativos a los que la clase Provider espera + * para tener un standar de eventos + * @returns + */ + busEvents = () => [ + { + event: 'connection.update', + func: async ({ qr, connection, lastDisconnect }) => { + if (qr) { + this.emit('require_action', { + instructions: [ + `Debes escanear el QR Code para iniciar session reivsa qr.png`, + `Recuerda que el QR se actualiza cada minuto `, + `Necesitas ayuda: https://link.codigoencasa.com/DISCORD`, + ], + }) + baileyGenerateImage(qr) + } - this.sock.ev.on( - 'connection.update', - ({ connection, lastDisconnect }) => { if (lastDisconnect?.error) { - saveCreds() - - this.baileys() + this.emit('require_action', { + instructions: [ + `Algo sucedio reinicia el bot o revisa tu whatsapp`, + `Necesitas ayuda: https://link.codigoencasa.com/DISCORD`, + ], + }) } - if (connection === 'open') { - console.log('Baileys is connected') + if (connection === 'open') this.emit('ready', true) + }, + }, + { + event: 'messages.upsert', + func: ({ messages }) => { + const [messageCtx] = messages + let payload = { + ...messageCtx, + body: messageCtx?.message?.conversation, + from: messageCtx?.key?.remoteJid, } - } - ) + if (payload.from === 'status@broadcast') { + return + } + + if (!baileyIsValidNumber(payload.from)) { + return + } + payload.from = baileyCleanNumber(payload.from, true) + this.emit('message', payload) + }, + }, + ] + + initBusEvents = () => { + const listEvents = this.busEvents() + + for (const { event, func } of listEvents) { + this.vendor.ev.on(event, func) + } } /** - * + * @alpha * @param {string} number * @param {string} message - * @example await sendMessage('+51925465621', 'Hello World') - */ - async sendMessage(number, message) { - const numberClean = number.replace('+', '') - await this.sock.sendMessage(`${numberClean}@c.us`, { text: message }) - } - - /** - * - * @param {string} number - * @param {string} message - * @example await sendMessage('+51925465621', 'https://dominio.com/imagen.jpg' | 'img/imagen.jpg') + * @example await sendMessage('+XXXXXXXXXXX', 'https://dominio.com/imagen.jpg' | 'img/imagen.jpg') */ - async sendImage(number, imageUrl) { - const numberClean = number.replace('+', '') - await this.sock.sendMessage(`${numberClean}@c.us`, { + sendMedia = async (number, imageUrl) => { + await this.vendor.sendMessage(number, { image: { url: imageUrl }, }) } /** - * + * @alpha * @param {string} number * @param {string} message * @param {boolean} voiceNote optional - * @example await sendMessage('+51925465621', 'audio.mp3') + * @example await sendMessage('+XXXXXXXXXXX', 'audio.mp3') */ - async sendAudio(number, audioUrl, voiceNote = false) { + sendAudio = async (number, audioUrl, voiceNote = false) => { const numberClean = number.replace('+', '') - await this.sock.sendMessage(`${numberClean}@c.us`, { + await this.vendor.sendMessage(`${numberClean}@c.us`, { audio: { url: audioUrl }, ptt: voiceNote, }) } + + /** + * + * @param {string} number + * @param {string} message + * @returns + */ + sendText = async (number, message) => { + return this.vendor.sendMessage(number, { text: message }) + } + /** + * TODO: Necesita terminar de implementar el sendMedia y sendButton guiarse: + * https://github.com/leifermendez/bot-whatsapp/blob/4e0fcbd8347f8a430adb43351b5415098a5d10df/packages/provider/src/web-whatsapp/index.js#L165 + * @param {string} number + * @param {string} message + * @example await sendMessage('+XXXXXXXXXXX', 'Hello World') + */ + sendMessage = async (numberIn, message, { options }) => { + const number = baileyCleanNumber(numberIn) + + // if (options?.buttons?.length) + // return this.sendButtons(number, message, options.buttons) + if (options?.media) return this.sendMedia(number, options.media) + return this.sendText(number, message) + } } + +module.exports = BaileysProvider diff --git a/packages/provider/src/baileys/utils.js b/packages/provider/src/baileys/utils.js new file mode 100644 index 0000000..7da3c0a --- /dev/null +++ b/packages/provider/src/baileys/utils.js @@ -0,0 +1,21 @@ +const { createWriteStream } = require('fs') +const qr = require('qr-image') + +const baileyCleanNumber = (number, full = false) => { + number = number.replace('@s.whatsapp.net', '') + number = !full ? `${number}@s.whatsapp.net` : `${number}` + return number +} + +const baileyGenerateImage = (base64) => { + let qr_svg = qr.image(base64, { type: 'png', margin: 4 }) + qr_svg.pipe(createWriteStream(`${process.cwd()}/qr.png`)) +} + +const baileyIsValidNumber = (rawNumber) => { + const regexGroup = /\@g.us\b/gm + const exist = rawNumber.match(regexGroup) + return !exist +} + +module.exports = { baileyCleanNumber, baileyGenerateImage, baileyIsValidNumber } diff --git a/packages/provider/src/venom/index.js b/packages/provider/src/venom/index.js index 04f5e3b..bb167e4 100644 --- a/packages/provider/src/venom/index.js +++ b/packages/provider/src/venom/index.js @@ -4,7 +4,7 @@ const { venomCleanNumber, venomGenerateImage, venomisValidNumber, -} = require('./utils/utils') +} = require('./utils') /** * ⚙️ VenomProvider: Es una clase tipo adaptor @@ -13,7 +13,6 @@ const { */ class VenomProvider extends ProviderClass { vendor - constructor() { super() this.init().then(() => this.initBusEvents()) diff --git a/packages/provider/src/venom/utils/utils.js b/packages/provider/src/venom/utils.js similarity index 100% rename from packages/provider/src/venom/utils/utils.js rename to packages/provider/src/venom/utils.js diff --git a/packages/provider/src/web-whatsapp/index.js b/packages/provider/src/web-whatsapp/index.js index d7593e4..ae8747e 100644 --- a/packages/provider/src/web-whatsapp/index.js +++ b/packages/provider/src/web-whatsapp/index.js @@ -3,10 +3,10 @@ const { ProviderClass } = require('@bot-whatsapp/bot') const { Console } = require('console') const { createWriteStream } = require('fs') const { - cleanNumber, - generateImage, - isValidNumber, - downloadMedia, + wwebCleanNumber, + wwebDownloadMedia, + wwebGenerateImage, + wwebIsValidNumber, } = require('./utils') const logger = new Console({ @@ -65,7 +65,7 @@ class WebWhatsappProvider extends ProviderClass { `Necesitas ayuda: https://link.codigoencasa.com/DISCORD`, ], }) - generateImage(qr) + wwebGenerateImage(qr) }, }, { @@ -79,10 +79,10 @@ class WebWhatsappProvider extends ProviderClass { return } - if (!isValidNumber(payload.from)) { + if (!wwebIsValidNumber(payload.from)) { return } - payload.from = cleanNumber(payload.from, true) + payload.from = wwebCleanNumber(payload.from, true) this.emit('message', payload) }, }, @@ -98,7 +98,7 @@ class WebWhatsappProvider extends ProviderClass { */ sendMedia = async (number, mediaInput = null) => { if (!mediaInput) throw new Error(`NO_SE_ENCONTRO: ${mediaInput}`) - const fileDownloaded = await downloadMedia(mediaInput) + const fileDownloaded = await wwebDownloadMedia(mediaInput) const media = MessageMedia.fromFilePath(fileDownloaded) return this.vendor.sendMessage(number, media, { sendAudioAsVoice: true, @@ -163,7 +163,7 @@ class WebWhatsappProvider extends ProviderClass { * @returns */ sendMessage = async (userId, message, { options }) => { - const number = cleanNumber(userId) + const number = wwebCleanNumber(userId) if (options?.buttons?.length) return this.sendButtons(number, message, options.buttons) if (options?.media) return this.sendMedia(number, options.media) diff --git a/packages/provider/src/web-whatsapp/utils.js b/packages/provider/src/web-whatsapp/utils.js index 2f987e2..4a29b35 100644 --- a/packages/provider/src/web-whatsapp/utils.js +++ b/packages/provider/src/web-whatsapp/utils.js @@ -4,18 +4,18 @@ const { tmpdir } = require('os') const http = require('http') const https = require('https') -const cleanNumber = (number, full = false) => { +const wwebCleanNumber = (number, full = false) => { number = number.replace('@c.us', '') number = !full ? `${number}@c.us` : `${number}` return number } -const generateImage = (base64) => { +const wwebGenerateImage = (base64) => { let qr_svg = qr.image(base64, { type: 'png', margin: 4 }) - qr_svg.pipe(createWriteStream(`${process.cwd()}/qr.svg`)) + qr_svg.pipe(createWriteStream(`${process.cwd()}/qr.png`)) } -const isValidNumber = (rawNumber) => { +const wwebIsValidNumber = (rawNumber) => { const regexGroup = /\@g.us\b/gm const exist = rawNumber.match(regexGroup) return !exist @@ -27,7 +27,7 @@ const isValidNumber = (rawNumber) => { * @param {*} url * @returns */ -const downloadMedia = (url) => { +const wwebDownloadMedia = (url) => { return new Promise((resolve, reject) => { const ext = url.split('.').pop() const checkProtocol = url.includes('https:') @@ -50,4 +50,9 @@ const downloadMedia = (url) => { }) } -module.exports = { cleanNumber, generateImage, isValidNumber, downloadMedia } +module.exports = { + wwebCleanNumber, + wwebGenerateImage, + wwebIsValidNumber, + wwebDownloadMedia, +}