mirror of
https://github.com/cheveguerra/bot-whatsapp.git
synced 2026-04-17 19:26:23 +00:00
Merge branch 'dev' into main
This commit is contained in:
17
.github/workflows/contributors.yml
vendored
17
.github/workflows/contributors.yml
vendored
@@ -1,17 +0,0 @@
|
||||
name: Revisando Colaboradores
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * *'
|
||||
|
||||
jobs:
|
||||
contrib-readme-job:
|
||||
runs-on: ubuntu-latest
|
||||
name: A job to automate contrib in readme
|
||||
steps:
|
||||
- name: Contribute List
|
||||
uses: akhilmhdh/contributors-readme-action@v2.3.6
|
||||
with:
|
||||
image_size: 50
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no -- commitlint --edit
|
||||
npm run lint:fix && npx --no -- commitlint --edit
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
packages/**/lib
|
||||
packages/docs/*.json
|
||||
packages/docs/
|
||||
**/.git
|
||||
**/.svn
|
||||
**/.hg
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
"singleQuote": true,
|
||||
"printWidth": 120
|
||||
}
|
||||
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"recommendations": ["xyc.vscode-mdx-preview"]
|
||||
"recommendations": ["xyc.vscode-mdx-preview", "vivaxy.vscode-conventional-commits", "mhutchie.git-graph"]
|
||||
}
|
||||
|
||||
51
CHANGELOG.md
51
CHANGELOG.md
@@ -2,6 +2,57 @@
|
||||
|
||||
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.20](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.19...v0.1.20) (2023-02-05)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **cli:** :fire: add regex expression in addKeyworkd ([e34560c](https://github.com/leifermendez/bot-whatsapp/commit/e34560c77d4852d2e90930f0858e51aa67d4eeab))
|
||||
* **provider:** :zap: possible get class provider ([76ba717](https://github.com/leifermendez/bot-whatsapp/commit/76ba717927a75b3d6299206aa0b8aee2bc25b726))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** :zap: working flowDynamic test ([c0113ca](https://github.com/leifermendez/bot-whatsapp/commit/c0113ca49295aff220d8defcb53f2ba7f2872d75))
|
||||
* **cli:** :zap: working flowDynamic test ([aef52d2](https://github.com/leifermendez/bot-whatsapp/commit/aef52d2694fa6616d614338643db198b4f7f1fe8))
|
||||
* **cli:** :zap: working flowDynamic test ([f769320](https://github.com/leifermendez/bot-whatsapp/commit/f76932021ce968d93241b55cfcdb8ae0e0e6c934))
|
||||
* **cli:** :zap: working flowDynamic test ([23e09ef](https://github.com/leifermendez/bot-whatsapp/commit/23e09efaeccaf51018c55da492edff45b625f0a9))
|
||||
* **database:** add support emoji in mysql ([9311aa0](https://github.com/leifermendez/bot-whatsapp/commit/9311aa0a65623a1bf40e96207a281625154dae90))
|
||||
* **database:** fix naming ([cd082f2](https://github.com/leifermendez/bot-whatsapp/commit/cd082f235012cd5f5844c6437f51711beee0c865))
|
||||
* **database:** fix naming ([1afc3ba](https://github.com/leifermendez/bot-whatsapp/commit/1afc3ba182070713b5bec40eaab0fa1f680830cd))
|
||||
* **database:** fix naming ([c9831d2](https://github.com/leifermendez/bot-whatsapp/commit/c9831d202ab2c85f15a0247cd2a2426bc435270c))
|
||||
* **provider:** :zap: baily wa.link ([96c2bff](https://github.com/leifermendez/bot-whatsapp/commit/96c2bffd093269be8e39474a84c156938504a6cb))
|
||||
|
||||
### [0.1.19](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.18...v0.1.19) (2023-01-29)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* :fire: bailey add media ([eab39e4](https://github.com/leifermendez/bot-whatsapp/commit/eab39e4ac06fd46f1a4671f8c15d1456b4400b97))
|
||||
* :zap: more feature ([e19c3a2](https://github.com/leifermendez/bot-whatsapp/commit/e19c3a25a40259c74b4add9635af4844907eed26))
|
||||
* **provider:** :rocket: fix issues in providers venom and wwebjs ([cbe438b](https://github.com/leifermendez/bot-whatsapp/commit/cbe438b77854e8df48b9dafaf7a837d21124ac5f))
|
||||
* **provider:** :rocket: fix provider ([0ad4c58](https://github.com/leifermendez/bot-whatsapp/commit/0ad4c58457b548dc41c0f9e8470d59c48de7b95a))
|
||||
* **provider:** :rocket: fix provider ([f8c7184](https://github.com/leifermendez/bot-whatsapp/commit/f8c7184487065443ab10f77aaf585e8bd63ca441))
|
||||
* **provider:** :rocket: fix provider ([b2afa45](https://github.com/leifermendez/bot-whatsapp/commit/b2afa45352a7ab1f5d9775f3c1fde475bd8ca204))
|
||||
* **provider:** :rocket: fix provider venom and wwebjs ([dcb0566](https://github.com/leifermendez/bot-whatsapp/commit/dcb0566d2bc3da40cd0c71554bb5ea0ec115d9ca))
|
||||
* **provider:** :rocket: implements all send media to venom provider ([9dd7c02](https://github.com/leifermendez/bot-whatsapp/commit/9dd7c02b6a5474aff063f7d6be0ca8519504b93c))
|
||||
* **provider:** :rocket: send file wwebjs ([6ff1a3a](https://github.com/leifermendez/bot-whatsapp/commit/6ff1a3a980196c01c66ed04ee07d0e7e57256504))
|
||||
* **provider:** :zap: bailey add send file video audio ([14d1a61](https://github.com/leifermendez/bot-whatsapp/commit/14d1a61fa259c09135c37c55bd79e97c9c8367e4))
|
||||
* **provider:** :zap: venom wweb ([fd2847a](https://github.com/leifermendez/bot-whatsapp/commit/fd2847aea0db17a0bdf33b5bca67a4cb8db2da16))
|
||||
* **provider:** :zap: venom wweb ([f95331d](https://github.com/leifermendez/bot-whatsapp/commit/f95331d3dc70e76a3dfbe4c8d24059f0e7a164ef))
|
||||
* **provider:** 🚀 implements all send media to venom provider ([bd7d150](https://github.com/leifermendez/bot-whatsapp/commit/bd7d150c047af41fdbb47f0a50a21e82cd79ee85))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bot:** :fire: endFlow with ctx ([f6114af](https://github.com/leifermendez/bot-whatsapp/commit/f6114affadfbc324536a86167d1fdfe8da3c8de6))
|
||||
* **bot:** :fire: endFlow with ctx ([b655ae4](https://github.com/leifermendez/bot-whatsapp/commit/b655ae449e7958ea940d8cc3c678fd66f60b6385))
|
||||
* **bot:** :zap: endFlow butons ([87a4203](https://github.com/leifermendez/bot-whatsapp/commit/87a4203cd5b88f566387a76d586248e4265d6e4e))
|
||||
* **bot:** :zap: fix fallback refactor ([e22780d](https://github.com/leifermendez/bot-whatsapp/commit/e22780d3faba94f71a70f1f201a20690608fa5bf))
|
||||
* **cli:** :zap: endflow ([1c66f17](https://github.com/leifermendez/bot-whatsapp/commit/1c66f178a56d284bb8cb9df5ca17685c7e5d1ddd))
|
||||
* **cli:** :zap: refactor fallback in child flow ([b33e346](https://github.com/leifermendez/bot-whatsapp/commit/b33e34692d3abcb6874308a9be79f74be4a2c3a8))
|
||||
* **cli:** :zap: refactor fallback in child flow ([8da4b20](https://github.com/leifermendez/bot-whatsapp/commit/8da4b204b41125b5d0fa0aee4fa87c1f5faf5568))
|
||||
|
||||
### [0.1.18](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.17...v0.1.18) (2023-01-24)
|
||||
|
||||
|
||||
|
||||
31
README.md
31
README.md
@@ -1,8 +1,7 @@
|
||||
# Chatbot Library
|
||||

|
||||
[](http://commitizen.github.io/cz-cli/)
|
||||
[](https://github.com/codigoencasa/bot-whatsapp/actions/workflows/releases.yml)
|
||||
|
||||
[](https://link.codigoencasa.com/DISCORD)
|
||||
|
||||
<p align="center">
|
||||
<img width="300" src="https://i.imgur.com/Oauef6t.png">
|
||||
@@ -112,14 +111,35 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/devrlbusiness">
|
||||
<img src="https://avatars.githubusercontent.com/u/66280283?v=4" width="50;" alt="devrlbusiness"/>
|
||||
<br />
|
||||
<sub><b>Developer RL Business</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Gregoriotecnico">
|
||||
<img src="https://avatars.githubusercontent.com/u/118696506?v=4" width="50;" alt="Gregoriotecnico"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jlferrete">
|
||||
<img src="https://avatars.githubusercontent.com/u/36698913?v=4" width="50;" alt="jlferrete"/>
|
||||
<br />
|
||||
<sub><b>Jose Luis Ferrete Olarte</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/lisandroprada">
|
||||
<img src="https://avatars.githubusercontent.com/u/7232326?v=4" width="50;" alt="lisandroprada"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/6rak0">
|
||||
<img src="https://avatars.githubusercontent.com/u/12260031?v=4" width="50;" alt="6rak0"/>
|
||||
@@ -140,7 +160,8 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<br />
|
||||
<sub><b>Rodrigo Mendoza Cabrera</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/yond1994">
|
||||
<img src="https://avatars.githubusercontent.com/u/47557263?v=4" width="50;" alt="yond1994"/>
|
||||
|
||||
39
__mocks__/env.js
Normal file
39
__mocks__/env.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const MOCK_DB = require('../packages/database/src/mock')
|
||||
const PROVIDER_DB = require('../packages/provider/src/mock')
|
||||
|
||||
class MOCK_FLOW {
|
||||
allCallbacks = { ref: () => 1 }
|
||||
flowSerialize = []
|
||||
flowRaw = []
|
||||
find = (arg) => {
|
||||
if (arg) {
|
||||
return [{ answer: 'answer', ref: 'ref' }]
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
findBySerialize = () => ({})
|
||||
findIndexByRef = () => 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Preparar env para el test
|
||||
* @param {*} context
|
||||
*/
|
||||
const setup = async (context) => {
|
||||
context.provider = new PROVIDER_DB()
|
||||
context.database = new MOCK_DB()
|
||||
context.flow = new MOCK_FLOW()
|
||||
}
|
||||
|
||||
const clear = async (context) => {
|
||||
context.provider = null
|
||||
context.database = null
|
||||
context.flow = null
|
||||
}
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
|
||||
module.exports = { setup, clear, delay }
|
||||
@@ -1,6 +0,0 @@
|
||||
const MOCK_MOBILE_WS = {
|
||||
from: 'XXXXXX',
|
||||
hasMedia: false,
|
||||
}
|
||||
|
||||
module.exports = { MOCK_MOBILE_WS }
|
||||
52
__test__/0.0.0-case.test.js
Normal file
52
__test__/0.0.0-case.test.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const { suite } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
|
||||
const { setup, clear, delay } = require('../__mocks__/env')
|
||||
|
||||
const suiteCase = suite('Flujo: Simple')
|
||||
|
||||
suiteCase.before.each(setup)
|
||||
suiteCase.after.each(clear)
|
||||
|
||||
suiteCase(`Responder a "hola"`, async ({ database, provider }) => {
|
||||
const flow = addKeyword('hola').addAnswer('Buenas!').addAnswer('Como vamos!')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(0)
|
||||
|
||||
assert.is('Buenas!', database.listHistory[0].answer)
|
||||
assert.is('Como vamos!', database.listHistory[1].answer)
|
||||
assert.is(undefined, database.listHistory[2])
|
||||
})
|
||||
|
||||
suiteCase(`NO reponder a "pepe"`, async ({ database, provider }) => {
|
||||
const flow = addKeyword('hola').addAnswer('Buenas!').addAnswer('Como vamos!')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'pepe',
|
||||
})
|
||||
|
||||
await delay(0)
|
||||
|
||||
assert.is(undefined, database.listHistory[0])
|
||||
assert.is(undefined, database.listHistory[1])
|
||||
})
|
||||
|
||||
suiteCase.run()
|
||||
51
__test__/0.1.0-case.test.js
Normal file
51
__test__/0.1.0-case.test.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const { suite } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
|
||||
const { setup, clear, delay } = require('../__mocks__/env')
|
||||
|
||||
const suiteCase = suite('Flujo: sensitive')
|
||||
|
||||
suiteCase.before.each(setup)
|
||||
suiteCase.after.each(clear)
|
||||
|
||||
suiteCase(`Responder a "ole" en minuscula`, async ({ database, provider }) => {
|
||||
const flow = addKeyword(['ola', 'ole'], { sensitive: true }).addAnswer('Bienvenido a la OLA')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'ole',
|
||||
})
|
||||
|
||||
await delay(0)
|
||||
|
||||
assert.is('Bienvenido a la OLA', database.listHistory[0].answer)
|
||||
assert.is(undefined, database.listHistory[1])
|
||||
})
|
||||
|
||||
suiteCase(`NO Responder a "ole" en minuscula`, async ({ database, provider }) => {
|
||||
const flow = addKeyword(['ola', 'ole'], { sensitive: true }).addAnswer('Bienvenido a la OLA')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'OLE',
|
||||
})
|
||||
|
||||
await delay(0)
|
||||
|
||||
assert.is(undefined, database.listHistory[0])
|
||||
assert.is(undefined, database.listHistory[1])
|
||||
})
|
||||
|
||||
suiteCase.run()
|
||||
89
__test__/0.1.1-case.test.js
Normal file
89
__test__/0.1.1-case.test.js
Normal file
@@ -0,0 +1,89 @@
|
||||
const { suite } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
|
||||
const { setup, clear, delay } = require('../__mocks__/env')
|
||||
|
||||
const fakeHTTP = async () => {
|
||||
await delay(10)
|
||||
}
|
||||
|
||||
const suiteCase = suite('Flujo: hijos con callbacks')
|
||||
|
||||
suiteCase.before.each(setup)
|
||||
suiteCase.after.each(clear)
|
||||
|
||||
suiteCase(`Debe continuar el flujo del hijo`, async ({ database, provider }) => {
|
||||
const flowCash = addKeyword('cash').addAnswer('Traeme los billetes! 😎')
|
||||
|
||||
const flowOnline = addKeyword('paypal')
|
||||
.addAnswer('Voy generar un link de paypal *escribe algo*', { capture: true }, async (_, { flowDynamic }) => {
|
||||
await fakeHTTP()
|
||||
await flowDynamic('Esperate.... estoy generando esto toma su tiempo')
|
||||
})
|
||||
.addAnswer('Aqui lo tienes 😎😎', null, async (_, { flowDynamic }) => {
|
||||
await fakeHTTP()
|
||||
await flowDynamic('http://paypal.com')
|
||||
})
|
||||
.addAnswer('Apurate!')
|
||||
|
||||
const flujoPrincipal = addKeyword('hola')
|
||||
.addAnswer('¿Como estas todo bien?')
|
||||
.addAnswer('Espero que si')
|
||||
.addAnswer('¿Cual es tu email?', { capture: true }, async (ctx, { fallBack }) => {
|
||||
if (!ctx.body.includes('@')) {
|
||||
return fallBack('Veo que no es um mail *bien*')
|
||||
}
|
||||
})
|
||||
.addAnswer('Voy a validar tu email...', null, async (_, { flowDynamic }) => {
|
||||
await fakeHTTP()
|
||||
return flowDynamic('Email validado correctamten!')
|
||||
})
|
||||
.addAnswer('¿Como vas a pagar *paypal* o *cash*?', { capture: true }, async () => {}, [flowCash, flowOnline])
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flujoPrincipal]),
|
||||
provider,
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(10, 'message', {
|
||||
from: '000',
|
||||
body: 'test@test.com',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(15, 'message', {
|
||||
from: '000',
|
||||
body: 'paypal',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(20, 'message', {
|
||||
from: '000',
|
||||
body: 'continue!',
|
||||
})
|
||||
|
||||
await delay(500)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
|
||||
assert.is('¿Como estas todo bien?', getHistory[0])
|
||||
assert.is('Espero que si', getHistory[1])
|
||||
assert.is('¿Cual es tu email?', getHistory[2])
|
||||
assert.is('test@test.com', getHistory[3])
|
||||
assert.is('Voy a validar tu email...', getHistory[4])
|
||||
assert.is('Email validado correctamten!', getHistory[5])
|
||||
assert.is('¿Como vas a pagar *paypal* o *cash*?', getHistory[6])
|
||||
assert.is('paypal', getHistory[7])
|
||||
assert.is('Voy generar un link de paypal *escribe algo*', getHistory[8])
|
||||
assert.is('continue!', getHistory[9])
|
||||
assert.is('Esperate.... estoy generando esto toma su tiempo', getHistory[10])
|
||||
assert.is('Aqui lo tienes 😎😎', getHistory[11])
|
||||
assert.is('http://paypal.com', getHistory[12])
|
||||
assert.is('Apurate!', getHistory[13])
|
||||
assert.is(undefined, getHistory[14])
|
||||
})
|
||||
|
||||
suiteCase.run()
|
||||
59
__test__/0.1.2-case.test.js
Normal file
59
__test__/0.1.2-case.test.js
Normal file
@@ -0,0 +1,59 @@
|
||||
const { suite } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
|
||||
const { setup, clear, delay } = require('../__mocks__/env')
|
||||
|
||||
const suiteCase = suite('Flujo: regex')
|
||||
|
||||
suiteCase.before.each(setup)
|
||||
suiteCase.after.each(clear)
|
||||
|
||||
suiteCase(`Responder a una expresion regular`, async ({ database, 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 flow = addKeyword(REGEX_CREDIT_NUMBER, { regex: true })
|
||||
.addAnswer(`Gracias por proporcionar un numero de tarjeta valido`)
|
||||
.addAnswer('Fin!')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: '374245455400126',
|
||||
})
|
||||
|
||||
await delay(10)
|
||||
|
||||
assert.is('Gracias por proporcionar un numero de tarjeta valido', database.listHistory[0].answer)
|
||||
assert.is('Fin!', database.listHistory[1].answer)
|
||||
assert.is(undefined, database.listHistory[2])
|
||||
})
|
||||
|
||||
suiteCase(`NO Responder a una expresion regular`, async ({ database, 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 flow = addKeyword(REGEX_CREDIT_NUMBER, { regex: true })
|
||||
.addAnswer(`Gracias por proporcionar un numero de tarjeta valido`)
|
||||
.addAnswer('Fin!')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(10)
|
||||
|
||||
assert.is(undefined, database.listHistory[0])
|
||||
assert.is(undefined, database.listHistory[1])
|
||||
})
|
||||
|
||||
suiteCase.run()
|
||||
40
__test__/0.1.3-case.test.js
Normal file
40
__test__/0.1.3-case.test.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const { suite } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
|
||||
const { setup, clear, delay } = require('../__mocks__/env')
|
||||
|
||||
const suiteCase = suite('Flujo: capture')
|
||||
|
||||
suiteCase.before.each(setup)
|
||||
suiteCase.after.each(clear)
|
||||
|
||||
suiteCase(`Responder a "pregunta"`, async ({ database, provider }) => {
|
||||
const flow = addKeyword(['hola'])
|
||||
.addAnswer(['Hola como estas?', '¿Cual es tu edad?'], { capture: true })
|
||||
.addAnswer('Gracias por tu respuesta')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(10, 'message', {
|
||||
from: '000',
|
||||
body: '90',
|
||||
})
|
||||
|
||||
await delay(20)
|
||||
|
||||
assert.is(['Hola como estas?', '¿Cual es tu edad?'].join('\n'), database.listHistory[0].answer)
|
||||
assert.is('90', database.listHistory[1].answer)
|
||||
assert.is('Gracias por tu respuesta', database.listHistory[2].answer)
|
||||
assert.is(undefined, database.listHistory[3])
|
||||
})
|
||||
|
||||
suiteCase.run()
|
||||
162
__test__/0.1.4-case.test.js
Normal file
162
__test__/0.1.4-case.test.js
Normal file
@@ -0,0 +1,162 @@
|
||||
const { suite } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
|
||||
const { setup, clear, delay } = require('../__mocks__/env')
|
||||
|
||||
const fakeHTTP = async (fakeData = []) => {
|
||||
await delay(50)
|
||||
const data = fakeData.map((u) => ({ body: `${u}` }))
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
|
||||
const suiteCase = suite('Flujo: flowDynamic')
|
||||
|
||||
suiteCase.before.each(setup)
|
||||
suiteCase.after.each(clear)
|
||||
|
||||
suiteCase(`Responder con mensajes asyncronos`, async ({ database, provider }) => {
|
||||
const MOCK_VALUES = [
|
||||
'Bienvenido te envio muchas marcas (5510)',
|
||||
'Seleccione marca del auto a cotizar, con el *número* correspondiente',
|
||||
'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:',
|
||||
'Los precios rondan:',
|
||||
]
|
||||
const flow = addKeyword(['hola'])
|
||||
.addAnswer(MOCK_VALUES[0], null, async (_, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Ford', 'GM', 'BMW'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[1], null, async (_, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Ranger', 'Explorer'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[2], null, async (_, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Usado', 'Nuevos'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[3], null, async (_, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['1000', '2000', '3000'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(1200)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('Ford', getHistory[1])
|
||||
assert.is('GM', getHistory[2])
|
||||
assert.is('BMW', getHistory[3])
|
||||
|
||||
assert.is(MOCK_VALUES[1], getHistory[4])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('Ranger', getHistory[5])
|
||||
assert.is('Explorer', getHistory[6])
|
||||
|
||||
assert.is(MOCK_VALUES[2], getHistory[7])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('Usado', getHistory[8])
|
||||
assert.is('Nuevos', getHistory[9])
|
||||
|
||||
assert.is(MOCK_VALUES[3], getHistory[10])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1000', getHistory[11])
|
||||
assert.is('2000', getHistory[12])
|
||||
assert.is('3000', getHistory[13])
|
||||
assert.is(undefined, getHistory[14])
|
||||
})
|
||||
|
||||
suiteCase(`Responder con un "string"`, async ({ database, provider }) => {
|
||||
const flow = addKeyword(['hola'])
|
||||
.addAnswer('Como vas?', null, async (_, { flowDynamic }) => {
|
||||
return flowDynamic('Todo bien!')
|
||||
})
|
||||
.addAnswer('y vos?')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(10)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is('Como vas?', getHistory[0])
|
||||
assert.is('Todo bien!', getHistory[1])
|
||||
assert.is('y vos?', getHistory[2])
|
||||
assert.is(undefined, getHistory[3])
|
||||
})
|
||||
|
||||
suiteCase(`Responder con un "array"`, async ({ database, provider }) => {
|
||||
const flow = addKeyword(['hola'])
|
||||
.addAnswer('Como vas?', null, async (_, { flowDynamic }) => {
|
||||
return flowDynamic(['Todo bien!', 'trabajando'])
|
||||
})
|
||||
.addAnswer('y vos?')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(10)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is('Como vas?', getHistory[0])
|
||||
assert.is('Todo bien!', getHistory[1])
|
||||
assert.is('trabajando', getHistory[2])
|
||||
assert.is('y vos?', getHistory[3])
|
||||
assert.is(undefined, getHistory[4])
|
||||
})
|
||||
|
||||
suiteCase(`Responder con un "object"`, async ({ database, provider }) => {
|
||||
const flow = addKeyword(['hola'])
|
||||
.addAnswer('Como vas?', null, async (_, { flowDynamic }) => {
|
||||
return flowDynamic([{ body: 'Todo bien!' }])
|
||||
})
|
||||
.addAnswer('y vos?')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(10)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is('Como vas?', getHistory[0])
|
||||
assert.is('Todo bien!', getHistory[1])
|
||||
assert.is('y vos?', getHistory[2])
|
||||
assert.is(undefined, getHistory[3])
|
||||
})
|
||||
|
||||
suiteCase.run()
|
||||
167
__test__/0.1.5-case.test.js
Normal file
167
__test__/0.1.5-case.test.js
Normal file
@@ -0,0 +1,167 @@
|
||||
const { suite } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
|
||||
const { setup, clear, delay } = require('../__mocks__/env')
|
||||
|
||||
const fakeHTTP = async (fakeData = []) => {
|
||||
await delay(50)
|
||||
const data = fakeData.map((u) => ({ body: `${u}` }))
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
|
||||
const suiteCase = suite('Flujo: endFlow')
|
||||
|
||||
suiteCase.before.each(setup)
|
||||
suiteCase.after.each(clear)
|
||||
|
||||
suiteCase(`Detener el flujo`, async ({ database, provider }) => {
|
||||
const MOCK_VALUES = [
|
||||
'Bienvenido te envio muchas marcas',
|
||||
'Seleccione marca del auto a cotizar, con el *número* correspondiente',
|
||||
'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:',
|
||||
'Los precios rondan:',
|
||||
]
|
||||
const flow = addKeyword(['hola'])
|
||||
.addAnswer(MOCK_VALUES[0], null, async (_, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Ford', 'GM', 'BMW'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[1], null, async (_, { endFlow }) => {
|
||||
return endFlow()
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[2])
|
||||
.addAnswer(MOCK_VALUES[3], null, async (_, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['1000', '2000', '3000'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(500)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('Ford', getHistory[1])
|
||||
assert.is('GM', getHistory[2])
|
||||
assert.is('BMW', getHistory[3])
|
||||
|
||||
assert.is(MOCK_VALUES[1], getHistory[4])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is(undefined, getHistory[5])
|
||||
assert.is(undefined, getHistory[6])
|
||||
})
|
||||
|
||||
suiteCase(`Detener el flujo flowDynamic`, async ({ database, provider }) => {
|
||||
const flow = addKeyword(['hola'])
|
||||
.addAnswer('Buenas!', null, async (_, { endFlow, flowDynamic }) => {
|
||||
await flowDynamic('Continuamos...')
|
||||
return endFlow()
|
||||
})
|
||||
.addAnswer('Como estas!')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(10)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is('Buenas!', getHistory[0])
|
||||
assert.is('Continuamos...', getHistory[1])
|
||||
assert.is(undefined, getHistory[2])
|
||||
})
|
||||
|
||||
suiteCase(`flowDynamic con capture`, async ({ database, provider }) => {
|
||||
const MOCK_VALUES = ['¿CUal es tu email?', 'Continuamos....', '¿Cual es tu edad?']
|
||||
|
||||
const flow = addKeyword(['hola'])
|
||||
.addAnswer(
|
||||
MOCK_VALUES[0],
|
||||
{
|
||||
capture: true,
|
||||
},
|
||||
async (ctx, { flowDynamic, fallBack }) => {
|
||||
const validation = ctx.body.includes('@')
|
||||
|
||||
if (validation) {
|
||||
const getDataFromApi = await fakeHTTP(['Gracias por tu email se ha validado de manera correcta'])
|
||||
return flowDynamic(getDataFromApi)
|
||||
}
|
||||
return fallBack()
|
||||
}
|
||||
)
|
||||
.addAnswer(MOCK_VALUES[1])
|
||||
.addAnswer(MOCK_VALUES[2], { capture: true }, async (ctx, { flowDynamic, fallBack }) => {
|
||||
if (ctx.body !== '18') {
|
||||
await delay(20)
|
||||
return fallBack('Ups creo que no eres mayor de edad')
|
||||
}
|
||||
return flowDynamic('Bien tu edad es correcta!')
|
||||
})
|
||||
.addAnswer('Puedes pasar')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(10, 'message', {
|
||||
from: '000',
|
||||
body: 'this is not email value',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(20, 'message', {
|
||||
from: '000',
|
||||
body: 'test@test.com',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(90, 'message', {
|
||||
from: '000',
|
||||
body: '20',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(200, 'message', {
|
||||
from: '000',
|
||||
body: '18',
|
||||
})
|
||||
|
||||
await delay(500)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
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('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])
|
||||
assert.is('Ups creo que no eres mayor de edad', getHistory[8])
|
||||
assert.is('18', getHistory[9])
|
||||
assert.is('Bien tu edad es correcta!', getHistory[10])
|
||||
assert.is('Puedes pasar', getHistory[11])
|
||||
})
|
||||
|
||||
suiteCase.run()
|
||||
97
__test__/0.1.6-case.test.js
Normal file
97
__test__/0.1.6-case.test.js
Normal file
@@ -0,0 +1,97 @@
|
||||
const { suite } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
|
||||
const { setup, clear, delay } = require('../__mocks__/env')
|
||||
|
||||
const suiteCase = suite('Flujo: manejo de estado')
|
||||
|
||||
suiteCase.before.each(setup)
|
||||
suiteCase.after.each(clear)
|
||||
|
||||
suiteCase(`Debe retornar un mensaje resumen`, async ({ database, provider }) => {
|
||||
let STATE_APP = {}
|
||||
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_APP[ctx.from] = { ...STATE_APP[ctx.from], name: ctx.body }
|
||||
|
||||
flowDynamic('Gracias por tu nombre!')
|
||||
}
|
||||
)
|
||||
.addAnswer(
|
||||
MOCK_VALUES[1],
|
||||
{
|
||||
capture: true,
|
||||
},
|
||||
async (ctx, { flowDynamic }) => {
|
||||
STATE_APP[ctx.from] = { ...STATE_APP[ctx.from], age: ctx.body }
|
||||
|
||||
await flowDynamic(`Gracias por tu edad! ${STATE_APP[ctx.from].name}`)
|
||||
}
|
||||
)
|
||||
.addAnswer(MOCK_VALUES[2], null, async (ctx, { flowDynamic }) => {
|
||||
flowDynamic(`Nombre: ${STATE_APP[ctx.from].name} Edad: ${STATE_APP[ctx.from].age}`)
|
||||
})
|
||||
.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(500)
|
||||
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', 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('100', getHistory[12])
|
||||
assert.is(undefined, getHistory[13])
|
||||
})
|
||||
|
||||
suiteCase.run()
|
||||
@@ -1,41 +0,0 @@
|
||||
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')
|
||||
|
||||
test(`[Caso - 01] Flow Basico`, async () => {
|
||||
const [VALUE_A, VALUE_B] = ['hola', 'buenas']
|
||||
|
||||
const flow = addKeyword(VALUE_A).addAnswer(VALUE_B)
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
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)
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
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')
|
||||
|
||||
/**
|
||||
* Falsear peticion async
|
||||
* @param {*} fakeData
|
||||
* @returns
|
||||
*/
|
||||
const fakeHTTP = async (fakeData = []) => {
|
||||
console.log('⚡ Server request!')
|
||||
await delay(50)
|
||||
console.log('⚡ Server return!')
|
||||
const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` }))
|
||||
console.log(data)
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
|
||||
test(`[Caso - 02] Flow (flowDynamic)`, async () => {
|
||||
const MOCK_VALUES = [
|
||||
'Bienvenido te envio muchas marcas (5510)',
|
||||
'Seleccione marca del auto a cotizar, con el *número* correspondiente',
|
||||
'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:',
|
||||
'Los precios rondan:',
|
||||
]
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
const database = new MOCK_DB()
|
||||
|
||||
const flujoPrincipal = addKeyword(['hola'])
|
||||
.addAnswer(MOCK_VALUES[0], null, async (ctx, { flowDynamic }) => {
|
||||
console.log('execute...')
|
||||
const data = await fakeHTTP(['Ford', 'GM', 'BMW'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[1], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Ranger', 'Explorer'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[2], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Usado', 'Nuevos'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[3], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['1000', '2000', '3000'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flujoPrincipal]),
|
||||
provider,
|
||||
})
|
||||
|
||||
provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(1200)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 Ford', getHistory[1])
|
||||
assert.is('2 GM', getHistory[2])
|
||||
assert.is('3 BMW', getHistory[3])
|
||||
|
||||
assert.is(MOCK_VALUES[1], getHistory[4])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 Ranger', getHistory[5])
|
||||
assert.is('2 Explorer', getHistory[6])
|
||||
|
||||
assert.is(MOCK_VALUES[2], getHistory[7])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 Usado', getHistory[8])
|
||||
assert.is('2 Nuevos', getHistory[9])
|
||||
|
||||
assert.is(MOCK_VALUES[3], getHistory[10])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 1000', getHistory[11])
|
||||
assert.is('2 2000', getHistory[12])
|
||||
assert.is('3 3000', getHistory[13])
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
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 - 03] Flow puro`, async () => {
|
||||
const MOCK_VALUES = ['Bienvenido a mi tienda', 'Como estas?']
|
||||
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
const database = new MOCK_DB()
|
||||
|
||||
const flujoPrincipal = addKeyword(['hola'])
|
||||
.addAnswer(MOCK_VALUES[0])
|
||||
.addAnswer(MOCK_VALUES[1])
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flujoPrincipal]),
|
||||
provider,
|
||||
})
|
||||
|
||||
provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(10)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
assert.is(MOCK_VALUES[1], getHistory[1])
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
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')
|
||||
|
||||
/**
|
||||
* Falsear peticion async
|
||||
* @param {*} fakeData
|
||||
* @returns
|
||||
*/
|
||||
const fakeHTTP = async (fakeData = []) => {
|
||||
console.log('⚡ Server request!')
|
||||
await delay(50)
|
||||
console.log('⚡ Server return!')
|
||||
const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` }))
|
||||
console.log(data)
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
|
||||
test(`[Caso - 04] Romper flujo (endFlow)`, async () => {
|
||||
const MOCK_VALUES = [
|
||||
'Bienvenido te envio muchas marcas (5510)',
|
||||
'Seleccione marca del auto a cotizar, con el *número* correspondiente',
|
||||
'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:',
|
||||
'Los precios rondan:',
|
||||
]
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
const database = new MOCK_DB()
|
||||
|
||||
const flujoPrincipal = addKeyword(['hola'])
|
||||
.addAnswer(MOCK_VALUES[0], null, async (ctx, { flowDynamic }) => {
|
||||
console.log('execute...')
|
||||
const data = await fakeHTTP(['Ford', 'GM', 'BMW'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[1], null, async (ctx, { endFlow }) => {
|
||||
return endFlow()
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[2], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Usado', 'Nuevos'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[3], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['1000', '2000', '3000'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flujoPrincipal]),
|
||||
provider,
|
||||
})
|
||||
|
||||
provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(1200)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 Ford', getHistory[1])
|
||||
assert.is('2 GM', getHistory[2])
|
||||
assert.is('3 BMW', getHistory[3])
|
||||
|
||||
assert.is(MOCK_VALUES[1], getHistory[4])
|
||||
assert.is(undefined, getHistory[5])
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
@@ -1,28 +1,10 @@
|
||||
module.exports = {
|
||||
disableEmoji: false,
|
||||
format: '{type}{scope}: {emoji}{subject}',
|
||||
list: [
|
||||
'test',
|
||||
'feat',
|
||||
'fix',
|
||||
'chore',
|
||||
'docs',
|
||||
'refactor',
|
||||
'style',
|
||||
'ci',
|
||||
'perf',
|
||||
],
|
||||
list: ['test', 'feat', 'fix', 'chore', 'docs', 'refactor', 'style', 'ci', 'perf'],
|
||||
maxMessageLength: 64,
|
||||
minMessageLength: 3,
|
||||
questions: [
|
||||
'type',
|
||||
'scope',
|
||||
'subject',
|
||||
'body',
|
||||
'breaking',
|
||||
'issues',
|
||||
'lerna',
|
||||
],
|
||||
questions: ['type', 'scope', 'subject', 'body', 'breaking', 'issues', 'lerna'],
|
||||
scopes: [],
|
||||
types: {
|
||||
chore: {
|
||||
@@ -56,8 +38,7 @@ module.exports = {
|
||||
value: 'perf',
|
||||
},
|
||||
refactor: {
|
||||
description:
|
||||
'A code change that neither fixes a bug or adds a feature',
|
||||
description: 'A code change that neither fixes a bug or adds a feature',
|
||||
emoji: '(💡)',
|
||||
value: 'refactor',
|
||||
},
|
||||
@@ -67,8 +48,7 @@ module.exports = {
|
||||
value: 'release',
|
||||
},
|
||||
style: {
|
||||
description:
|
||||
'Markup, white-space, formatting, missing semi-colons...',
|
||||
description: 'Markup, white-space, formatting, missing semi-colons...',
|
||||
emoji: '(💄)',
|
||||
value: 'style',
|
||||
},
|
||||
@@ -80,8 +60,7 @@ module.exports = {
|
||||
messages: {
|
||||
type: "Select the type of change that you're committing:",
|
||||
customScope: 'Select the scope this component affects:',
|
||||
subject:
|
||||
'Write a short, imperative mood description of the change:\n',
|
||||
subject: 'Write a short, imperative mood description of the change:\n',
|
||||
body: 'Provide a longer description of the change:\n ',
|
||||
breaking: 'List any breaking changes:\n',
|
||||
footer: 'Issues this commit closes, e.g #123:',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/root",
|
||||
"version": "0.1.18",
|
||||
"version": "0.1.20",
|
||||
"description": "Bot de wahtsapp open source para MVP o pequeños negocios",
|
||||
"main": "app.js",
|
||||
"private": true,
|
||||
@@ -81,6 +81,7 @@
|
||||
"fs-extra": "^11.1.0",
|
||||
"git-cz": "^4.9.0",
|
||||
"husky": "^8.0.2",
|
||||
"mime-types": "^2.1.35",
|
||||
"only-allow": "^1.1.1",
|
||||
"prettier": "^2.8.0",
|
||||
"pretty-quick": "^3.1.3",
|
||||
|
||||
@@ -43,8 +43,7 @@ class CoreClass {
|
||||
},
|
||||
{
|
||||
event: 'require_action',
|
||||
func: ({ instructions, title = '⚡⚡ ACCIÓN REQUERIDA ⚡⚡' }) =>
|
||||
printer(instructions, title),
|
||||
func: ({ instructions, title = '⚡⚡ ACCIÓN REQUERIDA ⚡⚡' }) => printer(instructions, title),
|
||||
},
|
||||
{
|
||||
event: 'ready',
|
||||
@@ -52,8 +51,7 @@ class CoreClass {
|
||||
},
|
||||
{
|
||||
event: 'auth_failure',
|
||||
func: ({ instructions }) =>
|
||||
printer(instructions, '⚡⚡ ERROR AUTH ⚡⚡'),
|
||||
func: ({ instructions }) => printer(instructions, '⚡⚡ ERROR AUTH ⚡⚡'),
|
||||
},
|
||||
|
||||
{
|
||||
@@ -71,16 +69,14 @@ class CoreClass {
|
||||
logger.log(`[handleMsg]: `, messageCtxInComming)
|
||||
const { body, from } = messageCtxInComming
|
||||
let msgToSend = []
|
||||
let fallBackFlag = false
|
||||
let endFlowFlag = false
|
||||
let fallBackFlag = false
|
||||
if (this.generalArgs.blackList.includes(from)) return
|
||||
if (!body) return
|
||||
if (!body.length) return
|
||||
|
||||
let prevMsg = await this.databaseClass.getPrevByNumber(from)
|
||||
const refToContinue = this.flowClass.findBySerialize(
|
||||
prevMsg?.refSerialize
|
||||
)
|
||||
const refToContinue = this.flowClass.findBySerialize(prevMsg?.refSerialize)
|
||||
|
||||
if (prevMsg?.ref) {
|
||||
const ctxByNumber = toCtx({
|
||||
@@ -91,6 +87,22 @@ class CoreClass {
|
||||
this.databaseClass.save(ctxByNumber)
|
||||
}
|
||||
|
||||
// 📄 Crar CTX de mensaje (uso private)
|
||||
const createCtxMessage = (payload = {}, index = 0) => {
|
||||
const body = typeof payload === 'string' ? payload : payload?.body ?? payload?.answer
|
||||
const media = payload?.media ?? null
|
||||
const buttons = payload?.buttons ?? []
|
||||
const capture = payload?.capture ?? false
|
||||
|
||||
return toCtx({
|
||||
body,
|
||||
from,
|
||||
keyword: null,
|
||||
index,
|
||||
options: { media, buttons, capture },
|
||||
})
|
||||
}
|
||||
|
||||
// 📄 Limpiar cola de procesos
|
||||
const clearQueue = () => {
|
||||
QueuePrincipal.pendingPromise = false
|
||||
@@ -98,94 +110,122 @@ class CoreClass {
|
||||
}
|
||||
|
||||
// 📄 Finalizar flujo
|
||||
const endFlow = async () => {
|
||||
prevMsg = null
|
||||
endFlowFlag = true
|
||||
clearQueue()
|
||||
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) => {
|
||||
// [1 Paso] esto esta bien!
|
||||
const sendFlow = async (messageToSend, numberOrId, options = { prev: prevMsg }) => {
|
||||
if (options.prev?.options?.capture) await cbEveryCtx(options.prev?.ref)
|
||||
|
||||
if (prevMsg?.options?.capture) await cbEveryCtx(prevMsg?.ref)
|
||||
const queue = []
|
||||
for (const ctxMessage of messageToSend) {
|
||||
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 () => {
|
||||
fallBackFlag = true
|
||||
await this.sendProviderAndSave(from, refToContinue)
|
||||
QueuePrincipal.queue = []
|
||||
return refToContinue
|
||||
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) {
|
||||
const refToContinueChild = this.flowClass.getRefToContinueChild(currentPrev?.keyword)
|
||||
const flowStandaloneChild = this.flowClass.getFlowsChild()
|
||||
const nextChildMessages =
|
||||
(await this.flowClass.find(refToContinueChild?.ref, true, flowStandaloneChild)) || []
|
||||
if (nextChildMessages?.length) return await sendFlow(nextChildMessages, from, { prev: undefined })
|
||||
}
|
||||
|
||||
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 = [],
|
||||
optListMsg = { limit: 5, fallback: false }
|
||||
) => {
|
||||
if (!Array.isArray(listMsg))
|
||||
throw new Error('Esto debe ser un ARRAY')
|
||||
const flowDynamic =
|
||||
(flag) =>
|
||||
async (listMsg = []) => {
|
||||
flag.flowDynamic = true
|
||||
if (!Array.isArray(listMsg)) listMsg = [listMsg]
|
||||
|
||||
fallBackFlag = optListMsg.fallback
|
||||
const parseListMsg = listMsg
|
||||
.map((opt, index) => {
|
||||
const body = typeof opt === 'string' ? opt : opt.body
|
||||
const media = opt?.media ?? null
|
||||
const buttons = opt?.buttons ?? []
|
||||
const parseListMsg = listMsg.map((opt, index) => createCtxMessage(opt, index))
|
||||
|
||||
return toCtx({
|
||||
body,
|
||||
from,
|
||||
keyword: null,
|
||||
index,
|
||||
options: { media, buttons },
|
||||
})
|
||||
})
|
||||
.slice(0, optListMsg.limit)
|
||||
|
||||
if (endFlowFlag) return
|
||||
for (const msg of parseListMsg) {
|
||||
await this.sendProviderAndSave(from, msg)
|
||||
if (endFlowFlag) return
|
||||
for (const msg of parseListMsg) {
|
||||
await this.sendProviderAndSave(from, msg)
|
||||
}
|
||||
await continueFlow()
|
||||
return
|
||||
}
|
||||
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)
|
||||
if (!ctxMessage?.options?.capture) return await cbEveryCtx(ctxMessage?.ref)
|
||||
}
|
||||
|
||||
// 📄 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, {
|
||||
fallBack,
|
||||
flowDynamic,
|
||||
endFlow,
|
||||
})
|
||||
|
||||
const argsCb = {
|
||||
provider,
|
||||
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
|
||||
if (!fallBackFlag && prevMsg?.options?.nested?.length) {
|
||||
if (!endFlowFlag && prevMsg?.options?.nested?.length) {
|
||||
const nestedRef = prevMsg.options.nested
|
||||
const flowStandalone = nestedRef.map((f) => ({
|
||||
...nestedRef.find((r) => r.refSerialize === f.refSerialize),
|
||||
@@ -193,18 +233,17 @@ class CoreClass {
|
||||
|
||||
msgToSend = this.flowClass.find(body, false, flowStandalone) || []
|
||||
|
||||
sendFlow(msgToSend, from)
|
||||
await sendFlow(msgToSend, from)
|
||||
return
|
||||
}
|
||||
|
||||
// 📄🤘(tiene return) [options: capture (boolean)]: Si se tiene option boolean
|
||||
if (!fallBackFlag && !prevMsg?.options?.nested?.length) {
|
||||
// 📄🤘(tiene return) Si el mensaje previo implementa capture
|
||||
if (!endFlowFlag && !prevMsg?.options?.nested?.length) {
|
||||
const typeCapture = typeof prevMsg?.options?.capture
|
||||
const valueCapture = prevMsg?.options?.capture
|
||||
|
||||
if (['string', 'boolean'].includes(typeCapture) && valueCapture) {
|
||||
if (typeCapture === 'boolean' && fallBackFlag) {
|
||||
msgToSend = this.flowClass.find(refToContinue?.ref, true) || []
|
||||
sendFlow(msgToSend, from)
|
||||
await sendFlow(msgToSend, from)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -219,15 +258,15 @@ class CoreClass {
|
||||
* @param {*} ctxMessage ver más en GLOSSARY.md
|
||||
* @returns
|
||||
*/
|
||||
sendProviderAndSave = (numberOrId, ctxMessage) => {
|
||||
sendProviderAndSave = async (numberOrId, ctxMessage) => {
|
||||
const { answer } = ctxMessage
|
||||
return Promise.all([
|
||||
this.providerClass.sendMessage(numberOrId, answer, ctxMessage),
|
||||
this.databaseClass.save({ ...ctxMessage, from: numberOrId }),
|
||||
])
|
||||
await this.providerClass.sendMessage(numberOrId, answer, ctxMessage)
|
||||
await this.databaseClass.save({ ...ctxMessage, from: numberOrId })
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @private
|
||||
* @param {*} message
|
||||
* @param {*} ref
|
||||
@@ -253,9 +292,7 @@ class CoreClass {
|
||||
for (const ctxMessage of messageToSend) {
|
||||
const delayMs = ctxMessage?.options?.delay || 0
|
||||
if (delayMs) await delay(delayMs)
|
||||
QueuePrincipal.enqueue(() =>
|
||||
this.sendProviderAndSave(numberOrId, ctxMessage)
|
||||
)
|
||||
QueuePrincipal.enqueue(() => this.sendProviderAndSave(numberOrId, ctxMessage))
|
||||
}
|
||||
return Promise.all(queue)
|
||||
}
|
||||
|
||||
@@ -8,8 +8,7 @@ const { addKeyword, addAnswer, addChild, toSerialize } = require('./io/methods')
|
||||
* @param {*} args
|
||||
* @returns
|
||||
*/
|
||||
const createBot = async ({ flow, database, provider }, args = {}) =>
|
||||
new CoreClass(flow, database, provider, args)
|
||||
const createBot = async ({ flow, database, provider }, args = {}) => new CoreClass(flow, database, provider, args)
|
||||
|
||||
/**
|
||||
* Crear instancia de clase Io (Flow)
|
||||
@@ -29,8 +28,7 @@ const createFlow = (args) => {
|
||||
*/
|
||||
const createProvider = (providerClass = class {}, args = null) => {
|
||||
const providerInstance = new providerClass(args)
|
||||
if (!providerClass.prototype instanceof ProviderClass)
|
||||
throw new Error('El provider no implementa ProviderClass')
|
||||
if (!providerClass.prototype instanceof ProviderClass) throw new Error('El provider no implementa ProviderClass')
|
||||
return providerInstance
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ class FlowClass {
|
||||
let refSymbol = null
|
||||
overFlow = overFlow ?? this.flowSerialize
|
||||
|
||||
/** Retornar expresion regular para buscar coincidencia */
|
||||
const mapSensitive = (str, flag = false) => {
|
||||
const regexSensitive = flag ? 'g' : 'i'
|
||||
const mapSensitive = (str, mapOptions = { sensitive: false, regex: false }) => {
|
||||
if (mapOptions.regex) return new RegExp(str)
|
||||
const regexSensitive = mapOptions.sensitive ? 'g' : 'i'
|
||||
if (Array.isArray(str)) {
|
||||
return new RegExp(str.join('|'), regexSensitive)
|
||||
}
|
||||
@@ -35,9 +35,7 @@ class FlowClass {
|
||||
}
|
||||
|
||||
const findIn = (keyOrWord, symbol = false, flow = overFlow) => {
|
||||
const sensitive = refSymbol?.options?.sensitive || false
|
||||
capture = refSymbol?.options?.capture || false
|
||||
|
||||
if (capture) return messages
|
||||
|
||||
if (symbol) {
|
||||
@@ -46,7 +44,9 @@ class FlowClass {
|
||||
if (refSymbol?.ref) findIn(refSymbol.ref, true)
|
||||
} else {
|
||||
refSymbol = flow.find((c) => {
|
||||
return mapSensitive(c.keyword, sensitive).test(keyOrWord)
|
||||
const sensitive = c?.options?.sensitive || false
|
||||
const regex = c?.options?.regex || false
|
||||
return mapSensitive(c.keyword, { sensitive, regex }).test(keyOrWord)
|
||||
})
|
||||
if (refSymbol?.ref) findIn(refSymbol.ref, true)
|
||||
return messages
|
||||
@@ -56,10 +56,40 @@ class FlowClass {
|
||||
return messages
|
||||
}
|
||||
|
||||
findBySerialize = (refSerialize) =>
|
||||
this.flowSerialize.find((r) => r.refSerialize === refSerialize)
|
||||
findBySerialize = (refSerialize) => this.flowSerialize.find((r) => r.refSerialize === refSerialize)
|
||||
|
||||
findIndexByRef = (ref) => this.flowSerialize.findIndex((r) => r.ref === ref)
|
||||
|
||||
getRefToContinueChild = (keyword) => {
|
||||
try {
|
||||
const flowChilds = this.flowSerialize
|
||||
.reduce((acc, cur) => {
|
||||
const merge = [...acc, cur?.options?.nested].flat(2)
|
||||
return merge
|
||||
}, [])
|
||||
.filter((i) => !!i && i?.refSerialize === keyword)
|
||||
.shift()
|
||||
|
||||
return flowChilds
|
||||
} catch (e) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
getFlowsChild = () => {
|
||||
try {
|
||||
const flowChilds = this.flowSerialize
|
||||
.reduce((acc, cur) => {
|
||||
const merge = [...acc, cur?.options?.nested].flat(2)
|
||||
return merge
|
||||
}, [])
|
||||
.filter((i) => !!i)
|
||||
|
||||
return flowChilds
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FlowClass
|
||||
|
||||
@@ -17,15 +17,10 @@ const addAnswer =
|
||||
* @returns
|
||||
*/
|
||||
const getAnswerOptions = () => ({
|
||||
media:
|
||||
typeof options?.media === 'string' ? `${options?.media}` : null,
|
||||
media: typeof options?.media === 'string' ? `${options?.media}` : null,
|
||||
buttons: Array.isArray(options?.buttons) ? options.buttons : [],
|
||||
capture:
|
||||
typeof options?.capture === 'boolean'
|
||||
? options?.capture
|
||||
: false,
|
||||
child:
|
||||
typeof options?.child === 'string' ? `${options?.child}` : null,
|
||||
capture: typeof options?.capture === 'boolean' ? options?.capture : false,
|
||||
child: typeof options?.child === 'string' ? `${options?.child}` : null,
|
||||
delay: typeof options?.delay === 'number' ? options?.delay : 0,
|
||||
})
|
||||
|
||||
@@ -49,8 +44,7 @@ const addAnswer =
|
||||
* Esta funcion aplana y busca los callback anidados de los hijos
|
||||
* @returns
|
||||
*/
|
||||
const getCbFromNested = () =>
|
||||
flatObject(Array.isArray(nested) ? nested : [nested])
|
||||
const getCbFromNested = () => flatObject(Array.isArray(nested) ? nested : [nested])
|
||||
|
||||
const callback = typeof cb === 'function' ? cb : () => null
|
||||
|
||||
|
||||
@@ -8,12 +8,14 @@ const { toJson } = require('./toJson')
|
||||
* @param {*} options {sensitive:boolean} default false
|
||||
*/
|
||||
const addKeyword = (keyword, options) => {
|
||||
if (typeof keyword !== 'string' && !Array.isArray(keyword)) {
|
||||
throw new Error('DEBE_SER_STRING_ARRAY_REGEX')
|
||||
}
|
||||
|
||||
const parseOptions = () => {
|
||||
const defaultProperties = {
|
||||
sensitive:
|
||||
typeof options?.sensitive === 'boolean'
|
||||
? options?.sensitive
|
||||
: false,
|
||||
sensitive: typeof options?.sensitive === 'boolean' ? options?.sensitive : false,
|
||||
regex: typeof options?.regex === 'boolean' ? options?.regex : false,
|
||||
}
|
||||
|
||||
return defaultProperties
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/bot",
|
||||
"version": "0.0.73-alpha.0",
|
||||
"version": "0.0.100-alpha.0",
|
||||
"description": "",
|
||||
"main": "./lib/bundle.bot.cjs",
|
||||
"scripts": {
|
||||
|
||||
@@ -20,10 +20,11 @@ class ProviderClass extends EventEmitter {
|
||||
*/
|
||||
|
||||
sendMessage = async (userId, message) => {
|
||||
if (NODE_ENV !== 'production')
|
||||
console.log('[sendMessage]', { userId, message })
|
||||
if (NODE_ENV !== 'production') console.log('[sendMessage]', { userId, message })
|
||||
return message
|
||||
}
|
||||
|
||||
getInstance = () => this.vendor
|
||||
}
|
||||
|
||||
module.exports = ProviderClass
|
||||
|
||||
@@ -2,13 +2,7 @@ const { test } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const FlowClass = require('../io/flow.class')
|
||||
const MockProvider = require('../../../__mocks__/mock.provider')
|
||||
const {
|
||||
createBot,
|
||||
CoreClass,
|
||||
createFlow,
|
||||
createProvider,
|
||||
ProviderClass,
|
||||
} = require('../index')
|
||||
const { createBot, CoreClass, createFlow, createProvider, ProviderClass } = require('../index')
|
||||
|
||||
class MockFlow {
|
||||
allCallbacks = { ref: () => 1 }
|
||||
@@ -23,6 +17,8 @@ class MockFlow {
|
||||
}
|
||||
findBySerialize = () => ({})
|
||||
findIndexByRef = () => 0
|
||||
getRefToContinueChild = () => ({})
|
||||
getFlowsChild = () => []
|
||||
}
|
||||
|
||||
class MockDBA {
|
||||
@@ -100,20 +96,13 @@ test(`[Bot] Eventos 'require_action,ready,auth_failure,message '`, async () => {
|
||||
await createBot(setting)
|
||||
|
||||
/// Escuchamos eventos
|
||||
mockProvider.on(
|
||||
'require_action',
|
||||
(r) => (responseEvents['require_action'] = r)
|
||||
)
|
||||
mockProvider.on('require_action', (r) => (responseEvents['require_action'] = r))
|
||||
mockProvider.on('ready', (r) => (responseEvents['ready'] = r))
|
||||
mockProvider.on('auth_failure', (r) => (responseEvents['auth_failure'] = r))
|
||||
mockProvider.on('message', (r) => (responseEvents['message'] = r))
|
||||
|
||||
/// Emitimos eventos
|
||||
mockProvider.delaySendMessage(
|
||||
0,
|
||||
'require_action',
|
||||
MOCK_EVENTS.require_action
|
||||
)
|
||||
mockProvider.delaySendMessage(0, 'require_action', MOCK_EVENTS.require_action)
|
||||
mockProvider.delaySendMessage(0, 'ready', MOCK_EVENTS.ready)
|
||||
mockProvider.delaySendMessage(0, 'auth_failure', MOCK_EVENTS.auth_failure)
|
||||
mockProvider.delaySendMessage(0, 'message', MOCK_EVENTS.message)
|
||||
@@ -121,21 +110,12 @@ test(`[Bot] Eventos 'require_action,ready,auth_failure,message '`, async () => {
|
||||
await delay(0)
|
||||
|
||||
/// Testeamos eventos
|
||||
assert.is(
|
||||
JSON.stringify(responseEvents.require_action),
|
||||
JSON.stringify(MOCK_EVENTS.require_action)
|
||||
)
|
||||
assert.is(JSON.stringify(responseEvents.require_action), JSON.stringify(MOCK_EVENTS.require_action))
|
||||
assert.is(responseEvents.ready, MOCK_EVENTS.ready)
|
||||
|
||||
assert.is(
|
||||
JSON.stringify(responseEvents.auth_failure),
|
||||
JSON.stringify(MOCK_EVENTS.auth_failure)
|
||||
)
|
||||
assert.is(JSON.stringify(responseEvents.auth_failure), JSON.stringify(MOCK_EVENTS.auth_failure))
|
||||
|
||||
assert.is(
|
||||
JSON.stringify(responseEvents.message),
|
||||
JSON.stringify(MOCK_EVENTS.message)
|
||||
)
|
||||
assert.is(JSON.stringify(responseEvents.message), JSON.stringify(MOCK_EVENTS.message))
|
||||
})
|
||||
|
||||
test(`[Bot] Probando Flujos Internos`, async () => {
|
||||
@@ -166,20 +146,13 @@ test(`[Bot] Probando Flujos Internos`, async () => {
|
||||
await createBot(setting)
|
||||
|
||||
/// Escuchamos eventos
|
||||
mockProvider.on(
|
||||
'require_action',
|
||||
(r) => (responseEvents['require_action'] = r)
|
||||
)
|
||||
mockProvider.on('require_action', (r) => (responseEvents['require_action'] = r))
|
||||
mockProvider.on('ready', (r) => (responseEvents['ready'] = r))
|
||||
mockProvider.on('auth_failure', (r) => (responseEvents['auth_failure'] = r))
|
||||
mockProvider.on('message', (r) => (responseEvents['message'] = r))
|
||||
|
||||
/// Emitimos eventos
|
||||
mockProvider.delaySendMessage(
|
||||
0,
|
||||
'require_action',
|
||||
MOCK_EVENTS.require_action
|
||||
)
|
||||
mockProvider.delaySendMessage(0, 'require_action', MOCK_EVENTS.require_action)
|
||||
mockProvider.delaySendMessage(0, 'ready', MOCK_EVENTS.ready)
|
||||
mockProvider.delaySendMessage(0, 'auth_failure', MOCK_EVENTS.auth_failure)
|
||||
mockProvider.delaySendMessage(0, 'message', MOCK_EVENTS.message)
|
||||
@@ -187,21 +160,12 @@ test(`[Bot] Probando Flujos Internos`, async () => {
|
||||
await delay(0)
|
||||
|
||||
/// Testeamos eventos
|
||||
assert.is(
|
||||
JSON.stringify(responseEvents.require_action),
|
||||
JSON.stringify(MOCK_EVENTS.require_action)
|
||||
)
|
||||
assert.is(JSON.stringify(responseEvents.require_action), JSON.stringify(MOCK_EVENTS.require_action))
|
||||
assert.is(responseEvents.ready, MOCK_EVENTS.ready)
|
||||
|
||||
assert.is(
|
||||
JSON.stringify(responseEvents.auth_failure),
|
||||
JSON.stringify(MOCK_EVENTS.auth_failure)
|
||||
)
|
||||
assert.is(JSON.stringify(responseEvents.auth_failure), JSON.stringify(MOCK_EVENTS.auth_failure))
|
||||
|
||||
assert.is(
|
||||
JSON.stringify(responseEvents.message),
|
||||
JSON.stringify(MOCK_EVENTS.message)
|
||||
)
|
||||
assert.is(JSON.stringify(responseEvents.message), JSON.stringify(MOCK_EVENTS.message))
|
||||
})
|
||||
|
||||
test(`[Bot] Probando Flujos Nested`, async () => {
|
||||
@@ -234,20 +198,13 @@ test(`[Bot] Probando Flujos Nested`, async () => {
|
||||
botInstance.sendProviderAndSave('xxxxx', 'xxxxx')
|
||||
botInstance.continue('xxxxx', 'xxxxx')
|
||||
/// Escuchamos eventos
|
||||
mockProvider.on(
|
||||
'require_action',
|
||||
(r) => (responseEvents['require_action'] = r)
|
||||
)
|
||||
mockProvider.on('require_action', (r) => (responseEvents['require_action'] = r))
|
||||
mockProvider.on('ready', (r) => (responseEvents['ready'] = r))
|
||||
mockProvider.on('auth_failure', (r) => (responseEvents['auth_failure'] = r))
|
||||
mockProvider.on('message', (r) => (responseEvents['message'] = r))
|
||||
|
||||
/// Emitimos eventos
|
||||
mockProvider.delaySendMessage(
|
||||
0,
|
||||
'require_action',
|
||||
MOCK_EVENTS.require_action
|
||||
)
|
||||
mockProvider.delaySendMessage(0, 'require_action', MOCK_EVENTS.require_action)
|
||||
mockProvider.delaySendMessage(0, 'ready', MOCK_EVENTS.ready)
|
||||
mockProvider.delaySendMessage(0, 'auth_failure', MOCK_EVENTS.auth_failure)
|
||||
mockProvider.delaySendMessage(0, 'message', MOCK_EVENTS.message)
|
||||
@@ -255,21 +212,12 @@ test(`[Bot] Probando Flujos Nested`, async () => {
|
||||
await delay(0)
|
||||
|
||||
/// Testeamos eventos
|
||||
assert.is(
|
||||
JSON.stringify(responseEvents.require_action),
|
||||
JSON.stringify(MOCK_EVENTS.require_action)
|
||||
)
|
||||
assert.is(JSON.stringify(responseEvents.require_action), JSON.stringify(MOCK_EVENTS.require_action))
|
||||
assert.is(responseEvents.ready, MOCK_EVENTS.ready)
|
||||
|
||||
assert.is(
|
||||
JSON.stringify(responseEvents.auth_failure),
|
||||
JSON.stringify(MOCK_EVENTS.auth_failure)
|
||||
)
|
||||
assert.is(JSON.stringify(responseEvents.auth_failure), JSON.stringify(MOCK_EVENTS.auth_failure))
|
||||
|
||||
assert.is(
|
||||
JSON.stringify(responseEvents.message),
|
||||
JSON.stringify(MOCK_EVENTS.message)
|
||||
)
|
||||
assert.is(JSON.stringify(responseEvents.message), JSON.stringify(MOCK_EVENTS.message))
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
@@ -35,10 +35,7 @@ test('Debere probar toSerialize', () => {
|
||||
const ARRANGE = {
|
||||
keyword: ['hola!', 'ole'],
|
||||
}
|
||||
const MAIN_CTX = addKeyword(ARRANGE.keyword)
|
||||
.addAnswer('Segundo!')
|
||||
.addAnswer('Segundo!')
|
||||
.toJson()
|
||||
const MAIN_CTX = addKeyword(ARRANGE.keyword).addAnswer('Segundo!').addAnswer('Segundo!').toJson()
|
||||
|
||||
const [ANSWER_A] = MAIN_CTX
|
||||
|
||||
@@ -71,9 +68,7 @@ test('Debere probar la anidación', () => {
|
||||
answer_A: 'Bienvenido',
|
||||
answer_B: 'Continuar',
|
||||
}
|
||||
const MAIN_CTX = addKeyword(ARRANGE.keyword)
|
||||
.addAnswer(ARRANGE.answer_A)
|
||||
.addAnswer(ARRANGE.answer_B)
|
||||
const MAIN_CTX = addKeyword(ARRANGE.keyword).addAnswer(ARRANGE.answer_A).addAnswer(ARRANGE.answer_B)
|
||||
|
||||
assert.is(MAIN_CTX.ctx.answer, ARRANGE.answer_B)
|
||||
})
|
||||
@@ -107,10 +102,7 @@ test('Debere probar error las addAnswer', () => {
|
||||
})
|
||||
|
||||
test('Obtener toJson', () => {
|
||||
const [ctxA, ctxB, ctxC] = addKeyword('hola')
|
||||
.addAnswer('pera!')
|
||||
.addAnswer('chao')
|
||||
.toJson()
|
||||
const [ctxA, ctxB, ctxC] = addKeyword('hola').addAnswer('pera!').addAnswer('chao').toJson()
|
||||
|
||||
assert.is(ctxA.keyword, 'hola')
|
||||
assert.match(ctxA.ref, /^key_/)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const delay = (miliseconds) =>
|
||||
new Promise((res) => setTimeout(res, miliseconds))
|
||||
const delay = (miliseconds) => new Promise((res) => setTimeout(res, miliseconds))
|
||||
|
||||
module.exports = { delay }
|
||||
|
||||
@@ -3,9 +3,7 @@ const flatObject = (listArray = []) => {
|
||||
|
||||
if (!listArray.length) return {}
|
||||
|
||||
const cbNestedObj = cbNestedList
|
||||
.map(({ ctx }) => ctx?.callbacks)
|
||||
.filter((i) => !!i)
|
||||
const cbNestedObj = cbNestedList.map(({ ctx }) => ctx?.callbacks).filter((i) => !!i)
|
||||
const queueCb = cbNestedObj.reduce((acc, current) => {
|
||||
const getKeys = Object.keys(current)
|
||||
const parse = getKeys.map((icb, i) => ({
|
||||
|
||||
@@ -16,9 +16,6 @@ const generateRef = (prefix = false) => {
|
||||
* @returns
|
||||
*/
|
||||
const generateRefSerialize = ({ index, answer, keyword }) =>
|
||||
crypto
|
||||
.createHash('md5')
|
||||
.update(JSON.stringify({ index, answer, keyword }))
|
||||
.digest('hex')
|
||||
crypto.createHash('md5').update(JSON.stringify({ index, answer, keyword })).digest('hex')
|
||||
|
||||
module.exports = { generateRef, generateRefSerialize }
|
||||
|
||||
@@ -4,9 +4,7 @@ const printer = (message, title) => {
|
||||
if (NODE_ENV !== 'test') {
|
||||
// console.clear()
|
||||
if (title) console.log(bgRed(`${title}`))
|
||||
console.log(
|
||||
yellow(Array.isArray(message) ? message.join('\n') : message)
|
||||
)
|
||||
console.log(yellow(Array.isArray(message) ? message.join('\n') : message))
|
||||
console.log(``)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,15 +5,9 @@ const checkNodeVersion = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(bgCyan('🚀 Revisando tu Node.js'))
|
||||
const version = process.version
|
||||
const majorVersion = parseInt(
|
||||
version.replace('v', '').split('.').shift()
|
||||
)
|
||||
const majorVersion = parseInt(version.replace('v', '').split('.').shift())
|
||||
if (majorVersion < 16) {
|
||||
console.error(
|
||||
red(
|
||||
`🔴 Se require Node.js 16 o superior. Actualmente esta ejecutando Node.js ${version}`
|
||||
)
|
||||
)
|
||||
console.error(red(`🔴 Se require Node.js 16 o superior. Actualmente esta ejecutando Node.js ${version}`))
|
||||
console.log(``)
|
||||
reject('ERROR_NODE')
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ const rimraf = require('rimraf')
|
||||
const { yellow } = require('kleur')
|
||||
const { join } = require('path')
|
||||
|
||||
const PATH_WW = [
|
||||
join(process.cwd(), '.wwebjs_auth'),
|
||||
join(process.cwd(), 'session.json'),
|
||||
]
|
||||
const PATH_WW = [join(process.cwd(), '.wwebjs_auth'), join(process.cwd(), 'session.json')]
|
||||
|
||||
const cleanSession = () => {
|
||||
const queue = []
|
||||
|
||||
@@ -23,11 +23,7 @@ const JSON_TEMPLATE = {
|
||||
const PATH_CONFIG = join(process.cwd(), 'config.json')
|
||||
|
||||
const jsonConfig = () => {
|
||||
return writeFile(
|
||||
PATH_CONFIG,
|
||||
JSON.stringify(JSON_TEMPLATE, null, 2),
|
||||
'utf-8'
|
||||
)
|
||||
return writeFile(PATH_CONFIG, JSON.stringify(JSON_TEMPLATE, null, 2), 'utf-8')
|
||||
}
|
||||
|
||||
module.exports = { jsonConfig }
|
||||
|
||||
@@ -20,13 +20,9 @@ const installDeps = (pkgManager, packageList) => {
|
||||
const installSingle = (pkgInstall) => () => {
|
||||
new Promise((resolve) => {
|
||||
try {
|
||||
childProcess = spawn(
|
||||
pkgManager,
|
||||
[PKG_OPTION[pkgManager], pkgInstall],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
}
|
||||
)
|
||||
childProcess = spawn(pkgManager, [PKG_OPTION[pkgManager], pkgInstall], {
|
||||
stdio: 'inherit',
|
||||
})
|
||||
|
||||
childProcess.on('error', (e) => {
|
||||
console.error(e)
|
||||
|
||||
@@ -30,9 +30,7 @@ const startInteractive = async () => {
|
||||
await nextSteps()
|
||||
} catch (e) {
|
||||
console.error(bgRed(`Ups! 🙄 algo no va bien.`))
|
||||
console.error(
|
||||
bgRed(`Revisa los requerimientos minimos en la documentacion`)
|
||||
)
|
||||
console.error(bgRed(`Revisa los requerimientos minimos en la documentacion`))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +80,7 @@ const nextSteps = async () => {
|
||||
const { outDir = '', providerDb = [], providerWs = [] } = response
|
||||
|
||||
const createApp = async (templateName = null) => {
|
||||
if (!templateName)
|
||||
throw new Error('TEMPLATE_NAME_INVALID: ', templateName)
|
||||
if (!templateName) throw new Error('TEMPLATE_NAME_INVALID: ', templateName)
|
||||
|
||||
const possiblesPath = [
|
||||
join(__dirname, '..', '..', 'starters', 'apps', templateName),
|
||||
@@ -115,11 +112,7 @@ const nextSteps = async () => {
|
||||
const vendorProvider = async () => {
|
||||
const [answer] = providerWs
|
||||
if (!providerWs.length) {
|
||||
console.log(
|
||||
red(
|
||||
`Debes seleccionar un proveedor de whatsapp. Tecla [Space] para seleccionar`
|
||||
)
|
||||
)
|
||||
console.log(red(`Debes seleccionar un proveedor de whatsapp. Tecla [Space] para seleccionar`))
|
||||
process.exit(1)
|
||||
}
|
||||
return answer
|
||||
@@ -132,11 +125,7 @@ const nextSteps = async () => {
|
||||
const dbProvider = async () => {
|
||||
const [answer] = providerDb
|
||||
if (!providerDb.length) {
|
||||
console.log(
|
||||
red(
|
||||
`Debes seleccionar un proveedor de base de datos. Tecla [Space] para seleccionar`
|
||||
)
|
||||
)
|
||||
console.log(red(`Debes seleccionar un proveedor de base de datos. Tecla [Space] para seleccionar`))
|
||||
process.exit(1)
|
||||
}
|
||||
return answer
|
||||
|
||||
@@ -38,10 +38,8 @@ class DialogFlowCXContext extends CoreClass {
|
||||
* */
|
||||
}
|
||||
|
||||
if (!this.optionsDX.location.length)
|
||||
throw new Error('LOCATION_NO_ENCONTRADO')
|
||||
if (!this.optionsDX.agentId.length)
|
||||
throw new Error('AGENTID_NO_ENCONTRADO')
|
||||
if (!this.optionsDX.location.length) throw new Error('LOCATION_NO_ENCONTRADO')
|
||||
if (!this.optionsDX.agentId.length) throw new Error('AGENTID_NO_ENCONTRADO')
|
||||
|
||||
const rawJson = readFileSync(GOOGLE_ACCOUNT_PATH, 'utf-8')
|
||||
const { project_id, private_key, client_email } = JSON.parse(rawJson)
|
||||
@@ -86,9 +84,7 @@ class DialogFlowCXContext extends CoreClass {
|
||||
},
|
||||
}
|
||||
|
||||
const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [
|
||||
null,
|
||||
]
|
||||
const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [null]
|
||||
|
||||
const listMessages = single.queryResult.responseMessages.map((res) => {
|
||||
if (res.message == 'text') {
|
||||
@@ -96,17 +92,11 @@ class DialogFlowCXContext extends CoreClass {
|
||||
}
|
||||
|
||||
if (res.message == 'payload') {
|
||||
const {
|
||||
media = null,
|
||||
buttons = [],
|
||||
answer = '',
|
||||
} = res.payload.fields
|
||||
const buttonsArray = buttons?.listValue?.values?.map(
|
||||
(btnValue) => {
|
||||
const { stringValue } = btnValue.structValue.fields.body
|
||||
return { body: stringValue }
|
||||
}
|
||||
)
|
||||
const { media = null, buttons = [], answer = '' } = res.payload.fields
|
||||
const buttonsArray = buttons?.listValue?.values?.map((btnValue) => {
|
||||
const { stringValue } = btnValue.structValue.fields.body
|
||||
return { body: stringValue }
|
||||
})
|
||||
return {
|
||||
answer: answer?.stringValue,
|
||||
options: {
|
||||
|
||||
@@ -5,8 +5,7 @@ const DialogCXFlowClass = require('./dialogflow-cx.class')
|
||||
* @param {*} args
|
||||
* @returns
|
||||
*/
|
||||
const createBotDialog = async ({ database, provider }, _options) =>
|
||||
new DialogCXFlowClass(database, provider, _options)
|
||||
const createBotDialog = async ({ database, provider }, _options) => new DialogCXFlowClass(database, provider, _options)
|
||||
|
||||
module.exports = {
|
||||
createBotDialog,
|
||||
|
||||
@@ -65,10 +65,7 @@ class DialogFlowContext extends CoreClass {
|
||||
* para evitar este problema.
|
||||
* https://github.com/codigoencasa/bot-whatsapp/pull/140
|
||||
*/
|
||||
const session = this.sessionClient.projectAgentSessionPath(
|
||||
this.projectId,
|
||||
from
|
||||
)
|
||||
const session = this.sessionClient.projectAgentSessionPath(this.projectId, from)
|
||||
const reqDialog = {
|
||||
session,
|
||||
queryInput: {
|
||||
@@ -79,15 +76,11 @@ class DialogFlowContext extends CoreClass {
|
||||
},
|
||||
}
|
||||
|
||||
const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [
|
||||
null,
|
||||
]
|
||||
const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [null]
|
||||
|
||||
const { queryResult } = single
|
||||
|
||||
const msgPayload = queryResult?.fulfillmentMessages?.find(
|
||||
(a) => a.message === 'payload'
|
||||
)
|
||||
const msgPayload = queryResult?.fulfillmentMessages?.find((a) => a.message === 'payload')
|
||||
|
||||
// Revisamos si el dialogFlow tiene multimedia
|
||||
if (msgPayload && msgPayload?.payload) {
|
||||
|
||||
@@ -5,8 +5,7 @@ const DialogFlowClass = require('./dialogflow.class')
|
||||
* @param {*} args
|
||||
* @returns
|
||||
*/
|
||||
const createBotDialog = async ({ database, provider }) =>
|
||||
new DialogFlowClass(database, provider)
|
||||
const createBotDialog = async ({ database, provider }) => new DialogFlowClass(database, provider)
|
||||
|
||||
module.exports = {
|
||||
createBotDialog,
|
||||
|
||||
@@ -5,8 +5,7 @@ const MockClass = require('./mock.class')
|
||||
* @param {*} args
|
||||
* @returns
|
||||
*/
|
||||
const createBotMock = async ({ database, provider }) =>
|
||||
new MockClass(database, provider)
|
||||
const createBotMock = async ({ database, provider }) => new MockClass(database, provider)
|
||||
|
||||
module.exports = {
|
||||
createBotMock,
|
||||
|
||||
@@ -10,7 +10,10 @@ class MockDatabase {
|
||||
constructor() {}
|
||||
|
||||
getPrevByNumber = (from) => {
|
||||
const history = this.listHistory.slice().reverse()
|
||||
const history = this.listHistory
|
||||
.slice()
|
||||
.reverse()
|
||||
.filter((i) => !!i.keyword)
|
||||
return history.find((a) => a.from === from)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,18 +24,12 @@ class MongoAdapter {
|
||||
}
|
||||
|
||||
getPrevByNumber = async (from) => {
|
||||
const result = await this.db
|
||||
.collection('history')
|
||||
.find({ from })
|
||||
.sort({ _id: -1 })
|
||||
.limit(1)
|
||||
.toArray()
|
||||
const result = await this.db.collection('history').find({ from }).sort({ _id: -1 }).limit(1).toArray()
|
||||
return result[0]
|
||||
}
|
||||
|
||||
save = async (ctx) => {
|
||||
await this.db.collection('history').insert(ctx)
|
||||
console.log('Guardando DB...', ctx)
|
||||
this.listHistory.push(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,18 +46,8 @@ class MyslAdapter {
|
||||
})
|
||||
|
||||
save = (ctx) => {
|
||||
const values = [
|
||||
[
|
||||
ctx.ref,
|
||||
ctx.keyword,
|
||||
ctx.answer,
|
||||
ctx.refSerialize,
|
||||
ctx.from,
|
||||
JSON.stringify(ctx.options),
|
||||
],
|
||||
]
|
||||
const sql =
|
||||
'INSERT INTO history (ref, keyword, answer, refSerialize, phone, options ) values ?'
|
||||
const values = [[ctx.ref, ctx.keyword, ctx.answer, ctx.refSerialize, ctx.from, JSON.stringify(ctx.options)]]
|
||||
const sql = 'INSERT INTO history (ref, keyword, answer, refSerialize, phone, options ) values ?'
|
||||
|
||||
this.db.query(sql, [values], (err) => {
|
||||
if (err) throw err
|
||||
@@ -71,14 +61,14 @@ class MyslAdapter {
|
||||
const tableName = 'history'
|
||||
|
||||
const sql = `CREATE TABLE ${tableName}
|
||||
(id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
ref varchar(255) NOT NULL,
|
||||
keyword varchar(255) NOT NULL,
|
||||
answer longtext NOT NULL,
|
||||
refSerialize varchar(255) NOT NULL,
|
||||
phone varchar(255) NOT NULL,
|
||||
options longtext NOT NULL
|
||||
)`
|
||||
(id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
ref varchar(255) NOT NULL,
|
||||
keyword varchar(255) NOT NULL,
|
||||
answer longtext NOT NULL,
|
||||
refSerialize varchar(255) NOT NULL,
|
||||
phone varchar(255) NOT NULL,
|
||||
options longtext NOT NULL)
|
||||
CHARACTER SET utf8mb4 COLLATE utf8mb4_General_ci`
|
||||
|
||||
this.db.query(sql, (err) => {
|
||||
if (err) throw err
|
||||
|
||||
@@ -8,6 +8,5 @@
|
||||
font-family: IBMPlexMono-Regular;
|
||||
src: url(IBMPlexMono-Regular-subset.woff2) format('woff2'),
|
||||
url(IBMPlexMono-Regular-subset.zopfli.woff) format('woff');
|
||||
unicode-range: U+20, U+2C, U+2E, U+41-43, U+46, U+49, U+4B-4F, U+53-55, U+58,
|
||||
U+61-65, U+67-69, U+6C-76;
|
||||
unicode-range: U+20, U+2C, U+2E, U+41-43, U+46, U+49, U+4B-4F, U+53-55, U+58, U+61-65, U+67-69, U+6C-76;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
font-family: IBMPlexMono-SemiBold;
|
||||
src: url(IBMPlexMono-SemiBold-subset.woff2) format('woff2'),
|
||||
url(IBMPlexMono-SemiBold-subset.zopfli.woff) format('woff');
|
||||
unicode-range: U+20, U+24, U+2E, U+30, U+38, U+39, U+41, U+42, U+44, U+47,
|
||||
U+4E, U+4F, U+52-55, U+57, U+59, U+65, U+68, U+6F, U+72, U+74;
|
||||
unicode-range: U+20, U+24, U+2E, U+30, U+38, U+39, U+41, U+42, U+44, U+47, U+4E, U+4F, U+52-55, U+57, U+59, U+65,
|
||||
U+68, U+6F, U+72, U+74;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
|
||||
@font-face {
|
||||
font-family: Pally-Variable;
|
||||
src: url(Pally-Variable-subset.woff2) format('woff2'),
|
||||
url(Pally-Variable-subset.zopfli.woff) format('woff');
|
||||
unicode-range: U+20, U+24, U+2C, U+2E, U+30, U+33, U+39, U+41-43, U+46,
|
||||
U+49-4D, U+53, U+55, U+58, U+61-65, U+67-69, U+6B-77, U+79;
|
||||
src: url(Pally-Variable-subset.woff2) format('woff2'), url(Pally-Variable-subset.zopfli.woff) format('woff');
|
||||
unicode-range: U+20, U+24, U+2C, U+2E, U+30, U+33, U+39, U+41-43, U+46, U+49-4D, U+53, U+55, U+58, U+61-65, U+67-69,
|
||||
U+6B-77, U+79;
|
||||
font-weight: 400 700;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,5 @@
|
||||
font-family: SourceSerifPro-Regular;
|
||||
src: url(SourceSerifPro-Regular-subset.woff2) format('woff2'),
|
||||
url(SourceSerifPro-Regular-subset.zopfli.woff) format('woff');
|
||||
unicode-range: U+20, U+2C, U+2E, U+41-44, U+49, U+4A, U+4C, U+53, U+55,
|
||||
U+61-65, U+67-69, U+6B-76, U+79;
|
||||
unicode-range: U+20, U+2C, U+2E, U+41-44, U+49, U+4A, U+4C, U+53, U+55, U+61-65, U+67-69, U+6B-76, U+79;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
|
||||
@font-face {
|
||||
font-family: Synonym-Variable;
|
||||
src: url(Synonym-Variable-subset.woff2) format('woff2'),
|
||||
url(Synonym-Variable-subset.zopfli.woff) format('woff');
|
||||
unicode-range: U+20, U+24, U+2C, U+2E, U+30, U+33, U+35, U+41-44, U+46, U+47,
|
||||
U+49, U+4B-4F, U+53-55, U+57-59, U+61, U+63-65, U+67-69, U+6C-76;
|
||||
src: url(Synonym-Variable-subset.woff2) format('woff2'), url(Synonym-Variable-subset.zopfli.woff) format('woff');
|
||||
unicode-range: U+20, U+24, U+2C, U+2E, U+30, U+33, U+35, U+41-44, U+46, U+47, U+49, U+4B-4F, U+53-55, U+57-59, U+61,
|
||||
U+63-65, U+67-69, U+6C-76;
|
||||
font-weight: 400 700;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
@font-face {
|
||||
font-family: TenorSans-Regular;
|
||||
src: url(TenorSans-Regular-subset.woff2) format('woff2'),
|
||||
url(TenorSans-Regular-subset.zopfli.woff) format('woff');
|
||||
unicode-range: U+20, U+24, U+2E, U+30, U+36, U+46, U+49, U+4A, U+53, U+54,
|
||||
U+61, U+63, U+65, U+69, U+6B, U+6E, U+6F, U+72-75, U+79;
|
||||
src: url(TenorSans-Regular-subset.woff2) format('woff2'), url(TenorSans-Regular-subset.zopfli.woff) format('woff');
|
||||
unicode-range: U+20, U+24, U+2E, U+30, U+36, U+46, U+49, U+4A, U+53, U+54, U+61, U+63, U+65, U+69, U+6B, U+6E, U+6F,
|
||||
U+72-75, U+79;
|
||||
}
|
||||
|
||||
BIN
packages/docs/src/assets/images/digital-ocean.png
Normal file
BIN
packages/docs/src/assets/images/digital-ocean.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.0 KiB |
189
packages/docs/src/components/atoms/DigitalOcean.tsx
Normal file
189
packages/docs/src/components/atoms/DigitalOcean.tsx
Normal file
@@ -0,0 +1,189 @@
|
||||
export const DigitalOcean = () => (
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 604 129"
|
||||
style="enable-background:new 0 0 604 129;"
|
||||
xml:space="preserve"
|
||||
>
|
||||
<g>
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
class="st0"
|
||||
d="M174.3,3c4.9,0,8.7,2.9,8.7,8.6c0,5.6-3.8,8.5-8.7,8.5h-7.6v11.1h-3.5V3H174.3z M166.7,17.1h7.2
|
||||
c3,0,5.6-1.8,5.6-5.5c0-3.8-2.5-5.5-5.6-5.5h-7.2V17.1z"
|
||||
/>
|
||||
<path
|
||||
class="st0"
|
||||
d="M208.8,21.7c0,6.1-4.3,10-9.9,10c-5.6,0-9.9-3.9-9.9-10c0-6.1,4.3-10,9.9-10
|
||||
C204.5,11.7,208.8,15.6,208.8,21.7z M192.3,21.7c0,4.5,2.9,7.2,6.6,7.2c3.7,0,6.6-2.7,6.6-7.2c0-4.5-2.9-7.1-6.6-7.1
|
||||
C195.2,14.5,192.3,17.2,192.3,21.7z"
|
||||
/>
|
||||
<path
|
||||
class="st0"
|
||||
d="M234.4,31.3l-5.2-13.8L224,31.3h-2.6L214.1,12h3.6l5.2,14l5.2-14h2.3l5.3,14l5.2-14h3.5L237,31.3H234.4z"
|
||||
/>
|
||||
<path
|
||||
class="st0"
|
||||
d="M253,22.9c0.2,3.7,2.6,5.9,6,5.9c2.8,0,4.8-1.3,5.4-3.4l3.2,0.2c-0.8,3.5-4.1,6.1-8.6,6.1
|
||||
c-5.5,0-9.6-3.7-9.6-10c0-6.3,4-10,9.5-10c5.5,0,8.8,3.7,8.8,9.4v1.8H253z M253,20.3h11.6c-0.1-3.4-2-5.7-5.6-5.7
|
||||
C255.6,14.5,253.2,16.5,253,20.3z"
|
||||
/>
|
||||
<path
|
||||
class="st0"
|
||||
d="M285.4,14.9c-3.4,0-5.6,2.3-5.6,5.3v11.1h-3.2V12h3.2v2.9c0.7-1.6,2.5-3.1,5.7-3.1V14.9z"
|
||||
/>
|
||||
<path
|
||||
class="st0"
|
||||
d="M294.7,22.9c0.2,3.7,2.6,5.9,6,5.9c2.8,0,4.8-1.3,5.4-3.4l3.2,0.2c-0.8,3.5-4.1,6.1-8.6,6.1
|
||||
c-5.5,0-9.6-3.7-9.6-10c0-6.3,4-10,9.5-10c5.5,0,8.8,3.7,8.8,9.4v1.8H294.7z M294.7,20.3h11.6c-0.1-3.4-2-5.7-5.6-5.7
|
||||
C297.4,14.5,294.9,16.5,294.7,20.3z"
|
||||
/>
|
||||
<path
|
||||
class="st0"
|
||||
d="M333.1,31.3v-3.1c-1.1,2-3.6,3.5-6.8,3.5c-5.3,0-9.3-3.8-9.3-10c0-6.2,4-10,9.3-10c3.2,0,5.6,1.4,6.6,3.2V2
|
||||
h3.2v29.4H333.1z M320.3,21.7c0,4.6,2.8,7.2,6.5,7.2c3.6,0,6.2-2.2,6.2-6.6v-1.1c0-4.3-2.6-6.6-6.2-6.6
|
||||
C323.1,14.5,320.3,17.1,320.3,21.7z"
|
||||
/>
|
||||
<path
|
||||
class="st0"
|
||||
d="M361.8,14.9c1.1-1.9,3.4-3.2,6.7-3.2c5.3,0,9.3,3.8,9.3,10c0,6.2-4,10-9.3,10c-3.3,0-5.7-1.5-6.8-3.5v3.1
|
||||
h-3.1V2h3.2V14.9z M361.9,21.1v1.1c0,4.4,2.6,6.6,6.2,6.6c3.7,0,6.5-2.5,6.5-7.2c0-4.6-2.8-7.1-6.5-7.1
|
||||
C364.5,14.5,361.9,16.8,361.9,21.1z"
|
||||
/>
|
||||
<path class="st0" d="M386.3,40.9l4.6-10.7L383.2,12h3.6l5.8,14.5l5.8-14.5h3.6l-12.2,28.9H386.3z" />
|
||||
</g>
|
||||
</g>
|
||||
<g id="XMLID_2369_">
|
||||
<g>
|
||||
<g id="XMLID_281_">
|
||||
<g id="XMLID_282_">
|
||||
<g>
|
||||
<g id="XMLID_283_">
|
||||
<g id="XMLID_287_">
|
||||
<path
|
||||
id="XMLID_288_"
|
||||
class="st0"
|
||||
d="M64.4,127l0-24.2c25.6,0,45.5-25.4,35.7-52.3c-3.6-10-11.6-17.9-21.6-21.6
|
||||
c-27-9.8-52.3,10-52.3,35.7c0,0,0,0,0,0L2,64.7C2,23.8,41.5-8,84.3,5.4c18.7,5.8,33.6,20.7,39.4,39.4
|
||||
C137,87.6,105.2,127,64.4,127z"
|
||||
/>
|
||||
</g>
|
||||
<polygon
|
||||
id="XMLID_286_"
|
||||
class="st1"
|
||||
points="64.4,102.9 40.4,102.9 40.4,78.9 40.4,78.9 64.4,78.9 64.4,78.9 "
|
||||
/>
|
||||
<polygon
|
||||
id="XMLID_285_"
|
||||
class="st1"
|
||||
points="40.3,121.5 21.8,121.5 21.8,121.5 21.8,102.9 40.4,102.9 40.4,121.5 "
|
||||
/>
|
||||
<path
|
||||
id="XMLID_284_"
|
||||
class="st1"
|
||||
d="M21.9,102.9H6.3c0,0,0,0,0,0V87.4c0,0,0,0,0,0h15.5c0,0,0,0,0,0V102.9z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="XMLID_254_">
|
||||
<path
|
||||
id="XMLID_278_"
|
||||
class="st0"
|
||||
d="M200.9,52.4c-5.5-3.8-12.4-5.8-20.5-5.8h-17.5v55.5h17.5c8,0,14.9-2.1,20.5-6.1
|
||||
c3-2.1,5.4-5.1,7.1-8.9c1.7-3.7,2.5-8.2,2.5-13.1c0-4.9-0.8-9.3-2.5-13C206.3,57.4,203.9,54.4,200.9,52.4z M173.1,56h5.5
|
||||
c6.1,0,11.1,1.2,15,3.6c4.2,2.6,6.4,7.4,6.4,14.4c0,7.2-2.2,12.3-6.4,15.1h0c-3.7,2.4-8.7,3.6-14.9,3.6h-5.6V56z"
|
||||
/>
|
||||
<path
|
||||
id="XMLID_277_"
|
||||
class="st0"
|
||||
d="M222.6,45.9c-1.7,0-3.1,0.6-4.3,1.8c-1.2,1.1-1.8,2.6-1.8,4.2c0,1.7,0.6,3.1,1.8,4.3
|
||||
c1.2,1.2,2.6,1.8,4.3,1.8c1.7,0,3.1-0.6,4.3-1.8c1.2-1.2,1.8-2.6,1.8-4.3c0-1.7-0.6-3.1-1.8-4.2
|
||||
C225.7,46.5,224.3,45.9,222.6,45.9z"
|
||||
/>
|
||||
<rect id="XMLID_276_" x="217.6" y="63" class="st0" width="9.8" height="39.1" />
|
||||
<path
|
||||
id="XMLID_273_"
|
||||
class="st0"
|
||||
d="M263.2,66.3c-3-2.6-6.3-4.2-9.9-4.2c-5.4,0-9.9,1.9-13.4,5.6c-3.5,3.7-5.3,8.4-5.3,14.1
|
||||
c0,5.5,1.8,10.2,5.2,14c3.5,3.7,8,5.5,13.5,5.5c3.8,0,7.1-1.1,9.7-3.1V99c0,3.2-0.9,5.8-2.6,7.5c-1.7,1.7-4.1,2.6-7.1,2.6
|
||||
c-4.5,0-7.4-1.8-10.9-6.5l-6.7,6.4l0.2,0.3c1.4,2,3.7,4,6.6,5.9c2.9,1.9,6.6,2.8,10.9,2.8c5.8,0,10.6-1.8,14.1-5.4
|
||||
c3.5-3.6,5.3-8.4,5.3-14.2V63h-9.7V66.3z M260.6,89.4c-1.7,2-3.9,2.9-6.8,2.9c-2.8,0-5-0.9-6.7-2.9c-1.7-1.9-2.5-4.5-2.5-7.7
|
||||
c0-3.2,0.9-5.8,2.5-7.7c1.7-1.9,3.9-2.9,6.7-2.9c2.8,0,5,1,6.8,2.9c1.7,2,2.6,4.6,2.6,7.7C263.2,84.9,262.3,87.5,260.6,89.4z"
|
||||
/>
|
||||
<rect id="XMLID_272_" x="281.3" y="63" class="st0" width="9.8" height="39.1" />
|
||||
<path
|
||||
id="XMLID_271_"
|
||||
class="st0"
|
||||
d="M286.3,45.9c-1.7,0-3.1,0.6-4.3,1.8c-1.2,1.1-1.8,2.6-1.8,4.2c0,1.7,0.6,3.1,1.8,4.3
|
||||
c1.2,1.2,2.6,1.8,4.3,1.8c1.7,0,3.1-0.6,4.3-1.8c1.2-1.2,1.8-2.6,1.8-4.3c0-1.7-0.6-3.1-1.8-4.2C289.4,46.5,288,45.9,286.3,45.9
|
||||
z"
|
||||
/>
|
||||
<path
|
||||
id="XMLID_270_"
|
||||
class="st0"
|
||||
d="M312.7,52.5H303V63h-5.6v9h5.6v16.2c0,5.1,1,8.7,3,10.8c2,2.1,5.6,3.2,10.6,3.2
|
||||
c1.6,0,3.2-0.1,4.8-0.2l0.4,0v-9l-3.4,0.2c-2.3,0-3.9-0.4-4.7-1.2c-0.8-0.8-1.1-2.6-1.1-5.2V72h9.2v-9h-9.2V52.5z"
|
||||
/>
|
||||
<rect id="XMLID_269_" x="368" y="46.6" class="st0" width="9.8" height="55.5" />
|
||||
<path
|
||||
id="XMLID_268_"
|
||||
class="st0"
|
||||
d="M477.3,88.2c-1.8,2-3.6,3.7-4.9,4.6v0c-1.4,0.9-3.1,1.3-5.1,1.3c-2.9,0-5.2-1.1-7.1-3.2
|
||||
c-1.9-2.2-2.8-4.9-2.8-8.3s0.9-6.1,2.8-8.2c1.9-2.2,4.2-3.2,7.1-3.2c3.2,0,6.5,2,9.4,5.4l6.5-6.2l0,0c-4.2-5.5-9.7-8.1-16.1-8.1
|
||||
c-5.4,0-10.1,2-13.9,5.8c-3.8,3.9-5.7,8.8-5.7,14.6s1.9,10.7,5.7,14.6c3.8,3.9,8.5,5.9,13.9,5.9c7.1,0,12.9-3.1,16.8-8.7
|
||||
L477.3,88.2z"
|
||||
/>
|
||||
<path
|
||||
id="XMLID_265_"
|
||||
class="st0"
|
||||
d="M517.7,68.5c-1.4-1.9-3.3-3.5-5.7-4.7c-2.3-1.1-5.1-1.7-8.1-1.7c-5.5,0-10,2-13.4,6
|
||||
c-3.3,4-4.9,8.9-4.9,14.7c0,5.9,1.8,10.8,5.4,14.6c3.6,3.7,8.4,5.6,14.2,5.6c6.6,0,12.1-2.7,16.2-8l0.2-0.3l-6.4-6.2l0,0
|
||||
c-0.6,0.7-1.4,1.5-2.2,2.3c-1,0.9-1.9,1.6-2.9,2.1c-1.5,0.7-3.1,1.1-5,1.1c-2.7,0-5-0.8-6.7-2.4c-1.6-1.5-2.6-3.5-2.8-5.9h26.1
|
||||
l0.1-3.6c0-2.5-0.3-5-1-7.3C520.1,72.6,519.1,70.4,517.7,68.5z M496.2,77.7c0.5-1.9,1.3-3.4,2.6-4.6c1.3-1.3,3.1-2,5.2-2
|
||||
c2.4,0,4.2,0.7,5.5,2c1.2,1.2,1.8,2.8,2,4.6H496.2z"
|
||||
/>
|
||||
<path
|
||||
id="XMLID_262_"
|
||||
class="st0"
|
||||
d="M555.5,66L555.5,66c-3-2.5-7.1-3.8-12.3-3.8c-3.3,0-6.3,0.7-9.1,2.1
|
||||
c-2.6,1.3-5.1,3.5-6.7,6.3l0.1,0.1l6.3,6c2.6-4.1,5.5-5.6,9.3-5.6c2.1,0,3.8,0.6,5.1,1.6c1.3,1.1,1.9,2.5,1.9,4.2v1.9
|
||||
c-2.4-0.7-4.9-1.1-7.2-1.1c-4.9,0-8.9,1.2-11.8,3.4c-3,2.3-4.5,5.6-4.5,9.8c0,3.7,1.3,6.7,3.8,8.9c2.6,2.1,5.8,3.2,9.5,3.2
|
||||
c3.7,0,7.3-1.5,10.4-4.1v3.2h9.7V77C560,72.2,558.5,68.5,555.5,66z M538,87.2c1.1-0.8,2.7-1.2,4.7-1.2c2.4,0,4.9,0.5,7.5,1.4
|
||||
v3.8c-2.1,2-5,3-8.5,3c-1.7,0-3-0.4-3.9-1.1c-0.9-0.7-1.3-1.7-1.3-2.8C536.4,89,536.9,88,538,87.2z"
|
||||
/>
|
||||
<path
|
||||
id="XMLID_261_"
|
||||
class="st0"
|
||||
d="M597.9,66.7c-2.7-3.1-6.6-4.6-11.5-4.6c-3.9,0-7.1,1.1-9.4,3.3V63h-9.7v39.1h9.8V80.6
|
||||
c0-3,0.7-5.3,2.1-7c1.4-1.7,3.3-2.5,5.8-2.5c2.2,0,3.9,0.7,5.2,2.2c1.3,1.5,1.9,3.6,1.9,6.2v22.7h9.8V79.5
|
||||
C602,74.1,600.6,69.8,597.9,66.7z"
|
||||
/>
|
||||
<path
|
||||
id="XMLID_258_"
|
||||
class="st0"
|
||||
d="M355.6,66L355.6,66c-3-2.5-7.1-3.8-12.3-3.8c-3.3,0-6.3,0.7-9.1,2.1
|
||||
c-2.6,1.3-5.1,3.5-6.7,6.3l0.1,0.1l6.3,6c2.6-4.1,5.5-5.6,9.3-5.6c2.1,0,3.8,0.6,5.1,1.6c1.3,1.1,1.9,2.5,1.9,4.2v1.9
|
||||
c-2.4-0.7-4.9-1.1-7.2-1.1c-4.9,0-8.9,1.2-11.8,3.4c-3,2.3-4.5,5.6-4.5,9.8c0,3.7,1.3,6.7,3.8,8.9c2.6,2.1,5.8,3.2,9.5,3.2
|
||||
c3.7,0,7.3-1.5,10.4-4.1v3.2h9.7V77C360.2,72.2,358.7,68.5,355.6,66z M338.2,87.2c1.1-0.8,2.7-1.2,4.7-1.2
|
||||
c2.4,0,4.9,0.5,7.5,1.4v3.8c-2.1,2-5,3-8.5,3c-1.7,0-3-0.4-3.9-1.1c-0.9-0.7-1.3-1.7-1.3-2.8C336.6,89,337.1,88,338.2,87.2z"
|
||||
/>
|
||||
<path
|
||||
id="XMLID_255_"
|
||||
class="st0"
|
||||
d="M413.6,103c-15.8,0-28.6-12.8-28.6-28.6s12.8-28.6,28.6-28.6s28.6,12.8,28.6,28.6
|
||||
S429.4,103,413.6,103z M413.6,55.8c-10.2,0-18.5,8.3-18.5,18.5s8.3,18.5,18.5,18.5s18.5-8.3,18.5-18.5S423.8,55.8,413.6,55.8z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
)
|
||||
@@ -5,14 +5,7 @@ import logoSrc from '~/assets/images/chatbot-whatsapp.png?width=64&height=64&png
|
||||
|
||||
export default component$(() => (
|
||||
<span class="self-center ml-2 text-2xl md:text-xl font-bold text-gray-900 whitespace-nowrap dark:text-white flex items-center">
|
||||
<img
|
||||
src={logoSrc}
|
||||
class="inline-block mr-1"
|
||||
width={32}
|
||||
height={32}
|
||||
alt="Qwind Logo"
|
||||
loading="lazy"
|
||||
/>
|
||||
<img src={logoSrc} class="inline-block mr-1" width={32} height={32} alt="Qwind Logo" loading="lazy" />
|
||||
Chatbot
|
||||
</span>
|
||||
))
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
export const Netlify = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={147}
|
||||
height={40}
|
||||
role="img"
|
||||
fill="currentColor"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={147} height={40} role="img" fill="currentColor">
|
||||
<g fill-rule="evenodd">
|
||||
<path d="M53.37 12.978l.123 2.198c1.403-1.7 3.245-2.55 5.525-2.55 3.951 0 5.962 2.268 6.032 6.804v12.568H60.79V19.676c0-1.207-.26-2.1-.78-2.681-.52-.58-1.371-.87-2.552-.87-1.719 0-3 .78-3.84 2.338v13.535h-4.262v-19.02h4.016zM77.748 32.35c-2.7 0-4.89-.852-6.567-2.557-1.678-1.705-2.517-3.976-2.517-6.812v-.527c0-1.898.365-3.595 1.096-5.089.73-1.494 1.757-2.657 3.078-3.49 1.321-.831 2.794-1.247 4.42-1.247 2.583 0 4.58.826 5.988 2.478 1.41 1.653 2.114 3.99 2.114 7.014v1.723h-12.4c.13 1.57.652 2.812 1.57 3.726.918.914 2.073 1.371 3.464 1.371 1.952 0 3.542-.79 4.77-2.373l2.297 2.198c-.76 1.136-1.774 2.018-3.042 2.645-1.269.627-2.692.94-4.27.94zm-.508-16.294c-1.17 0-2.113.41-2.832 1.23-.72.82-1.178 1.963-1.377 3.428h8.12v-.317c-.094-1.43-.474-2.51-1.14-3.243-.667-.732-1.59-1.098-2.771-1.098zm16.765-7.7v4.623h3.35v3.164h-3.35V26.76c0 .726.144 1.25.43 1.573.286.322.798.483 1.535.483a6.55 6.55 0 0 0 1.49-.176v3.305c-.97.27-1.905.404-2.806.404-3.273 0-4.91-1.81-4.91-5.431V16.142H86.62v-3.164h3.122V8.355h4.261zm11.137 23.643h-4.262v-27h4.262v27zm9.172 0h-4.262v-19.02h4.262v19.02zm-4.525-23.96c0-.655.207-1.2.622-1.634.416-.433 1.009-.65 1.78-.65.772 0 1.368.217 1.79.65.42.434.63.979.63 1.635 0 .644-.21 1.18-.63 1.608-.422.428-1.018.642-1.79.642-.771 0-1.364-.214-1.78-.642-.415-.427-.622-.964-.622-1.608zm10.663 23.96V16.142h-2.894v-3.164h2.894v-1.74c0-2.11.584-3.738 1.753-4.887 1.17-1.148 2.806-1.722 4.91-1.722.749 0 1.544.105 2.386.316l-.105 3.34a8.375 8.375 0 0 0-1.631-.14c-2.035 0-3.052 1.048-3.052 3.146v1.687h3.858v3.164h-3.858v15.856h-4.261zm17.87-6.117l3.858-12.903h4.542l-7.54 21.903c-1.158 3.199-3.122 4.799-5.893 4.799-.62 0-1.304-.106-2.052-.317v-3.305l.807.053c1.075 0 1.885-.196 2.429-.589.543-.392.973-1.051 1.289-1.977l.613-1.635-6.664-18.932h4.595l4.016 12.903z" />
|
||||
<path
|
||||
|
||||
@@ -13,10 +13,7 @@ export const RouterHead = component$(() => {
|
||||
<title>{head.title}</title>
|
||||
|
||||
<link rel="canonical" href={loc.href} />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
|
||||
{head.meta.map((m) => (
|
||||
|
||||
@@ -34,14 +34,8 @@ export const Social = () => {
|
||||
content="https://campaign.codigoencasa.com"
|
||||
/>
|
||||
<meta property="og:site_name" content="campaign.codigoencasa.com" /> */}
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://i.imgur.com/0HpzsEm.png"
|
||||
></meta>
|
||||
<meta
|
||||
property="og:image:secure_url"
|
||||
content="https://i.imgur.com/0HpzsEm.png"
|
||||
/>
|
||||
<meta property="og:image" content="https://i.imgur.com/0HpzsEm.png"></meta>
|
||||
<meta property="og:image:secure_url" content="https://i.imgur.com/0HpzsEm.png" />
|
||||
<meta property="og:image:type" content="image/png"></meta>
|
||||
<meta property="og:image:width" content="1200"></meta>
|
||||
<meta property="og:image:height" content="630"></meta>
|
||||
@@ -52,10 +46,7 @@ export const Social = () => {
|
||||
name="twitter:title"
|
||||
content="💻 Conviértete en un Programador Backend aprendiendo todo de Cloud y Nodejs"
|
||||
/>
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://i.imgur.com/0HpzsEm.png"
|
||||
/>
|
||||
<meta name="twitter:image" content="https://i.imgur.com/0HpzsEm.png" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,9 +27,7 @@ export default component$((props: ItemProps) => {
|
||||
// TODO:
|
||||
document.body.classList.toggle('overflow-hidden')
|
||||
document.getElementById('header')?.classList.toggle('h-screen')
|
||||
document
|
||||
.querySelector('#header nav')
|
||||
?.classList.toggle('hidden')
|
||||
document.querySelector('#header nav')?.classList.toggle('hidden')
|
||||
}}
|
||||
>
|
||||
<IconMenu class={iconClass} />
|
||||
|
||||
@@ -10,16 +10,13 @@ interface ItemProps {
|
||||
export default component$((props: ItemProps) => {
|
||||
const { iconClass } = props
|
||||
const store = useStore({
|
||||
theme:
|
||||
(typeof window !== 'undefined' && window?.localStorage?.theme) ||
|
||||
undefined,
|
||||
theme: (typeof window !== 'undefined' && window?.localStorage?.theme) || undefined,
|
||||
})
|
||||
|
||||
useClientEffect$(() => {
|
||||
store.theme =
|
||||
window.localStorage.theme === 'dark' ||
|
||||
(!('theme' in window.localStorage) &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
(!('theme' in window.localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
? 'dark'
|
||||
: 'light'
|
||||
})
|
||||
@@ -42,11 +39,7 @@ export default component$((props: ItemProps) => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{store.theme == 'dark' ? (
|
||||
<IconMoon class={iconClass} />
|
||||
) : (
|
||||
<IconSun class={iconClass} />
|
||||
)}
|
||||
{store.theme == 'dark' ? <IconMoon class={iconClass} /> : <IconSun class={iconClass} />}
|
||||
</button>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -7,9 +7,7 @@ export const IconArrowDownRight = (props: ItemProps) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={`icon icon-tabler icon-tabler-arrow-down-right ${
|
||||
className || 'w-5 h-5'
|
||||
}`}
|
||||
class={`icon icon-tabler icon-tabler-arrow-down-right ${className || 'w-5 h-5'}`}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
|
||||
@@ -8,9 +8,7 @@ export const IconMenu = (props: ItemProps) => {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
class={`icon icon-tabler icon-tabler-menu ${
|
||||
className || 'w-5 h-5'
|
||||
}`}
|
||||
class={`icon icon-tabler icon-tabler-menu ${className || 'w-5 h-5'}`}
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
|
||||
@@ -7,9 +7,7 @@ export const IconMoon = (props: ItemProps) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class={`icon icon-tabler icon-tabler-moon ${
|
||||
className || 'w-5 h-5'
|
||||
}`}
|
||||
class={`icon icon-tabler icon-tabler-moon ${className || 'w-5 h-5'}`}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
|
||||
@@ -23,9 +23,7 @@ export default component$(
|
||||
|
||||
<div class="pt-2 space-y-4 justify-center flex">
|
||||
<figcaption class="text-sm">
|
||||
<div class={'font-semibold truncate'}>
|
||||
{props.user.login}
|
||||
</div>
|
||||
<div class={'font-semibold truncate'}>{props.user.login}</div>
|
||||
</figcaption>
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
@@ -32,8 +32,7 @@ export default component$((props: { users: User[] }) => {
|
||||
Super estrellas
|
||||
</h2>
|
||||
<p class="max-w-3xl mx-auto sm:text-center text-xl text-gray-600 dark:text-slate-400">
|
||||
Todo es posible gracias a el mayor recursos de todos, el
|
||||
recurso humano. Tu tambien puedes{' '}
|
||||
Todo es posible gracias a el mayor recursos de todos, el recurso humano. Tu tambien puedes{' '}
|
||||
<a class={'font-semibold'} href="/docs/contributing">
|
||||
formar parte
|
||||
</a>
|
||||
|
||||
@@ -15,8 +15,8 @@ export default component$(() => {
|
||||
📄 Editar esta pagina
|
||||
</a>
|
||||
<p class={'text-xs'}>
|
||||
Forma parte de esta comunidad mejorando la documentación
|
||||
siente libre de poder agregar o editar lo que quieras
|
||||
Forma parte de esta comunidad mejorando la documentación siente libre de poder agregar o editar
|
||||
lo que quieras
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -56,13 +56,9 @@ export default component$(() => {
|
||||
<IconArrowDownRight class="w-7 h-7 text-primary-600 inline-block" />
|
||||
{question}
|
||||
</div>
|
||||
{answer
|
||||
.split('\n\n')
|
||||
.map((paragraph) => (
|
||||
<p class="text-gray-700 dark:text-gray-400 mb-2">
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
{answer.split('\n\n').map((paragraph) => (
|
||||
<p class="text-gray-700 dark:text-gray-400 mb-2">{paragraph}</p>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -50,13 +50,11 @@ export default component$(() => {
|
||||
Caracteristicas
|
||||
</p>
|
||||
<h2 class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading">
|
||||
Nuestras principales{' '}
|
||||
<span class="whitespace-nowrap">funciones</span>
|
||||
Nuestras principales <span class="whitespace-nowrap">funciones</span>
|
||||
</h2>
|
||||
<p class="max-w-3xl mx-auto sm:text-center text-xl text-gray-600 dark:text-slate-400">
|
||||
El secreto es mantener los procesos repetitivos en
|
||||
procesos automatizados simples, por eso te mostramos en
|
||||
que destacamos.
|
||||
El secreto es mantener los procesos repetitivos en procesos automatizados simples, por eso te
|
||||
mostramos en que destacamos.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid mx-auto space-y-6 md:grid-cols-2 md:space-y-0">
|
||||
@@ -70,12 +68,8 @@ export default component$(() => {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="mb-3 text-xl font-bold">
|
||||
{title}
|
||||
</h3>
|
||||
<p class="text-gray-600 dark:text-slate-400">
|
||||
{description}
|
||||
</p>
|
||||
<h3 class="mb-3 text-xl font-bold">{title}</h3>
|
||||
<p class="text-gray-600 dark:text-slate-400">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -9,36 +9,24 @@ import { src as placeholder } from '~/assets/images/chatbot-whatsapp.png?width=4
|
||||
|
||||
export default component$(() => {
|
||||
return (
|
||||
<section
|
||||
class={` from-white via-purple-50 to-sky-100 dark:bg-none mt-[-95px]`}
|
||||
>
|
||||
<section class={` from-white via-purple-50 to-sky-100 dark:bg-none mt-[-95px]`}>
|
||||
<div class="max-w-6xl mx-auto px-4 sm:px-6 md:flex md:h-screen 2xl:h-auto pt-[72px]">
|
||||
<div class="py-12 md:py-12 lg:py-16 block md:flex text-center md:text-left">
|
||||
<div class="pb-12 md:pb-0 md:py-0 max-w-5xl mx-auto md:pr-16 flex items-center basis-[56%]">
|
||||
<div>
|
||||
<h1 class="text-5xl md:text-[3.48rem] font-bold leading-tighter tracking-tighter mb-4 font-heading px-4 md:px-0">
|
||||
Crear chatbot{' '}
|
||||
<span class="sm:whitespace-nowrap text-[#25b637]">
|
||||
WhatsApp
|
||||
</span>
|
||||
<br class="hidden lg:block" />{' '}
|
||||
<span class="lg:inline">en minutos</span>
|
||||
Crear chatbot <span class="sm:whitespace-nowrap text-[#25b637]">WhatsApp</span>
|
||||
<br class="hidden lg:block" /> <span class="lg:inline">en minutos</span>
|
||||
</h1>
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<p class="text-xl text-gray-600 mb-8 dark:text-slate-400">
|
||||
<span class="font-semibold ">
|
||||
Con esta libreria,{' '}
|
||||
</span>
|
||||
<span class="font-semibold ">Con esta libreria, </span>
|
||||
<span class="font-semibold ">
|
||||
puedes configurar respuestas
|
||||
automatizadas para preguntas frecuentes
|
||||
puedes configurar respuestas automatizadas para preguntas frecuentes
|
||||
</span>{' '}
|
||||
, recibir y responder mensajes de manera
|
||||
automatizada, y hacer un seguimiento de las
|
||||
interacciones con los clientes. Además,
|
||||
nuestro Chatbot se integra fácilmente con
|
||||
otros sistemas y herramientas que ya esté
|
||||
utilizando en su negocio.
|
||||
, recibir y responder mensajes de manera automatizada, y hacer un seguimiento de las
|
||||
interacciones con los clientes. Además, nuestro Chatbot se integra fácilmente con
|
||||
otros sistemas y herramientas que ya esté utilizando en su negocio.
|
||||
</p>
|
||||
|
||||
<div class="max-w-xs sm:max-w-md flex flex-nowrap flex-col sm:flex-col gap-4 m-auto md:m-0 justify-center md:justify-start">
|
||||
@@ -48,10 +36,7 @@ export default component$(() => {
|
||||
</code>
|
||||
</div>
|
||||
<div class="flex w-full sm:w-auto gap-3">
|
||||
<a
|
||||
href="/docs"
|
||||
class="btn bg-gray-50 dark:bg-transparent"
|
||||
>
|
||||
<a href="/docs" class="btn bg-gray-50 dark:bg-transparent">
|
||||
Ver documentación
|
||||
</a>
|
||||
<a
|
||||
|
||||
@@ -32,14 +32,9 @@ export default component$((props: { users: User[] }) => {
|
||||
Miembros
|
||||
</h2>
|
||||
<p class="max-w-3xl mx-auto sm:text-center text-xl text-gray-600 dark:text-slate-400">
|
||||
Conviértete en un miembro destacado y forma parte del
|
||||
proyecto y disfruta de manera adelantada de las
|
||||
actualizaciones{' '}
|
||||
<a
|
||||
class={'font-semibold'}
|
||||
target={'_blank'}
|
||||
href="https://opencollective.com/bot-whatsapp"
|
||||
>
|
||||
Conviértete en un miembro destacado y forma parte del proyecto y disfruta de manera adelantada
|
||||
de las actualizaciones{' '}
|
||||
<a class={'font-semibold'} target={'_blank'} href="https://opencollective.com/bot-whatsapp">
|
||||
Únete
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -5,55 +5,45 @@ import { DocumentationCtx } from '~/contexts'
|
||||
/**
|
||||
* options = [] array con la lista de opciones de la documentacion
|
||||
*/
|
||||
export default component$(
|
||||
({ options = [] }: { options: DocumentationCtx[] }) => {
|
||||
return (
|
||||
<div>
|
||||
{options.map((item, i) => (
|
||||
<UlCompoent key={i} title={item.title} list={item.list} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
export default component$(({ options = [] }: { options: DocumentationCtx[] }) => {
|
||||
return (
|
||||
<div>
|
||||
{options.map((item, i) => (
|
||||
<UlCompoent key={i} title={item.title} list={item.list} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export const UlCompoent = component$(
|
||||
(porps: { title: string; list: { link: string; name: string }[] }) => {
|
||||
return (
|
||||
<ul>
|
||||
<li class="mt-2 lg:mt-2">
|
||||
<h5 class="mb-8 lg:mb-3 font-semibold text-slate-900 dark:text-slate-200">
|
||||
{porps.title}
|
||||
</h5>
|
||||
<LiComponent list={porps.list} />
|
||||
export const UlCompoent = component$((porps: { title: string; list: { link: string; name: string }[] }) => {
|
||||
return (
|
||||
<ul>
|
||||
<li class="mt-2 lg:mt-2">
|
||||
<h5 class="mb-8 lg:mb-3 font-semibold text-slate-900 dark:text-slate-200">{porps.title}</h5>
|
||||
<LiComponent list={porps.list} />
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
})
|
||||
|
||||
export const LiComponent = component$((porps: { list: { link: string; name: string }[] }) => {
|
||||
const location = useLocation()
|
||||
const currentPage = location.pathname
|
||||
return (
|
||||
<ul class="space-y-6 lg:space-y-2 border-l border-slate-100 dark:border-slate-800">
|
||||
{porps.list.map((opt) => (
|
||||
<li>
|
||||
<Link
|
||||
class={[
|
||||
currentPage === `${opt.link}/` ? 'font-semibold' : '',
|
||||
'block border-l pl-4 -ml-px border-transparent hover:border-slate-400 dark:hover:border-slate-500 text-slate-700 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300 ',
|
||||
]}
|
||||
href={opt.link}
|
||||
>
|
||||
{opt.name}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export const LiComponent = component$(
|
||||
(porps: { list: { link: string; name: string }[] }) => {
|
||||
const location = useLocation()
|
||||
const currentPage = location.pathname
|
||||
return (
|
||||
<ul class="space-y-6 lg:space-y-2 border-l border-slate-100 dark:border-slate-800">
|
||||
{porps.list.map((opt) => (
|
||||
<li>
|
||||
<Link
|
||||
class={[
|
||||
currentPage === `${opt.link}/`
|
||||
? 'font-semibold'
|
||||
: '',
|
||||
'block border-l pl-4 -ml-px border-transparent hover:border-slate-400 dark:hover:border-slate-500 text-slate-700 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300 ',
|
||||
]}
|
||||
href={opt.link}
|
||||
>
|
||||
{opt.name}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
)
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1,74 +1,63 @@
|
||||
import { component$ } from '@builder.io/qwik'
|
||||
|
||||
export const ButtonLink = component$(
|
||||
(props: { name: string; link: string; direction: 'left' | 'right' }) => {
|
||||
const ArrowRight = () => (
|
||||
<svg
|
||||
viewBox="0 0 3 6"
|
||||
class="ml-3 w-auto h-1.5 text-slate-400 overflow-visible group-hover:text-slate-600 dark:group-hover:text-slate-300"
|
||||
>
|
||||
<path
|
||||
d="M0 0L3 3L0 6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
)
|
||||
export const ButtonLink = component$((props: { name: string; link: string; direction: 'left' | 'right' }) => {
|
||||
const ArrowRight = () => (
|
||||
<svg
|
||||
viewBox="0 0 3 6"
|
||||
class="ml-3 w-auto h-1.5 text-slate-400 overflow-visible group-hover:text-slate-600 dark:group-hover:text-slate-300"
|
||||
>
|
||||
<path
|
||||
d="M0 0L3 3L0 6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const ArrowLeft = () => (
|
||||
<svg
|
||||
viewBox="0 0 3 6"
|
||||
class="mr-3 w-auto h-1.5 text-slate-400 overflow-visible group-hover:text-slate-600 dark:group-hover:text-slate-300"
|
||||
>
|
||||
<path
|
||||
d="M3 0L0 3L3 6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
)
|
||||
const ArrowLeft = () => (
|
||||
<svg
|
||||
viewBox="0 0 3 6"
|
||||
class="mr-3 w-auto h-1.5 text-slate-400 overflow-visible group-hover:text-slate-600 dark:group-hover:text-slate-300"
|
||||
>
|
||||
<path
|
||||
d="M3 0L0 3L3 6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
></path>
|
||||
</svg>
|
||||
)
|
||||
|
||||
return (
|
||||
<a
|
||||
class="group flex items-center hover:text-slate-900 dark:hover:text-white"
|
||||
href={props.link}
|
||||
>
|
||||
{props.direction === 'left' ? (
|
||||
<>
|
||||
<ArrowLeft />
|
||||
{props.name}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{props.name}
|
||||
<ArrowRight />
|
||||
</>
|
||||
)}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
)
|
||||
return (
|
||||
<a class="group flex items-center hover:text-slate-900 dark:hover:text-white" href={props.link}>
|
||||
{props.direction === 'left' ? (
|
||||
<>
|
||||
<ArrowLeft />
|
||||
{props.name}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{props.name}
|
||||
<ArrowRight />
|
||||
</>
|
||||
)}
|
||||
</a>
|
||||
)
|
||||
})
|
||||
|
||||
export default component$(
|
||||
(props: { pages: ({ name: string; link: string } | null)[] }) => {
|
||||
const { pages } = props
|
||||
return (
|
||||
<div class="text-sm leading-6 mt-12">
|
||||
<div class="mb-10 text-slate-700 font-semibold flex justify-between items-center dark:text-slate-200">
|
||||
{pages[0] ? (
|
||||
<ButtonLink direction="left" {...pages[0]} />
|
||||
) : null}
|
||||
{pages[1] ? (
|
||||
<ButtonLink direction="right" {...pages[1]} />
|
||||
) : null}
|
||||
</div>
|
||||
export default component$((props: { pages: ({ name: string; link: string } | null)[] }) => {
|
||||
const { pages } = props
|
||||
return (
|
||||
<div class="text-sm leading-6 mt-12">
|
||||
<div class="mb-10 text-slate-700 font-semibold flex justify-between items-center dark:text-slate-200">
|
||||
{pages[0] ? <ButtonLink direction="left" {...pages[0]} /> : null}
|
||||
{pages[1] ? <ButtonLink direction="right" {...pages[1]} /> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -7,6 +7,8 @@ import { src as qwik } from '~/assets/images/qwik.png?width=100&metadata'
|
||||
import { src as leanga } from '~/assets/images/leanga.png?width=40&metadata'
|
||||
// @ts-ignore
|
||||
import { src as netlify } from '~/assets/images/full-logo-light.png?width=100&metadata'
|
||||
// @ts-ignore
|
||||
import { src as digitalOcean } from '~/assets/images/digital-ocean.png?width=100&metadata'
|
||||
|
||||
/**
|
||||
* options = [] array con la lista de opciones de la documentacion
|
||||
@@ -41,7 +43,20 @@ export default component$(() => {
|
||||
<img
|
||||
src={netlify}
|
||||
class="border border-slate-200 rounded my-2 p-1 bg-gray-50 dark:border-gray-600 dark:bg-gray-700"
|
||||
alt="Qwind Hero Image (Cool dog)"
|
||||
alt="Netlify"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
/>
|
||||
</picture>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target={'_blank'} href="https://m.do.co/c/140291d21736">
|
||||
<picture>
|
||||
<img
|
||||
src={digitalOcean}
|
||||
class="border border-slate-200 rounded my-2 p-1 bg-gray-50 dark:border-gray-600 dark:bg-gray-700"
|
||||
alt="DigitalOcean"
|
||||
loading="eager"
|
||||
decoding="async"
|
||||
/>
|
||||
|
||||
@@ -5,33 +5,25 @@ export default component$(() => {
|
||||
<div class="px-4 py-8 md:py-16 sm:px-6 mx-auto md:px-24 lg:px-8 lg:py-20 max-w-6xl">
|
||||
<div class="grid grid-cols-2 row-gap-8 md:grid-cols-4">
|
||||
<div class="text-center md:border-r dark:md:border-slate-500 mb-10 md:mb-0">
|
||||
<div class="text-4xl font-bold lg:text-5xl xl:text-6xl text-[#039de1] font-heading">
|
||||
132K
|
||||
</div>
|
||||
<div class="text-4xl font-bold lg:text-5xl xl:text-6xl text-[#039de1] font-heading">132K</div>
|
||||
<p class="text-sm font-medium tracking-widest text-gray-800 dark:text-slate-400 uppercase lg:text-base">
|
||||
Downloads
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-center md:border-r dark:md:border-slate-500 mb-10 md:mb-0">
|
||||
<div class="text-4xl font-bold lg:text-5xl xl:text-6xl text-[#039de1] font-heading">
|
||||
24.8K
|
||||
</div>
|
||||
<div class="text-4xl font-bold lg:text-5xl xl:text-6xl text-[#039de1] font-heading">24.8K</div>
|
||||
<p class="text-sm font-medium tracking-widest text-gray-800 dark:text-slate-400 uppercase lg:text-base">
|
||||
Stars
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-center md:border-r dark:md:border-slate-500 font-heading">
|
||||
<div class="text-4xl font-bold lg:text-5xl xl:text-6xl text-[#039de1]">
|
||||
10.3K
|
||||
</div>
|
||||
<div class="text-4xl font-bold lg:text-5xl xl:text-6xl text-[#039de1]">10.3K</div>
|
||||
<p class="text-sm font-medium tracking-widest text-gray-800 dark:text-slate-400 uppercase lg:text-base">
|
||||
Forks
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-4xl font-bold lg:text-5xl xl:text-6xl text-[#039de1] font-heading">
|
||||
48.4K
|
||||
</div>
|
||||
<div class="text-4xl font-bold lg:text-5xl xl:text-6xl text-[#039de1] font-heading">48.4K</div>
|
||||
<p class="text-sm font-medium tracking-widest text-gray-800 dark:text-slate-400 uppercase lg:text-base">
|
||||
Users
|
||||
</p>
|
||||
|
||||
@@ -13,5 +13,4 @@ export interface User {
|
||||
avatar_url: string
|
||||
}
|
||||
|
||||
export const GlobalStore =
|
||||
createContext<DocumentationCtx[]>('documentation-site')
|
||||
export const GlobalStore = createContext<DocumentationCtx[]>('documentation-site')
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
import {
|
||||
component$,
|
||||
useContextProvider,
|
||||
useStore,
|
||||
useStyles$,
|
||||
} from '@builder.io/qwik'
|
||||
import {
|
||||
QwikCityProvider,
|
||||
RouterOutlet,
|
||||
ServiceWorkerRegister,
|
||||
} from '@builder.io/qwik-city'
|
||||
import { component$, useContextProvider, useStore, useStyles$ } from '@builder.io/qwik'
|
||||
import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city'
|
||||
|
||||
import { RouterHead } from '~/components/core/RouterHead'
|
||||
import { DarkThemeLauncher } from '~/components/core/DarkThemeLauncher'
|
||||
@@ -78,10 +69,7 @@ export default component$(() => {
|
||||
<QwikCityProvider>
|
||||
<head>
|
||||
<meta charSet="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<RouterHead />
|
||||
|
||||
@@ -4,11 +4,10 @@ import Navigation from '../../../components/widgets/Navigation'
|
||||
# DataBase (Base de datos)
|
||||
|
||||
<Alert>
|
||||
⚡ Dependiendo del tipo de conector que utlices puede que necesites pasar
|
||||
algunas configuracion adicional como **user, host, password** para esos
|
||||
casos te recomendamos guiarte de los
|
||||
**[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)**
|
||||
o si gustas puedes editar esta documentación para ir agregando más info
|
||||
⚡ Dependiendo del tipo de conector que utlices puede que necesites pasar algunas configuracion adicional como
|
||||
**user, host, password** para esos casos te recomendamos guiarte de los
|
||||
**[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** o si gustas puedes editar esta
|
||||
documentación para ir agregando más info
|
||||
</Alert>
|
||||
|
||||
Es la pieza encargada de mantener el **"estado"** de una conversación, para mayor facilidad la libreria te proporcia diferentes conectores que se de adapten mejor a tu desarrollo
|
||||
|
||||
@@ -22,12 +22,7 @@ Tan sencillo como decir **palabra/s clave** y **mensaje a responder**
|
||||
Ambos metodos **[addKeyword](https://github.com/codigoencasa/bot-whatsapp/blob/dev/packages/bot/io/methods/addKeyword.js)** y el **[addAnswer](https://github.com/codigoencasa/bot-whatsapp/blob/dev/packages/bot/io/methods/addAnswer.js)** tienen una serie opciones disponibles
|
||||
|
||||
```js
|
||||
const {
|
||||
createBot,
|
||||
createProvider,
|
||||
createFlow,
|
||||
addKeyword,
|
||||
} = require('@bot-whatsapp/bot')
|
||||
const { createBot, createProvider, createFlow, addKeyword } = require('@bot-whatsapp/bot')
|
||||
|
||||
const flowPrincipal = addKeyword(['hola', 'alo'])
|
||||
.addAnswer(['Hola, bienvenido a mi tienda', '¿Como puedo ayudarte?'])
|
||||
@@ -39,11 +34,10 @@ const flowPrincipal = addKeyword(['hola', 'alo'])
|
||||
## Provider (Proveedor)
|
||||
|
||||
<Alert>
|
||||
⚡ Dependiendo del tipo de proveedor que utlices puede que necesites pasar
|
||||
algunas configuracion adicional como **token, api, etc.** para esos casos te
|
||||
recomendamos guiarte de los
|
||||
**[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)**
|
||||
o si gustas puedes editar esta documentación para ir agregando más info
|
||||
⚡ Dependiendo del tipo de proveedor que utlices puede que necesites pasar algunas configuracion adicional como
|
||||
**token, api, etc.** para esos casos te recomendamos guiarte de los
|
||||
**[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** o si gustas puedes editar esta
|
||||
documentación para ir agregando más info
|
||||
</Alert>
|
||||
|
||||
Es la pieza que conectara tu flujo con Whatsapp. En este chatbot tenemos varios proveedores disponibles la mayoria gratis pero tambien tenemos integracion la api oficial de whatsapp o twilio
|
||||
@@ -71,11 +65,10 @@ Los proveedores disponibles hasta el momento son los siguientes:
|
||||
## DataBase (Base de datos)
|
||||
|
||||
<Alert>
|
||||
⚡ Dependiendo del tipo de conector que utlices puede que necesites pasar
|
||||
algunas configuracion adicional como **user, host, password** para esos
|
||||
casos te recomendamos guiarte de los
|
||||
**[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)**
|
||||
o si gustas puedes editar esta documentación para ir agregando más info
|
||||
⚡ Dependiendo del tipo de conector que utlices puede que necesites pasar algunas configuracion adicional como
|
||||
**user, host, password** para esos casos te recomendamos guiarte de los
|
||||
**[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** o si gustas puedes editar esta
|
||||
documentación para ir agregando más info
|
||||
</Alert>
|
||||
|
||||
Es la pieza encargada de mantener el **"estado"** de una conversación, para mayor facilidad la libreria te proporcia diferentes conectores que se de adapten mejor a tu desarrollo
|
||||
|
||||
@@ -5,12 +5,7 @@ import Navigation from '../../../components/widgets/Navigation'
|
||||
Si copias y pegas este codigo y tu entorno de trabajo cumple con todos los requesitos te debe funcionar abajo explico muy por encima
|
||||
|
||||
```js
|
||||
const {
|
||||
createBot,
|
||||
createProvider,
|
||||
createFlow,
|
||||
addKeyword,
|
||||
} = require('@bot-whatsapp/bot')
|
||||
const { createBot, createProvider, createFlow, addKeyword } = require('@bot-whatsapp/bot')
|
||||
|
||||
const WebWhatsappProvider = require('@bot-whatsapp/provider/web-whatsapp')
|
||||
const MockAdapter = require('@bot-whatsapp/database/mock')
|
||||
@@ -42,12 +37,7 @@ main()
|
||||
En esta parte solo estamos declaramos las dependencias que vamos a utilizar. Si quieres saber a fondo cada una de las funciones te recomiendo pasarte por la seccion de **[conceptos](/docs/concepts)**
|
||||
|
||||
```js
|
||||
const {
|
||||
createBot,
|
||||
createProvider,
|
||||
createFlow,
|
||||
addKeyword,
|
||||
} = require('@bot-whatsapp/bot')
|
||||
const { createBot, createProvider, createFlow, addKeyword } = require('@bot-whatsapp/bot')
|
||||
|
||||
const WebWhatsappProvider = require('@bot-whatsapp/provider/web-whatsapp')
|
||||
const MockAdapter = require('@bot-whatsapp/database/mock')
|
||||
|
||||
@@ -9,12 +9,7 @@ Tan sencillo como decir **palabra/s clave** y **mensaje a responder**
|
||||
Ambos metodos **[addKeyword](https://github.com/codigoencasa/bot-whatsapp/blob/dev/packages/bot/io/methods/addKeyword.js)** y el **[addAnswer](https://github.com/codigoencasa/bot-whatsapp/blob/dev/packages/bot/io/methods/addAnswer.js)** tienen una serie opciones disponibles
|
||||
|
||||
```js
|
||||
const {
|
||||
createBot,
|
||||
createProvider,
|
||||
createFlow,
|
||||
addKeyword,
|
||||
} = require('@bot-whatsapp/bot')
|
||||
const { createBot, createProvider, createFlow, addKeyword } = require('@bot-whatsapp/bot')
|
||||
|
||||
const flowPrincipal = addKeyword(['hola', 'alo'])
|
||||
.addAnswer(['Hola, bienvenido a mi tienda', '¿Como puedo ayudarte?'])
|
||||
@@ -81,37 +76,21 @@ Esta funcion se utliza para responder un mensaje despues del `addKeyword()`
|
||||
```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 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 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 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,
|
||||
}
|
||||
)
|
||||
const flowString = addKeyword('hola').addAnswer('Este mensaje espera una respueta del usuario', {
|
||||
capture: true,
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
@@ -123,13 +102,9 @@ 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)
|
||||
}
|
||||
)
|
||||
const flowString = addKeyword('hola').addAnswer('Indica cual es tu email', null, (ctx) => {
|
||||
console.log('👉 Informacion del contexto: ', ctx)
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
@@ -143,13 +118,9 @@ 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()
|
||||
}
|
||||
)
|
||||
const flowString = addKeyword('hola').addAnswer('Indica cual es tu email', null, (ctx, { fallBack }) => {
|
||||
if (!ctx.body.includes('@')) return fallBack()
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
@@ -181,57 +152,65 @@ const flowString = addKeyword('hola')
|
||||
|
||||
## endFlow()
|
||||
|
||||
Esta funcion se utliza para finalizar un flujo con dos o más addAnswer. Un ejemplo de uso sería registrar 3 datos de un usuario en 3 preguntas distinas y
|
||||
que el usuario pueda finalizar por él mismo el flujo.
|
||||
Esta funcion se utliza para **finalizar un flujo con dos** o más addAnswer. Un ejemplo de uso sería registrar 3 datos de un usuario en 3 preguntas distinas y
|
||||
que el usuario **pueda finalizar por él mismo el flujo**.
|
||||
Como podrás comprobar en el ejemplo siguiente, se puede vincular flowDynamic y todas sus funciones; como por ejemplo botones.
|
||||
|
||||
```js
|
||||
const flowFormulario = addKeyword(['Hola'])
|
||||
let nombre;
|
||||
let apellidos;
|
||||
let telefono;
|
||||
|
||||
const flowFormulario = addKeyword(['Hola','⬅️ Volver al Inicio'])
|
||||
.addAnswer(
|
||||
['Hola!', 'Escriba su *Nombre* para generar su solicitud'],
|
||||
['Hola!','Para enviar el formulario necesito unos datos...' ,'Escriba su *Nombre*'],
|
||||
{ capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] },
|
||||
|
||||
async (ctx, { flowDynamic, endFlow }) => {
|
||||
if (ctx.body == '❌ Cancelar solicitud') {
|
||||
await flowDynamic([
|
||||
{
|
||||
body: '❌ *Su solicitud de cita ha sido cancelada* ❌',
|
||||
buttons: [{ body: '⬅️ Volver al Inicio' }],
|
||||
},
|
||||
])
|
||||
return endFlow()
|
||||
}
|
||||
if (ctx.body == '❌ Cancelar solicitud')
|
||||
return endFlow({body: '❌ Su solicitud ha sido cancelada ❌', // Aquí terminamos el flow si la condicion se comple
|
||||
buttons:[{body:'⬅️ Volver al Inicio' }] // Y además, añadimos un botón por si necesitas derivarlo a otro flow
|
||||
|
||||
|
||||
})
|
||||
nombre = ctx.body
|
||||
return flowDynamic(`Encantado *${nombre}*, continuamos...`)
|
||||
}
|
||||
)
|
||||
.addAnswer(
|
||||
['También necesito tus dos apellidos'],
|
||||
{ capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] },
|
||||
|
||||
async (ctx, { flowDynamic, endFlow }) => {
|
||||
if (ctx.body == '❌ Cancelar solicitud') {
|
||||
await flowDynamic([
|
||||
{
|
||||
body: '❌ *Su solicitud de cita ha sido cancelada* ❌',
|
||||
buttons: [{ body: '⬅️ Volver al Inicio' }],
|
||||
},
|
||||
])
|
||||
return endFlow()
|
||||
}
|
||||
if (ctx.body == '❌ Cancelar solicitud')
|
||||
return endFlow({body: '❌ Su solicitud ha sido cancelada ❌',
|
||||
buttons:[{body:'⬅️ Volver al Inicio' }]
|
||||
|
||||
|
||||
})
|
||||
apellidos = ctx.body
|
||||
return flowDynamic(`Perfecto *${nombre}*, por último...`)
|
||||
}
|
||||
)
|
||||
.addAnswer(
|
||||
['Dejeme su número de teléfono y le llamaré lo antes posible.'],
|
||||
{ capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] },
|
||||
|
||||
async (ctx, { flowDynamic, endFlow }) => {
|
||||
if (ctx.body == '❌ Cancelar solicitud') {
|
||||
await flowDynamic([
|
||||
{
|
||||
body: '❌ *Su solicitud de cita ha sido cancelada* ❌',
|
||||
buttons: [{ body: '⬅️ Volver al Inicio' }],
|
||||
},
|
||||
])
|
||||
return endFlow()
|
||||
}
|
||||
if (ctx.body == '❌ Cancelar solicitud')
|
||||
return endFlow({body: '❌ Su solicitud ha sido cancelada ❌',
|
||||
buttons:[{body:'⬅️ Volver al Inicio' }]
|
||||
})
|
||||
|
||||
|
||||
telefono = ctx.body
|
||||
await delay(2000)
|
||||
return flowDynamic(`Estupendo *${nombre}*! te dejo el resumen de tu formulario
|
||||
\n- Nombre y apellidos: *${nombre} ${apellidos}*
|
||||
\n- Telefono: *${telefono}*`)
|
||||
}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
@@ -241,6 +220,7 @@ const flowFormulario = addKeyword(['Hola'])
|
||||
Argumento para asignar nombre y puerto al BOT
|
||||
|
||||
```js
|
||||
const BOTNAME = 'bot'
|
||||
QRPortalWeb({ name: BOTNAME, port: 3005 })
|
||||
```
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ import Navigation from '../../components/widgets/Navigation'
|
||||
# Introducción
|
||||
|
||||
<Alert>
|
||||
**Atención** estás leyendo la documentación de la **versión v2** de esta
|
||||
librería, si vienes de la versión anterior te recomendamos pasarte por la
|
||||
sección de **[migración](/docs/migration/)** para que puedas disfrutar de
|
||||
las nuevas características.
|
||||
**Atención** estás leyendo la documentación de la **versión v2** de esta librería, si vienes de la versión anterior
|
||||
te recomendamos pasarte por la sección de **[migración](/docs/migration/)** para que puedas disfrutar de las nuevas
|
||||
características.
|
||||
</Alert>
|
||||
|
||||
## ¿Qué es esto?
|
||||
@@ -34,10 +33,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="https://leifer-landing-page.s3.us-east-2.amazonaws.com/console.webm" type="video/webm" />
|
||||
</video>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -28,10 +28,7 @@ El **CLI** te hace una revisión previa, de versión de Node y sistema operativo
|
||||
muted
|
||||
playsinline
|
||||
>
|
||||
<source
|
||||
src="https://leifer-landing-page.s3.us-east-2.amazonaws.com/console.webm"
|
||||
type="video/webm"
|
||||
/>
|
||||
<source src="https://leifer-landing-page.s3.us-east-2.amazonaws.com/console.webm" type="video/webm" />
|
||||
</video>
|
||||
</div>
|
||||
|
||||
@@ -54,10 +51,8 @@ Cada plantilla tiene sus dependencias necesarias basadas en tu previa selección
|
||||
```
|
||||
|
||||
<Alert>
|
||||
📄 Si deseas cambiar tu **proveedor o tu motor** de base de datos no es
|
||||
necesario volver ejecutar el CLI (lo puedes hacer sin problema) aunque
|
||||
tambien basta con solo modificar un par de lineas. [Ver
|
||||
explicación](/docs/essential)
|
||||
📄 Si deseas cambiar tu **proveedor o tu motor** de base de datos no es necesario volver ejecutar el CLI (lo puedes
|
||||
hacer sin problema) aunque tambien basta con solo modificar un par de lineas. [Ver explicación](/docs/essential)
|
||||
</Alert>
|
||||
|
||||
---
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { DocumentHead } from '@builder.io/qwik-city'
|
||||
import ExtraBar from '~/components/widgets/ExtraBar'
|
||||
import Header from '~/components/widgets/Header'
|
||||
import NavBar from '~/components/widgets/NavBar'
|
||||
import { SearchModal } from '~/components/widgets/SearchModal'
|
||||
// import { SearchModal } from '~/components/widgets/SearchModal'
|
||||
import SponsorBar from '~/components/widgets/SponsorBar'
|
||||
import { GlobalStore } from '~/contexts'
|
||||
// import Navigation from '~/components/widgets/Navigation'
|
||||
@@ -15,7 +15,7 @@ export default component$(() => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchModal />
|
||||
{/* <SearchModal /> */}
|
||||
<Header />
|
||||
<main class={'overflow-hidden'}>
|
||||
<div class={'max-w-8xl'}>
|
||||
@@ -27,11 +27,7 @@ export default component$(() => {
|
||||
<NavBar options={store} />
|
||||
</div>
|
||||
<div class={'lg:pl-[14.5rem] lg:pr-[14.5rem]'}>
|
||||
<div
|
||||
class={
|
||||
'slot max-w-3xl mx-auto relative z-20 p-5 xl:max-w-none'
|
||||
}
|
||||
>
|
||||
<div class={'slot max-w-3xl mx-auto relative z-20 p-5 xl:max-w-none'}>
|
||||
<Slot />
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,8 +50,7 @@ export const head: DocumentHead = {
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content:
|
||||
'Crear chatbot WhatsApp en minutos — Servicio de chatbot para whatspp gratis proyecto OpenSource',
|
||||
content: 'Crear chatbot WhatsApp en minutos — Servicio de chatbot para whatspp gratis proyecto OpenSource',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -49,11 +49,7 @@ En la **_versión (legacy)_** se implementas los flujos de esta manera, en dos a
|
||||
"title": "¿Que te interesa ver?",
|
||||
"message": "Abajo unos botons",
|
||||
"footer": "",
|
||||
"buttons": [
|
||||
{ "body": "Telefonos" },
|
||||
{ "body": "Computadoras" },
|
||||
{ "body": "Otros" }
|
||||
]
|
||||
"buttons": [{ "body": "Telefonos" }, { "body": "Computadoras" }, { "body": "Otros" }]
|
||||
}
|
||||
},
|
||||
"catalogo": {
|
||||
@@ -70,51 +66,28 @@ En esta versión es mucho más sencillo, abajo encontrarás un ejemplo del mismo
|
||||
|
||||
```js
|
||||
//app.js
|
||||
const {
|
||||
createBot,
|
||||
createProvider,
|
||||
createFlow,
|
||||
addKeyword,
|
||||
addChild,
|
||||
} = require('@bot-whatsapp/bot')
|
||||
const { createBot, createProvider, createFlow, addKeyword, addChild } = require('@bot-whatsapp/bot')
|
||||
|
||||
const BaileysProvider = require('@bot-whatsapp/provider/baileys') //Provider
|
||||
const MockAdapter = require('@bot-whatsapp/database/mock') //Base de datos
|
||||
/**
|
||||
* Declarando flujos principales.
|
||||
*/
|
||||
const flowHola = addKeyword(['hola', 'ola', 'alo']).addAnswer(
|
||||
'Bienvenido a tu tienda online!'
|
||||
)
|
||||
const flowHola = addKeyword(['hola', 'ola', 'alo']).addAnswer('Bienvenido a tu tienda online!')
|
||||
|
||||
const flowAdios = addKeyword(['adios', 'bye'])
|
||||
.addAnswer('Que te vaya bien!!')
|
||||
.addAnswer('Hasta luego!')
|
||||
const flowAdios = addKeyword(['adios', 'bye']).addAnswer('Que te vaya bien!!').addAnswer('Hasta luego!')
|
||||
|
||||
const flowProductos = addKeyword(['productos', 'info']).addAnswer(
|
||||
'Te envio una imagen',
|
||||
{
|
||||
buttons: [
|
||||
{ body: 'Telefonos' },
|
||||
{ body: 'Computadoras' },
|
||||
{ body: 'Otros' },
|
||||
],
|
||||
}
|
||||
)
|
||||
const flowProductos = addKeyword(['productos', 'info']).addAnswer('Te envio una imagen', {
|
||||
buttons: [{ body: 'Telefonos' }, { body: 'Computadoras' }, { body: 'Otros' }],
|
||||
})
|
||||
|
||||
const flowCatalogo = addKeyword(['imagen', 'foto']).addAnswer(
|
||||
'Te envio una imagen',
|
||||
{ media: 'https://media2.giphy.com/media/VQJu0IeULuAmCwf5SL/giphy.gif' }
|
||||
)
|
||||
const flowCatalogo = addKeyword(['imagen', 'foto']).addAnswer('Te envio una imagen', {
|
||||
media: 'https://media2.giphy.com/media/VQJu0IeULuAmCwf5SL/giphy.gif',
|
||||
})
|
||||
|
||||
const main = async () => {
|
||||
const adapterDB = new MockAdapter()
|
||||
const adapterFlow = createFlow([
|
||||
flowHola,
|
||||
flowAdios,
|
||||
flowProductos,
|
||||
flowCatalogo,
|
||||
]) //Se crean los flujos.
|
||||
const adapterFlow = createFlow([flowHola, flowAdios, flowProductos, flowCatalogo]) //Se crean los flujos.
|
||||
const adapterProvider = createProvider(BaileysProvider)
|
||||
createBot({
|
||||
flow: adapterFlow,
|
||||
|
||||
@@ -49,17 +49,15 @@ Qwik is a new kind of web framework that can deliver instant loading web applica
|
||||
<div class="card">
|
||||
<h3>Instant-on</h3>
|
||||
<p>
|
||||
Unlike other frameworks, Qwik is resumable which means Qwik
|
||||
applications require 0 hydration. This allows Qwik apps to have
|
||||
instant-on interactivity, regardless of size or complexity
|
||||
Unlike other frameworks, Qwik is resumable which means Qwik applications require 0 hydration. This allows
|
||||
Qwik apps to have instant-on interactivity, regardless of size or complexity
|
||||
</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Optimized for speed</h3>
|
||||
<p>
|
||||
Qwik has unprecedented performance, offering sub-second full page
|
||||
loads even on mobile devices. Qwik achieves this by delivering pure
|
||||
HTML, and incrementally loading JS only as-needed.
|
||||
Qwik has unprecedented performance, offering sub-second full page loads even on mobile devices. Qwik
|
||||
achieves this by delivering pure HTML, and incrementally loading JS only as-needed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,11 +4,10 @@ import Navigation from '../../../components/widgets/Navigation'
|
||||
# Proveedores
|
||||
|
||||
<Alert>
|
||||
⚡ Dependiendo del tipo de proveedor que utlices puede que necesites pasar
|
||||
algunas configuracion adicional como **token, api, etc.** para esos casos te
|
||||
recomendamos guiarte de los
|
||||
**[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)**
|
||||
o si gustas puedes editar esta documentación para ir agregando más info
|
||||
⚡ Dependiendo del tipo de proveedor que utlices puede que necesites pasar algunas configuracion adicional como
|
||||
**token, api, etc.** para esos casos te recomendamos guiarte de los
|
||||
**[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** o si gustas puedes editar esta
|
||||
documentación para ir agregando más info
|
||||
</Alert>
|
||||
|
||||
Es la pieza que conectara tu flujo con Whatsapp. En este chatbot tenemos varios proveedores disponibles la mayoria gratis pero tambien tenemos integracion la api oficial de whatsapp o twilio
|
||||
|
||||
@@ -59,10 +59,7 @@ En el **archivo principal** del bot donde estás implementando la función del a
|
||||
- **jwtToken:** Lo puedes encontrar en la pagina anterior
|
||||
- **verifyToken:** Puedes escribir lo que quieras es como una palabra clave
|
||||
|
||||
<Alert>
|
||||
En el ejemplo de abajo puedes ver como una sugerencia de como puede ser
|
||||
utilizando variables de entorno
|
||||
</Alert>
|
||||
<Alert>En el ejemplo de abajo puedes ver como una sugerencia de como puede ser utilizando variables de entorno</Alert>
|
||||
|
||||
```js
|
||||
const main = async () => {
|
||||
|
||||
@@ -5,10 +5,7 @@ import Navigation from '../../../../components/widgets/Navigation'
|
||||
|
||||
Twilio es una plataforma de desarrollo que permite a los desarrolladores construir aplicaciones de comunicación en la nube y sistemas web. Las API de comunicaciones de Twilio permiten a las empresas proporcionar la experiencia de comunicación adecuada para sus clientes dentro de la web y las aplicaciones móviles. Al usar las API de Twilio, los desarrolladores pueden agregar rápidamente esta funcionalidad a una aplicación, como mensajes de voz, videollamadas, mensajes de texto y más.
|
||||
|
||||
<Alert>
|
||||
Twilio te proporciona una cuenta **Sandbox** para que puedas probar
|
||||
gratuitamente el servicio
|
||||
</Alert>
|
||||
<Alert>Twilio te proporciona una cuenta **Sandbox** para que puedas probar gratuitamente el servicio</Alert>
|
||||
|
||||
### Requerimientos
|
||||
|
||||
@@ -66,10 +63,7 @@ En el **archivo principal** del bot donde estás implementando la función del a
|
||||
- **ACC_VENDOR:** Es el numero de whatsapp (si ya tienes el plan de pago de Twilio usa el numero que compraste), si aun estas en modo
|
||||
sandbox utliza el numero proporcionado en el paso numero 2
|
||||
|
||||
<Alert>
|
||||
En el ejemplo de abajo puedes ver como una sugerencia de como puede ser
|
||||
utilizando variables de entorno
|
||||
</Alert>
|
||||
<Alert>En el ejemplo de abajo puedes ver como una sugerencia de como puede ser utilizando variables de entorno</Alert>
|
||||
|
||||
```js
|
||||
const main = async () => {
|
||||
|
||||
@@ -13,8 +13,7 @@ import { GITHUB_TOKEN } from './docs/constant'
|
||||
// import { SearchModal } from '~/components/widgets/SearchModal'
|
||||
|
||||
export const onGet: RequestHandlerNetlify = async ({ platform }) => {
|
||||
const CHECK_GITHUB_TOKEN =
|
||||
(platform as any)?.['GITHUB_TOKEN'] ?? GITHUB_TOKEN
|
||||
const CHECK_GITHUB_TOKEN = (platform as any)?.['GITHUB_TOKEN'] ?? GITHUB_TOKEN
|
||||
const dataGithub = await fetchGithub(CHECK_GITHUB_TOKEN)
|
||||
const dataOpenCollective = await fetchOpenCollective()
|
||||
return {
|
||||
@@ -52,8 +51,7 @@ export const head: DocumentHead = {
|
||||
meta: [
|
||||
{
|
||||
name: 'description',
|
||||
content:
|
||||
'Crear chatbot WhatsApp en minutos — Servicio de chatbot para whatspp gratis proyecto OpenSource',
|
||||
content: 'Crear chatbot WhatsApp en minutos — Servicio de chatbot para whatspp gratis proyecto OpenSource',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -3,17 +3,14 @@
|
||||
* @returns
|
||||
*/
|
||||
export const fetchGithub = async (token: string) => {
|
||||
const data = await fetch(
|
||||
`https://api.github.com/repos/codigoencasa/bot-whatsapp/contributors`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
const data = await fetch(`https://api.github.com/repos/codigoencasa/bot-whatsapp/contributors`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Accept: 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
const listUsers = await data.json()
|
||||
return listUsers.map((u: any) => ({
|
||||
...u,
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
* @returns
|
||||
*/
|
||||
export const fetchOpenCollective = async () => {
|
||||
const data = await fetch(
|
||||
`https://opencollective.com/bot-whatsapp/members/users.json?limit=22&offset=0`,
|
||||
{
|
||||
method: 'GET',
|
||||
}
|
||||
)
|
||||
const data = await fetch(`https://opencollective.com/bot-whatsapp/members/users.json?limit=22&offset=0`, {
|
||||
method: 'GET',
|
||||
})
|
||||
const listUsers = await data.json()
|
||||
return listUsers.map((u: any) => ({
|
||||
html_url: u.profile,
|
||||
|
||||
@@ -76,9 +76,7 @@ module.exports = {
|
||||
a: {
|
||||
fontWeight: theme('fontWeight.semibold'),
|
||||
textDecoration: 'none',
|
||||
borderBottom: `1px solid ${theme(
|
||||
'colors.sky.300'
|
||||
)}`,
|
||||
borderBottom: `1px solid ${theme('colors.sky.300')}`,
|
||||
},
|
||||
'a:hover': {
|
||||
borderBottomWidth: '2px',
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:onGet",
|
||||
"description": "onGet function for a route index",
|
||||
"body": [
|
||||
"export const onGet: RequestHandler = (request) => {",
|
||||
" $0",
|
||||
"};"
|
||||
]
|
||||
"body": ["export const onGet: RequestHandler = (request) => {", " $0", "};"]
|
||||
},
|
||||
"onGet (typed)": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
|
||||
15
packages/portal/.vscode/qwik.code-snippets
vendored
15
packages/portal/.vscode/qwik.code-snippets
vendored
@@ -56,24 +56,13 @@
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:useTask",
|
||||
"description": "useTask$() function hook",
|
||||
"body": [
|
||||
"useTask$(({ track }) => {",
|
||||
" track(() => $1);",
|
||||
" $0",
|
||||
"});",
|
||||
""
|
||||
]
|
||||
"body": ["useTask$(({ track }) => {", " track(() => $1);", " $0", "});", ""]
|
||||
},
|
||||
"useResource": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:useResource",
|
||||
"description": "useResource$() declaration",
|
||||
"body": [
|
||||
"const $1 = useResource$(({ track, cleanup }) => {",
|
||||
" $0",
|
||||
"});",
|
||||
""
|
||||
]
|
||||
"body": ["const $1 = useResource$(({ track, cleanup }) => {", " $0", "});", ""]
|
||||
},
|
||||
"useServerMount": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
|
||||
@@ -5,15 +5,9 @@ const polka = require('polka')
|
||||
|
||||
const HTTP_PORT = process.env.PORT || 3000
|
||||
const QR_FILE = process.env.QR_FILE ?? 'bot'
|
||||
const PUBLIC_URL =
|
||||
process.env.PUBLIC_URL ??
|
||||
process.env.RAILWAY_STATIC_URL ??
|
||||
'http://localhost'
|
||||
const PUBLIC_URL = process.env.PUBLIC_URL ?? process.env.RAILWAY_STATIC_URL ?? 'http://localhost'
|
||||
|
||||
/* const dir = [join(__dirname, 'dist'), join(__dirname, '..', 'dist')].find((i) =>
|
||||
existsSync(i)
|
||||
) */
|
||||
const dir = join(__dirname, 'dist')
|
||||
const dir = [join(__dirname, 'dist'), join(__dirname, '..', 'dist')].find((i) => existsSync(i))
|
||||
const serve = require('serve-static')(dir)
|
||||
|
||||
/**
|
||||
@@ -33,14 +27,8 @@ const start = (args) => {
|
||||
console.log(``)
|
||||
console.log(bgYellow(`🚩 ESCANEAR QR 🚩`))
|
||||
console.log(cyan(`Existen varias maneras de escanear el QR code`))
|
||||
console.log(
|
||||
cyan(`- Tambien puedes visitar `),
|
||||
yellow(`${publicSite}:${port}`)
|
||||
)
|
||||
console.log(
|
||||
cyan(`- Se ha creado un archivo que finaliza `),
|
||||
yellow('qr.png')
|
||||
)
|
||||
console.log(cyan(`- Tambien puedes visitar `), yellow(`${publicSite}:${port}`))
|
||||
console.log(cyan(`- Se ha creado un archivo que finaliza `), yellow('qr.png'))
|
||||
console.log(``)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,28 +8,18 @@ export default component$(() => {
|
||||
return (
|
||||
<header>
|
||||
<div class="logo">
|
||||
<a
|
||||
href="https://github.com/codigoencasa/bot-whatsapp"
|
||||
target="_blank"
|
||||
title="qwik"
|
||||
>
|
||||
<a href="https://github.com/codigoencasa/bot-whatsapp" target="_blank" title="qwik">
|
||||
<BotLogo />
|
||||
</a>
|
||||
</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/codigoencasa/bot-whatsapp"
|
||||
target="_blank"
|
||||
>
|
||||
<a href="https://github.com/codigoencasa/bot-whatsapp" target="_blank">
|
||||
Docs
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/codigoencasa/bot-whatsapp/tree/main/starters/apps"
|
||||
target="_blank"
|
||||
>
|
||||
<a href="https://github.com/codigoencasa/bot-whatsapp/tree/main/starters/apps" target="_blank">
|
||||
Examples
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
export const QwikLogo = () => (
|
||||
<svg
|
||||
width="100"
|
||||
height="35"
|
||||
viewBox="0 0 167 53"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="100" height="35" viewBox="0 0 167 53" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M81.9545 46.5859H75.5513V35.4045C73.4363 36.8579 71.0496 37.5749 68.4884 37.5749C65.0151 37.5749 62.4344 36.6253 60.8239 34.6487C59.2134 32.6915 58.3984 29.2034 58.3984 24.2231C58.3984 19.1266 59.3492 15.5997 61.2702 13.5456C63.23 11.4721 66.3734 10.4644 70.7004 10.4644C74.7946 10.4644 78.5201 11.0264 81.9545 12.131V46.5859ZM75.5513 16.278C74.096 15.8323 72.4661 15.6191 70.7004 15.6191C68.5272 15.6191 66.9749 16.1811 66.1017 17.3244C65.2479 18.4871 64.7823 20.6962 64.7823 23.9712C64.7823 27.0524 65.1897 29.1065 66.0435 30.2304C66.8973 31.335 68.3719 31.897 70.5452 31.897C73.3781 31.897 75.5513 30.7343 75.5513 29.2809V16.278Z"
|
||||
fill="black"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user