diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index aab4756..d587c64 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -67,9 +67,13 @@ jobs: - name: Release Github run: yarn node ./scripts/github.js --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.OCTO_TOKEN }}" + - name: 'Run if changes have been detected' + run: | + git add . + git commit -m "chore(version): pre release" + - name: Commit Versioning & Push changes if: github.event_name == 'push' uses: stefanzweifel/git-auto-commit-action@v4 with: commit_message: 'chore(version): launch release ๐Ÿš€ "${{ steps.package-version.outputs.current-version}}"' - branch: dev diff --git a/CHANGELOG.md b/CHANGELOG.md index 023883a..48381f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.1.16](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.15...v0.1.16) (2023-01-11) + +### [0.1.15](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.14...v0.1.15) (2023-01-11) + +### [0.1.14](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.13...v0.1.14) (2023-01-11) + ### [0.1.13](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.12...v0.1.13) (2023-01-11) ### [0.1.12](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.10...v0.1.12) (2023-01-11) diff --git a/package.json b/package.json index 07bc07d..a6e64e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bot-whatsapp/root", - "version": "0.1.13", + "version": "0.1.16", "description": "Bot de wahtsapp open source para MVP o pequeรฑos negocios", "main": "app.js", "private": true, diff --git a/packages/bot/core/core.class.js b/packages/bot/core/core.class.js index 4243713..79d996b 100644 --- a/packages/bot/core/core.class.js +++ b/packages/bot/core/core.class.js @@ -8,6 +8,9 @@ const { createWriteStream } = require('fs') const logger = new Console({ stdout: createWriteStream(`${process.cwd()}/core.class.log`), }) + +const QueuePrincipal = new Queue() + /** * [ ] Escuchar eventos del provider asegurarte que los provider emitan eventos * [ ] Guardar historial en db @@ -84,20 +87,40 @@ class CoreClass { this.databaseClass.save(ctxByNumber) } + // ๐Ÿ“„ Esta funcion se encarga de enviar un array de mensajes dentro de este ctx + const sendFlow = async (messageToSend, numberOrId) => { + const queue = [] + for (const ctxMessage of messageToSend) { + const delayMs = ctxMessage?.options?.delay || 0 + if (delayMs) await delay(delayMs) + QueuePrincipal.enqueue(() => + Promise.all([ + this.sendProviderAndSave(numberOrId, ctxMessage), + resolveCbEveryCtx(ctxMessage), + ]) + ) + } + return Promise.all(queue) + } + // ๐Ÿ“„ [options: fallBack]: esta funcion se encarga de repetir el ultimo mensaje - const fallBack = () => { + const fallBack = async () => { fallBackFlag = true - msgToSend = this.flowClass.find(refToContinue?.keyword, true) || [] - this.sendFlow(msgToSend, from) + await this.sendProviderAndSave(from, refToContinue) + QueuePrincipal.queue = [] return refToContinue } // ๐Ÿ“„ [options: flowDynamic]: esta funcion se encarga de responder un array de respuesta esta limitado a 5 mensajes // para evitar bloque de whatsapp - const flowDynamic = (listMsg = [], optListMsg = { limit: 3 }) => { + const flowDynamic = async ( + listMsg = [], + optListMsg = { limit: 5, fallback: false } + ) => { if (!Array.isArray(listMsg)) throw new Error('Esto debe ser un ARRAY') + fallBackFlag = optListMsg.fallback const parseListMsg = listMsg .map(({ body }, index) => toCtx({ @@ -108,26 +131,38 @@ class CoreClass { }) ) .slice(0, optListMsg.limit) - msgToSend = parseListMsg - this.sendFlow(msgToSend, from) + for (const msg of parseListMsg) { + await this.sendProviderAndSave(from, msg) + } return } + // ๐Ÿ“„ Se encarga de revisar si el contexto del mensaje tiene callback o fallback + const resolveCbEveryCtx = async (ctxMessage) => { + if (prevMsg?.options?.capture) return cbEveryCtx(prevMsg?.ref) + if (!ctxMessage?.options?.capture) + return await cbEveryCtx(ctxMessage?.ref) + } + // ๐Ÿ“„ Se encarga de revisar si el contexto del mensaje tiene callback y ejecutarlo - const cbEveryCtx = (inRef) => { - this.flowClass.allCallbacks[inRef](messageCtxInComming, { + const cbEveryCtx = async (inRef) => { + if (!this.flowClass.allCallbacks[inRef]) return Promise.resolve() + return this.flowClass.allCallbacks[inRef](messageCtxInComming, { fallBack, flowDynamic, }) } + if (prevMsg?.ref) resolveCbEveryCtx(prevMsg) + // ๐Ÿ“„ [options: callback]: Si se tiene un callback se ejecuta - if (!fallBackFlag) { - if (prevMsg?.options?.capture) cbEveryCtx(prevMsg?.ref) - for (const ite of this.flowClass.find(body)) { - if (!ite?.options?.capture) cbEveryCtx(ite?.ref) - } - } + //TODO AQUI + // if (!fallBackFlag) { + // if (prevMsg?.options?.capture) cbEveryCtx(prevMsg?.ref) + // for (const ite of this.flowClass.find(body)) { + // if (!ite?.options?.capture) cbEveryCtx(ite?.ref) + // } + // } // ๐Ÿ“„๐Ÿค˜(tiene return) [options: nested(array)]: Si se tiene flujos hijos los implementa if (!fallBackFlag && prevMsg?.options?.nested?.length) { @@ -138,11 +173,12 @@ class CoreClass { msgToSend = this.flowClass.find(body, false, flowStandalone) || [] - for (const ite of msgToSend) { - cbEveryCtx(ite?.ref) - } + // //TODO AQUI + // for (const ite of msgToSend) { + // cbEveryCtx(ite?.ref) + // } - this.sendFlow(msgToSend, from) + sendFlow(msgToSend, from) return } @@ -153,13 +189,13 @@ class CoreClass { if (['string', 'boolean'].includes(typeCapture) && valueCapture) { msgToSend = this.flowClass.find(refToContinue?.ref, true) || [] - this.sendFlow(msgToSend, from) + sendFlow(msgToSend, from) return } } msgToSend = this.flowClass.find(body) || [] - this.sendFlow(msgToSend, from) + sendFlow(msgToSend, from) } /** @@ -176,18 +212,6 @@ class CoreClass { ]) } - sendFlow = async (messageToSend, numberOrId) => { - const queue = [] - for (const ctxMessage of messageToSend) { - const delayMs = ctxMessage?.options?.delay || 0 - if (delayMs) await delay(delayMs) - Queue.enqueue(() => - this.sendProviderAndSave(numberOrId, ctxMessage) - ) - } - return Promise.all(queue) - } - /** * @private * @param {*} message diff --git a/packages/bot/rollup-bot.config.js b/packages/bot/rollup-bot.config.js index f8ffa2a..36bbb87 100644 --- a/packages/bot/rollup-bot.config.js +++ b/packages/bot/rollup-bot.config.js @@ -10,6 +10,7 @@ module.exports = [ banner: banner['banner.output'].join(''), file: join(__dirname, 'lib', 'bundle.bot.cjs'), format: 'cjs', + sourcemap: true, }, plugins: [commonjs(), nodeResolve()], }, diff --git a/packages/bot/utils/queue.js b/packages/bot/utils/queue.js index 1f610e9..873024a 100644 --- a/packages/bot/utils/queue.js +++ b/packages/bot/utils/queue.js @@ -1,8 +1,8 @@ class Queue { - static queue = [] - static pendingPromise = false + queue = [] + pendingPromise = false - static enqueue(promise) { + enqueue(promise) { return new Promise((resolve, reject) => { this.queue.push({ promise, @@ -13,7 +13,7 @@ class Queue { }) } - static dequeue() { + dequeue() { if (this.workingOnPromise) { return false } diff --git a/packages/docs/src/routes/docs/flows/index.mdx b/packages/docs/src/routes/docs/flows/index.mdx index 027c793..7a2171c 100644 --- a/packages/docs/src/routes/docs/flows/index.mdx +++ b/packages/docs/src/routes/docs/flows/index.mdx @@ -1,6 +1,6 @@ import Navigation from '../../../components/widgets/Navigation' -# Flow (Flujos) +# Flow Los flujos hace referencia al hecho de construir un flujo de conversion. Esto es un flow podemos observar que estan presente dos metodos importantes **addKeyword** y el **addAnswer**. @@ -23,6 +23,142 @@ const flowPrincipal = addKeyword(['hola', 'alo']) --- +## addKeyword() + +Esta funcion se utliza para iniciar un flujo de conversion.
Recibe un `string` o un `array` +de string `['hola','buenas']`. + +**Opciones** + +- sensitive: Sensible a mayusculas y minusculas por defecto `false` + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola') + +const flowArray = addKeyword(['hola', 'alo']) + +const flowSensitive = addKeyword(['hola', 'alo'], { + sensitive: true, +}) +``` + +--- + +## addAnswer() + +Esta funcion se utliza para responder un mensaje despues del `addKeyword()` + +**Opciones** + +- delay: 0 (milisegundos) +- media: url de imagen +- buttons: array `[{body:'Boton1'}, {body:'Boton2'}, {body:'Boton3'}]` +- capture: false (para esperar respuesta) +- child: Objecto tipo flujo o arra de flujos hijos + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola').addAnswer( + 'Este mensaje se enviara 1 segundo despues', + { + delay: 1000, + } +) + +const flowString = addKeyword('hola').addAnswer( + 'Este mensaje envia una imagen', + { + media: 'https://i.imgur.com/0HpzsEm.png', + } +) + +const flowString = addKeyword('hola').addAnswer( + 'Este mensaje envia tres botones', + { + buttons: [ + { body: 'Boton 1' }, + { body: 'Boton 2' }, + { body: 'Boton 3' }, + ], + } +) + +const flowString = addKeyword('hola').addAnswer( + 'Este mensaje espera una respueta del usuario', + { + capture: true, + } +) +``` + +--- + +## ctx + +Este argumento se utiliza para obtener el contexto de la conversaciรณn + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola').addAnswer( + 'Indica cual es tu email', + null, + (ctx) => { + console.log('๐Ÿ‘‰ Informacion del contexto: ', ctx) + } +) +``` + +--- + +## fallBack() + +Esta funcion se utliza para volver a enviar el ultimo mensaje abajo un ejemplo. +En el ejemplo de abajo esperamos que el usuario ingrese un mensaje que contenga `@` sino contiene +se repetira el mensaje `Indica cual es tu email` + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola').addAnswer( + 'Indica cual es tu email', + null, + (ctx, { fallBack }) => { + if (!ctx.body.includes('@')) return fallBack() + } +) +``` + +--- + +## flowDynamic() + +Esta funcion se utliza para devolver mensajes dinamicos que pueden venir de una API o Base de datos. +La funcion recibe un array que debe contener la siguiente estrucutura: + +`[{body:'Mensaje}, {body:'Mensaje2}]` + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola') + .addAnswer('Indica cual es tu email', null, async (ctx, {flowDynamic}) => { + const mensajesDB = () => { + const categories = db.find(...) + const mapDatos = categories.map((c) => ({body:c.name})) + return mapDatos + } + await flowDynamic(mensajesDB()) + }) + + +``` + +--- +