Skip to content

Commit

Permalink
Merge pull request #872 from codigoencasa/dev
Browse files Browse the repository at this point in the history
feat: next-release
  • Loading branch information
leifermendez authored Oct 2, 2023
2 parents 843a6c2 + 8cd3abb commit 0b800a5
Show file tree
Hide file tree
Showing 19 changed files with 384 additions and 37 deletions.
95 changes: 93 additions & 2 deletions __test__/0.1.7-case.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ suiteCase(`Debe retornar un mensaje resumen`, async ({ database, provider }) =>
},
async (ctx, { flowDynamic, state }) => {
await state.update({ age: ctx.body })
const myState = state.getMyState()
await flowDynamic(`Gracias por tu edad! ${myState.name}`)
const name = state.get('name')
await flowDynamic(`Gracias por tu edad! ${name}`)
}
)
.addAnswer(MOCK_VALUES[2], null, async (_, { flowDynamic, state }) => {
Expand Down Expand Up @@ -98,4 +98,95 @@ suiteCase(`Debe retornar un mensaje resumen`, async ({ database, provider }) =>
assert.is(undefined, getHistory[18])
})

suiteCase(`Manejando globalState`, async ({ database, provider }) => {
const MOCK_VALUES = ['¿Cual es tu nombre?', '¿Cual es tu edad?', 'Tu datos son:']

const flujoPrincipal = addKeyword(['hola'])
.addAnswer(
MOCK_VALUES[0],
{
capture: true,
},
async (ctx, { flowDynamic, state, globalState }) => {
await state.update({ name: ctx.body })
await globalState.update({ value: 'Soy el valor global' })
await flowDynamic('Gracias por tu nombre!')
}
)
.addAnswer(
MOCK_VALUES[1],
{
capture: true,
},
async (ctx, { flowDynamic, state }) => {
await state.update({ age: ctx.body })
const name = state.get('name')
await flowDynamic(`Gracias por tu edad! ${name}`)
}
)
.addAnswer(MOCK_VALUES[2], null, async (_, { flowDynamic, state, globalState }) => {
const myState = state.getMyState()
const value = globalState.get('value')
await flowDynamic(`Nombre: ${myState.name} Edad: ${myState.age} Valor Global: ${value}`)
})
.addAnswer('🤖🤖 Gracias por tu participacion')

createBot({
database,
flow: createFlow([flujoPrincipal]),
provider,
})

await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})

await provider.delaySendMessage(5, 'message', {
from: '001',
body: 'hola',
})

await provider.delaySendMessage(10, 'message', {
from: '000',
body: 'Leifer',
})

await provider.delaySendMessage(15, 'message', {
from: '000',
body: '90',
})

await provider.delaySendMessage(20, 'message', {
from: '001',
body: 'Maria',
})

await provider.delaySendMessage(25, 'message', {
from: '001',
body: '100',
})

await delay(1000)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is(MOCK_VALUES[0], getHistory[0])
assert.is('¿Cual es tu nombre?', getHistory[1])
assert.is('Leifer', getHistory[2])
assert.is('Gracias por tu nombre!', getHistory[3])
assert.is('¿Cual es tu edad?', getHistory[4])
assert.is('90', getHistory[5])
assert.is('Gracias por tu edad! Leifer', getHistory[6])
assert.is('Tu datos son:', getHistory[7])
assert.is('Nombre: Leifer Edad: 90 Valor Global: Soy el valor global', getHistory[8])
assert.is('🤖🤖 Gracias por tu participacion', getHistory[9])
assert.is('Maria', getHistory[10])
assert.is('Gracias por tu nombre!', getHistory[11])
assert.is('¿Cual es tu edad?', getHistory[12])
assert.is('100', getHistory[13])
assert.is('Gracias por tu edad! Maria', getHistory[14])
assert.is('Tu datos son:', getHistory[15])
assert.is('Nombre: Maria Edad: 100 Valor Global: Soy el valor global', getHistory[16])
assert.is('🤖🤖 Gracias por tu participacion', getHistory[17])
assert.is(undefined, getHistory[18])
})
suiteCase.run()
15 changes: 9 additions & 6 deletions __test__/0.2.0-case.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@ suiteCase(`Encadenanos addAction con captures`, async ({ database, provider }) =
.addAction(async (ctx, { flowDynamic }) => {
await flowDynamic(`Hola! primer flow dynamic. respondeme algo`)
})
.addAction({ capture: true }, async (ctx, { flowDynamic }) => {
respuesta = ctx.body
await flowDynamic(`Esto me respondieste ${respuesta}`)
.addAction({ capture: true }, async (ctx, { flowDynamic, state }) => {
const reply = ctx.body
await state.update({ reply })
await flowDynamic(`Esto me respondieste ${reply}`)
})
.addAction(async (ctx, { flowDynamic }) => {
await flowDynamic(`Hola! segundo flow dynamic. respondeme algo`)
})
.addAction({ capture: true }, async (ctx, { flowDynamic }) => {
respuesta = ctx.body
await flowDynamic(`Esto me respondieste ${respuesta}`)
.addAction({ capture: true }, async (ctx, { flowDynamic, state }) => {
const currentState = state.getMyState()?.reply
const reply = ctx.body
await state.update({ reply: currentState + ' ' + reply })
await flowDynamic(`Esto me respondieste ${reply}`)
})
.addAnswer('Chao')

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"build": "pnpm run cli:rollup && pnpm run bot:rollup && pnpm run provider:rollup && pnpm run database:rollup && pnpm run contexts:rollup && pnpm run create-bot-whatsapp:rollup && pnpm run portal:rollup",
"copy.lib": "node ./scripts/move.js",
"test.unit": "node ./node_modules/uvu/bin.js packages test",
"test.e2e": "node ./node_modules/uvu/bin.js __test__ 0.2.1-case.test.js",
"test.e2e": "node ./node_modules/uvu/bin.js __test__",
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit",
"test": "npm run test.coverage",
"cli": "node ./packages/cli/bin/cli.js",
Expand Down
3 changes: 3 additions & 0 deletions packages/bot/context/globalState.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ class GlobalState {
return () => this.STATE.get('__global__')
}

get = () => {
return (prop) => this.STATE.get('__global__')[prop]
}
/**
*
* @returns
Expand Down
9 changes: 9 additions & 0 deletions packages/bot/context/state.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ class SingleState {
return () => this.STATE.get(from)
}

/**
*
* @param {*} prop
* @returns
*/
get = (from) => {
return (prop) => this.STATE.get(from)[prop]
}

/**
*
* @returns
Expand Down
2 changes: 2 additions & 0 deletions packages/bot/core/core.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class CoreClass extends EventEmitter {
// 📄 Mantener estado de conversacion por numero
const state = {
getMyState: this.stateHandler.getMyState(messageCtxInComming.from),
get: this.stateHandler.get(messageCtxInComming.from),
getAllState: this.stateHandler.getAllState,
update: this.stateHandler.updateState(messageCtxInComming),
clear: this.stateHandler.clear(messageCtxInComming.from),
Expand All @@ -135,6 +136,7 @@ class CoreClass extends EventEmitter {
// 📄 Mantener estado global
const globalState = {
getMyState: this.globalStateHandler.getMyState(),
get: this.globalStateHandler.get(),
getAllState: this.globalStateHandler.getAllState,
update: this.globalStateHandler.updateState(messageCtxInComming),
clear: this.globalStateHandler.clear(),
Expand Down
Binary file added packages/docs/public/videos/console.mp4
Binary file not shown.
Binary file added packages/docs/public/videos/console.webm
Binary file not shown.
Binary file added packages/docs/public/videos/xbmcc-kx99h.webm
Binary file not shown.
4 changes: 2 additions & 2 deletions packages/docs/src/components/widgets/CallToAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ export default component$(() => {
playsInline
>
<source
src="https://leifer-landing-page.s3.us-east-2.amazonaws.com/xbmcc-kx99h.webm"
src="/videos/xbmcc-kx99h.webm"
type='video/mp4; codecs="hvc1"'
/>

<source
src="https://leifer-landing-page.s3.us-east-2.amazonaws.com/xbmcc-kx99h.webm"
src="/videos/xbmcc-kx99h.webm"
type="video/webm"
/>
</video>
Expand Down
96 changes: 92 additions & 4 deletions packages/docs/src/routes/docs/add-action/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import Navigation from '../../../components/widgets/Navigation'

# addAction

Es una función útil para definir acciones que se activan cuando se recibe un mensaje de WhatsApp específico.
La función action puede realizar diversas tareas y acceder a los métodos y propiedades. En general, **addAction()** te permite crear Chat Bots de WhatsApp personalizados que pueden interactuar con los usuarios de una manera programática.
Puedes definir acciones específicas que se activan cuando se recibe un mensaje específico, lo que te permite crear una variedad de flujos de conversación y respuestas automatizadas.
El addAction es una función que se utiliza en la librería bot-whatsapp para definir acciones específicas en respuesta a los mensajes de WhatsApp recibidos. Esta función te permite crear flujos de conversación y definir cómo el bot debe responder a ciertos mensajes o palabras clave.

Aquí tienes un ejemplo de cómo se utiliza el addAction:
```js
const { addKeyword } = require('@bot-whatsapp/bot')
const flowPrincipal = addKeyword(['hola', 'alo'])
Expand All @@ -27,10 +26,99 @@ const { addKeyword } = require('@bot-whatsapp/bot')
return flowDynamic('Buenas! ¿Cual es tu nombre?')
})
.addAction({ capture: true }, async (ctx, { flowDynamic, state }) => {
state.update({ name: ctx.body })
await state.update({ name: ctx.body })
return flowDynamic(`Gracias por tu nombre!: ${ctx.body}`)
})
.addAnswer('Chao!')
```

---

## Ejemplos

En este ejemplo, hemos creado un flujo de conversación utilizando la palabra clave "hola" y "alo".

Cuando el usuario envía un mensaje con alguna de estas palabras clave, el bot responde con un saludo. Luego, el bot captura el siguiente mensaje del usuario y lo muestra en la respuesta.

Puedes agregar tantas acciones como desees en tu flujo de conversación, y cada acción puede tener su propia lógica y respuesta personalizada.

Espero que esto te ayude a entender cómo utilizar el addAction en la creación de tu bot de WhatsApp. Si tienes alguna otra pregunta, no dudes en hacerla.

```js
const { addKeyword } = require('@bot-whatsapp/bot')

const flowPrincipal = addKeyword(['hola', 'alo'])
.addAction(async (_, { flowDynamic }) => {
return flowDynamic('¡Hola! ¿En qué puedo ayudarte?')
})
.addAction({ capture: true }, async (ctx, { flowDynamic }) => {
const mensaje = ctx.body
return flowDynamic(`Has dicho: ${mensaje}`)
})
```

---

En este ejemplo, hemos creado varios flujos de conversación utilizando la función addKeyword y la función addAction. Cada flujo se activará cuando se envíe un mensaje que contenga una palabra clave específica. Dentro de cada flujo, definimos acciones que se ejecutarán en respuesta al mensaje recibido.

El flujo principal se activará cuando se envíe un mensaje que contenga las palabras clave "hola" o "alo". La acción asociada simplemente devuelve un mensaje de bienvenida.

Los flujos de consulta de producto, soporte técnico y otras consultas se activarán cuando se envíen mensajes que contengan las palabras clave "producto", "soporte" y "consultas", respectivamente. Cada acción asociada devuelve un mensaje solicitando más información sobre el tema específico.

El flujo de ayuda se activará cuando se envíe un mensaje que contenga la palabra clave "ayuda". La primera acción asociada devuelve un mensaje que enumera los temas de ayuda disponibles. La segunda acción, con la opción capture: true, captura la respuesta del usuario y la utiliza para determinar qué acción ejecutar a continuación.

Finalmente, creamos el bot utilizando la función createBot y agregamos todos los flujos creados. Luego, iniciamos el bot con bot.start().

Este ejemplo muestra cómo utilizar addAction para definir acciones específicas en respuesta a mensajes de WhatsApp y cómo crear flujos de conversación utilizando addKeyword. Puedes adaptar este ejemplo a tus necesidades y agregar más flujos y acciones según sea necesario.

```js
const { addKeyword } = require('@bot-whatsapp/bot');

// Crear flujo principal
const flowPrincipal = addKeyword(['hola', 'alo'])
.addAction(async (_, { flowDynamic }) => {
return flowDynamic('¡Hola! ¿En qué puedo ayudarte?');
});

// Crear flujo de consulta de producto
const flowConsultaProducto = addKeyword(['producto'])
.addAction(async (_, { flowDynamic }) => {
return flowDynamic('Por favor, especifica qué producto te interesa.');
});

// Crear flujo de soporte técnico
const flowSoporteTecnico = addKeyword(['soporte'])
.addAction(async (_, { flowDynamic }) => {
return flowDynamic('Por favor, describe el problema que estás experimentando.');
});

// Crear flujo de otras consultas
const flowOtrasConsultas = addKeyword(['consultas'])
.addAction(async (_, { flowDynamic }) => {
return flowDynamic('Por favor, proporciona más detalles sobre tu consulta.');
});

// Crear flujo de ayuda
const flowAyuda = addKeyword(['ayuda'])
.addAction(async (_, { flowDynamic }) => {
return flowDynamic('¡Estoy aquí para ayudarte! ¿Necesitas ayuda con alguno de los siguientes temas?:\n 1. Información de producto.\n 2. Soporte técnico.\n 3. Otras consultas.');
})
.addAction({ capture: true }, async (ctx, { flowDynamic }) => {
const opcion = parseInt(ctx.body);
switch (opcion) {
case 1: return flowDynamic('Especifica qué producto te interesa.');
case 2: return flowDynamic('Describe el problema que tienes.');
case 3: return flowDynamic('Proporciona más detalles sobre tu consulta.');
}
});

// Crear bot y agregar flujos
const bot = createBot({
flows: [flowPrincipal, flowConsultaProducto, flowSoporteTecnico, flowOtrasConsultas, flowAyuda],
});



```

---
Expand Down
43 changes: 42 additions & 1 deletion packages/docs/src/routes/docs/add-answers/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ Dependiendo de tus sistema operativo asegurate de colocar bien la ruta absoluta
media: 'c:/ruta/imagen.png', //'c:\ruta\imagen.png'
})


```

---
Expand Down Expand Up @@ -121,6 +120,48 @@ En algunas ocaciones necesitamos esperar por la respuesta del usuario para ello

```

---

### Ejemplos

En este ejemplo, se crean dos flujos de conversación: uno para saludar al usuario cuando escribe "hola" y otro para despedirse cuando escribe "adios".

Cada flujo tiene una respuesta predefinida que se mostrará al usuario. Puedes personalizar las respuestas según tus necesidades.

```js
const { addKeyword } = require('@bot-whatsapp/bot')

const flowSaludo = addKeyword('hola')
.addAnswer('¡Hola! ¿En qué puedo ayudarte?')

const flowDespedida = addKeyword('adios')
.addAnswer('¡Hasta luego! Espero haberte sido de ayuda. ¡Vuelve pronto!')

```

---

En este ejemplo, se crean dos flujos de conversación: uno para el saludo y otro para la despedida.

Además, se agrega un flujo adicional para enviar una imagen cuando se recibe el mensaje 'imagen'.

```js
const flowSaludo = addKeyword('hola').addAnswer('¡Hola! ¿En qué puedo ayudarte?');
const flowDespedida = addKeyword('adios').addAnswer('¡Hasta luego! Espero haberte sido de ayuda.');

const flowImagen = addKeyword('imagen').addAnswer('¡Aquí tienes una imagen!', { media: 'https://ruta/a/imagen.jpg' });

const adapterFlow = createFlow([flowSaludo, flowDespedida, flowImagen]);

const adapterProvider = createProvider(WebWhatsappProvider);
createBot({
flow: adapterFlow,
provider: adapterProvider,
database: adapterDB,
});

```


---

Expand Down
11 changes: 9 additions & 2 deletions packages/docs/src/routes/docs/flow-dynamic/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,18 @@ const flowString = addKeyword('ver categorias')

await flowDynamic(mapeoDeLista)

await flowDynamic({body:'Tambien puedes enviar un mensaje de esta manera'})
await flowDynamic([
{body:'Tambien puedes enviar un mensaje de esta manera'}
])

// Enviar una imagen o pdf o etc

await flowDynamic({media:'https://i.imgur.com/0HpzsEm.png'})
await flowDynamic([
{
body:"soy una imagen",
media:'https://i.imgur.com/0HpzsEm.png'
}
])

})
```
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/src/routes/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ npm create bot-whatsapp@latest
muted
playsinline
>
<source src="https://leifer-landing-page.s3.us-east-2.amazonaws.com/console.webm" type="video/webm" />
<source src="/videos/console.webm" type="video/webm" />
</video>
</div>

Expand Down
Loading

0 comments on commit 0b800a5

Please sign in to comment.