mirror of
https://github.com/cheveguerra/bot-whatsapp.git
synced 2026-04-20 20:49:15 +00:00
Compare commits
73 Commits
feature/pr
...
contributo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa69ec4f16 | ||
|
|
7e2bf22d63 | ||
|
|
a4b610e21f | ||
|
|
120520df50 | ||
|
|
1551aafd54 | ||
|
|
eca876db9c | ||
|
|
96a387ed50 | ||
|
|
558013b2b4 | ||
|
|
58df7ddc71 | ||
|
|
e25f3abf1c | ||
|
|
0f6b82a9ab | ||
|
|
8f505bdb2b | ||
|
|
24220822f4 | ||
|
|
943fe8698c | ||
|
|
371b403456 | ||
|
|
77145bcc54 | ||
|
|
8f241834e8 | ||
|
|
5174c6b3bb | ||
|
|
f3ed6da4ba | ||
|
|
0dc839e531 | ||
|
|
7475d6f8fd | ||
|
|
a6a33ac7b7 | ||
|
|
71c969f3e9 | ||
|
|
cebfed0382 | ||
|
|
e89ad450a1 | ||
|
|
ac39ac831c | ||
|
|
0af74602f5 | ||
|
|
767e0764d6 | ||
|
|
b2a3343f82 | ||
|
|
5ddd885554 | ||
|
|
7078dc4c93 | ||
|
|
c7e829e954 | ||
|
|
5a81a77802 | ||
|
|
588411653e | ||
|
|
3c4b1c0fc4 | ||
|
|
0f06fd3e80 | ||
|
|
79cc31a96f | ||
|
|
7067b4a80b | ||
|
|
aa7e4239ae | ||
|
|
877252bd4a | ||
|
|
f5a7de3a00 | ||
|
|
c792d47344 | ||
|
|
f6130cf0b9 | ||
|
|
9f9d833925 | ||
|
|
02c0d8af76 | ||
|
|
880d5323e8 | ||
|
|
9bb33582fb | ||
|
|
c8ff84e9cd | ||
|
|
e942bd1d5e | ||
|
|
7e557bdd30 | ||
|
|
fcd1a63676 | ||
|
|
4ade5f02a7 | ||
|
|
05c834d6b1 | ||
|
|
234cc3ffb6 | ||
|
|
e321f35d86 | ||
|
|
5edd755491 | ||
|
|
10748a46a4 | ||
|
|
5b3136999b | ||
|
|
cb047cca8e | ||
|
|
4b8c09633e | ||
|
|
7cabc53eed | ||
|
|
40c34dd7a5 | ||
|
|
7e25dcaa93 | ||
|
|
bfa622fad0 | ||
|
|
e88141077f | ||
|
|
d743fdcfe3 | ||
|
|
1261580004 | ||
|
|
a14c67ad45 | ||
|
|
c5af1f8107 | ||
|
|
01a4edb7e3 | ||
|
|
98793d0cfc | ||
|
|
13e0530c01 | ||
|
|
04c5209cac |
15
.github/workflows/releases-dev.yml
vendored
15
.github/workflows/releases-dev.yml
vendored
@@ -10,9 +10,15 @@ jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
commit: ${{ steps.vars.outputs.commit }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{github.event.after}}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
@@ -50,8 +56,9 @@ jobs:
|
||||
- name: Release @bot-whatsapp/portal
|
||||
run: yarn node ./scripts/release.js --name=portal --version= --token="${{ secrets.NPM_TOKEN }}"
|
||||
|
||||
- name: Commit Versioning & Push changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
- name: Commit & Push changes
|
||||
uses: actions-js/push@master
|
||||
with:
|
||||
commit_message: 'ci(version): :zap: automatic - "${date}" updated versions every packages'
|
||||
branch: dev
|
||||
branch: release/next
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
force: true
|
||||
|
||||
14
.github/workflows/releases.yml
vendored
14
.github/workflows/releases.yml
vendored
@@ -67,13 +67,9 @@ jobs:
|
||||
- name: Release Github
|
||||
run: yarn node ./scripts/github.js --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.OCTO_TOKEN }}"
|
||||
|
||||
- name: 'Run if changes have been detected'
|
||||
run: |
|
||||
git add .
|
||||
git commit -m "chore(version): pre release"
|
||||
|
||||
- name: Commit Versioning & Push changes
|
||||
if: github.event_name == 'push'
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
- name: Commit & Push changes
|
||||
uses: actions-js/push@master
|
||||
with:
|
||||
commit_message: 'chore(version): launch release 🚀 "${{ steps.package-version.outputs.current-version}}"'
|
||||
branch: release/production
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
force: true
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/node_modules
|
||||
/packages/repl
|
||||
/packages/*/starters
|
||||
/packages/*/node_modules
|
||||
/packages/*/dist
|
||||
|
||||
41
CHANGELOG.md
41
CHANGELOG.md
@@ -2,6 +2,47 @@
|
||||
|
||||
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.18](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.17...v0.1.18) (2023-01-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bot:** :zap: add blacklist ([7078dc4](https://github.com/leifermendez/bot-whatsapp/commit/7078dc4c93d01bf90ef08ecb34e89a1abbe16fd2))
|
||||
* **bot:** :zap: flowDynamic buttons, media ([3c4b1c0](https://github.com/leifermendez/bot-whatsapp/commit/3c4b1c0fc4b6d98d67c67806d918d3604bb2209b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bot:** :bug: body undefined ([bb6ed4a](https://github.com/leifermendez/bot-whatsapp/commit/bb6ed4a084ae98070dfdf0c4ba1eca574c4092cc))
|
||||
* **bot:** :bug: body undefined ([9234cf1](https://github.com/leifermendez/bot-whatsapp/commit/9234cf1c5d00abdd35e62a826b3c450ab056987a))
|
||||
* **bot:** :bug: body undefined ([a118bbb](https://github.com/leifermendez/bot-whatsapp/commit/a118bbbf7f0a7023cb7f33c23f37db72adad151f))
|
||||
* **bot:** :bug: body undefined ([f54dea5](https://github.com/leifermendez/bot-whatsapp/commit/f54dea52b01063acd6122eeba1fbbe324aa7805d))
|
||||
* **bot:** :bug: body undefined ([72e0a91](https://github.com/leifermendez/bot-whatsapp/commit/72e0a910503e9643db7dfbc6e09c41c96934e1f7))
|
||||
* **bot:** :bug: body undefined ([70dd4d7](https://github.com/leifermendez/bot-whatsapp/commit/70dd4d73e814fc5636d19a887f3621c483b837c1))
|
||||
* **bot:** :bug: body undefined ([ecf0eef](https://github.com/leifermendez/bot-whatsapp/commit/ecf0eef928917d76c59bd23886cb7a4108b421f1))
|
||||
* **bot:** :bug: flowDynamic stranger behaviour ([877252b](https://github.com/leifermendez/bot-whatsapp/commit/877252bd4a8a7bbbbf083c3ceaeaeb952b0a1828))
|
||||
* **bot:** :bug: flowDynamic stranger behaviour ([f5a7de3](https://github.com/leifermendez/bot-whatsapp/commit/f5a7de3a003c012e2164e51fff26892cfc3144be))
|
||||
* **bot:** :memo: more docs ([98793d0](https://github.com/leifermendez/bot-whatsapp/commit/98793d0cfc1674830beaa3707f933c5a791eec14))
|
||||
* **cli:** :zap: refactor ([a29b9d4](https://github.com/leifermendez/bot-whatsapp/commit/a29b9d4e1f85fc163cf1d633c0857f0c8b7f03e1))
|
||||
* **cli:** :zap: refactor ([18ef4e9](https://github.com/leifermendez/bot-whatsapp/commit/18ef4e9d726575ca390ca24354825860328d3347))
|
||||
* **cli:** :zap: refactor ([3648757](https://github.com/leifermendez/bot-whatsapp/commit/3648757fa083bdb88a16bf6c2e90c828c233bdb1))
|
||||
* **cli:** :zap: refactor ([32f6a70](https://github.com/leifermendez/bot-whatsapp/commit/32f6a70f8f6fb26d8ea2a0f1a4aec4827b9d6a93))
|
||||
* **cli:** :zap: refactor ([8c825e7](https://github.com/leifermendez/bot-whatsapp/commit/8c825e7f6b7133f7cc7f3041ce331b80a9fe60e0))
|
||||
* **cli:** :zap: refactor ([0c0f437](https://github.com/leifermendez/bot-whatsapp/commit/0c0f4375b84549bee809340a85f9ce038ee2739e))
|
||||
* **cli:** :zap: refactor ([039ce5d](https://github.com/leifermendez/bot-whatsapp/commit/039ce5dd7cac8115b335ad5de05f7bd871e24140))
|
||||
* **cli:** :zap: refactor ([5e87918](https://github.com/leifermendez/bot-whatsapp/commit/5e879188b8bf9d486399b308a9a9c2612607d465))
|
||||
* **cli:** :zap: refactor ([21a7270](https://github.com/leifermendez/bot-whatsapp/commit/21a72702817bc6b344223b34ca4513a7ff45fc93))
|
||||
* **cli:** :zap: refactor ([82a99b2](https://github.com/leifermendez/bot-whatsapp/commit/82a99b2c80e6738566042ea738bbab8208a17758))
|
||||
* **cli:** :zap: refactor ([cc19974](https://github.com/leifermendez/bot-whatsapp/commit/cc19974579379777b05cb69c38cec0fce6740471))
|
||||
* **cli:** :zap: refactor ([56fcb8f](https://github.com/leifermendez/bot-whatsapp/commit/56fcb8fb72169bc21fce7c4fcdceccf2acd39c73))
|
||||
* **cli:** :zap: refactor ([f36cff1](https://github.com/leifermendez/bot-whatsapp/commit/f36cff1eefdd96be4ab531e1cb2d3b630b1a81c3))
|
||||
* **cli:** :zap: refactor ([b393c11](https://github.com/leifermendez/bot-whatsapp/commit/b393c11af6c0ebccb0a690be8b90b9df8877dad1))
|
||||
* **cli:** :zap: refactor ([6683715](https://github.com/leifermendez/bot-whatsapp/commit/6683715ad617ea1075654a475a1c62ea607c733f))
|
||||
* **contexts:** :bug: fixed [#524](https://github.com/leifermendez/bot-whatsapp/issues/524) issue ([79cc31a](https://github.com/leifermendez/bot-whatsapp/commit/79cc31a96f6a9836447cc4e6bb1e1521c54183fe))
|
||||
* **contexts:** :bug: fixed [#524](https://github.com/leifermendez/bot-whatsapp/issues/524) issue ([7067b4a](https://github.com/leifermendez/bot-whatsapp/commit/7067b4a80b7938ccfaf1ed141a37d645a1a3a062))
|
||||
* **provider:** wwebjs upgrade ([345f256](https://github.com/leifermendez/bot-whatsapp/commit/345f256a1b4a238519dafc15c9a31bc5e6bad4fe))
|
||||
* se agrego @bot-whatsapp/portal a package.json ([46a9fa6](https://github.com/leifermendez/bot-whatsapp/commit/46a9fa6793e06600335de998d2bd9d0691b02ca4))
|
||||
|
||||
### [0.1.17](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.16...v0.1.17) (2023-01-13)
|
||||
|
||||
|
||||
|
||||
82
README.md
82
README.md
@@ -34,6 +34,13 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<!-- readme: collaborators,contributors -start -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/cheveguerra">
|
||||
<img src="https://avatars.githubusercontent.com/u/5891114?v=4" width="50;" alt="cheveguerra"/>
|
||||
<br />
|
||||
<sub><b>Jose Alberto Guerra Ugalde</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/leifermendez">
|
||||
<img src="https://avatars.githubusercontent.com/u/15802366?v=4" width="50;" alt="leifermendez"/>
|
||||
@@ -42,10 +49,10 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/aurik3">
|
||||
<img src="https://avatars.githubusercontent.com/u/37228512?v=4" width="50;" alt="aurik3"/>
|
||||
<a href="https://github.com/leifermendezfroged">
|
||||
<img src="https://avatars.githubusercontent.com/u/97020486?v=4" width="50;" alt="leifermendezfroged"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
<sub><b>Leifer Mendez</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
@@ -56,12 +63,20 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/leifermendezfroged">
|
||||
<img src="https://avatars.githubusercontent.com/u/97020486?v=4" width="50;" alt="leifermendezfroged"/>
|
||||
<a href="https://github.com/danielcasta0398">
|
||||
<img src="https://avatars.githubusercontent.com/u/98791147?v=4" width="50;" alt="danielcasta0398"/>
|
||||
<br />
|
||||
<sub><b>Leifer Mendez</b></sub>
|
||||
<sub><b>Juan Daniel Castaño</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/marianarolfo">
|
||||
<img src="https://avatars.githubusercontent.com/u/68322254?v=4" width="50;" alt="marianarolfo"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/HKong31">
|
||||
<img src="https://avatars.githubusercontent.com/u/113340082?v=4" width="50;" alt="HKong31"/>
|
||||
@@ -75,8 +90,14 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<br />
|
||||
<sub><b>Zvi</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/JosephVTX">
|
||||
<img src="https://avatars.githubusercontent.com/u/91026290?v=4" width="50;" alt="JosephVTX"/>
|
||||
<br />
|
||||
<sub><b>Joseph Vega Callupe</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Gonzalito87">
|
||||
<img src="https://avatars.githubusercontent.com/u/100331586?v=4" width="50;" alt="Gonzalito87"/>
|
||||
@@ -84,6 +105,42 @@ 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>
|
||||
<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></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/aurik3">
|
||||
<img src="https://avatars.githubusercontent.com/u/37228512?v=4" width="50;" alt="aurik3"/>
|
||||
<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>
|
||||
<td align="center">
|
||||
<a href="https://github.com/6rak0">
|
||||
<img src="https://avatars.githubusercontent.com/u/12260031?v=4" width="50;" alt="6rak0"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/tonyvazgar">
|
||||
<img src="https://avatars.githubusercontent.com/u/21047090?v=4" width="50;" alt="tonyvazgar"/>
|
||||
@@ -92,10 +149,10 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ulisesvina">
|
||||
<img src="https://avatars.githubusercontent.com/u/20508563?v=4" width="50;" alt="ulisesvina"/>
|
||||
<a href="https://github.com/lisandroprada">
|
||||
<img src="https://avatars.githubusercontent.com/u/7232326?v=4" width="50;" alt="lisandroprada"/>
|
||||
<br />
|
||||
<sub><b>Ulises Viña</b></sub>
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
@@ -104,7 +161,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"/>
|
||||
|
||||
41
__test__/01-case.test.js
Normal file
41
__test__/01-case.test.js
Normal file
@@ -0,0 +1,41 @@
|
||||
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))
|
||||
}
|
||||
99
__test__/02-case.test.js
Normal file
99
__test__/02-case.test.js
Normal file
@@ -0,0 +1,99 @@
|
||||
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))
|
||||
}
|
||||
44
__test__/03-case.test.js
Normal file
44
__test__/03-case.test.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const { test } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const MOCK_DB = require('../packages/database/src/mock')
|
||||
const PROVIDER_DB = require('../packages/provider/src/mock')
|
||||
const {
|
||||
addKeyword,
|
||||
createBot,
|
||||
createFlow,
|
||||
createProvider,
|
||||
} = require('../packages/bot/index')
|
||||
|
||||
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))
|
||||
}
|
||||
82
__test__/04-case.test.js
Normal file
82
__test__/04-case.test.js
Normal file
@@ -0,0 +1,82 @@
|
||||
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,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/root",
|
||||
"version": "0.1.17",
|
||||
"version": "0.1.18",
|
||||
"description": "Bot de wahtsapp open source para MVP o pequeños negocios",
|
||||
"main": "app.js",
|
||||
"private": true,
|
||||
@@ -24,7 +24,8 @@
|
||||
"build": "yarn run cli:rollup && yarn run bot:rollup && yarn run provider:rollup && yarn run database:rollup && yarn run contexts:rollup && yarn run create-bot-whatsapp:rollup && yarn run portal:rollup",
|
||||
"copy.lib": "node ./scripts/move.js",
|
||||
"test.unit": "node ./node_modules/uvu/bin.js packages test",
|
||||
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit",
|
||||
"test.e2e": "node ./node_modules/uvu/bin.js __test__",
|
||||
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit && npm run test.e2e",
|
||||
"test": "npm run test.coverage",
|
||||
"cli": "node ./packages/cli/bin/cli.js",
|
||||
"create": "node ./packages/create-bot-whatsapp/bin/create.js",
|
||||
|
||||
@@ -21,10 +21,12 @@ class CoreClass {
|
||||
flowClass
|
||||
databaseClass
|
||||
providerClass
|
||||
constructor(_flow, _database, _provider) {
|
||||
generalArgs = { blackList: [] }
|
||||
constructor(_flow, _database, _provider, _args) {
|
||||
this.flowClass = _flow
|
||||
this.databaseClass = _database
|
||||
this.providerClass = _provider
|
||||
this.generalArgs = { ...this.generalArgs, ..._args }
|
||||
|
||||
for (const { event, func } of this.listenerBusEvents()) {
|
||||
this.providerClass.on(event, func)
|
||||
@@ -70,10 +72,12 @@ class CoreClass {
|
||||
const { body, from } = messageCtxInComming
|
||||
let msgToSend = []
|
||||
let fallBackFlag = false
|
||||
let endFlowFlag = false
|
||||
if (this.generalArgs.blackList.includes(from)) return
|
||||
if (!body) return
|
||||
if (!body.length) return
|
||||
|
||||
const prevMsg = await this.databaseClass.getPrevByNumber(from)
|
||||
let prevMsg = await this.databaseClass.getPrevByNumber(from)
|
||||
const refToContinue = this.flowClass.findBySerialize(
|
||||
prevMsg?.refSerialize
|
||||
)
|
||||
@@ -87,16 +91,35 @@ class CoreClass {
|
||||
this.databaseClass.save(ctxByNumber)
|
||||
}
|
||||
|
||||
// 📄 Limpiar cola de procesos
|
||||
const clearQueue = () => {
|
||||
QueuePrincipal.pendingPromise = false
|
||||
QueuePrincipal.queue = []
|
||||
}
|
||||
|
||||
// 📄 Finalizar flujo
|
||||
const endFlow = async () => {
|
||||
prevMsg = null
|
||||
endFlowFlag = true
|
||||
clearQueue()
|
||||
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!
|
||||
|
||||
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),
|
||||
resolveCbEveryCtx(ctxMessage),
|
||||
this.sendProviderAndSave(numberOrId, ctxMessage).then(
|
||||
() => resolveCbEveryCtx(ctxMessage)
|
||||
),
|
||||
])
|
||||
)
|
||||
}
|
||||
@@ -113,6 +136,7 @@ class CoreClass {
|
||||
|
||||
// 📄 [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 }
|
||||
@@ -122,15 +146,22 @@ class CoreClass {
|
||||
|
||||
fallBackFlag = optListMsg.fallback
|
||||
const parseListMsg = listMsg
|
||||
.map(({ body }, index) =>
|
||||
toCtx({
|
||||
.map((opt, index) => {
|
||||
const body = typeof opt === 'string' ? opt : opt.body
|
||||
const media = opt?.media ?? null
|
||||
const buttons = opt?.buttons ?? []
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -139,7 +170,6 @@ class CoreClass {
|
||||
|
||||
// 📄 Se encarga de revisar si el contexto del mensaje tiene callback o fallback
|
||||
const resolveCbEveryCtx = async (ctxMessage) => {
|
||||
if (prevMsg?.options?.capture) return cbEveryCtx(prevMsg?.ref)
|
||||
if (!ctxMessage?.options?.capture)
|
||||
return await cbEveryCtx(ctxMessage?.ref)
|
||||
}
|
||||
@@ -150,20 +180,10 @@ class CoreClass {
|
||||
return this.flowClass.allCallbacks[inRef](messageCtxInComming, {
|
||||
fallBack,
|
||||
flowDynamic,
|
||||
endFlow,
|
||||
})
|
||||
}
|
||||
|
||||
if (prevMsg?.ref) resolveCbEveryCtx(prevMsg)
|
||||
|
||||
// 📄 [options: callback]: Si se tiene un callback se ejecuta
|
||||
//TODO AQUI
|
||||
// if (!fallBackFlag) {
|
||||
// if (prevMsg?.options?.capture) cbEveryCtx(prevMsg?.ref)
|
||||
// for (const ite of this.flowClass.find(body)) {
|
||||
// if (!ite?.options?.capture) cbEveryCtx(ite?.ref)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 📄🤘(tiene return) [options: nested(array)]: Si se tiene flujos hijos los implementa
|
||||
if (!fallBackFlag && prevMsg?.options?.nested?.length) {
|
||||
const nestedRef = prevMsg.options.nested
|
||||
@@ -173,11 +193,6 @@ class CoreClass {
|
||||
|
||||
msgToSend = this.flowClass.find(body, false, flowStandalone) || []
|
||||
|
||||
// //TODO AQUI
|
||||
// for (const ite of msgToSend) {
|
||||
// cbEveryCtx(ite?.ref)
|
||||
// }
|
||||
|
||||
sendFlow(msgToSend, from)
|
||||
return
|
||||
}
|
||||
@@ -225,5 +240,24 @@ class CoreClass {
|
||||
this.continue(null, responde.ref)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Funcion dedicada a enviar el mensaje sin pasar por el flow
|
||||
* (dialogflow)
|
||||
* @param {*} messageToSend
|
||||
* @param {*} numberOrId
|
||||
* @returns
|
||||
*/
|
||||
sendFlowSimple = async (messageToSend, numberOrId) => {
|
||||
const queue = []
|
||||
for (const ctxMessage of messageToSend) {
|
||||
const delayMs = ctxMessage?.options?.delay || 0
|
||||
if (delayMs) await delay(delayMs)
|
||||
QueuePrincipal.enqueue(() =>
|
||||
this.sendProviderAndSave(numberOrId, ctxMessage)
|
||||
)
|
||||
}
|
||||
return Promise.all(queue)
|
||||
}
|
||||
}
|
||||
module.exports = CoreClass
|
||||
|
||||
@@ -8,8 +8,8 @@ const { addKeyword, addAnswer, addChild, toSerialize } = require('./io/methods')
|
||||
* @param {*} args
|
||||
* @returns
|
||||
*/
|
||||
const createBot = async ({ flow, database, provider }) =>
|
||||
new CoreClass(flow, database, provider)
|
||||
const createBot = async ({ flow, database, provider }, args = {}) =>
|
||||
new CoreClass(flow, database, provider, args)
|
||||
|
||||
/**
|
||||
* Crear instancia de clase Io (Flow)
|
||||
|
||||
@@ -5,12 +5,12 @@ const { generateRef, generateRefSerialize } = require('../../utils/hash')
|
||||
* @param options {media:string, buttons:[], capture:true default false}
|
||||
* @returns
|
||||
*/
|
||||
const toCtx = ({ body, from, prevRef, index }) => {
|
||||
const toCtx = ({ body, from, prevRef, options = {}, index }) => {
|
||||
return {
|
||||
ref: generateRef(),
|
||||
keyword: prevRef,
|
||||
answer: body,
|
||||
options: {},
|
||||
options: options ?? {},
|
||||
from,
|
||||
refSerialize: generateRefSerialize({ index, answer: body }),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/bot",
|
||||
"version": "0.0.66-alpha.0",
|
||||
"version": "0.0.73-alpha.0",
|
||||
"description": "",
|
||||
"main": "./lib/bundle.bot.cjs",
|
||||
"scripts": {
|
||||
|
||||
28
packages/bot/tests/flow.class.test.js
Normal file
28
packages/bot/tests/flow.class.test.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const { test } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const FlowClass = require('../io/flow.class')
|
||||
const { addKeyword } = require('../index')
|
||||
|
||||
test(`[FlowClass] Probando instanciamiento de clase`, async () => {
|
||||
const MOCK_FLOW = addKeyword('hola').addAnswer('Buenas!')
|
||||
const flowClass = new FlowClass([MOCK_FLOW])
|
||||
assert.is(flowClass instanceof FlowClass, true)
|
||||
})
|
||||
|
||||
test(`[FlowClass] Probando find`, async () => {
|
||||
const MOCK_FLOW = addKeyword('hola').addAnswer('Buenas!')
|
||||
const flowClass = new FlowClass([MOCK_FLOW])
|
||||
|
||||
flowClass.find('hola')
|
||||
assert.is(flowClass instanceof FlowClass, true)
|
||||
})
|
||||
|
||||
test(`[FlowClass] Probando findBySerialize`, async () => {
|
||||
const MOCK_FLOW = addKeyword('hola').addAnswer('Buenas!')
|
||||
const flowClass = new FlowClass([MOCK_FLOW])
|
||||
|
||||
flowClass.findBySerialize('')
|
||||
assert.is(flowClass instanceof FlowClass, true)
|
||||
})
|
||||
|
||||
test.run()
|
||||
@@ -48,11 +48,11 @@ const nextSteps = async () => {
|
||||
name: 'providerWs',
|
||||
message: '¿Cuál proveedor de whatsapp quieres utilizar?',
|
||||
choices: [
|
||||
{ title: 'whatsapp-web.js (gratis)', value: 'wweb' },
|
||||
{ title: 'Venom (gratis)', value: 'venom' },
|
||||
{ title: 'Baileys (gratis)', value: 'baileys' },
|
||||
{ title: 'Venom (gratis)', value: 'venom' },
|
||||
{ title: 'whatsapp-web.js (gratis)', value: 'wweb' },
|
||||
{ title: 'Twilio', value: 'twilio' },
|
||||
{ title: 'API Oficial (Meta)', value: 'meta' },
|
||||
{ title: 'Meta', value: 'meta' },
|
||||
],
|
||||
max: 1,
|
||||
hint: 'Espacio para seleccionar',
|
||||
|
||||
@@ -117,7 +117,7 @@ class DialogFlowCXContext extends CoreClass {
|
||||
}
|
||||
})
|
||||
|
||||
this.sendFlow(listMessages, from)
|
||||
this.sendFlowSimple(listMessages, from)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class DialogFlowContext extends CoreClass {
|
||||
...customPayload,
|
||||
answer: fields?.answer?.stringValue,
|
||||
}
|
||||
this.sendFlow([ctxFromDX], from)
|
||||
this.sendFlowSimple([ctxFromDX], from)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class DialogFlowContext extends CoreClass {
|
||||
answer: queryResult?.fulfillmentText,
|
||||
}
|
||||
|
||||
this.sendFlow([ctxFromDX], from)
|
||||
this.sendFlowSimple([ctxFromDX], from)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export default component$(
|
||||
<a href={props.user.html_url} target="_blank">
|
||||
<img
|
||||
class="w-16 h-16 rounded-full mx-auto object-cover"
|
||||
src={props.user.avatar_url + '&s=80'}
|
||||
src={props.user.avatar_url}
|
||||
alt={props.user.login}
|
||||
width="80"
|
||||
height="80"
|
||||
@@ -23,7 +23,9 @@ export default component$(
|
||||
|
||||
<div class="pt-2 space-y-4 justify-center flex">
|
||||
<figcaption class="text-sm">
|
||||
<div class={'font-semibold'}>{props.user.login}</div>
|
||||
<div class={'font-semibold truncate'}>
|
||||
{props.user.login}
|
||||
</div>
|
||||
</figcaption>
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
@@ -56,7 +56,7 @@ export default component$(() => {
|
||||
</a>
|
||||
<a
|
||||
target={'_blank'}
|
||||
href="https://youtu.be/DEIyGyJNGa8"
|
||||
href="https://youtu.be/UgoS8PXxe-A"
|
||||
class="btn bg-gray-50 dark:bg-transparent"
|
||||
>
|
||||
Ver video
|
||||
|
||||
54
packages/docs/src/components/widgets/Members.tsx
Normal file
54
packages/docs/src/components/widgets/Members.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { component$ } from '@builder.io/qwik'
|
||||
import { RequestHandlerCloudflarePages } from '@builder.io/qwik-city/middleware/cloudflare-pages'
|
||||
import { User } from '~/contexts'
|
||||
import Collaborator from './Collaborator'
|
||||
|
||||
export const onRequest: RequestHandlerCloudflarePages = async () => {
|
||||
console.log('??heree')
|
||||
}
|
||||
|
||||
export const TaleUsers = component$((props: { users: User[] }) => {
|
||||
return (
|
||||
<>
|
||||
{props.users.map((user) => (
|
||||
<div class="col-span-2 ">
|
||||
{' '}
|
||||
<Collaborator user={user} />
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default component$((props: { users: User[] }) => {
|
||||
return (
|
||||
<section class="relative ">
|
||||
<div class={'px-4 py-16 mx-auto max-w-6xl lg:py-20'}>
|
||||
<div class="mb-10 md:mx-auto sm:text-center md:mb-12 max-w-3xl">
|
||||
<p class="text-base text-primary-600 dark:text-purple-200 font-semibold tracking-wide uppercase">
|
||||
Premium
|
||||
</p>
|
||||
<h2 class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading">
|
||||
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"
|
||||
>
|
||||
Únete
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid lg:grid-cols-12 grid-cols-1 gap-4 ">
|
||||
<TaleUsers users={props.users} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
})
|
||||
20
packages/docs/src/components/widgets/SearchModal.tsx
Normal file
20
packages/docs/src/components/widgets/SearchModal.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { component$ } from '@builder.io/qwik'
|
||||
|
||||
export const SearchModal = component$(() => {
|
||||
// const state = useStore({
|
||||
// open: false,
|
||||
// src: '',
|
||||
// })
|
||||
|
||||
return (
|
||||
<div class={'bg-gray-100/75 fixed w-[100vw] h-[100vh] z-50'}>
|
||||
<div class={'bg-red-200 w-1/3 m-auto mt-12'}>
|
||||
<SingleModal />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export const SingleModal = component$(() => {
|
||||
return <div class={'bg-blue-300 w-100 px-3 py-2'}>Modal singlke</div>
|
||||
})
|
||||
@@ -52,16 +52,22 @@ export default component$(() => {
|
||||
title: 'Avanzado',
|
||||
list: [
|
||||
{ name: 'Migración', link: '/docs/migration' },
|
||||
{ name: 'Extender funcionalidades', link: '/docs/custom' },
|
||||
{ name: 'MasterClass', link: '/docs/masterclass' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Despliegue',
|
||||
list: [
|
||||
{ name: 'Local', link: '/docs/deploy/local' },
|
||||
{ name: 'Docker', link: '/docs/deploy/docker' },
|
||||
{ name: 'Cloud', link: '/docs/deploy/cloud' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Comunidad',
|
||||
list: [
|
||||
{ name: 'MasterClass', link: '/docs/masterclass' },
|
||||
{ name: 'Colabores', link: '/docs/contributing' },
|
||||
{ name: 'Unirme al proyecto', link: '/docs/join' },
|
||||
{ name: 'Sponsors', link: '/docs/sponsors' },
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
108
packages/docs/src/routes/docs/deploy/cloud/index.mdx
Normal file
108
packages/docs/src/routes/docs/deploy/cloud/index.mdx
Normal file
@@ -0,0 +1,108 @@
|
||||
import Alert from '../../../../components/widgets/Alert'
|
||||
import Navigation from '../../../../components/widgets/Navigation'
|
||||
|
||||
# Entorno Cloud
|
||||
|
||||
Si deseas tener tu chatbot en ejecución en un servidor en la nueba esta, guía te ayudará.
|
||||
El servidor deberá cumplir con los requisitos mínimos, puedes ver en [este enlace.](/docs/requirements)
|
||||
|
||||
---
|
||||
|
||||
Dependiendo de tu proveedor de **servicio Cloud** debes crear una instancia (máquina virtual), este ejemplo iremos orientando en un entorno de AWS.
|
||||
En nuestro ejemplo creamos una maquina virtual con **Ubuntu 20.04**
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
Posterior al proceso de crear la máquina esperamos unos minutos hasta que ya está operativo y tomamos nota del usuario y la IP pública para proceder a conectarnos vía SSH
|
||||
|
||||
## 
|
||||
|
||||
## Conectarse via SSH
|
||||
|
||||
Luego de obtener los datos necesarios para conectarnos a nuestra máquina, procedemos a hacerlo
|
||||
|
||||
```shell
|
||||
ssh -i llaveBot.pem ubutnu@34.228.208.104
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Luego puede aparecer un mensaje como el siguiente donde solo debes de responder **yes**
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
Una vez conectado ya estás dentro de la máquina virtual, te aconsejamos la primera vez hacer una actualización de dependencias de Ubuntu con los siguientes comandos
|
||||
|
||||
```shell
|
||||
sudo apt-get update
|
||||
```
|
||||
|
||||
```shell
|
||||
sudo apt-get upgrade
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Recuerda instalar Node 16 o superior
|
||||
|
||||
Puedes ver más a detalle los pasos de la instalacion en este [blog](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04-es)
|
||||
|
||||
```shell
|
||||
curl -sL https://deb.nodesource.com/setup_18.x -o nodesource_setup.sh
|
||||
```
|
||||
|
||||
```shell
|
||||
sudo bash nodesource_setup.sh
|
||||
```
|
||||
|
||||
```shell
|
||||
sudo apt-get install -y nodejs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementar el bot
|
||||
|
||||
Si tienes el código de tu chatbot en un repositorio, solo falta que clones el repo en el servidor y ejecutes `npm start`
|
||||
|
||||
Para escanear el **QR** puedes hacerlo vía WEB accediendo a la URL `http://[TU_IP_PUBLICA]:3000` en este ejemplo seria `http://34.228.208.10:3000`
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Firewall
|
||||
|
||||
Si no te abre la pagina web asegurate de tener el puerto abierto en tu firewall.
|
||||
Ejemplo permitir el puerto **3000**
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Escanear QR
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Ejecutar en Producción
|
||||
|
||||
Debes ubicarte en el directorio donde tienes el codigo fuente de tu chatbot.
|
||||
|
||||
Independientemente de tu sistema operativo deberás ejecutar el chatbot con el comando atrevés de un sistema que mantenga el proceso en ejecución.
|
||||
Recomendamos [Pm2](https://pm2.keymetrics.io/)
|
||||
|
||||
```shell
|
||||
pm2 start app.js --name=bot1
|
||||
```
|
||||
|
||||

|
||||
|
||||
La consola de devolver un mensaje con una lista de procesos, en el ejemplo puedes observar, que tenemos un proceso llamado `bot1`
|
||||
|
||||
De esta manera ya puedes cerrar la terminal y tu bot seguirá en ejecución sin problema
|
||||
32
packages/docs/src/routes/docs/deploy/docker/index.mdx
Normal file
32
packages/docs/src/routes/docs/deploy/docker/index.mdx
Normal file
@@ -0,0 +1,32 @@
|
||||
import Alert from '../../../../components/widgets/Alert'
|
||||
import Navigation from '../../../../components/widgets/Navigation'
|
||||
|
||||
# Entorno Docker
|
||||
|
||||
Previamente, necesitas tener instalado Docker en tu servidor dependiendo del sistema operativo, los procesos cambian,
|
||||
puedes encontrar toda la información oficial de docker en [este enlace.](https://docs.docker.com/get-docker/)
|
||||
|
||||
---
|
||||
|
||||
Dependiendo del proveedor que has elegido necesitaras una implementación de Docker específica, pero no te preocupes, ya que viene implementada automáticamente en un archivo llamado **Dockerfile**, también puedes ver los otros Dockerfile en el apartado de [plantillas.](https://github.com/codigoencasa/bot-whatsapp/tree/main/starters/apps)
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Contruir imagen
|
||||
|
||||
Solo es necesario construir la imagen del docker lo puedes hacer con el siguiente comando
|
||||
|
||||
```shell
|
||||
docker build . -t botwhatsapp:latest
|
||||
```
|
||||
|
||||
## Iniciar contenedor
|
||||
|
||||
Para iniciar el contenedor con la imagen previamente construida puedes realizarlo ejecutando el siguiente comando.
|
||||
Se utiliza el puerto **3001** solo com un ejemplo puedes usar el puerto que tu quieras
|
||||
|
||||
```shell
|
||||
docker run -e PORT=3001 -p 3001:3001 botwhatsapp:latest
|
||||
```
|
||||
26
packages/docs/src/routes/docs/deploy/local/index.mdx
Normal file
26
packages/docs/src/routes/docs/deploy/local/index.mdx
Normal file
@@ -0,0 +1,26 @@
|
||||
import Alert from '../../../../components/widgets/Alert'
|
||||
import Navigation from '../../../../components/widgets/Navigation'
|
||||
|
||||
# Entorno Local
|
||||
|
||||
Si deseas tener tu chatbot en ejecución en un servidor local (computadora personal, etc.) esta, guía te ayudará.
|
||||
El servidor local deberá cumplir con los requisitos mínimos, puedes ver en [este enlace.](/docs/requirements)
|
||||
|
||||
---
|
||||
|
||||
<Alert>Si deseas instalar pm2 puedes ejecutar `npm install pm2 --global`</Alert>
|
||||
|
||||
Debes ubicarte en el directorio donde tienes el codigo fuente de tu chatbot.
|
||||
|
||||
Independientemente de tu sistema operativo deberás ejecutar el chatbot con el comando atrevés de un sistema que mantenga el proceso en ejecución.
|
||||
Recomendamos [Pm2](https://pm2.keymetrics.io/)
|
||||
|
||||
```shell
|
||||
pm2 start app.js --name=bot1
|
||||
```
|
||||
|
||||

|
||||
|
||||
La consola de devolver un mensaje con una lista de procesos, en el ejemplo puedes observar, que tenemos un proceso llamado `bot1`
|
||||
|
||||
De esta manera ya puedes cerrar la terminal y tu bot seguirá en ejecución sin problema
|
||||
@@ -23,6 +23,23 @@ const flowPrincipal = addKeyword(['hola', 'alo'])
|
||||
|
||||
---
|
||||
|
||||
## blackList
|
||||
|
||||
Éste argumento se utiliza para **evitar que el bot se active** cuando los números de la lista activen el bot.
|
||||
Es importante que el número **vaya acompañado de su prefijo**, en el caso de España "34".
|
||||
|
||||
```js
|
||||
createBot({
|
||||
flow: adapterFlow,
|
||||
provider: adapterProvider,
|
||||
database: adapterDB,
|
||||
},{
|
||||
blackList:['34XXXXXXXXX','34XXXXXXXXX','34XXXXXXXXX','34XXXXXXXXX']
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## addKeyword()
|
||||
|
||||
Esta funcion se utliza para iniciar un flujo de conversion. <br /> Recibe un `string` o un `array`
|
||||
@@ -55,7 +72,7 @@ Esta funcion se utliza para responder un mensaje despues del `addKeyword()`
|
||||
- delay: 0 (milisegundos)
|
||||
- media: url de imagen
|
||||
- buttons: array `[{body:'Boton1'}, {body:'Boton2'}, {body:'Boton3'}]`
|
||||
- capture: false (para esperar respuesta)
|
||||
- capture: true (para esperar respuesta)
|
||||
- child: Objecto tipo flujo o arra de flujos hijos
|
||||
|
||||
```js
|
||||
@@ -158,6 +175,64 @@ 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.
|
||||
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'])
|
||||
|
||||
.addAnswer(['Hola!','Escriba su *Nombre* para generar su solicitud'],
|
||||
{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()
|
||||
}
|
||||
})
|
||||
.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()
|
||||
}
|
||||
})
|
||||
.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()
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# QRPortalWeb
|
||||
|
||||
Argumento para asignar nombre y puerto al BOT
|
||||
|
||||
```js
|
||||
QRPortalWeb({name:BOTNAME, port:3005 });
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
<Navigation
|
||||
pages={[
|
||||
|
||||
@@ -3,6 +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 SponsorBar from '~/components/widgets/SponsorBar'
|
||||
import { GlobalStore } from '~/contexts'
|
||||
// import Navigation from '~/components/widgets/Navigation'
|
||||
@@ -14,6 +15,7 @@ export default component$(() => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchModal />
|
||||
<Header />
|
||||
<main class={'overflow-hidden'}>
|
||||
<div class={'max-w-8xl'}>
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
[](https://hackmd.io/79xQyVSgRD6RsTpqtMPPdw)
|
||||
### Preguntas Frecuentes para Master Class BOT v2
|
||||
> Anota aqui las preguntas o dudas que tengas
|
||||
> Pronto estare publicando fecha y hora para la masterclass
|
||||
|
||||
1.- Si necesito correr dos bots al mismo tiempo ¿donde puedo cambiar el puerto?
|
||||
|
||||
2.- Si necesito agregar o modificar funciones del bot, ¿como puedo hacerlo?
|
||||
|
||||
3.- Si quiero mi bot con otra base de datos diferente a MySQL ¿como lo puedo hacer?
|
||||
|
||||
4.- Quiero conectarme a tal o cual API con JSON/XML/etc, ¿se puede hacer?
|
||||
|
||||
5.- ¿Como integrar listas?
|
||||
|
||||
6.- Preguntas y respuestas con el Bot
|
||||
|
||||
7.- Guardar conversaciones en Excel.
|
||||
|
||||
8.- ¿Puedo usar 2 o mas sesiones (códigos QR) al mismo tiempo?
|
||||
|
||||
9.- ¿Puede ser que al usar el provider bailey, al leer el qr.png, que sea desde una url en el navegador, y no desde visual studio? Gracias
|
||||
|
||||
10.- ¿Cómo tomo los datos que me envían en un mensaje para utilizarlo internamente en la búsqueda de datos propios y devolver la respuesta?
|
||||
26
packages/docs/src/routes/docs/masterclass/index.mdx
Normal file
26
packages/docs/src/routes/docs/masterclass/index.mdx
Normal file
@@ -0,0 +1,26 @@
|
||||
# MasterClass
|
||||
|
||||
<iframe
|
||||
width="560"
|
||||
height="315"
|
||||
src="https://www.youtube.com/embed/22jiE2Z3XGM"
|
||||
title="YouTube video player"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
|
||||
---
|
||||
|
||||
### Preguntas de la masterclass
|
||||
|
||||
- Si necesito correr dos bots al mismo tiempo ¿donde puedo cambiar el puerto?
|
||||
- Si necesito agregar o modificar funciones del bot, ¿como puedo hacerlo?
|
||||
- Si quiero mi bot con otra base de datos diferente a MySQL ¿como lo puedo hacer?
|
||||
- Quiero conectarme a tal o cual API con JSON/XML/etc, ¿se puede hacer?
|
||||
- ¿Como integrar listas?
|
||||
- Preguntas y respuestas con el Bot
|
||||
- Guardar conversaciones en Excel.
|
||||
- ¿Puedo usar 2 o mas sesiones (códigos QR) al mismo tiempo?
|
||||
- ¿Puede ser que al usar el provider bailey, al leer el qr.png, que sea desde una url en el navegador, y no desde visual studio? Gracias
|
||||
- ¿Cómo tomo los datos que me envían en un mensaje para utilizarlo internamente en la búsqueda de datos propios y devolver la respuesta?
|
||||
@@ -5,16 +5,22 @@ import Features from '~/components/widgets/Features'
|
||||
import FAQs from '~/components/widgets/FAQs'
|
||||
import CallToAction from '~/components/widgets/CallToAction'
|
||||
import Collaborators from '~/components/widgets/Collaborators'
|
||||
import Members from '~/components/widgets/Members'
|
||||
import { fetchGithub } from '~/services/github'
|
||||
import { fetchOpenCollective } from '~/services/opencollective'
|
||||
import { RequestHandlerNetlify } from '@builder.io/qwik-city/middleware/netlify-edge'
|
||||
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
|
||||
console.log(`[🚩 platform]: `, GITHUB_TOKEN)
|
||||
const data = await fetchGithub(CHECK_GITHUB_TOKEN)
|
||||
return data
|
||||
const dataGithub = await fetchGithub(CHECK_GITHUB_TOKEN)
|
||||
const dataOpenCollective = await fetchOpenCollective()
|
||||
return {
|
||||
dataGithub,
|
||||
dataOpenCollective,
|
||||
}
|
||||
}
|
||||
|
||||
export default component$(() => {
|
||||
@@ -27,9 +33,16 @@ export default component$(() => {
|
||||
<CallToAction />
|
||||
<Resource
|
||||
value={resource}
|
||||
onResolved={(data: any) => <Collaborators users={data} />}
|
||||
onResolved={(data: any) => {
|
||||
return (
|
||||
<>
|
||||
<Collaborators users={data.dataGithub} />
|
||||
<FAQs />
|
||||
<Members users={data.dataOpenCollective} />
|
||||
</>
|
||||
)
|
||||
}}
|
||||
></Resource>
|
||||
<FAQs />
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -14,6 +14,9 @@ export const fetchGithub = async (token: string) => {
|
||||
},
|
||||
}
|
||||
)
|
||||
const listUsers = data.json()
|
||||
return listUsers
|
||||
const listUsers = await data.json()
|
||||
return listUsers.map((u: any) => ({
|
||||
...u,
|
||||
avatar_url: `${u.avatar_url}&s=80`,
|
||||
}))
|
||||
}
|
||||
|
||||
19
packages/docs/src/services/opencollective.ts
Normal file
19
packages/docs/src/services/opencollective.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* GET API from OpenCollective
|
||||
* @returns
|
||||
*/
|
||||
export const fetchOpenCollective = async () => {
|
||||
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,
|
||||
avatar_url: u.image ?? 'https://i.imgur.com/HhiYKwN.png',
|
||||
login: u.name,
|
||||
id: u.MemberId,
|
||||
}))
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
const notFounds = [
|
||||
[
|
||||
'/',
|
||||
'<!DOCTYPE html>\n<html>\n<head>\n <meta charset="utf-8">\n <meta http-equiv="Status" content="404"/>\n <title>404 Resource Not Found</title>\n <meta name="viewport" content="width=device-width,initial-scale=1">\n <style>\n body { color: #006ce9; background-color: #fafafa; padding: 30px; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Roboto, sans-serif; }\n p { max-width: 600px; margin: 60px auto 30px auto; background: white; border-radius: 4px; box-shadow: 0px 0px 50px -20px #006ce9; overflow: hidden; }\n strong { display: inline-block; padding: 15px; background: #006ce9; color: white; }\n span { display: inline-block; padding: 15px; }\n pre { max-width: 580px; margin: 0 auto; }\n </style>\n</head>\n<body>\n <p><strong>404</strong> <span>Resource Not Found</span></p>\n</body>\n</html>',
|
||||
'<!DOCTYPE html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta http-equiv="Status" content="404" />\n <title>404 Resource Not Found</title>\n <meta name="viewport" content="width=device-width,initial-scale=1" />\n <style>\n body {\n color: #006ce9;\n background-color: #fafafa;\n padding: 30px;\n font-family: ui-sans-serif, system-ui, -apple-system,\n BlinkMacSystemFont, Roboto, sans-serif;\n }\n p {\n max-width: 600px;\n margin: 60px auto 30px auto;\n background: white;\n border-radius: 4px;\n box-shadow: 0px 0px 50px -20px #006ce9;\n overflow: hidden;\n }\n strong {\n display: inline-block;\n padding: 15px;\n background: #006ce9;\n color: white;\n }\n span {\n display: inline-block;\n padding: 15px;\n }\n pre {\n max-width: 580px;\n margin: 0 auto;\n }\n </style>\n </head>\n <body>\n <p><strong>404</strong> <span>Resource Not Found</span></p>\n </body>\n</html>\n',
|
||||
],
|
||||
]
|
||||
function getNotFound(p) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const staticPaths = new Set([
|
||||
'/',
|
||||
'/favicon.svg',
|
||||
'/manifest.json',
|
||||
'/q-manifest.json',
|
||||
|
||||
Reference in New Issue
Block a user