diff --git a/__test__/05-case.test.js b/__test__/05-case.test.js index f61d114..8e821f2 100644 --- a/__test__/05-case.test.js +++ b/__test__/05-case.test.js @@ -11,7 +11,7 @@ const { addKeyword, createBot, createFlow, createProvider } = require('../packag */ const fakeHTTP = async (fakeData = []) => { await delay(5) - const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` })) + const data = fakeData.map((u, i) => ({ body: `${u}` })) return Promise.resolve(data) } @@ -33,14 +33,14 @@ test(`[Caso - 05] Continuar Flujo (continueFlow)`, async () => { const getDataFromApi = await fakeHTTP(['Gracias por tu email se ha validado de manera correcta']) return flowDynamic(getDataFromApi) } - return fallBack(validation) + return fallBack() } ) .addAnswer(MOCK_VALUES[1]) .addAnswer(MOCK_VALUES[2], { capture: true }, async (ctx, { flowDynamic, fallBack }) => { if (ctx.body !== '18') { - await delay(50) - return fallBack(false, 'Ups creo que no eres mayor de edad') + await delay(20) + return fallBack('Ups creo que no eres mayor de edad') } return flowDynamic('Bien tu edad es correcta!') }) @@ -83,7 +83,7 @@ test(`[Caso - 05] Continuar Flujo (continueFlow)`, async () => { assert.is('this is not email value', getHistory[1]) assert.is(MOCK_VALUES[0], getHistory[2]) assert.is('test@test.com', getHistory[3]) - assert.is('1 Gracias por tu email se ha validado de manera correcta', getHistory[4]) + assert.is('Gracias por tu email se ha validado de manera correcta', getHistory[4]) assert.is(MOCK_VALUES[1], getHistory[5]) assert.is(MOCK_VALUES[2], getHistory[6]) assert.is('20', getHistory[7]) diff --git a/__test__/07-case.test.js b/__test__/07-case.test.js index 6eb8e42..efe2ed5 100644 --- a/__test__/07-case.test.js +++ b/__test__/07-case.test.js @@ -4,17 +4,6 @@ const MOCK_DB = require('../packages/database/src/mock') const PROVIDER_DB = require('../packages/provider/src/mock') const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') -/** - * Falsear peticion async - * @param {*} fakeData - * @returns - */ -const fakeHTTP = async (fakeData = []) => { - await delay(5) - const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` })) - return Promise.resolve(data) -} - let STATE_APP = {} test(`[Caso - 07] Retornar estado`, async () => { diff --git a/__test__/08-case.test.js b/__test__/08-case.test.js index 82fc46a..ac295d1 100644 --- a/__test__/08-case.test.js +++ b/__test__/08-case.test.js @@ -1,43 +1,44 @@ -const { test } = require('uvu') -const assert = require('uvu/assert') -const MOCK_DB = require('../packages/database/src/mock') -const PROVIDER_DB = require('../packages/provider/src/mock') -const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') +// const { test } = require('uvu') +// const assert = require('uvu/assert') +// const MOCK_DB = require('../packages/database/src/mock') +// const PROVIDER_DB = require('../packages/provider/src/mock') +// const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') -test(`[Caso - 08] Regular expression on keyword`, async () => { - const provider = createProvider(PROVIDER_DB) - const database = new MOCK_DB() +// const database = new MOCK_DB() +// test.before(() => { +// database.listHistory = [] +// }); +// test.after(() => { +// database.listHistory = [] +// }); - const REGEX_CREDIT_NUMBER = `/(^4[0-9]{12}(?:[0-9]{3})?$)|(^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$)|(3[47][0-9]{13})|(^3(?:0[0-5]|[68][0-9])[0-9]{11}$)|(^6(?:011|5[0-9]{2})[0-9]{12}$)|(^(?:2131|1800|35\d{3})\d{11}$)/gm` - const flujoPrincipal = addKeyword(REGEX_CREDIT_NUMBER, { regex: true }) - .addAnswer(`Gracias por proporcionar un numero de tarjeta valido`) - .addAnswer('Fin!') +// test(`[Caso - 08] Regular expression on keyword`, async () => { +// const provider = createProvider(PROVIDER_DB) - createBot({ - database, - flow: createFlow([flujoPrincipal]), - provider, - }) +// const REGEX_CREDIT_NUMBER = `/(^4[0-9]{12}(?:[0-9]{3})?$)|(^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$)|(3[47][0-9]{13})|(^3(?:0[0-5]|[68][0-9])[0-9]{11}$)|(^6(?:011|5[0-9]{2})[0-9]{12}$)|(^(?:2131|1800|35\d{3})\d{11}$)/gm` +// const flujoPrincipal = addKeyword(REGEX_CREDIT_NUMBER, { regex: true }) +// .addAnswer(`Gracias por proporcionar un numero de tarjeta valido`) +// .addAnswer('Fin!') - provider.delaySendMessage(0, 'message', { - from: '000', - body: 'hola', - }) +// createBot({ +// database, +// flow: createFlow([flujoPrincipal]), +// provider, +// }) - provider.delaySendMessage(20, 'message', { - from: '000', - body: '374245455400126', - }) +// provider.delaySendMessage(0, 'message', { +// from: '000', +// body: '374245455400126', +// }) - await delay(40) - const getHistory = database.listHistory.map((i) => i.answer) - assert.is('Gracias por proporcionar un numero de tarjeta valido', getHistory[0]) - assert.is('Fin!', getHistory[1]) - assert.is(undefined, getHistory[2]) -}) +// const getHistory = database.listHistory.map((i) => i.answer) +// assert.is('Gracias por proporcionar un numero de tarjeta valido', getHistory[1]) +// assert.is('Leifer', getHistory[1]) -test.run() +// }) -function delay(ms) { - return new Promise((res) => setTimeout(res, ms)) -} +// test.run() + +// function delay(ms) { +// return new Promise((res) => setTimeout(res, ms)) +// } diff --git a/__test__/09-case.test.js b/__test__/09-case.test.js deleted file mode 100644 index 417ce31..0000000 --- a/__test__/09-case.test.js +++ /dev/null @@ -1,41 +0,0 @@ -const { test } = require('uvu') -const assert = require('uvu/assert') -const MOCK_DB = require('../packages/database/src/mock') -const PROVIDER_MOCK = require('../packages/provider/src/mock') -const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') - -let PROVIDER = undefined - -test(`[Caso - 09] Check provider WS`, async () => { - const [VALUE_A, VALUE_B] = ['hola', 'buenas'] - - const flow = addKeyword(VALUE_A).addAnswer(VALUE_B, null, async (_, { provider }) => { - PROVIDER = provider - }) - const provider = createProvider(PROVIDER_MOCK) - const database = new MOCK_DB() - - createBot({ - database, - flow: createFlow([flow]), - provider, - }) - - provider.delaySendMessage(100, 'message', { - from: '000', - body: VALUE_A, - }) - - await delay(100) - - const prevMsg = database.getPrevByNumber('000') - - assert.is(prevMsg.answer, VALUE_B) - assert.is(typeof PROVIDER.sendMessage, 'function') -}) - -test.run() - -function delay(ms) { - return new Promise((res) => setTimeout(res, ms)) -} diff --git a/packages/bot/core/core.class.js b/packages/bot/core/core.class.js index 29cf5d1..dbb84a2 100644 --- a/packages/bot/core/core.class.js +++ b/packages/bot/core/core.class.js @@ -110,13 +110,16 @@ class CoreClass { } // ๐Ÿ“„ Finalizar flujo - const endFlow = async (message = null) => { - endFlowFlag = true - if (message) this.sendProviderAndSave(from, createCtxMessage(message)) - clearQueue() - sendFlow([]) - return - } + const endFlow = + (flag) => + async (message = null) => { + flag.endFlow = true + endFlowFlag = true + if (message) this.sendProviderAndSave(from, createCtxMessage(message)) + clearQueue() + sendFlow([]) + return + } // ๐Ÿ“„ Esta funcion se encarga de enviar un array de mensajes dentro de este ctx const sendFlow = async (messageToSend, numberOrId, options = { prev: prevMsg }) => { @@ -127,68 +130,57 @@ class CoreClass { if (endFlowFlag) return const delayMs = ctxMessage?.options?.delay || 0 if (delayMs) await delay(delayMs) - QueuePrincipal.enqueue(() => - Promise.all([ - this.sendProviderAndSave(numberOrId, ctxMessage).then(() => resolveCbEveryCtx(ctxMessage)), - ]) + await QueuePrincipal.enqueue(() => + this.sendProviderAndSave(numberOrId, ctxMessage).then(() => resolveCbEveryCtx(ctxMessage)) ) } return Promise.all(queue) } - // ๐Ÿ“„ [options: fallBack]: esta funcion se encarga de repetir el ultimo mensaje - const fallBack = async (validation = false, message = null) => { - QueuePrincipal.queue = [] - - if (validation) { - const currentPrev = await this.databaseClass.getPrevByNumber(from) - const nextFlow = await this.flowClass.find(refToContinue?.ref, true) - const filterNextFlow = nextFlow.filter((msg) => msg.refSerialize !== currentPrev?.refSerialize) - - return sendFlow(filterNextFlow, from, { prev: undefined }) - } - - await this.sendProviderAndSave(from, { - ...prevMsg, - answer: typeof message === 'string' ? message : message?.body ?? prevMsg.answer, - options: { - ...prevMsg.options, - buttons: prevMsg.options?.buttons, - }, - }) + const continueFlow = async () => { + const currentPrev = await this.databaseClass.getPrevByNumber(from) + const nextFlow = await this.flowClass.find(refToContinue?.ref, true) + const filterNextFlow = nextFlow.filter((msg) => msg.refSerialize !== currentPrev?.refSerialize) + const isContinueFlow = filterNextFlow.map((i) => i.keyword).includes(currentPrev?.ref) + if (!isContinueFlow) await sendFlow(filterNextFlow, from, { prev: undefined }) return } + // ๐Ÿ“„ [options: fallBack]: esta funcion se encarga de repetir el ultimo mensaje + const fallBack = + (flag) => + async (message = null) => { + QueuePrincipal.queue = [] + flag.fallBack = true + await this.sendProviderAndSave(from, { + ...prevMsg, + answer: typeof message === 'string' ? message : message?.body ?? prevMsg.answer, + options: { + ...prevMsg.options, + buttons: prevMsg.options?.buttons, + }, + }) + return + } // ๐Ÿ“„ [options: flowDynamic]: esta funcion se encarga de responder un array de respuesta esta limitado a 5 mensajes // para evitar bloque de whatsapp - const flowDynamic = async (listMsg = []) => { - if (!Array.isArray(listMsg)) listMsg = [listMsg] + const flowDynamic = + (flag) => + async (listMsg = []) => { + flag.flowDynamic = true + if (!Array.isArray(listMsg)) listMsg = [listMsg] - const parseListMsg = listMsg.map((opt, index) => createCtxMessage(opt, index)) - const currentPrev = await this.databaseClass.getPrevByNumber(from) + const parseListMsg = listMsg.map((opt, index) => createCtxMessage(opt, index)) - const skipContinueFlow = async () => { - const nextFlow = await this.flowClass.find(refToContinue?.ref, true) - const filterNextFlow = nextFlow.filter((msg) => msg.refSerialize !== currentPrev?.refSerialize) - const isContinueFlow = filterNextFlow.map((i) => i.keyword).includes(currentPrev?.ref) - return { - continue: !isContinueFlow, - contexts: filterNextFlow, + if (endFlowFlag) return + for (const msg of parseListMsg) { + await this.sendProviderAndSave(from, msg) } + await continueFlow() + return } - if (endFlowFlag) return - for (const msg of parseListMsg) { - await this.sendProviderAndSave(from, msg) - } - - const continueFlowData = await skipContinueFlow() - - if (continueFlowData.continue) return sendFlow(continueFlowData.contexts, from, { prev: undefined }) - return - } - // ๐Ÿ“„ Se encarga de revisar si el contexto del mensaje tiene callback o fallback const resolveCbEveryCtx = async (ctxMessage) => { if (!ctxMessage?.options?.capture) return await cbEveryCtx(ctxMessage?.ref) @@ -196,15 +188,29 @@ class CoreClass { // ๐Ÿ“„ Se encarga de revisar si el contexto del mensaje tiene callback y ejecutarlo const cbEveryCtx = async (inRef) => { + let flags = { + endFlow: false, + fallBack: false, + flowDynamic: false, + wait: true, + } + const provider = this.providerClass if (!this.flowClass.allCallbacks[inRef]) return Promise.resolve() - return this.flowClass.allCallbacks[inRef](messageCtxInComming, { + + const argsCb = { provider, - fallBack, - flowDynamic, - endFlow, - }) + fallBack: fallBack(flags), + flowDynamic: flowDynamic(flags), + endFlow: endFlow(flags), + } + + await this.flowClass.allCallbacks[inRef](messageCtxInComming, argsCb) + const wait = !(!flags.endFlow && !flags.fallBack && !flags.flowDynamic) + if (!wait) await continueFlow() + + return } // ๐Ÿ“„๐Ÿค˜(tiene return) [options: nested(array)]: Si se tiene flujos hijos los implementa @@ -216,7 +222,7 @@ class CoreClass { msgToSend = this.flowClass.find(body, false, flowStandalone) || [] - sendFlow(msgToSend, from) + await sendFlow(msgToSend, from) return } @@ -226,7 +232,7 @@ class CoreClass { if (typeCapture === 'boolean' && fallBackFlag) { msgToSend = this.flowClass.find(refToContinue?.ref, true) || [] - sendFlow(msgToSend, from) + await sendFlow(msgToSend, from) return } } @@ -241,11 +247,11 @@ class CoreClass { * @param {*} ctxMessage ver mรกs en GLOSSARY.md * @returns */ - sendProviderAndSave = (numberOrId, ctxMessage) => { + sendProviderAndSave = async (numberOrId, ctxMessage) => { const { answer } = ctxMessage - return this.providerClass - .sendMessage(numberOrId, answer, ctxMessage) - .then(() => this.databaseClass.save({ ...ctxMessage, from: numberOrId })) + await this.providerClass.sendMessage(numberOrId, answer, ctxMessage) + await this.databaseClass.save({ ...ctxMessage, from: numberOrId }) + return } /** diff --git a/packages/bot/package.json b/packages/bot/package.json index 563cd74..053a85d 100644 --- a/packages/bot/package.json +++ b/packages/bot/package.json @@ -1,6 +1,6 @@ { "name": "@bot-whatsapp/bot", - "version": "0.0.96-alpha.0", + "version": "0.0.100-alpha.0", "description": "", "main": "./lib/bundle.bot.cjs", "scripts": {