mirror of
https://github.com/cheveguerra/bot-whatsapp.git
synced 2026-04-20 20:49:15 +00:00
Compare commits
125 Commits
contributo
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b76c117b78 | |||
|
|
eb79505c19 | ||
|
|
1c602ff955 | ||
|
|
ce066c3922 | ||
|
|
647d6afd4b | ||
|
|
e26040f4ce | ||
|
|
9a7f113d66 | ||
|
|
1eb0403529 | ||
|
|
2c20d47c53 | ||
|
|
b158e50824 | ||
|
|
104cde012c | ||
|
|
8609c30c89 | ||
|
|
856165e7a5 | ||
|
|
bd9093956a | ||
|
|
2f07d86fb9 | ||
|
|
64cea7f050 | ||
|
|
405d73af04 | ||
|
|
bea38cff3b | ||
|
|
a147677a26 | ||
|
|
2b2b4ef2ee | ||
|
|
c7de860803 | ||
|
|
6933fed395 | ||
|
|
a46a9efd8d | ||
|
|
b343204040 | ||
|
|
ba2ed54405 | ||
|
|
b558afb3f9 | ||
|
|
c9a6350f42 | ||
|
|
6f7808c167 | ||
|
|
b238752df1 | ||
|
|
a88b01e69f | ||
|
|
31a42ddcad | ||
|
|
dda59fa93b | ||
| dac474c31e | |||
|
|
cc9dfa10cb | ||
|
|
14ee9eea89 | ||
|
|
cfc5309e52 | ||
|
|
af803415e3 | ||
|
|
c0ece6feb2 | ||
|
|
2278149295 | ||
|
|
2d44a002ff | ||
|
|
652d0ef2c3 | ||
|
|
9a63fa54e1 | ||
|
|
0b6729c6ad | ||
|
|
309dd1da99 | ||
|
|
93bee15ab8 | ||
|
|
276fc0cf88 | ||
|
|
5d38cd377c | ||
|
|
176f337f18 | ||
|
|
76ba717927 | ||
|
|
2ecc41f3f0 | ||
|
|
99b9b17f52 | ||
|
|
cd082f2350 | ||
|
|
1afc3ba182 | ||
|
|
8b79e4e616 | ||
|
|
c9831d202a | ||
|
|
9311aa0a65 | ||
|
|
e34560c77d | ||
|
|
d220f2c622 | ||
|
|
c0113ca492 | ||
|
|
aef52d2694 | ||
|
|
f76932021c | ||
|
|
23e09efaec | ||
|
|
96c2bffd09 | ||
|
|
c3a4162ff6 | ||
|
|
186c48d884 | ||
|
|
bb31045a95 | ||
|
|
74fb3b864d | ||
|
|
88c05c12a4 | ||
|
|
5260a7eb47 | ||
|
|
9e1698b729 | ||
|
|
811618b256 | ||
|
|
331d2a309c | ||
|
|
c655c48009 | ||
|
|
ebd30ea1d7 | ||
|
|
0f8a33cc0c | ||
|
|
85b2d13290 | ||
|
|
318ac3adec | ||
|
|
0a69b8d9b5 | ||
|
|
f434d6a101 | ||
|
|
753d80b93e | ||
|
|
c07e148dfd | ||
|
|
3f30a1cb51 | ||
|
|
9b679192db | ||
|
|
f7d90efc2f | ||
|
|
de5de37d65 | ||
|
|
0aa793e08c | ||
|
|
eeb4bb305e | ||
|
|
012d43847c | ||
|
|
87a4203cd5 | ||
|
|
f6114affad | ||
|
|
15b64185cb | ||
|
|
b655ae449e | ||
|
|
1c66f178a5 | ||
|
|
f201c5097b | ||
|
|
b33e34692d | ||
|
|
8f967578c6 | ||
|
|
fd2847aea0 | ||
|
|
f95331d3dc | ||
|
|
791ab8e970 | ||
|
|
880c729199 | ||
|
|
8da4b204b4 | ||
|
|
3fdd49ff86 | ||
|
|
e22780d3fa | ||
|
|
0ad4c58457 | ||
|
|
f8c7184487 | ||
|
|
b2afa45352 | ||
|
|
dcb0566d2b | ||
|
|
cbe438b778 | ||
|
|
6ff1a3a980 | ||
|
|
dceb13f4f5 | ||
|
|
bbbdb1c206 | ||
|
|
bd7d150c04 | ||
|
|
9dd7c02b6a | ||
|
|
11a74b8bea | ||
|
|
e7a8e85ead | ||
|
|
6bfbae7b94 | ||
|
|
d5d7f9dfee | ||
|
|
0a23d2c761 | ||
|
|
b2feaea588 | ||
|
|
01c7db8fe7 | ||
|
|
14d1a61fa2 | ||
|
|
e19c3a25a4 | ||
|
|
eab39e4ac0 | ||
|
|
8cbfd560a3 | ||
|
|
3905ae40ae |
52
.github/workflows/check-provider-major.yml
vendored
52
.github/workflows/check-provider-major.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: Rev Major Providers
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * *'
|
||||
|
||||
jobs:
|
||||
check-npm:
|
||||
name: Install Dependencies
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
cache: 'yarn'
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- run: corepack enable
|
||||
|
||||
- name: Install NPM Dependencies
|
||||
run: yarn install --immutable --network-timeout 300000
|
||||
|
||||
- name: Check Baileys
|
||||
run: yarn node ./scripts/checker.js --name=baileys --stable=true
|
||||
|
||||
- name: Check Venom
|
||||
run: yarn node ./scripts/checker.js --name=venom --stable=true
|
||||
|
||||
- name: Check web-whatsapp
|
||||
run: yarn node ./scripts/checker.js --name=web-whatsapp --stable=true
|
||||
|
||||
- name: Check Meta
|
||||
run: yarn node ./scripts/checker.js --name=meta --stable=true
|
||||
|
||||
- name: Check Twilio
|
||||
run: yarn node ./scripts/checker.js --name=twilio --stable=true
|
||||
|
||||
- name: Add and commit changes to gh-pages branch
|
||||
run: |
|
||||
git config --local user.email 'action@github.com'
|
||||
git config --local user.name 'GitHub Action'
|
||||
git add .
|
||||
|
||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
commit_message: 'ci(providers): check provider versions'
|
||||
create_branch: true
|
||||
branch: feature/providers-major
|
||||
41
.github/workflows/ci.yml
vendored
41
.github/workflows/ci.yml
vendored
@@ -12,6 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- test-unit
|
||||
- test-e2e
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -56,20 +57,15 @@ jobs:
|
||||
|
||||
- name: Unit Tests
|
||||
run: yarn test
|
||||
|
||||
############ UNIT TEST ############
|
||||
check-providers:
|
||||
name: Check Providers Versions
|
||||
test-e2e:
|
||||
name: e2e Tests
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
commit: ${{ steps.vars.outputs.commit }}
|
||||
needs:
|
||||
- test-unit
|
||||
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
|
||||
@@ -83,28 +79,5 @@ jobs:
|
||||
- name: Install NPM Dependencies
|
||||
run: yarn install --immutable --network-timeout 300000
|
||||
|
||||
- name: Check Baileys
|
||||
run: yarn node ./scripts/checker.js --name=baileys --stable=true
|
||||
|
||||
- name: Check Venom
|
||||
run: yarn node ./scripts/checker.js --name=venom --stable=true
|
||||
|
||||
- name: Check web-whatsapp
|
||||
run: yarn node ./scripts/checker.js --name=web-whatsapp --stable=true
|
||||
|
||||
- name: Check Meta
|
||||
run: yarn node ./scripts/checker.js --name=meta --stable=true
|
||||
|
||||
- name: Check Twilio
|
||||
run: yarn node ./scripts/checker.js --name=twilio --stable=true
|
||||
|
||||
- name: Set output
|
||||
id: vars
|
||||
run: echo "commit=$(git log --format=%B -n 1 ${{github.event.after}})" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit & Push changes
|
||||
uses: actions-js/push@master
|
||||
with:
|
||||
branch: feature/providers-major
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
force: true
|
||||
- name: e2e Tests
|
||||
run: yarn test.e2e
|
||||
|
||||
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
.gitignore
vendored
1
.gitignore
vendored
@@ -36,6 +36,7 @@ tmp/
|
||||
.fleet/
|
||||
example-app*/
|
||||
base-*/
|
||||
test-*.json
|
||||
!starters/apps/base-*/
|
||||
qr.svg
|
||||
package-lock.json
|
||||
|
||||
@@ -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"]
|
||||
}
|
||||
|
||||
68
CHANGELOG.md
68
CHANGELOG.md
@@ -2,6 +2,74 @@
|
||||
|
||||
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.21](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.20...v0.1.21) (2023-02-12)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **provider:** :bug: add_location ([a46a9ef](https://github.com/leifermendez/bot-whatsapp/commit/a46a9efd8dbd921c773395d331154dc9a8aae783))
|
||||
* **provider:** :rocket: feat Instance provider ([2278149](https://github.com/leifermendez/bot-whatsapp/commit/227814929561cedc11a1f69c8029515a7f47c9ff))
|
||||
* **provider:** :rocket: provider raw ([2d44a00](https://github.com/leifermendez/bot-whatsapp/commit/2d44a002ff226fb0eb7362ad49936f1e00b84242))
|
||||
* **provider:** :zap: add location provider ([c7de860](https://github.com/leifermendez/bot-whatsapp/commit/c7de860803fb362f5afe06cc38ad71b2c316d524))
|
||||
* **provider:** :zap: add location provider ([c0ece6f](https://github.com/leifermendez/bot-whatsapp/commit/c0ece6feb2b0325476880a604c32de341eb60544))
|
||||
* **provider:** :zap: support location <20> ([a147677](https://github.com/leifermendez/bot-whatsapp/commit/a147677a26b36bba429c3dd3c2c81a44a7a4d2b6))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* arreglando dir y varios mensajes en dialog flow essential ([01c7db8](https://github.com/leifermendez/bot-whatsapp/commit/01c7db8fe7dda2482eb0aa1fd7b3469b6ae8eae1))
|
||||
|
||||
### [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)
|
||||
|
||||
|
||||
|
||||
52
README.md
52
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">
|
||||
@@ -70,13 +69,20 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
</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"/>
|
||||
<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></tr>
|
||||
<tr>
|
||||
<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>
|
||||
<td align="center">
|
||||
<a href="https://github.com/HKong31">
|
||||
<img src="https://avatars.githubusercontent.com/u/113340082?v=4" width="50;" alt="HKong31"/>
|
||||
@@ -111,18 +117,11 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<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"/>
|
||||
<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>
|
||||
@@ -134,6 +133,13 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<sub><b>Jose Luis Ferrete Olarte</b></sub>
|
||||
</a>
|
||||
</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"/>
|
||||
@@ -141,28 +147,28 @@ 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/Jhonarias13">
|
||||
<img src="https://avatars.githubusercontent.com/u/19483021?v=4" width="50;" alt="Jhonarias13"/>
|
||||
<br />
|
||||
<sub><b>Jhon Freiman Arias</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"/>
|
||||
<br />
|
||||
<sub><b>Luis Antonio Vázquez García</b></sub>
|
||||
</a>
|
||||
</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></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rrruuuyyy">
|
||||
<img src="https://avatars.githubusercontent.com/u/33061671?v=4" width="50;" alt="rrruuuyyy"/>
|
||||
<br />
|
||||
<sub><b>Rodrigo Mendoza Cabrera</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/yond1994">
|
||||
<img src="https://avatars.githubusercontent.com/u/47557263?v=4" width="50;" alt="yond1994"/>
|
||||
|
||||
50
__mocks__/env.js
Normal file
50
__mocks__/env.js
Normal file
@@ -0,0 +1,50 @@
|
||||
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
|
||||
}
|
||||
|
||||
const cleaName = (name) => {
|
||||
name = name.toLowerCase()
|
||||
name = name.replaceAll(' ', '-')
|
||||
name = name.replaceAll(':', '-')
|
||||
name = name.replaceAll('"', '-')
|
||||
return name
|
||||
}
|
||||
|
||||
/**
|
||||
* Preparar env para el test
|
||||
* @param {*} context
|
||||
*/
|
||||
const setup = async (context) => {
|
||||
const name = cleaName(`${context.__suite__}-${context.__test__}`)
|
||||
const filename = `test-${name}.json`
|
||||
context.provider = new PROVIDER_DB()
|
||||
context.database = new MOCK_DB({ filename })
|
||||
context.flow = new MOCK_FLOW()
|
||||
await delay(10)
|
||||
}
|
||||
|
||||
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 }
|
||||
51
__test__/0.0.0-case.test.js
Normal file
51
__test__/0.0.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: 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(10)
|
||||
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(100)
|
||||
|
||||
assert.is(undefined, database.listHistory[0])
|
||||
assert.is(undefined, database.listHistory[1])
|
||||
})
|
||||
|
||||
suiteCase.run()
|
||||
31
__test__/0.0.1-case.test.js
Normal file
31
__test__/0.0.1-case.test.js
Normal file
@@ -0,0 +1,31 @@
|
||||
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: Provider envia un location')
|
||||
|
||||
suiteCase.before.each(setup)
|
||||
suiteCase.after.each(clear)
|
||||
|
||||
suiteCase(`Responder a "CURRENT_LOCATION"`, async ({ database, provider }) => {
|
||||
const flow = addKeyword('#CURRENT_LOCATION#').addAnswer('Gracias por tu location')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
provider,
|
||||
flow: createFlow([flow]),
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: '#CURRENT_LOCATION#',
|
||||
})
|
||||
|
||||
await delay(200)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is('Gracias por tu location', getHistory[0])
|
||||
assert.is(undefined, getHistory[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(100)
|
||||
|
||||
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(100)
|
||||
|
||||
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(30, 'message', {
|
||||
from: '000',
|
||||
body: 'test@test.com',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(60, 'message', {
|
||||
from: '000',
|
||||
body: 'paypal',
|
||||
})
|
||||
|
||||
await provider.delaySendMessage(90, 'message', {
|
||||
from: '000',
|
||||
body: 'continue!',
|
||||
})
|
||||
|
||||
await delay(800)
|
||||
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(100)
|
||||
|
||||
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(100)
|
||||
|
||||
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(100)
|
||||
|
||||
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(1500)
|
||||
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(100)
|
||||
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(100)
|
||||
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(100)
|
||||
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(900)
|
||||
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(100)
|
||||
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(900)
|
||||
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(1000)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
assert.is('¿Cual es tu nombre?', getHistory[1])
|
||||
assert.is('Leifer', getHistory[2])
|
||||
assert.is('Gracias por tu nombre!', getHistory[3])
|
||||
assert.is('¿Cual es tu edad?', getHistory[4])
|
||||
assert.is('90', getHistory[5])
|
||||
assert.is('Gracias por tu edad! Leifer', getHistory[6])
|
||||
assert.is('Tu datos son:', getHistory[7])
|
||||
assert.is('Nombre: Leifer Edad: 90', 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.21",
|
||||
"description": "Bot de wahtsapp open source para MVP o pequeños negocios",
|
||||
"main": "app.js",
|
||||
"private": true,
|
||||
@@ -25,7 +25,7 @@
|
||||
"copy.lib": "node ./scripts/move.js",
|
||||
"test.unit": "node ./node_modules/uvu/bin.js packages test",
|
||||
"test.e2e": "node ./node_modules/uvu/bin.js __test__",
|
||||
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit && npm run test.e2e",
|
||||
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit",
|
||||
"test": "npm run test.coverage",
|
||||
"cli": "node ./packages/cli/bin/cli.js",
|
||||
"create": "node ./packages/create-bot-whatsapp/bin/create.js",
|
||||
@@ -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({
|
||||
@@ -88,7 +84,23 @@ class CoreClass {
|
||||
from,
|
||||
prevRef: prevMsg.refSerialize,
|
||||
})
|
||||
this.databaseClass.save(ctxByNumber)
|
||||
await 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
|
||||
@@ -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) {
|
||||
@@ -111,11 +104,19 @@ class DialogFlowContext extends CoreClass {
|
||||
return
|
||||
}
|
||||
|
||||
const ctxFromDX = {
|
||||
/* const ctxFromDX = {
|
||||
answer: queryResult?.fulfillmentText,
|
||||
}
|
||||
} */
|
||||
|
||||
this.sendFlowSimple([ctxFromDX], from)
|
||||
const messagesFromCX = queryResult['fulfillmentMessages']
|
||||
.map((a) => {
|
||||
if (a.message === 'text') {
|
||||
return { answer: a.text.text[0] }
|
||||
}
|
||||
})
|
||||
.filter((e) => e)
|
||||
|
||||
this.sendFlowSimple(messagesFromCX, from)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,63 +1,79 @@
|
||||
const { join } = require('path')
|
||||
const { existsSync, writeFileSync, readFileSync } = require('fs')
|
||||
const { existsSync } = require('fs')
|
||||
const { writeFile, readFile } = require('fs').promises
|
||||
|
||||
class JsonFileAdapter {
|
||||
db
|
||||
pathFile
|
||||
listHistory = []
|
||||
options = { filename: 'db.json' }
|
||||
|
||||
constructor() {
|
||||
this.pathFile = join(process.cwd(), 'db.json')
|
||||
constructor(options = {}) {
|
||||
this.options = { ...this.options, ...options }
|
||||
this.pathFile = join(process.cwd(), this.options.filename)
|
||||
this.init().then()
|
||||
}
|
||||
|
||||
databaseExists() {
|
||||
return existsSync(this.pathFile)
|
||||
}
|
||||
|
||||
async init() {
|
||||
const dbExists = await this.databaseExists()
|
||||
|
||||
if (!dbExists) {
|
||||
const data = {
|
||||
history: [],
|
||||
}
|
||||
await this.saveData(data)
|
||||
/**
|
||||
* Revisamos si existe o no el json file
|
||||
* @returns
|
||||
*/
|
||||
init = async () => {
|
||||
if (existsSync(this.pathFile)) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
try {
|
||||
const parseData = JSON.stringify([], null, 2)
|
||||
return writeFile(this.pathFile, parseData, 'utf-8')
|
||||
} catch (e) {
|
||||
return Promise.reject(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
readDatabase() {
|
||||
const db = readFileSync(this.pathFile)
|
||||
return JSON.parse(db)
|
||||
validateJson = (raw) => {
|
||||
try {
|
||||
return JSON.parse(raw)
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
saveData(data) {
|
||||
writeFileSync(this.pathFile, JSON.stringify(data, null, 2))
|
||||
/**
|
||||
* Leer archivo y parsear
|
||||
* @returns
|
||||
*/
|
||||
readFileAndParse = async () => {
|
||||
const data = await readFile(this.pathFile, 'utf-8')
|
||||
const parseData = this.validateJson(data)
|
||||
return parseData
|
||||
}
|
||||
|
||||
/**
|
||||
* Buscamos el ultimo mensaje por numero
|
||||
* @param {*} from
|
||||
* @returns
|
||||
*/
|
||||
getPrevByNumber = async (from) => {
|
||||
const { history } = await this.readDatabase()
|
||||
|
||||
const history = await this.readFileAndParse()
|
||||
if (!history.length) {
|
||||
return null
|
||||
return []
|
||||
}
|
||||
|
||||
const result = history.filter((res) => res.from === from).pop()
|
||||
|
||||
return {
|
||||
...result,
|
||||
}
|
||||
const result = history
|
||||
.slice()
|
||||
.reverse()
|
||||
.filter((i) => !!i.keyword)
|
||||
return result.find((a) => a.from === from)
|
||||
}
|
||||
|
||||
/**
|
||||
* Guardar dato
|
||||
* @param {*} ctx
|
||||
*/
|
||||
save = async (ctx) => {
|
||||
this.db = await this.readDatabase()
|
||||
|
||||
this.db.history.push(ctx)
|
||||
|
||||
await this.saveData(this.db)
|
||||
|
||||
this.listHistory.push(ctx)
|
||||
console.log('Guardado en DB...', ctx)
|
||||
const parseData = JSON.stringify(this.listHistory, null, 2)
|
||||
await writeFile(this.pathFile, parseData, 'utf-8')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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?'])
|
||||
@@ -29,13 +24,16 @@ const flowPrincipal = addKeyword(['hola', 'alo'])
|
||||
Es importante que el número **vaya acompañado de su prefijo**, en el caso de España "34".
|
||||
|
||||
```js
|
||||
createBot({
|
||||
createBot(
|
||||
{
|
||||
flow: adapterFlow,
|
||||
provider: adapterProvider,
|
||||
database: adapterDB,
|
||||
},{
|
||||
blackList:['34XXXXXXXXX','34XXXXXXXXX','34XXXXXXXXX','34XXXXXXXXX']
|
||||
})
|
||||
},
|
||||
{
|
||||
blackList: ['34XXXXXXXXX', '34XXXXXXXXX', '34XXXXXXXXX', '34XXXXXXXXX'],
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -78,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,
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
@@ -120,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)
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
@@ -140,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()
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
@@ -175,65 +149,83 @@ 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!','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')
|
||||
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')
|
||||
return endFlow({body: '❌ Su solicitud ha sido cancelada ❌',
|
||||
buttons:[{body:'⬅️ Volver al Inicio' }]
|
||||
|
||||
|
||||
.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()
|
||||
}
|
||||
})
|
||||
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')
|
||||
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}*`)
|
||||
}
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# QRPortalWeb
|
||||
|
||||
Argumento para asignar nombre y puerto al BOT
|
||||
|
||||
```js
|
||||
QRPortalWeb({name:BOTNAME, port:3005 });
|
||||
|
||||
const BOTNAME = 'bot'
|
||||
QRPortalWeb({ name: BOTNAME, port: 3005 })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
<Navigation
|
||||
pages={[
|
||||
{ name: 'Conceptos', link: '/docs/essential' },
|
||||
|
||||
@@ -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',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user