This commit is contained in:
2022-12-17 07:44:49 -06:00
25 changed files with 905 additions and 61 deletions

26
.vscode/bot.code-snippets vendored Normal file
View File

@@ -0,0 +1,26 @@
{
"Flow Bot (simple)": {
"scope": "javascript",
"prefix": "bot:flow",
"description": "Crear un flujo simple",
"body": [
"export const ${1:${TM_FILENAME_BASE/(.*)/${1:/capitalize}/}}Flow = addKeyword(['hola', 'buenas'])",
" .addAnswer('Hola! 🚀 Bienvenido a este CHATBOT')",
" .addAnswer('¿Como puedo ayudarte?', {",
" delay: 1500",
"})"
]
},
"Flow Bot (completo)": {
"scope": "javascript",
"prefix": "bot:flow completo",
"description": "Crear un flujo completo",
"body": [
"export const ${1:${TM_FILENAME_BASE/(.*)/${1:/capitalize}/}}Flow = addKeyword(['hola', 'buenas'])",
" .addAnswer('Hola! 🚀 Bienvenido a este CHATBOT')",
" .addAnswer('¿Como puedo ayudarte?', {",
" delay: 1500",
"})"
]
}
}

View File

@@ -1,6 +1,8 @@
[![Test / Coverage](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml/badge.svg)](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml) [![Test / Coverage](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml/badge.svg)](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
-------- --------
🐤 Tablero de tareas : [https://github.com/users/leifermendez/projects/4/views/1](https://github.com/users/leifermendez/projects/4/views/1)
🦊 Documentación: [https://bot-whatsapp.pages.dev/](https://bot-whatsapp.pages.dev/) 🦊 Documentación: [https://bot-whatsapp.pages.dev/](https://bot-whatsapp.pages.dev/)
Video como hacer PR: https://youtu.be/Lxt8Acob6aU Video como hacer PR: https://youtu.be/Lxt8Acob6aU

View File

@@ -1,6 +1,6 @@
{ {
"name": "@bot-whatsapp/bot", "name": "@bot-whatsapp/bot",
"version": "0.0.22-alpha.0", "version": "0.0.24-alpha.0",
"description": "", "description": "",
"main": "./lib/bundle.bot.cjs", "main": "./lib/bundle.bot.cjs",
"scripts": { "scripts": {

View File

@@ -33,10 +33,10 @@ const startInteractive = async () => {
message: '¿Cuál proveedor de whatsapp quieres utilizar?', message: '¿Cuál proveedor de whatsapp quieres utilizar?',
choices: [ choices: [
{ title: 'whatsapp-web.js (gratis)', value: 'wweb' }, { title: 'whatsapp-web.js (gratis)', value: 'wweb' },
{ title: 'Twilio', value: 'twilio' },
{ title: 'Venom (gratis)', value: 'venom' }, { title: 'Venom (gratis)', value: 'venom' },
{ title: 'Baileys (gratis)', value: 'bailey' }, { title: 'Baileys (gratis)', value: 'bailey' },
{ title: 'API Oficial (Meta)', value: 'meta', disabled: true }, { title: 'Twilio', value: 'twilio' },
{ title: 'API Oficial (Meta)', value: 'meta' },
], ],
max: 1, max: 1,
hint: 'Espacio para seleccionar', hint: 'Espacio para seleccionar',

View File

@@ -1,6 +1,6 @@
{ {
"name": "@bot-whatsapp/cli", "name": "@bot-whatsapp/cli",
"version": "0.0.29-alpha.0", "version": "0.0.31-alpha.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"devDependencies": { "devDependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "create-bot-whatsapp", "name": "create-bot-whatsapp",
"version": "0.0.40-alpha.0", "version": "0.0.42-alpha.0",
"description": "", "description": "",
"main": "./lib/bundle.create-bot-whatsapp.cjs", "main": "./lib/bundle.create-bot-whatsapp.cjs",
"files": [ "files": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "@bot-whatsapp/database", "name": "@bot-whatsapp/database",
"version": "0.0.21-alpha.0", "version": "0.0.23-alpha.0",
"description": "Esto es el conector a mysql, pg, mongo", "description": "Esto es el conector a mysql, pg, mongo",
"main": "./lib/mock/index.cjs", "main": "./lib/mock/index.cjs",
"keywords": [], "keywords": [],

View File

@@ -1,23 +1,20 @@
require('dotenv').config()
const { MongoClient } = require('mongodb') const { MongoClient } = require('mongodb')
const DB_URI = process.env.DB_URI || 'mongodb://0.0.0.0:27017'
const DB_NAME = process.env.DB_NAME || 'db_bot'
class MongoAdapter { class MongoAdapter {
db db
listHistory = [] listHistory = []
credentials = { dbUri: null, dbName: null }
constructor() { constructor(_credentials) {
this.credentials = _credentials
this.init().then() this.init().then()
} }
init = async () => { init = async () => {
try { try {
const client = new MongoClient(DB_URI, {}) const client = new MongoClient(this.credentials.dbUri, {})
await client.connect() await client.connect()
console.log('🆗 Conexión Correcta DB') console.log('🆗 Conexión Correcta DB')
const db = client.db(DB_NAME) const db = client.db(this.credentials.dbName)
this.db = db this.db = db
return true return true
} catch (e) { } catch (e) {

View File

@@ -3,7 +3,7 @@ const mysql = require('mysql2')
class MyslAdapter { class MyslAdapter {
db db
listHistory = [] listHistory = []
credentials = { host: null, user: null, database: null } credentials = { host: null, user: null, database: null, password: null }
constructor(_credentials) { constructor(_credentials) {
this.credentials = _credentials this.credentials = _credentials

View File

@@ -1,6 +1,6 @@
{ {
"name": "@bot-whatsapp/provider", "name": "@bot-whatsapp/provider",
"version": "0.0.27-alpha.0", "version": "0.0.29-alpha.0",
"description": "Esto es el conector a Twilio, Meta, etc...", "description": "Esto es el conector a Twilio, Meta, etc...",
"main": "./lib/mock/index.cjs", "main": "./lib/mock/index.cjs",
"keywords": [], "keywords": [],
@@ -11,6 +11,7 @@
], ],
"dependencies": { "dependencies": {
"@bot-whatsapp/bot": "*", "@bot-whatsapp/bot": "*",
"combine-image": "^1.0.3",
"qr-image": "^3.2.0" "qr-image": "^3.2.0"
}, },
"exports": { "exports": {

View File

@@ -70,7 +70,7 @@ class BaileysProvider extends ProviderClass {
`Necesitas ayuda: https://link.codigoencasa.com/DISCORD`, `Necesitas ayuda: https://link.codigoencasa.com/DISCORD`,
], ],
}) })
baileyGenerateImage(qr) await baileyGenerateImage(qr)
} }
if (lastDisconnect?.error) { if (lastDisconnect?.error) {

View File

@@ -1,4 +1,5 @@
const { createWriteStream } = require('fs') const { createWriteStream } = require('fs')
const combineImage = require('combine-image')
const qr = require('qr-image') const qr = require('qr-image')
const baileyCleanNumber = (number, full = false) => { const baileyCleanNumber = (number, full = false) => {
@@ -7,9 +8,28 @@ const baileyCleanNumber = (number, full = false) => {
return number return number
} }
const baileyGenerateImage = (base64) => { /**
* Hace promesa el write
* @param {*} base64
*/
const baileyGenerateImage = async (base64) => {
const PATH_QR = `${process.cwd()}/qr.png`
let qr_svg = qr.image(base64, { type: 'png', margin: 4 }) let qr_svg = qr.image(base64, { type: 'png', margin: 4 })
qr_svg.pipe(createWriteStream(`${process.cwd()}/qr.png`))
const writeFilePromise = () =>
new Promise((resolve, reject) => {
const file = qr_svg.pipe(createWriteStream(PATH_QR))
file.on('finish', () => resolve(true))
file.on('error', reject)
})
await writeFilePromise()
const cleanImage = await combineImage([PATH_QR], {
margin: 15,
color: 0xffffffff,
})
cleanImage.write(PATH_QR)
} }
const baileyIsValidNumber = (rawNumber) => { const baileyIsValidNumber = (rawNumber) => {

View File

@@ -32,7 +32,7 @@ class MetaProvider extends ProviderClass {
} }
/** /**
* Mapeamos los eventos nativos de whatsapp-web.js a los que la clase Provider espera * Mapeamos los eventos nativos a los que la clase Provider espera
* para tener un standar de eventos * para tener un standar de eventos
* @returns * @returns
*/ */
@@ -53,7 +53,7 @@ class MetaProvider extends ProviderClass {
}, },
] ]
async sendMessageMeta(body) { sendMessageMeta = async (body) => {
try { try {
const response = await axios.post( const response = await axios.post(
`${URL}/${this.numberId}/messages`, `${URL}/${this.numberId}/messages`,
@@ -70,7 +70,7 @@ class MetaProvider extends ProviderClass {
} }
} }
async sendtext(number, message) { sendtext = async (number, message) => {
const body = { const body = {
messaging_product: 'whatsapp', messaging_product: 'whatsapp',
to: number, to: number,
@@ -83,7 +83,7 @@ class MetaProvider extends ProviderClass {
await this.sendMessageMeta(body) await this.sendMessageMeta(body)
} }
async sendMedia(number, message, mediaInput = null) { sendMedia = async (number, _, mediaInput = null) => {
if (!mediaInput) throw new Error(`MEDIA_INPUT_NULL_: ${mediaInput}`) if (!mediaInput) throw new Error(`MEDIA_INPUT_NULL_: ${mediaInput}`)
const body = { const body = {
messaging_product: 'whatsapp', messaging_product: 'whatsapp',

View File

@@ -49,7 +49,7 @@ class VenomProvider extends ProviderClass {
/** /**
* Generamos QR Code pra escanear con el Whatsapp * Generamos QR Code pra escanear con el Whatsapp
*/ */
generateQr = (qr) => { generateQr = async (qr) => {
console.clear() console.clear()
this.emit('require_action', { this.emit('require_action', {
instructions: [ instructions: [
@@ -58,7 +58,7 @@ class VenomProvider extends ProviderClass {
`Necesitas ayuda: https://link.codigoencasa.com/DISCORD`, `Necesitas ayuda: https://link.codigoencasa.com/DISCORD`,
], ],
}) })
venomGenerateImage(qr) await venomGenerateImage(qr)
} }
/** /**

View File

@@ -1,4 +1,5 @@
const { writeFile } = require('fs') const { writeFile } = require('fs')
const combineImage = require('combine-image')
const venomCleanNumber = (number, full = false) => { const venomCleanNumber = (number, full = false) => {
number = number.replace('@c.us', '') number = number.replace('@c.us', '')
@@ -6,7 +7,8 @@ const venomCleanNumber = (number, full = false) => {
return number return number
} }
const venomGenerateImage = (base) => { const venomGenerateImage = async (base) => {
const PATH_QR = `${process.cwd()}/qr.png`
const matches = base.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/) const matches = base.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/)
if (matches.length !== 3) { if (matches.length !== 3) {
return new Error('Invalid input string') return new Error('Invalid input string')
@@ -16,16 +18,21 @@ const venomGenerateImage = (base) => {
response.type = matches[1] response.type = matches[1]
response.data = new Buffer.from(matches[2], 'base64') response.data = new Buffer.from(matches[2], 'base64')
var imageBuffer = response const writeFilePromise = () =>
writeFile( new Promise((resolve, reject) => {
`${process.cwd()}/qr.png`, writeFile(PATH_QR, response['data'], 'binary', (err) => {
imageBuffer['data'], if (err != null) reject('ERROR_QR_GENERATE')
'binary', resolve(true)
(err) => { })
if (err != null) throw new Error('ERROR_QR_GENERATE') })
return
} await writeFilePromise()
)
const cleanImage = await combineImage([PATH_QR], {
margin: 15,
color: 0xffffffff,
})
cleanImage.write(PATH_QR)
} }
const venomisValidNumber = (rawNumber) => { const venomisValidNumber = (rawNumber) => {

View File

@@ -57,7 +57,7 @@ class WebWhatsappProvider extends ProviderClass {
}, },
{ {
event: 'qr', event: 'qr',
func: (qr) => { func: async (qr) => {
this.emit('require_action', { this.emit('require_action', {
instructions: [ instructions: [
`Debes escanear el QR Code para iniciar session reivsa qr.png`, `Debes escanear el QR Code para iniciar session reivsa qr.png`,
@@ -65,7 +65,7 @@ class WebWhatsappProvider extends ProviderClass {
`Necesitas ayuda: https://link.codigoencasa.com/DISCORD`, `Necesitas ayuda: https://link.codigoencasa.com/DISCORD`,
], ],
}) })
wwebGenerateImage(qr) await wwebGenerateImage(qr)
}, },
}, },
{ {

View File

@@ -1,4 +1,5 @@
const { createWriteStream } = require('fs') const { createWriteStream } = require('fs')
const combineImage = require('combine-image')
const qr = require('qr-image') const qr = require('qr-image')
const { tmpdir } = require('os') const { tmpdir } = require('os')
const http = require('http') const http = require('http')
@@ -10,9 +11,24 @@ const wwebCleanNumber = (number, full = false) => {
return number return number
} }
const wwebGenerateImage = (base64) => { const wwebGenerateImage = async (base64) => {
const PATH_QR = `${process.cwd()}/qr.png`
let qr_svg = qr.image(base64, { type: 'png', margin: 4 }) let qr_svg = qr.image(base64, { type: 'png', margin: 4 })
qr_svg.pipe(createWriteStream(`${process.cwd()}/qr.png`))
const writeFilePromise = () =>
new Promise((resolve, reject) => {
const file = qr_svg.pipe(createWriteStream(PATH_QR))
file.on('finish', () => resolve(true))
file.on('error', reject)
})
await writeFilePromise()
const cleanImage = await combineImage([PATH_QR], {
margin: 15,
color: 0xffffffff,
})
cleanImage.write(PATH_QR)
} }
const wwebIsValidNumber = (rawNumber) => { const wwebIsValidNumber = (rawNumber) => {

View File

@@ -7,12 +7,17 @@ const {
} = require('@bot-whatsapp/bot') } = require('@bot-whatsapp/bot')
const BaileysProvider = require('@bot-whatsapp/provider/baileys') const BaileysProvider = require('@bot-whatsapp/provider/baileys')
const MockAdapter = require('@bot-whatsapp/database/mock') const MongoAdapter = require('@bot-whatsapp/database/mock')
/**
* Declaramos las conexiones de Mongo
*/
const MONGO_DB_URI = 'mongodb://0.0.0.0:27017'
const MONGO_DB_NAME = 'db_bot'
/** /**
* Declarando flujo hijo * Declarando flujo hijo
*/ */
const flowBolsos2 = addKeyword(['bolsos2', '2']) const flowBolsos2 = addKeyword(['bolsos2', '2'])
.addAnswer('🤯 *MUCHOS* bolsos ...') .addAnswer('🤯 *MUCHOS* bolsos ...')
.addAnswer('y mas bolsos... bla bla') .addAnswer('y mas bolsos... bla bla')
@@ -68,7 +73,10 @@ const flowPrincipal = addKeyword(['hola', 'ole', 'alo'])
) )
const main = async () => { const main = async () => {
const adapterDB = new MockAdapter() const adapterDB = new MongoAdapter({
dbUri: MONGO_DB_URI,
dbName: MONGO_DB_NAME,
})
const adapterFlow = createFlow([flowPrincipal]) const adapterFlow = createFlow([flowPrincipal])
const adapterProvider = createProvider(BaileysProvider) const adapterProvider = createProvider(BaileysProvider)
createBot({ createBot({

View File

@@ -9,12 +9,13 @@
}, },
"keywords": [], "keywords": [],
"dependencies": { "dependencies": {
"@adiwajshing/baileys": "^4.4.0",
"mime-types": "^2.1.35",
"@bot-whatsapp/bot": "latest", "@bot-whatsapp/bot": "latest",
"@bot-whatsapp/cli": "latest", "@bot-whatsapp/cli": "latest",
"@bot-whatsapp/database": "latest", "@bot-whatsapp/database": "latest",
"@bot-whatsapp/provider": "latest" "@bot-whatsapp/provider": "latest",
"@adiwajshing/baileys": "^4.4.0",
"mime-types": "^2.1.35",
"mongodb": "^4.12.1"
}, },
"author": "", "author": "",
"license": "ISC" "license": "ISC"

View File

@@ -9,6 +9,14 @@ const {
const BaileysProvider = require('@bot-whatsapp/provider/baileys') const BaileysProvider = require('@bot-whatsapp/provider/baileys')
const MySQLAdapter = require('@bot-whatsapp/database/mysql') const MySQLAdapter = require('@bot-whatsapp/database/mysql')
/**
* Declaramos las conexiones de MySQL
*/
const MYSQL_DB_HOST = 'localhost'
const MYSQL_DB_USER = 'user'
const MYSQL_DB_PASSWORD = 'pass'
const MYSQL_DB_NAME = 'bot'
/** /**
* Declarando flujo hijo * Declarando flujo hijo
*/ */

View File

@@ -9,12 +9,12 @@
}, },
"keywords": [], "keywords": [],
"dependencies": { "dependencies": {
"@adiwajshing/baileys": "^4.4.0",
"mime-types": "^2.1.35",
"@bot-whatsapp/bot": "latest", "@bot-whatsapp/bot": "latest",
"@bot-whatsapp/cli": "latest", "@bot-whatsapp/cli": "latest",
"@bot-whatsapp/database": "latest", "@bot-whatsapp/database": "latest",
"@bot-whatsapp/provider": "latest", "@bot-whatsapp/provider": "latest",
"@adiwajshing/baileys": "^4.4.0",
"mime-types": "^2.1.35",
"mysql2": "^2.3.3" "mysql2": "^2.3.3"
}, },
"author": "", "author": "",

View File

@@ -0,0 +1,12 @@
### BASE APP
Este bot contiene un flujo basico en el cual una persona (cliente) escribe **"hola"** y el bot responde.
- Bienvenido a mi tienda
- Como puedo ayudarte?
- Tengo: Zapatos Bolsos etc..
------
- [Discord](https://link.codigoencasa.com/DISCORD)
- [Twitter](https://twitter.com/leifermendez)
- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
- [Telegram](https://t.me/leifermendez)

View File

@@ -0,0 +1,88 @@
const {
createBot,
createProvider,
createFlow,
addKeyword,
addChild,
} = require('@bot-whatsapp/bot')
const TwilioProvider = require('@bot-whatsapp/provider/twilio')
const MockAdapter = require('@bot-whatsapp/database/mock')
/**
* Declarando flujo hijo
*/
const flowBolsos2 = addKeyword(['bolsos2', '2'])
.addAnswer('🤯 *MUCHOS* bolsos ...')
.addAnswer('y mas bolsos... bla bla')
const flowZapatos2 = addKeyword(['zapatos2', '2'])
.addAnswer('🤯 repito que tengo *MUCHOS* zapatos.')
.addAnswer('y algunas otras cosas.')
const flowZapatos = addKeyword(['zapatos', 'ZAPATOS'])
.addAnswer('🤯 Veo que elegiste zapatos')
.addAnswer('Tengo muchos zapatos...bla bla')
.addAnswer(
['Manda:', '*2*', 'o', '*zapatos2*', 'para mas información'],
{ capture: true },
(ctx) => {
console.log('Aqui puedes ver más info del usuario...')
console.log('Puedes enviar un mail, hook, etc..')
console.log(ctx)
},
[...addChild(flowZapatos2)]
)
const flowBolsos = addKeyword(['bolsos', 'BOLSOS'])
.addAnswer('🙌 Veo que elegiste bolsos')
.addAnswer('Tengo muchos bolsos...bla bla')
.addAnswer(
['Manda:', '*2*', 'o', '*bolsos2*', 'para mas información.'],
{ capture: true },
(ctx) => {
console.log('Aqui puedes ver más info del usuario...')
console.log('Puedes enviar un mail, hook, etc..')
console.log(ctx)
},
[...addChild(flowBolsos2)]
)
/**
* Declarando flujo principal
*/
const flowPrincipal = addKeyword(['hola', 'ole', 'alo'])
.addAnswer('Hola, bienvenido a mi tienda')
.addAnswer('Como puedo ayudarte?')
.addAnswer(['Tengo:', 'Zapatos', 'Bolsos', 'etc..'])
.addAnswer(
['Para continuar escribe:', '*Zapatos*', 'o', '*Bolsos*'],
{ capture: true },
(ctx) => {
console.log('Aqui puedes ver más info del usuario...')
console.log('Puedes enviar un mail, hook, etc..')
console.log(ctx)
console.log(ctx['_data']['notifyName'])
},
[...addChild(flowBolsos), ...addChild(flowZapatos)]
)
const main = async () => {
const adapterDB = new MockAdapter()
const adapterFlow = createFlow([flowPrincipal])
const adapterProvider = createProvider(TwilioProvider, {
accountSid: 'YOUR_ACCOUNT_SID',
authToken: 'YOUR_ACCOUNT_TOKEN',
vendorNumber: '+14155238886',
})
createBot({
flow: adapterFlow,
provider: adapterProvider,
database: adapterDB,
})
}
main()

View File

@@ -0,0 +1,22 @@
{
"name": "bot-whatsapp-base-meta-memory",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"pre-copy": "cd .. && yarn run copy.lib base-meta-memory",
"start": "node app.js"
},
"keywords": [],
"dependencies": {
"body-parser": "^1.20.1",
"polka": "^0.5.2",
"twilio": "^3.83.4",
"@bot-whatsapp/bot": "latest",
"@bot-whatsapp/cli": "latest",
"@bot-whatsapp/database": "latest",
"@bot-whatsapp/provider": "latest"
},
"author": "",
"license": "ISC"
}

660
yarn.lock

File diff suppressed because it is too large Load Diff