mirror of
https://github.com/cheveguerra/bot-whatsapp.git
synced 2026-04-17 19:26:23 +00:00
Merge branch 'contributors-readme-action-2AwvRwXk2x'
This commit is contained in:
10
.github/workflows/check-provider-major.yml
vendored
10
.github/workflows/check-provider-major.yml
vendored
@@ -39,8 +39,14 @@ jobs:
|
||||
- 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 BREAKING CHANGE'
|
||||
commit_message: 'ci(providers): check provider versions'
|
||||
create_branch: true
|
||||
branch: feature/breaking-change
|
||||
branch: feature/providers-major
|
||||
|
||||
52
.github/workflows/ci.yml
vendored
52
.github/workflows/ci.yml
vendored
@@ -56,3 +56,55 @@ jobs:
|
||||
|
||||
- name: Unit Tests
|
||||
run: yarn test
|
||||
|
||||
############ UNIT TEST ############
|
||||
check-providers:
|
||||
name: Check Providers Versions
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
commit: ${{ steps.vars.outputs.commit }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{github.event.after}}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
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: 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
|
||||
|
||||
15
.github/workflows/releases-dev.yml
vendored
15
.github/workflows/releases-dev.yml
vendored
@@ -10,9 +10,15 @@ jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
commit: ${{ steps.vars.outputs.commit }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{github.event.after}}
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
@@ -50,8 +56,9 @@ jobs:
|
||||
- name: Release @bot-whatsapp/portal
|
||||
run: yarn node ./scripts/release.js --name=portal --version= --token="${{ secrets.NPM_TOKEN }}"
|
||||
|
||||
- name: Commit Versioning & Push changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
- name: Commit & Push changes
|
||||
uses: actions-js/push@master
|
||||
with:
|
||||
commit_message: 'ci(version): :zap: automatic - "${date}" updated versions every packages'
|
||||
branch: dev
|
||||
branch: release/next
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
force: true
|
||||
|
||||
14
.github/workflows/releases.yml
vendored
14
.github/workflows/releases.yml
vendored
@@ -67,13 +67,9 @@ jobs:
|
||||
- name: Release Github
|
||||
run: yarn node ./scripts/github.js --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.OCTO_TOKEN }}"
|
||||
|
||||
- name: 'Run if changes have been detected'
|
||||
run: |
|
||||
git add .
|
||||
git commit -m "chore(version): pre release"
|
||||
|
||||
- name: Commit Versioning & Push changes
|
||||
if: github.event_name == 'push'
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
- name: Commit & Push changes
|
||||
uses: actions-js/push@master
|
||||
with:
|
||||
commit_message: 'chore(version): launch release 🚀 "${{ steps.package-version.outputs.current-version}}"'
|
||||
branch: release/production
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
force: true
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
/node_modules
|
||||
/packages/repl
|
||||
/packages/*/starters
|
||||
/packages/*/node_modules
|
||||
/packages/*/dist
|
||||
|
||||
85
CHANGELOG.md
85
CHANGELOG.md
@@ -2,6 +2,91 @@
|
||||
|
||||
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.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)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **bot:** :zap: add blacklist ([7078dc4](https://github.com/leifermendez/bot-whatsapp/commit/7078dc4c93d01bf90ef08ecb34e89a1abbe16fd2))
|
||||
* **bot:** :zap: flowDynamic buttons, media ([3c4b1c0](https://github.com/leifermendez/bot-whatsapp/commit/3c4b1c0fc4b6d98d67c67806d918d3604bb2209b))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bot:** :bug: body undefined ([bb6ed4a](https://github.com/leifermendez/bot-whatsapp/commit/bb6ed4a084ae98070dfdf0c4ba1eca574c4092cc))
|
||||
* **bot:** :bug: body undefined ([9234cf1](https://github.com/leifermendez/bot-whatsapp/commit/9234cf1c5d00abdd35e62a826b3c450ab056987a))
|
||||
* **bot:** :bug: body undefined ([a118bbb](https://github.com/leifermendez/bot-whatsapp/commit/a118bbbf7f0a7023cb7f33c23f37db72adad151f))
|
||||
* **bot:** :bug: body undefined ([f54dea5](https://github.com/leifermendez/bot-whatsapp/commit/f54dea52b01063acd6122eeba1fbbe324aa7805d))
|
||||
* **bot:** :bug: body undefined ([72e0a91](https://github.com/leifermendez/bot-whatsapp/commit/72e0a910503e9643db7dfbc6e09c41c96934e1f7))
|
||||
* **bot:** :bug: body undefined ([70dd4d7](https://github.com/leifermendez/bot-whatsapp/commit/70dd4d73e814fc5636d19a887f3621c483b837c1))
|
||||
* **bot:** :bug: body undefined ([ecf0eef](https://github.com/leifermendez/bot-whatsapp/commit/ecf0eef928917d76c59bd23886cb7a4108b421f1))
|
||||
* **bot:** :bug: flowDynamic stranger behaviour ([877252b](https://github.com/leifermendez/bot-whatsapp/commit/877252bd4a8a7bbbbf083c3ceaeaeb952b0a1828))
|
||||
* **bot:** :bug: flowDynamic stranger behaviour ([f5a7de3](https://github.com/leifermendez/bot-whatsapp/commit/f5a7de3a003c012e2164e51fff26892cfc3144be))
|
||||
* **bot:** :memo: more docs ([98793d0](https://github.com/leifermendez/bot-whatsapp/commit/98793d0cfc1674830beaa3707f933c5a791eec14))
|
||||
* **cli:** :zap: refactor ([a29b9d4](https://github.com/leifermendez/bot-whatsapp/commit/a29b9d4e1f85fc163cf1d633c0857f0c8b7f03e1))
|
||||
* **cli:** :zap: refactor ([18ef4e9](https://github.com/leifermendez/bot-whatsapp/commit/18ef4e9d726575ca390ca24354825860328d3347))
|
||||
* **cli:** :zap: refactor ([3648757](https://github.com/leifermendez/bot-whatsapp/commit/3648757fa083bdb88a16bf6c2e90c828c233bdb1))
|
||||
* **cli:** :zap: refactor ([32f6a70](https://github.com/leifermendez/bot-whatsapp/commit/32f6a70f8f6fb26d8ea2a0f1a4aec4827b9d6a93))
|
||||
* **cli:** :zap: refactor ([8c825e7](https://github.com/leifermendez/bot-whatsapp/commit/8c825e7f6b7133f7cc7f3041ce331b80a9fe60e0))
|
||||
* **cli:** :zap: refactor ([0c0f437](https://github.com/leifermendez/bot-whatsapp/commit/0c0f4375b84549bee809340a85f9ce038ee2739e))
|
||||
* **cli:** :zap: refactor ([039ce5d](https://github.com/leifermendez/bot-whatsapp/commit/039ce5dd7cac8115b335ad5de05f7bd871e24140))
|
||||
* **cli:** :zap: refactor ([5e87918](https://github.com/leifermendez/bot-whatsapp/commit/5e879188b8bf9d486399b308a9a9c2612607d465))
|
||||
* **cli:** :zap: refactor ([21a7270](https://github.com/leifermendez/bot-whatsapp/commit/21a72702817bc6b344223b34ca4513a7ff45fc93))
|
||||
* **cli:** :zap: refactor ([82a99b2](https://github.com/leifermendez/bot-whatsapp/commit/82a99b2c80e6738566042ea738bbab8208a17758))
|
||||
* **cli:** :zap: refactor ([cc19974](https://github.com/leifermendez/bot-whatsapp/commit/cc19974579379777b05cb69c38cec0fce6740471))
|
||||
* **cli:** :zap: refactor ([56fcb8f](https://github.com/leifermendez/bot-whatsapp/commit/56fcb8fb72169bc21fce7c4fcdceccf2acd39c73))
|
||||
* **cli:** :zap: refactor ([f36cff1](https://github.com/leifermendez/bot-whatsapp/commit/f36cff1eefdd96be4ab531e1cb2d3b630b1a81c3))
|
||||
* **cli:** :zap: refactor ([b393c11](https://github.com/leifermendez/bot-whatsapp/commit/b393c11af6c0ebccb0a690be8b90b9df8877dad1))
|
||||
* **cli:** :zap: refactor ([6683715](https://github.com/leifermendez/bot-whatsapp/commit/6683715ad617ea1075654a475a1c62ea607c733f))
|
||||
* **contexts:** :bug: fixed [#524](https://github.com/leifermendez/bot-whatsapp/issues/524) issue ([79cc31a](https://github.com/leifermendez/bot-whatsapp/commit/79cc31a96f6a9836447cc4e6bb1e1521c54183fe))
|
||||
* **contexts:** :bug: fixed [#524](https://github.com/leifermendez/bot-whatsapp/issues/524) issue ([7067b4a](https://github.com/leifermendez/bot-whatsapp/commit/7067b4a80b7938ccfaf1ed141a37d645a1a3a062))
|
||||
* **provider:** wwebjs upgrade ([345f256](https://github.com/leifermendez/bot-whatsapp/commit/345f256a1b4a238519dafc15c9a31bc5e6bad4fe))
|
||||
* se agrego @bot-whatsapp/portal a package.json ([46a9fa6](https://github.com/leifermendez/bot-whatsapp/commit/46a9fa6793e06600335de998d2bd9d0691b02ca4))
|
||||
|
||||
### [0.1.17](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.16...v0.1.17) (2023-01-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* mod de starters para habiltar portal ([eceb170](https://github.com/leifermendez/bot-whatsapp/commit/eceb170df03721dca4183b658c863b94fa04bc84))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ci:** pre-release ([aaec075](https://github.com/leifermendez/bot-whatsapp/commit/aaec0751408ab49483d428810d94aaf7d46acb94))
|
||||
* correccion en starters app.js para portal QR ([f430380](https://github.com/leifermendez/bot-whatsapp/commit/f430380b4f23d41702395c96c628bf13bf443278))
|
||||
* **starters:** :zap: added dockerfile ([230981e](https://github.com/leifermendez/bot-whatsapp/commit/230981e2676361149cb2a99def7f705e75009260))
|
||||
|
||||
### [0.1.16](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.15...v0.1.16) (2023-01-11)
|
||||
|
||||
### [0.1.15](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.14...v0.1.15) (2023-01-11)
|
||||
|
||||
78
README.md
78
README.md
@@ -34,6 +34,13 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<!-- readme: collaborators,contributors -start -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/cheveguerra">
|
||||
<img src="https://avatars.githubusercontent.com/u/5891114?v=4" width="50;" alt="cheveguerra"/>
|
||||
<br />
|
||||
<sub><b>Jose Alberto Guerra Ugalde</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/leifermendez">
|
||||
<img src="https://avatars.githubusercontent.com/u/15802366?v=4" width="50;" alt="leifermendez"/>
|
||||
@@ -62,6 +69,21 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<sub><b>Leifer Mendez</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/danielcasta0398">
|
||||
<img src="https://avatars.githubusercontent.com/u/98791147?v=4" width="50;" alt="danielcasta0398"/>
|
||||
<br />
|
||||
<sub><b>Juan Daniel Castaño</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"/>
|
||||
@@ -75,8 +97,14 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<br />
|
||||
<sub><b>Zvi</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/JosephVTX">
|
||||
<img src="https://avatars.githubusercontent.com/u/91026290?v=4" width="50;" alt="JosephVTX"/>
|
||||
<br />
|
||||
<sub><b>Joseph Vega Callupe</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Gonzalito87">
|
||||
<img src="https://avatars.githubusercontent.com/u/100331586?v=4" width="50;" alt="Gonzalito87"/>
|
||||
@@ -84,6 +112,42 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/devrlbusiness">
|
||||
<img src="https://avatars.githubusercontent.com/u/66280283?v=4" width="50;" alt="devrlbusiness"/>
|
||||
<br />
|
||||
<sub><b>Developer RL Business</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Gregoriotecnico">
|
||||
<img src="https://avatars.githubusercontent.com/u/118696506?v=4" width="50;" alt="Gregoriotecnico"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jlferrete">
|
||||
<img src="https://avatars.githubusercontent.com/u/36698913?v=4" width="50;" alt="jlferrete"/>
|
||||
<br />
|
||||
<sub><b>Jose Luis Ferrete Olarte</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<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"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/tonyvazgar">
|
||||
<img src="https://avatars.githubusercontent.com/u/21047090?v=4" width="50;" alt="tonyvazgar"/>
|
||||
@@ -91,20 +155,14 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
|
||||
<sub><b>Luis Antonio Vázquez García</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ulisesvina">
|
||||
<img src="https://avatars.githubusercontent.com/u/20508563?v=4" width="50;" alt="ulisesvina"/>
|
||||
<br />
|
||||
<sub><b>Ulises Viña</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<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>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/yond1994">
|
||||
<img src="https://avatars.githubusercontent.com/u/47557263?v=4" width="50;" alt="yond1994"/>
|
||||
|
||||
41
__test__/01-case.test.js
Normal file
41
__test__/01-case.test.js
Normal file
@@ -0,0 +1,41 @@
|
||||
const { test } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const MOCK_DB = require('../packages/database/src/mock')
|
||||
const PROVIDER_DB = require('../packages/provider/src/mock')
|
||||
const {
|
||||
addKeyword,
|
||||
createBot,
|
||||
createFlow,
|
||||
createProvider,
|
||||
} = require('../packages/bot')
|
||||
|
||||
test(`[Caso - 01] Flow Basico`, async () => {
|
||||
const [VALUE_A, VALUE_B] = ['hola', 'buenas']
|
||||
|
||||
const flow = addKeyword(VALUE_A).addAnswer(VALUE_B)
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
const database = new MOCK_DB()
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flow]),
|
||||
provider,
|
||||
})
|
||||
|
||||
provider.delaySendMessage(100, 'message', {
|
||||
from: '000',
|
||||
body: VALUE_A,
|
||||
})
|
||||
|
||||
await delay(100)
|
||||
|
||||
const prevMsg = database.getPrevByNumber('000')
|
||||
|
||||
assert.is(prevMsg.answer, VALUE_B)
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
99
__test__/02-case.test.js
Normal file
99
__test__/02-case.test.js
Normal file
@@ -0,0 +1,99 @@
|
||||
const { test } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const MOCK_DB = require('../packages/database/src/mock')
|
||||
const PROVIDER_DB = require('../packages/provider/src/mock')
|
||||
const {
|
||||
addKeyword,
|
||||
createBot,
|
||||
createFlow,
|
||||
createProvider,
|
||||
} = require('../packages/bot/index')
|
||||
|
||||
/**
|
||||
* Falsear peticion async
|
||||
* @param {*} fakeData
|
||||
* @returns
|
||||
*/
|
||||
const fakeHTTP = async (fakeData = []) => {
|
||||
console.log('⚡ Server request!')
|
||||
await delay(50)
|
||||
console.log('⚡ Server return!')
|
||||
const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` }))
|
||||
console.log(data)
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
|
||||
test(`[Caso - 02] Flow (flowDynamic)`, async () => {
|
||||
const MOCK_VALUES = [
|
||||
'Bienvenido te envio muchas marcas (5510)',
|
||||
'Seleccione marca del auto a cotizar, con el *número* correspondiente',
|
||||
'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:',
|
||||
'Los precios rondan:',
|
||||
]
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
const database = new MOCK_DB()
|
||||
|
||||
const flujoPrincipal = addKeyword(['hola'])
|
||||
.addAnswer(MOCK_VALUES[0], null, async (ctx, { flowDynamic }) => {
|
||||
console.log('execute...')
|
||||
const data = await fakeHTTP(['Ford', 'GM', 'BMW'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[1], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Ranger', 'Explorer'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[2], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Usado', 'Nuevos'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[3], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['1000', '2000', '3000'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flujoPrincipal]),
|
||||
provider,
|
||||
})
|
||||
|
||||
provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(1200)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 Ford', getHistory[1])
|
||||
assert.is('2 GM', getHistory[2])
|
||||
assert.is('3 BMW', getHistory[3])
|
||||
|
||||
assert.is(MOCK_VALUES[1], getHistory[4])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 Ranger', getHistory[5])
|
||||
assert.is('2 Explorer', getHistory[6])
|
||||
|
||||
assert.is(MOCK_VALUES[2], getHistory[7])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 Usado', getHistory[8])
|
||||
assert.is('2 Nuevos', getHistory[9])
|
||||
|
||||
assert.is(MOCK_VALUES[3], getHistory[10])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 1000', getHistory[11])
|
||||
assert.is('2 2000', getHistory[12])
|
||||
assert.is('3 3000', getHistory[13])
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
44
__test__/03-case.test.js
Normal file
44
__test__/03-case.test.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const { test } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const MOCK_DB = require('../packages/database/src/mock')
|
||||
const PROVIDER_DB = require('../packages/provider/src/mock')
|
||||
const {
|
||||
addKeyword,
|
||||
createBot,
|
||||
createFlow,
|
||||
createProvider,
|
||||
} = require('../packages/bot/index')
|
||||
|
||||
test(`[Caso - 03] Flow puro`, async () => {
|
||||
const MOCK_VALUES = ['Bienvenido a mi tienda', 'Como estas?']
|
||||
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
const database = new MOCK_DB()
|
||||
|
||||
const flujoPrincipal = addKeyword(['hola'])
|
||||
.addAnswer(MOCK_VALUES[0])
|
||||
.addAnswer(MOCK_VALUES[1])
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flujoPrincipal]),
|
||||
provider,
|
||||
})
|
||||
|
||||
provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(10)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
assert.is(MOCK_VALUES[1], getHistory[1])
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
82
__test__/04-case.test.js
Normal file
82
__test__/04-case.test.js
Normal file
@@ -0,0 +1,82 @@
|
||||
const { test } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const MOCK_DB = require('../packages/database/src/mock')
|
||||
const PROVIDER_DB = require('../packages/provider/src/mock')
|
||||
const {
|
||||
addKeyword,
|
||||
createBot,
|
||||
createFlow,
|
||||
createProvider,
|
||||
} = require('../packages/bot/index')
|
||||
|
||||
/**
|
||||
* Falsear peticion async
|
||||
* @param {*} fakeData
|
||||
* @returns
|
||||
*/
|
||||
const fakeHTTP = async (fakeData = []) => {
|
||||
console.log('⚡ Server request!')
|
||||
await delay(50)
|
||||
console.log('⚡ Server return!')
|
||||
const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` }))
|
||||
console.log(data)
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
|
||||
test(`[Caso - 04] Romper flujo (endFlow)`, async () => {
|
||||
const MOCK_VALUES = [
|
||||
'Bienvenido te envio muchas marcas (5510)',
|
||||
'Seleccione marca del auto a cotizar, con el *número* correspondiente',
|
||||
'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:',
|
||||
'Los precios rondan:',
|
||||
]
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
const database = new MOCK_DB()
|
||||
|
||||
const flujoPrincipal = addKeyword(['hola'])
|
||||
.addAnswer(MOCK_VALUES[0], null, async (ctx, { flowDynamic }) => {
|
||||
console.log('execute...')
|
||||
const data = await fakeHTTP(['Ford', 'GM', 'BMW'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[1], null, async (ctx, { endFlow }) => {
|
||||
return endFlow()
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[2], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['Usado', 'Nuevos'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
.addAnswer(MOCK_VALUES[3], null, async (ctx, { flowDynamic }) => {
|
||||
const data = await fakeHTTP(['1000', '2000', '3000'])
|
||||
return flowDynamic(data)
|
||||
})
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flujoPrincipal]),
|
||||
provider,
|
||||
})
|
||||
|
||||
provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
await delay(1200)
|
||||
const getHistory = database.listHistory.map((i) => i.answer)
|
||||
assert.is(MOCK_VALUES[0], getHistory[0])
|
||||
|
||||
//FlowDynamic
|
||||
assert.is('1 Ford', getHistory[1])
|
||||
assert.is('2 GM', getHistory[2])
|
||||
assert.is('3 BMW', getHistory[3])
|
||||
|
||||
assert.is(MOCK_VALUES[1], getHistory[4])
|
||||
assert.is(undefined, getHistory[5])
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
118
__test__/05-case.test.js
Normal file
118
__test__/05-case.test.js
Normal file
@@ -0,0 +1,118 @@
|
||||
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 = []) => {
|
||||
await delay(5)
|
||||
const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` }))
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
|
||||
test(`[Caso - 05] Continuar Flujo (continueFlow)`, async () => {
|
||||
const MOCK_VALUES = [
|
||||
'¿CUal es tu email?',
|
||||
'Continuamos....',
|
||||
'¿Cual es tu edad?',
|
||||
]
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
const database = new MOCK_DB()
|
||||
|
||||
const flujoPrincipal = 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(validation)
|
||||
}
|
||||
)
|
||||
.addAnswer(MOCK_VALUES[1])
|
||||
.addAnswer(
|
||||
MOCK_VALUES[2],
|
||||
{ capture: true },
|
||||
async (ctx, { flowDynamic, fallBack }) => {
|
||||
if (ctx.body !== '18') {
|
||||
await delay(50)
|
||||
return fallBack(false, 'Ups creo que no eres mayor de edad')
|
||||
}
|
||||
return flowDynamic('Bien tu edad es correcta!')
|
||||
}
|
||||
)
|
||||
.addAnswer('Puedes pasar')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flujoPrincipal]),
|
||||
provider,
|
||||
})
|
||||
|
||||
provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
provider.delaySendMessage(10, 'message', {
|
||||
from: '000',
|
||||
body: 'this is not email value',
|
||||
})
|
||||
|
||||
provider.delaySendMessage(20, 'message', {
|
||||
from: '000',
|
||||
body: 'test@test.com',
|
||||
})
|
||||
|
||||
provider.delaySendMessage(90, 'message', {
|
||||
from: '000',
|
||||
body: '20',
|
||||
})
|
||||
|
||||
provider.delaySendMessage(200, 'message', {
|
||||
from: '000',
|
||||
body: '18',
|
||||
})
|
||||
|
||||
await delay(1200)
|
||||
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(
|
||||
'1 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])
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
111
__test__/06-case.test.js
Normal file
111
__test__/06-case.test.js
Normal file
@@ -0,0 +1,111 @@
|
||||
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 = []) => {
|
||||
await delay(5)
|
||||
const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` }))
|
||||
return Promise.resolve(data)
|
||||
}
|
||||
|
||||
test(`[Caso - 06] Finalizar Flujo (endFlow)`, async () => {
|
||||
const MOCK_VALUES = [
|
||||
'¿CUal es tu email?',
|
||||
'Continuamos....',
|
||||
'¿Cual es tu edad?',
|
||||
]
|
||||
const provider = createProvider(PROVIDER_DB)
|
||||
const database = new MOCK_DB()
|
||||
|
||||
const flujoPrincipal = 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(validation)
|
||||
}
|
||||
)
|
||||
.addAnswer(MOCK_VALUES[1], null, async (_, { endFlow }) => {
|
||||
return endFlow()
|
||||
})
|
||||
.addAnswer(
|
||||
MOCK_VALUES[2],
|
||||
{ capture: true },
|
||||
async (ctx, { flowDynamic, fallBack }) => {
|
||||
if (ctx.body !== '18') {
|
||||
await delay(50)
|
||||
return fallBack(false, 'Ups creo que no eres mayor de edad')
|
||||
}
|
||||
return flowDynamic('Bien tu edad es correcta!')
|
||||
}
|
||||
)
|
||||
.addAnswer('Puedes pasar')
|
||||
|
||||
createBot({
|
||||
database,
|
||||
flow: createFlow([flujoPrincipal]),
|
||||
provider,
|
||||
})
|
||||
|
||||
provider.delaySendMessage(0, 'message', {
|
||||
from: '000',
|
||||
body: 'hola',
|
||||
})
|
||||
|
||||
provider.delaySendMessage(10, 'message', {
|
||||
from: '000',
|
||||
body: 'this is not email value',
|
||||
})
|
||||
|
||||
provider.delaySendMessage(20, 'message', {
|
||||
from: '000',
|
||||
body: 'test@test.com',
|
||||
})
|
||||
|
||||
provider.delaySendMessage(90, 'message', {
|
||||
from: '000',
|
||||
body: '20',
|
||||
})
|
||||
|
||||
await delay(1200)
|
||||
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(
|
||||
'1 Gracias por tu email se ha validado de manera correcta',
|
||||
getHistory[4]
|
||||
)
|
||||
assert.is(MOCK_VALUES[1], getHistory[5])
|
||||
assert.is('20', getHistory[6])
|
||||
assert.is(undefined, getHistory[7])
|
||||
})
|
||||
|
||||
test.run()
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((res) => setTimeout(res, ms))
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/root",
|
||||
"version": "0.1.16",
|
||||
"version": "0.1.19",
|
||||
"description": "Bot de wahtsapp open source para MVP o pequeños negocios",
|
||||
"main": "app.js",
|
||||
"private": true,
|
||||
@@ -24,7 +24,8 @@
|
||||
"build": "yarn run cli:rollup && yarn run bot:rollup && yarn run provider:rollup && yarn run database:rollup && yarn run contexts:rollup && yarn run create-bot-whatsapp:rollup && yarn run portal:rollup",
|
||||
"copy.lib": "node ./scripts/move.js",
|
||||
"test.unit": "node ./node_modules/uvu/bin.js packages test",
|
||||
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit",
|
||||
"test.e2e": "node ./node_modules/uvu/bin.js __test__",
|
||||
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit && npm run test.e2e",
|
||||
"test": "npm run test.coverage",
|
||||
"cli": "node ./packages/cli/bin/cli.js",
|
||||
"create": "node ./packages/create-bot-whatsapp/bin/create.js",
|
||||
@@ -80,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",
|
||||
|
||||
@@ -21,10 +21,12 @@ class CoreClass {
|
||||
flowClass
|
||||
databaseClass
|
||||
providerClass
|
||||
constructor(_flow, _database, _provider) {
|
||||
generalArgs = { blackList: [] }
|
||||
constructor(_flow, _database, _provider, _args) {
|
||||
this.flowClass = _flow
|
||||
this.databaseClass = _database
|
||||
this.providerClass = _provider
|
||||
this.generalArgs = { ...this.generalArgs, ..._args }
|
||||
|
||||
for (const { event, func } of this.listenerBusEvents()) {
|
||||
this.providerClass.on(event, func)
|
||||
@@ -69,11 +71,13 @@ class CoreClass {
|
||||
logger.log(`[handleMsg]: `, messageCtxInComming)
|
||||
const { body, from } = messageCtxInComming
|
||||
let msgToSend = []
|
||||
let endFlowFlag = false
|
||||
let fallBackFlag = false
|
||||
|
||||
if (this.generalArgs.blackList.includes(from)) return
|
||||
if (!body) return
|
||||
if (!body.length) return
|
||||
|
||||
const prevMsg = await this.databaseClass.getPrevByNumber(from)
|
||||
let prevMsg = await this.databaseClass.getPrevByNumber(from)
|
||||
const refToContinue = this.flowClass.findBySerialize(
|
||||
prevMsg?.refSerialize
|
||||
)
|
||||
@@ -87,16 +91,68 @@ class CoreClass {
|
||||
this.databaseClass.save(ctxByNumber)
|
||||
}
|
||||
|
||||
// 📄 Crar CTX de mensaje (uso private)
|
||||
const createCtxMessage = (payload = {}, index = 0) => {
|
||||
const body =
|
||||
typeof payload === 'string'
|
||||
? payload
|
||||
: payload?.body ?? payload?.answer
|
||||
const media = payload?.media ?? null
|
||||
const buttons = payload?.buttons ?? []
|
||||
const capture = payload?.capture ?? false
|
||||
|
||||
return toCtx({
|
||||
body,
|
||||
from,
|
||||
keyword: null,
|
||||
index,
|
||||
options: { media, buttons, capture },
|
||||
})
|
||||
}
|
||||
|
||||
// 📄 Limpiar cola de procesos
|
||||
const clearQueue = () => {
|
||||
QueuePrincipal.pendingPromise = false
|
||||
QueuePrincipal.queue = []
|
||||
}
|
||||
|
||||
// 📄 Finalizar flujo
|
||||
const endFlow = async (message = null) => {
|
||||
prevMsg = null
|
||||
endFlowFlag = true
|
||||
if (message)
|
||||
this.sendProviderAndSave(from, createCtxMessage(message))
|
||||
clearQueue()
|
||||
return
|
||||
}
|
||||
|
||||
// 📄 Continuar con el siguiente flujo
|
||||
const continueFlow = async () => {
|
||||
const cotinueMessage =
|
||||
this.flowClass.find(refToContinue?.ref, true) || []
|
||||
sendFlow(cotinueMessage, from, { continue: true })
|
||||
return
|
||||
}
|
||||
|
||||
// 📄 Esta funcion se encarga de enviar un array de mensajes dentro de este ctx
|
||||
const sendFlow = async (messageToSend, numberOrId) => {
|
||||
const sendFlow = async (
|
||||
messageToSend,
|
||||
numberOrId,
|
||||
options = { continue: false }
|
||||
) => {
|
||||
if (!options.continue && prevMsg?.options?.capture)
|
||||
await cbEveryCtx(prevMsg?.ref)
|
||||
|
||||
const queue = []
|
||||
for (const ctxMessage of messageToSend) {
|
||||
if (endFlowFlag) return
|
||||
const delayMs = ctxMessage?.options?.delay || 0
|
||||
if (delayMs) await delay(delayMs)
|
||||
QueuePrincipal.enqueue(() =>
|
||||
Promise.all([
|
||||
this.sendProviderAndSave(numberOrId, ctxMessage),
|
||||
resolveCbEveryCtx(ctxMessage),
|
||||
this.sendProviderAndSave(numberOrId, ctxMessage).then(
|
||||
() => resolveCbEveryCtx(ctxMessage)
|
||||
),
|
||||
])
|
||||
)
|
||||
}
|
||||
@@ -104,42 +160,41 @@ class CoreClass {
|
||||
}
|
||||
|
||||
// 📄 [options: fallBack]: esta funcion se encarga de repetir el ultimo mensaje
|
||||
const fallBack = async () => {
|
||||
fallBackFlag = true
|
||||
await this.sendProviderAndSave(from, refToContinue)
|
||||
const fallBack = async (next = false, message = null) => {
|
||||
QueuePrincipal.queue = []
|
||||
return refToContinue
|
||||
if (next) return continueFlow()
|
||||
return this.sendProviderAndSave(from, {
|
||||
...prevMsg,
|
||||
answer:
|
||||
typeof message === 'string'
|
||||
? message
|
||||
: message?.body ?? prevMsg.answer,
|
||||
options: {
|
||||
...prevMsg.options,
|
||||
buttons: message?.buttons ?? prevMsg.options?.buttons,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 📄 [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')
|
||||
|
||||
fallBackFlag = optListMsg.fallback
|
||||
const parseListMsg = listMsg
|
||||
.map(({ body }, index) =>
|
||||
toCtx({
|
||||
body,
|
||||
from,
|
||||
keyword: null,
|
||||
index,
|
||||
})
|
||||
const flowDynamic = async (listMsg = []) => {
|
||||
if (!Array.isArray(listMsg)) listMsg = [listMsg]
|
||||
|
||||
const parseListMsg = listMsg.map((opt, index) =>
|
||||
createCtxMessage(opt, index)
|
||||
)
|
||||
.slice(0, optListMsg.limit)
|
||||
|
||||
if (endFlowFlag) return
|
||||
for (const msg of parseListMsg) {
|
||||
await this.sendProviderAndSave(from, msg)
|
||||
}
|
||||
return
|
||||
return continueFlow()
|
||||
}
|
||||
|
||||
// 📄 Se encarga de revisar si el contexto del mensaje tiene callback o fallback
|
||||
const resolveCbEveryCtx = async (ctxMessage) => {
|
||||
if (prevMsg?.options?.capture) return cbEveryCtx(prevMsg?.ref)
|
||||
if (!ctxMessage?.options?.capture)
|
||||
return await cbEveryCtx(ctxMessage?.ref)
|
||||
}
|
||||
@@ -150,22 +205,13 @@ class CoreClass {
|
||||
return this.flowClass.allCallbacks[inRef](messageCtxInComming, {
|
||||
fallBack,
|
||||
flowDynamic,
|
||||
endFlow,
|
||||
continueFlow,
|
||||
})
|
||||
}
|
||||
|
||||
if (prevMsg?.ref) resolveCbEveryCtx(prevMsg)
|
||||
|
||||
// 📄 [options: callback]: Si se tiene un callback se ejecuta
|
||||
//TODO AQUI
|
||||
// if (!fallBackFlag) {
|
||||
// if (prevMsg?.options?.capture) cbEveryCtx(prevMsg?.ref)
|
||||
// for (const ite of this.flowClass.find(body)) {
|
||||
// if (!ite?.options?.capture) cbEveryCtx(ite?.ref)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 📄🤘(tiene return) [options: nested(array)]: Si se tiene flujos hijos los implementa
|
||||
if (!fallBackFlag && prevMsg?.options?.nested?.length) {
|
||||
if (!endFlowFlag && prevMsg?.options?.nested?.length) {
|
||||
const nestedRef = prevMsg.options.nested
|
||||
const flowStandalone = nestedRef.map((f) => ({
|
||||
...nestedRef.find((r) => r.refSerialize === f.refSerialize),
|
||||
@@ -173,21 +219,15 @@ class CoreClass {
|
||||
|
||||
msgToSend = this.flowClass.find(body, false, flowStandalone) || []
|
||||
|
||||
// //TODO AQUI
|
||||
// for (const ite of msgToSend) {
|
||||
// cbEveryCtx(ite?.ref)
|
||||
// }
|
||||
|
||||
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)
|
||||
return
|
||||
@@ -213,6 +253,7 @@ class CoreClass {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @private
|
||||
* @param {*} message
|
||||
* @param {*} ref
|
||||
@@ -225,5 +266,24 @@ class CoreClass {
|
||||
this.continue(null, responde.ref)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Funcion dedicada a enviar el mensaje sin pasar por el flow
|
||||
* (dialogflow)
|
||||
* @param {*} messageToSend
|
||||
* @param {*} numberOrId
|
||||
* @returns
|
||||
*/
|
||||
sendFlowSimple = async (messageToSend, numberOrId) => {
|
||||
const queue = []
|
||||
for (const ctxMessage of messageToSend) {
|
||||
const delayMs = ctxMessage?.options?.delay || 0
|
||||
if (delayMs) await delay(delayMs)
|
||||
QueuePrincipal.enqueue(() =>
|
||||
this.sendProviderAndSave(numberOrId, ctxMessage)
|
||||
)
|
||||
}
|
||||
return Promise.all(queue)
|
||||
}
|
||||
}
|
||||
module.exports = CoreClass
|
||||
|
||||
@@ -8,8 +8,8 @@ const { addKeyword, addAnswer, addChild, toSerialize } = require('./io/methods')
|
||||
* @param {*} args
|
||||
* @returns
|
||||
*/
|
||||
const createBot = async ({ flow, database, provider }) =>
|
||||
new CoreClass(flow, database, provider)
|
||||
const createBot = async ({ flow, database, provider }, args = {}) =>
|
||||
new CoreClass(flow, database, provider, args)
|
||||
|
||||
/**
|
||||
* Crear instancia de clase Io (Flow)
|
||||
|
||||
@@ -5,12 +5,12 @@ const { generateRef, generateRefSerialize } = require('../../utils/hash')
|
||||
* @param options {media:string, buttons:[], capture:true default false}
|
||||
* @returns
|
||||
*/
|
||||
const toCtx = ({ body, from, prevRef, index }) => {
|
||||
const toCtx = ({ body, from, prevRef, options = {}, index }) => {
|
||||
return {
|
||||
ref: generateRef(),
|
||||
keyword: prevRef,
|
||||
answer: body,
|
||||
options: {},
|
||||
options: options ?? {},
|
||||
from,
|
||||
refSerialize: generateRefSerialize({ index, answer: body }),
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/bot",
|
||||
"version": "0.0.64-alpha.0",
|
||||
"version": "0.0.91-alpha.0",
|
||||
"description": "",
|
||||
"main": "./lib/bundle.bot.cjs",
|
||||
"scripts": {
|
||||
|
||||
28
packages/bot/tests/flow.class.test.js
Normal file
28
packages/bot/tests/flow.class.test.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const { test } = require('uvu')
|
||||
const assert = require('uvu/assert')
|
||||
const FlowClass = require('../io/flow.class')
|
||||
const { addKeyword } = require('../index')
|
||||
|
||||
test(`[FlowClass] Probando instanciamiento de clase`, async () => {
|
||||
const MOCK_FLOW = addKeyword('hola').addAnswer('Buenas!')
|
||||
const flowClass = new FlowClass([MOCK_FLOW])
|
||||
assert.is(flowClass instanceof FlowClass, true)
|
||||
})
|
||||
|
||||
test(`[FlowClass] Probando find`, async () => {
|
||||
const MOCK_FLOW = addKeyword('hola').addAnswer('Buenas!')
|
||||
const flowClass = new FlowClass([MOCK_FLOW])
|
||||
|
||||
flowClass.find('hola')
|
||||
assert.is(flowClass instanceof FlowClass, true)
|
||||
})
|
||||
|
||||
test(`[FlowClass] Probando findBySerialize`, async () => {
|
||||
const MOCK_FLOW = addKeyword('hola').addAnswer('Buenas!')
|
||||
const flowClass = new FlowClass([MOCK_FLOW])
|
||||
|
||||
flowClass.findBySerialize('')
|
||||
assert.is(flowClass instanceof FlowClass, true)
|
||||
})
|
||||
|
||||
test.run()
|
||||
@@ -1,22 +1,30 @@
|
||||
const { red, yellow, green, bgCyan } = require('kleur')
|
||||
const { exec } = require('node:child_process')
|
||||
|
||||
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}`
|
||||
)
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
console.log(green(`Node.js compatible ${version}`))
|
||||
console.log(``)
|
||||
reject('ERROR_NODE')
|
||||
}
|
||||
console.log(green(`Node.js: ${version} compatible ✅`))
|
||||
console.log(``)
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
|
||||
const checkOs = () => {
|
||||
return new Promise((resolve) => {
|
||||
console.log(bgCyan('🙂 Revisando tu sistema operativo'))
|
||||
const os = process.platform
|
||||
if (!os.includes('win32')) {
|
||||
@@ -31,8 +39,27 @@ const checkOs = () => {
|
||||
|
||||
console.log(yellow(messages.join(' \n')))
|
||||
}
|
||||
|
||||
console.log(green(`OS: compatible ✅`))
|
||||
console.log(``)
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { checkNodeVersion, checkOs }
|
||||
const checkGit = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
console.log(bgCyan('🤓 Revisando GIT'))
|
||||
exec('git --version', (error) => {
|
||||
if (error) {
|
||||
console.error(red(`🔴 Se require instalar GIT`))
|
||||
console.log(``)
|
||||
reject('ERROR_GIT')
|
||||
} else {
|
||||
console.log(green(`Git: Compatible ✅`))
|
||||
console.log(``)
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { checkNodeVersion, checkOs, checkGit }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const prompts = require('prompts')
|
||||
const { join } = require('path')
|
||||
const { yellow, red, cyan, bgMagenta } = require('kleur')
|
||||
const { yellow, red, cyan, bgMagenta, bgRed } = require('kleur')
|
||||
const { existsSync } = require('fs')
|
||||
const { copyBaseApp } = require('../create-app')
|
||||
const { checkNodeVersion, checkOs } = require('../check')
|
||||
const { checkNodeVersion, checkOs, checkGit } = require('../check')
|
||||
|
||||
const bannerDone = () => {
|
||||
console.log(``)
|
||||
@@ -21,6 +21,22 @@ const bannerDone = () => {
|
||||
}
|
||||
|
||||
const startInteractive = async () => {
|
||||
try {
|
||||
console.clear()
|
||||
await checkNodeVersion()
|
||||
checkOs()
|
||||
await checkGit()
|
||||
console.clear()
|
||||
await nextSteps()
|
||||
} catch (e) {
|
||||
console.error(bgRed(`Ups! 🙄 algo no va bien.`))
|
||||
console.error(
|
||||
bgRed(`Revisa los requerimientos minimos en la documentacion`)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const nextSteps = async () => {
|
||||
const questions = [
|
||||
{
|
||||
type: 'text',
|
||||
@@ -32,11 +48,11 @@ const startInteractive = async () => {
|
||||
name: 'providerWs',
|
||||
message: '¿Cuál proveedor de whatsapp quieres utilizar?',
|
||||
choices: [
|
||||
{ title: 'whatsapp-web.js (gratis)', value: 'wweb' },
|
||||
{ title: 'Venom (gratis)', value: 'venom' },
|
||||
{ title: 'Baileys (gratis)', value: 'baileys' },
|
||||
{ title: 'Venom (gratis)', value: 'venom' },
|
||||
{ title: 'whatsapp-web.js (gratis)', value: 'wweb' },
|
||||
{ title: 'Twilio', value: 'twilio' },
|
||||
{ title: 'API Oficial (Meta)', value: 'meta' },
|
||||
{ title: 'Meta', value: 'meta' },
|
||||
],
|
||||
max: 1,
|
||||
hint: 'Espacio para seleccionar',
|
||||
@@ -58,9 +74,6 @@ const startInteractive = async () => {
|
||||
},
|
||||
]
|
||||
|
||||
console.clear()
|
||||
checkNodeVersion()
|
||||
checkOs()
|
||||
const onCancel = () => {
|
||||
console.log('¡Proceso cancelado!')
|
||||
return true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/cli",
|
||||
"version": "0.0.70-alpha.0",
|
||||
"version": "0.0.72-alpha.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/contexts",
|
||||
"version": "0.0.14-alpha.0",
|
||||
"version": "0.0.16-alpha.0",
|
||||
"description": "",
|
||||
"main": "./lib/bundle.contexts.cjs",
|
||||
"files": [
|
||||
|
||||
@@ -117,7 +117,7 @@ class DialogFlowCXContext extends CoreClass {
|
||||
}
|
||||
})
|
||||
|
||||
this.sendFlow(listMessages, from)
|
||||
this.sendFlowSimple(listMessages, from)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class DialogFlowContext extends CoreClass {
|
||||
...customPayload,
|
||||
answer: fields?.answer?.stringValue,
|
||||
}
|
||||
this.sendFlow([ctxFromDX], from)
|
||||
this.sendFlowSimple([ctxFromDX], from)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ class DialogFlowContext extends CoreClass {
|
||||
answer: queryResult?.fulfillmentText,
|
||||
}
|
||||
|
||||
this.sendFlow([ctxFromDX], from)
|
||||
this.sendFlowSimple([ctxFromDX], from)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "create-bot-whatsapp",
|
||||
"version": "0.0.81-alpha.0",
|
||||
"version": "0.0.93-alpha.0",
|
||||
"description": "",
|
||||
"main": "./lib/bundle.create-bot-whatsapp.cjs",
|
||||
"files": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/database",
|
||||
"version": "0.0.62-alpha.0",
|
||||
"version": "0.0.64-alpha.0",
|
||||
"description": "Esto es el conector a mysql, pg, mongo",
|
||||
"main": "./lib/mock/index.cjs",
|
||||
"keywords": [],
|
||||
|
||||
@@ -14,7 +14,7 @@ export default component$(
|
||||
<a href={props.user.html_url} target="_blank">
|
||||
<img
|
||||
class="w-16 h-16 rounded-full mx-auto object-cover"
|
||||
src={props.user.avatar_url + '&s=80'}
|
||||
src={props.user.avatar_url}
|
||||
alt={props.user.login}
|
||||
width="80"
|
||||
height="80"
|
||||
@@ -23,7 +23,9 @@ export default component$(
|
||||
|
||||
<div class="pt-2 space-y-4 justify-center flex">
|
||||
<figcaption class="text-sm">
|
||||
<div class={'font-semibold'}>{props.user.login}</div>
|
||||
<div class={'font-semibold truncate'}>
|
||||
{props.user.login}
|
||||
</div>
|
||||
</figcaption>
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
@@ -56,7 +56,7 @@ export default component$(() => {
|
||||
</a>
|
||||
<a
|
||||
target={'_blank'}
|
||||
href="https://youtu.be/DEIyGyJNGa8"
|
||||
href="https://youtu.be/UgoS8PXxe-A"
|
||||
class="btn bg-gray-50 dark:bg-transparent"
|
||||
>
|
||||
Ver video
|
||||
|
||||
54
packages/docs/src/components/widgets/Members.tsx
Normal file
54
packages/docs/src/components/widgets/Members.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { component$ } from '@builder.io/qwik'
|
||||
import { RequestHandlerCloudflarePages } from '@builder.io/qwik-city/middleware/cloudflare-pages'
|
||||
import { User } from '~/contexts'
|
||||
import Collaborator from './Collaborator'
|
||||
|
||||
export const onRequest: RequestHandlerCloudflarePages = async () => {
|
||||
console.log('??heree')
|
||||
}
|
||||
|
||||
export const TaleUsers = component$((props: { users: User[] }) => {
|
||||
return (
|
||||
<>
|
||||
{props.users.map((user) => (
|
||||
<div class="col-span-2 ">
|
||||
{' '}
|
||||
<Collaborator user={user} />
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default component$((props: { users: User[] }) => {
|
||||
return (
|
||||
<section class="relative ">
|
||||
<div class={'px-4 py-16 mx-auto max-w-6xl lg:py-20'}>
|
||||
<div class="mb-10 md:mx-auto sm:text-center md:mb-12 max-w-3xl">
|
||||
<p class="text-base text-primary-600 dark:text-purple-200 font-semibold tracking-wide uppercase">
|
||||
Premium
|
||||
</p>
|
||||
<h2 class="text-4xl md:text-5xl font-bold leading-tighter tracking-tighter mb-4 font-heading">
|
||||
Miembros
|
||||
</h2>
|
||||
<p class="max-w-3xl mx-auto sm:text-center text-xl text-gray-600 dark:text-slate-400">
|
||||
Conviértete en un miembro destacado y forma parte del
|
||||
proyecto y disfruta de manera adelantada de las
|
||||
actualizaciones{' '}
|
||||
<a
|
||||
class={'font-semibold'}
|
||||
target={'_blank'}
|
||||
href="https://opencollective.com/bot-whatsapp"
|
||||
>
|
||||
Únete
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grid lg:grid-cols-12 grid-cols-1 gap-4 ">
|
||||
<TaleUsers users={props.users} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
})
|
||||
20
packages/docs/src/components/widgets/SearchModal.tsx
Normal file
20
packages/docs/src/components/widgets/SearchModal.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { component$ } from '@builder.io/qwik'
|
||||
|
||||
export const SearchModal = component$(() => {
|
||||
// const state = useStore({
|
||||
// open: false,
|
||||
// src: '',
|
||||
// })
|
||||
|
||||
return (
|
||||
<div class={'bg-gray-100/75 fixed w-[100vw] h-[100vh] z-50'}>
|
||||
<div class={'bg-red-200 w-1/3 m-auto mt-12'}>
|
||||
<SingleModal />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
export const SingleModal = component$(() => {
|
||||
return <div class={'bg-blue-300 w-100 px-3 py-2'}>Modal singlke</div>
|
||||
})
|
||||
@@ -52,16 +52,22 @@ export default component$(() => {
|
||||
title: 'Avanzado',
|
||||
list: [
|
||||
{ name: 'Migración', link: '/docs/migration' },
|
||||
{ name: 'Extender funcionalidades', link: '/docs/custom' },
|
||||
{ name: 'MasterClass', link: '/docs/masterclass' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Despliegue',
|
||||
list: [
|
||||
{ name: 'Local', link: '/docs/deploy/local' },
|
||||
{ name: 'Docker', link: '/docs/deploy/docker' },
|
||||
{ name: 'Cloud', link: '/docs/deploy/cloud' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Comunidad',
|
||||
list: [
|
||||
{ name: 'MasterClass', link: '/docs/masterclass' },
|
||||
{ name: 'Colabores', link: '/docs/contributing' },
|
||||
{ name: 'Unirme al proyecto', link: '/docs/join' },
|
||||
{ name: 'Sponsors', link: '/docs/sponsors' },
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
108
packages/docs/src/routes/docs/deploy/cloud/index.mdx
Normal file
108
packages/docs/src/routes/docs/deploy/cloud/index.mdx
Normal file
@@ -0,0 +1,108 @@
|
||||
import Alert from '../../../../components/widgets/Alert'
|
||||
import Navigation from '../../../../components/widgets/Navigation'
|
||||
|
||||
# Entorno Cloud
|
||||
|
||||
Si deseas tener tu chatbot en ejecución en un servidor en la nueba esta, guía te ayudará.
|
||||
El servidor deberá cumplir con los requisitos mínimos, puedes ver en [este enlace.](/docs/requirements)
|
||||
|
||||
---
|
||||
|
||||
Dependiendo de tu proveedor de **servicio Cloud** debes crear una instancia (máquina virtual), este ejemplo iremos orientando en un entorno de AWS.
|
||||
En nuestro ejemplo creamos una maquina virtual con **Ubuntu 20.04**
|
||||
|
||||

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

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

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

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

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

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

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

|
||||
|
||||
La consola de devolver un mensaje con una lista de procesos, en el ejemplo puedes observar, que tenemos un proceso llamado `bot1`
|
||||
|
||||
De esta manera ya puedes cerrar la terminal y tu bot seguirá en ejecución sin problema
|
||||
@@ -23,6 +23,26 @@ const flowPrincipal = addKeyword(['hola', 'alo'])
|
||||
|
||||
---
|
||||
|
||||
## blackList
|
||||
|
||||
Éste argumento se utiliza para **evitar que el bot se active** cuando los números de la lista activen el bot.
|
||||
Es importante que el número **vaya acompañado de su prefijo**, en el caso de España "34".
|
||||
|
||||
```js
|
||||
createBot(
|
||||
{
|
||||
flow: adapterFlow,
|
||||
provider: adapterProvider,
|
||||
database: adapterDB,
|
||||
},
|
||||
{
|
||||
blackList: ['34XXXXXXXXX', '34XXXXXXXXX', '34XXXXXXXXX', '34XXXXXXXXX'],
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## addKeyword()
|
||||
|
||||
Esta funcion se utliza para iniciar un flujo de conversion. <br /> Recibe un `string` o un `array`
|
||||
@@ -55,7 +75,7 @@ Esta funcion se utliza para responder un mensaje despues del `addKeyword()`
|
||||
- delay: 0 (milisegundos)
|
||||
- media: url de imagen
|
||||
- buttons: array `[{body:'Boton1'}, {body:'Boton2'}, {body:'Boton3'}]`
|
||||
- capture: false (para esperar respuesta)
|
||||
- capture: true (para esperar respuesta)
|
||||
- child: Objecto tipo flujo o arra de flujos hijos
|
||||
|
||||
```js
|
||||
@@ -159,6 +179,74 @@ const flowString = addKeyword('hola')
|
||||
|
||||
---
|
||||
|
||||
## endFlow()
|
||||
|
||||
Esta funcion se utliza para finalizar un flujo con dos o más addAnswer. Un ejemplo de uso sería registrar 3 datos de un usuario en 3 preguntas distinas y
|
||||
que el usuario pueda finalizar por él mismo el flujo.
|
||||
Como podrás comprobar en el ejemplo siguiente, se puede vincular flowDynamic y todas sus funciones; como por ejemplo botones.
|
||||
|
||||
```js
|
||||
const flowFormulario = addKeyword(['Hola'])
|
||||
.addAnswer(
|
||||
['Hola!', 'Escriba su *Nombre* para generar su solicitud'],
|
||||
{ capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] },
|
||||
async (ctx, { flowDynamic, endFlow }) => {
|
||||
if (ctx.body == '❌ Cancelar solicitud') {
|
||||
await flowDynamic([
|
||||
{
|
||||
body: '❌ *Su solicitud de cita ha sido cancelada* ❌',
|
||||
buttons: [{ body: '⬅️ Volver al Inicio' }],
|
||||
},
|
||||
])
|
||||
return endFlow()
|
||||
}
|
||||
}
|
||||
)
|
||||
.addAnswer(
|
||||
['También necesito tus dos apellidos'],
|
||||
{ capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] },
|
||||
async (ctx, { flowDynamic, endFlow }) => {
|
||||
if (ctx.body == '❌ Cancelar solicitud') {
|
||||
await flowDynamic([
|
||||
{
|
||||
body: '❌ *Su solicitud de cita ha sido cancelada* ❌',
|
||||
buttons: [{ body: '⬅️ Volver al Inicio' }],
|
||||
},
|
||||
])
|
||||
return endFlow()
|
||||
}
|
||||
}
|
||||
)
|
||||
.addAnswer(
|
||||
['Dejeme su número de teléfono y le llamaré lo antes posible.'],
|
||||
{ capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] },
|
||||
async (ctx, { flowDynamic, endFlow }) => {
|
||||
if (ctx.body == '❌ Cancelar solicitud') {
|
||||
await flowDynamic([
|
||||
{
|
||||
body: '❌ *Su solicitud de cita ha sido cancelada* ❌',
|
||||
buttons: [{ body: '⬅️ Volver al Inicio' }],
|
||||
},
|
||||
])
|
||||
return endFlow()
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# QRPortalWeb
|
||||
|
||||
Argumento para asignar nombre y puerto al BOT
|
||||
|
||||
```js
|
||||
const BOTNAME = 'bot'
|
||||
QRPortalWeb({ name: BOTNAME, port: 3005 })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<Navigation
|
||||
pages={[
|
||||
{ name: 'Conceptos', link: '/docs/essential' },
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { DocumentHead } from '@builder.io/qwik-city'
|
||||
import ExtraBar from '~/components/widgets/ExtraBar'
|
||||
import Header from '~/components/widgets/Header'
|
||||
import NavBar from '~/components/widgets/NavBar'
|
||||
// import { SearchModal } from '~/components/widgets/SearchModal'
|
||||
import SponsorBar from '~/components/widgets/SponsorBar'
|
||||
import { GlobalStore } from '~/contexts'
|
||||
// import Navigation from '~/components/widgets/Navigation'
|
||||
@@ -14,6 +15,7 @@ export default component$(() => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <SearchModal /> */}
|
||||
<Header />
|
||||
<main class={'overflow-hidden'}>
|
||||
<div class={'max-w-8xl'}>
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
[](https://hackmd.io/79xQyVSgRD6RsTpqtMPPdw)
|
||||
### Preguntas Frecuentes para Master Class BOT v2
|
||||
> Anota aqui las preguntas o dudas que tengas
|
||||
> Pronto estare publicando fecha y hora para la masterclass
|
||||
|
||||
1.- Si necesito correr dos bots al mismo tiempo ¿donde puedo cambiar el puerto?
|
||||
|
||||
2.- Si necesito agregar o modificar funciones del bot, ¿como puedo hacerlo?
|
||||
|
||||
3.- Si quiero mi bot con otra base de datos diferente a MySQL ¿como lo puedo hacer?
|
||||
|
||||
4.- Quiero conectarme a tal o cual API con JSON/XML/etc, ¿se puede hacer?
|
||||
|
||||
5.- ¿Como integrar listas?
|
||||
|
||||
6.- Preguntas y respuestas con el Bot
|
||||
|
||||
7.- Guardar conversaciones en Excel.
|
||||
|
||||
8.- ¿Puedo usar 2 o mas sesiones (códigos QR) al mismo tiempo?
|
||||
|
||||
9.- ¿Puede ser que al usar el provider bailey, al leer el qr.png, que sea desde una url en el navegador, y no desde visual studio? Gracias
|
||||
|
||||
10.- ¿Cómo tomo los datos que me envían en un mensaje para utilizarlo internamente en la búsqueda de datos propios y devolver la respuesta?
|
||||
26
packages/docs/src/routes/docs/masterclass/index.mdx
Normal file
26
packages/docs/src/routes/docs/masterclass/index.mdx
Normal file
@@ -0,0 +1,26 @@
|
||||
# MasterClass
|
||||
|
||||
<iframe
|
||||
width="560"
|
||||
height="315"
|
||||
src="https://www.youtube.com/embed/22jiE2Z3XGM"
|
||||
title="YouTube video player"
|
||||
frameborder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
|
||||
---
|
||||
|
||||
### Preguntas de la masterclass
|
||||
|
||||
- Si necesito correr dos bots al mismo tiempo ¿donde puedo cambiar el puerto?
|
||||
- Si necesito agregar o modificar funciones del bot, ¿como puedo hacerlo?
|
||||
- Si quiero mi bot con otra base de datos diferente a MySQL ¿como lo puedo hacer?
|
||||
- Quiero conectarme a tal o cual API con JSON/XML/etc, ¿se puede hacer?
|
||||
- ¿Como integrar listas?
|
||||
- Preguntas y respuestas con el Bot
|
||||
- Guardar conversaciones en Excel.
|
||||
- ¿Puedo usar 2 o mas sesiones (códigos QR) al mismo tiempo?
|
||||
- ¿Puede ser que al usar el provider bailey, al leer el qr.png, que sea desde una url en el navegador, y no desde visual studio? Gracias
|
||||
- ¿Cómo tomo los datos que me envían en un mensaje para utilizarlo internamente en la búsqueda de datos propios y devolver la respuesta?
|
||||
@@ -6,7 +6,9 @@ A continuación se describen los puntos técnicos que debes de tener en cuenta a
|
||||
|
||||
- Node v16 o superior - **[descargar node](https://nodejs.org/es/download/)**
|
||||
- Git - **[descargar Git](https://git-scm.com/download/win)**
|
||||
|
||||
---
|
||||
|
||||
## ¿Como saber que tengo el Node?
|
||||
|
||||
Solo debes ejecutar el siguiente comando y esperar que la versión que te arroja sea superior a v16
|
||||
@@ -15,12 +17,16 @@ Solo debes ejecutar el siguiente comando y esperar que la versión que te arroja
|
||||
$ node -v
|
||||
v18.12.1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ¿Como instalar Node?
|
||||
|
||||
- **Windows**: [Ver video](https://youtu.be/xRXHQlqA3Ak?t=376). Si necesitas ayuda para instalar Node en Windows. A continuación te comparto un video en el minuto exacto donde explico como instalar.
|
||||
- **Ubuntu**: Te comparto un recurso de **[Digital Ocean](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04-es)** donde explica como instalar node en Ubuntu.
|
||||
- ---
|
||||
|
||||
---
|
||||
|
||||
## ¿Como saber que tengo Git?
|
||||
|
||||
Solo debes ejecutar el siguiente comando y esperar que te mande la versión que tienes instalada, si te manda un error de comando no reconocido es que no lo tienes instalado.
|
||||
@@ -29,7 +35,9 @@ Solo debes ejecutar el siguiente comando y esperar que te mande la versión que
|
||||
$ git -v
|
||||
git
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ¿Como instalar Git?
|
||||
|
||||
- Solo es necesario instalar Git si estás usando **Windows**, ya que Mac y Linux lo traen preinstalado.
|
||||
@@ -37,6 +45,7 @@ git
|
||||
- Descarga la versión necesaria para tu sistema operativo (32-bit o 64-bit).
|
||||
- Una vez terminada la descarga, ejecuta el archivo descargado y dale "Siguiente" en todas las pantallas.
|
||||
- Haz clic en el botón de "Finalizar".
|
||||
|
||||
---
|
||||
|
||||
<Navigation
|
||||
|
||||
@@ -5,16 +5,22 @@ import Features from '~/components/widgets/Features'
|
||||
import FAQs from '~/components/widgets/FAQs'
|
||||
import CallToAction from '~/components/widgets/CallToAction'
|
||||
import Collaborators from '~/components/widgets/Collaborators'
|
||||
import Members from '~/components/widgets/Members'
|
||||
import { fetchGithub } from '~/services/github'
|
||||
import { fetchOpenCollective } from '~/services/opencollective'
|
||||
import { RequestHandlerNetlify } from '@builder.io/qwik-city/middleware/netlify-edge'
|
||||
import { GITHUB_TOKEN } from './docs/constant'
|
||||
// import { SearchModal } from '~/components/widgets/SearchModal'
|
||||
|
||||
export const onGet: RequestHandlerNetlify = async ({ platform }) => {
|
||||
const CHECK_GITHUB_TOKEN =
|
||||
(platform as any)?.['GITHUB_TOKEN'] ?? GITHUB_TOKEN
|
||||
console.log(`[🚩 platform]: `, GITHUB_TOKEN)
|
||||
const data = await fetchGithub(CHECK_GITHUB_TOKEN)
|
||||
return data
|
||||
const dataGithub = await fetchGithub(CHECK_GITHUB_TOKEN)
|
||||
const dataOpenCollective = await fetchOpenCollective()
|
||||
return {
|
||||
dataGithub,
|
||||
dataOpenCollective,
|
||||
}
|
||||
}
|
||||
|
||||
export default component$(() => {
|
||||
@@ -27,9 +33,16 @@ export default component$(() => {
|
||||
<CallToAction />
|
||||
<Resource
|
||||
value={resource}
|
||||
onResolved={(data: any) => <Collaborators users={data} />}
|
||||
></Resource>
|
||||
onResolved={(data: any) => {
|
||||
return (
|
||||
<>
|
||||
<Collaborators users={data.dataGithub} />
|
||||
<FAQs />
|
||||
<Members users={data.dataOpenCollective} />
|
||||
</>
|
||||
)
|
||||
}}
|
||||
></Resource>
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -14,6 +14,9 @@ export const fetchGithub = async (token: string) => {
|
||||
},
|
||||
}
|
||||
)
|
||||
const listUsers = data.json()
|
||||
return listUsers
|
||||
const listUsers = await data.json()
|
||||
return listUsers.map((u: any) => ({
|
||||
...u,
|
||||
avatar_url: `${u.avatar_url}&s=80`,
|
||||
}))
|
||||
}
|
||||
|
||||
19
packages/docs/src/services/opencollective.ts
Normal file
19
packages/docs/src/services/opencollective.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* GET API from OpenCollective
|
||||
* @returns
|
||||
*/
|
||||
export const fetchOpenCollective = async () => {
|
||||
const data = await fetch(
|
||||
`https://opencollective.com/bot-whatsapp/members/users.json?limit=22&offset=0`,
|
||||
{
|
||||
method: 'GET',
|
||||
}
|
||||
)
|
||||
const listUsers = await data.json()
|
||||
return listUsers.map((u: any) => ({
|
||||
html_url: u.profile,
|
||||
avatar_url: u.image ?? 'https://i.imgur.com/HhiYKwN.png',
|
||||
login: u.name,
|
||||
id: u.MemberId,
|
||||
}))
|
||||
}
|
||||
4
packages/portal/.vscode/extensions.json
vendored
Normal file
4
packages/portal/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"recommendations": ["dbaeumer.vscode-eslint", "unifiedjs.vscode-mdx"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
32
packages/portal/.vscode/qwik-city.code-snippets
vendored
Normal file
32
packages/portal/.vscode/qwik-city.code-snippets
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"onGet": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:onGet",
|
||||
"description": "onGet function for a route index",
|
||||
"body": [
|
||||
"export const onGet: RequestHandler = (request) => {",
|
||||
" $0",
|
||||
"};"
|
||||
]
|
||||
},
|
||||
"onGet (typed)": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:onGet typed",
|
||||
"description": "onGet function for a route index",
|
||||
"body": [
|
||||
"export interface ${1:PageData} {",
|
||||
" $2",
|
||||
"};",
|
||||
"",
|
||||
"export const onGet: RequestHandler<$1> = (request) => {",
|
||||
" $4",
|
||||
"};"
|
||||
]
|
||||
},
|
||||
"useEndpoint": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:useEndpoint",
|
||||
"description": "useEndpoint declaration",
|
||||
"body": "const $1 = useEndpoint<typeof onGet>();"
|
||||
}
|
||||
}
|
||||
84
packages/portal/.vscode/qwik.code-snippets
vendored
Normal file
84
packages/portal/.vscode/qwik.code-snippets
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"Qwik component (simple)": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:component",
|
||||
"description": "Simple Qwik component",
|
||||
"body": [
|
||||
"export const ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}} = component$(() => {",
|
||||
" return <${2:div}>$4</$2>",
|
||||
"});"
|
||||
]
|
||||
},
|
||||
"Qwik component (props)": {
|
||||
"scope": "typescriptreact",
|
||||
"prefix": "q:component w/props",
|
||||
"description": "Qwik component w/ props",
|
||||
"body": [
|
||||
"export interface ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}}Props {",
|
||||
" $2",
|
||||
"}",
|
||||
"",
|
||||
"export const $1 = component$<$1Props>((props) => {",
|
||||
" const ${2:count} = useSignal(0);",
|
||||
" return (",
|
||||
" <${3:div} on${4:Click}$={(ev) => {$5}}>",
|
||||
" $6",
|
||||
" </${3}>",
|
||||
" );",
|
||||
"});"
|
||||
]
|
||||
},
|
||||
"Qwik signal": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:useSignal",
|
||||
"description": "useSignal() declaration",
|
||||
"body": ["const ${1:foo} = useSignal($2);", "$0"]
|
||||
},
|
||||
"Qwik store": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:useStore",
|
||||
"description": "useStore() declaration",
|
||||
"body": ["const ${1:state} = useStore({", " $2", "});", "$0"]
|
||||
},
|
||||
"$ hook": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:$",
|
||||
"description": "$() function hook",
|
||||
"body": ["$(() => {", " $0", "});", ""]
|
||||
},
|
||||
"useClientEffect": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:useClientEffect",
|
||||
"description": "useClientEffect$() function hook",
|
||||
"body": ["useClientEffect$(({ track }) => {", " $0", "});", ""]
|
||||
},
|
||||
"useTask": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:useTask",
|
||||
"description": "useTask$() function hook",
|
||||
"body": [
|
||||
"useTask$(({ track }) => {",
|
||||
" track(() => $1);",
|
||||
" $0",
|
||||
"});",
|
||||
""
|
||||
]
|
||||
},
|
||||
"useResource": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:useResource",
|
||||
"description": "useResource$() declaration",
|
||||
"body": [
|
||||
"const $1 = useResource$(({ track, cleanup }) => {",
|
||||
" $0",
|
||||
"});",
|
||||
""
|
||||
]
|
||||
},
|
||||
"useServerMount": {
|
||||
"scope": "javascriptreact,typescriptreact",
|
||||
"prefix": "q:useServerMount",
|
||||
"description": "useServerMount$() function hook",
|
||||
"body": ["useServerMount$(() => {", " $0", "});", ""]
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/portal",
|
||||
"version": "0.0.20-alpha.0",
|
||||
"version": "0.0.22-alpha.0",
|
||||
"description": "Portal WEB para escanear QR",
|
||||
"main": "./lib/portal.http.cjs",
|
||||
"scripts": {
|
||||
|
||||
15
packages/portal/server/@qwik-city-not-found-paths.js
Normal file
15
packages/portal/server/@qwik-city-not-found-paths.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const notFounds = [
|
||||
[
|
||||
'/',
|
||||
'<!DOCTYPE html>\n<html>\n <head>\n <meta charset="utf-8" />\n <meta http-equiv="Status" content="404" />\n <title>404 Resource Not Found</title>\n <meta name="viewport" content="width=device-width,initial-scale=1" />\n <style>\n body {\n color: #006ce9;\n background-color: #fafafa;\n padding: 30px;\n font-family: ui-sans-serif, system-ui, -apple-system,\n BlinkMacSystemFont, Roboto, sans-serif;\n }\n p {\n max-width: 600px;\n margin: 60px auto 30px auto;\n background: white;\n border-radius: 4px;\n box-shadow: 0px 0px 50px -20px #006ce9;\n overflow: hidden;\n }\n strong {\n display: inline-block;\n padding: 15px;\n background: #006ce9;\n color: white;\n }\n span {\n display: inline-block;\n padding: 15px;\n }\n pre {\n max-width: 580px;\n margin: 0 auto;\n }\n </style>\n </head>\n <body>\n <p><strong>404</strong> <span>Resource Not Found</span></p>\n </body>\n</html>\n',
|
||||
],
|
||||
]
|
||||
function getNotFound(p) {
|
||||
for (const r of notFounds) {
|
||||
if (p.startsWith(r[0])) {
|
||||
return r[1]
|
||||
}
|
||||
}
|
||||
return 'Resource Not Found'
|
||||
}
|
||||
export { getNotFound }
|
||||
5
packages/portal/server/@qwik-city-plan.mjs
Normal file
5
packages/portal/server/@qwik-city-plan.mjs
Normal file
File diff suppressed because one or more lines are too long
22
packages/portal/server/@qwik-city-static-paths.js
Normal file
22
packages/portal/server/@qwik-city-static-paths.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const staticPaths = new Set([
|
||||
'/favicon.svg',
|
||||
'/manifest.json',
|
||||
'/q-manifest.json',
|
||||
'/robots.txt',
|
||||
'/service-worker.js',
|
||||
'/sitemap.xml',
|
||||
'/water-mark.png',
|
||||
])
|
||||
function isStaticPath(p) {
|
||||
if (p.startsWith('/build/')) {
|
||||
return true
|
||||
}
|
||||
if (p.startsWith('/assets/')) {
|
||||
return true
|
||||
}
|
||||
if (staticPaths.has(p)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
export { isStaticPath }
|
||||
1
packages/portal/server/entry.ssr.js
Normal file
1
packages/portal/server/entry.ssr.js
Normal file
@@ -0,0 +1 @@
|
||||
export * from './entry.ssr.mjs'
|
||||
2
packages/portal/server/entry.ssr.mjs
Normal file
2
packages/portal/server/entry.ssr.mjs
Normal file
File diff suppressed because one or more lines are too long
3
packages/portal/server/package.json
Normal file
3
packages/portal/server/package.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
65
packages/provider/common/download.js
Normal file
65
packages/provider/common/download.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const mimeDep = require('mime-types')
|
||||
const { tmpdir } = require('os')
|
||||
const http = require('http')
|
||||
const https = require('https')
|
||||
const { rename, createWriteStream } = require('fs')
|
||||
|
||||
/**
|
||||
* Extrar el mimetype from buffer
|
||||
* @param {string} response
|
||||
* @returns
|
||||
*/
|
||||
const fileTypeFromFile = async (response) => {
|
||||
const type = response.headers['content-type'] ?? null
|
||||
const ext = mimeDep.extension(type)
|
||||
return {
|
||||
type,
|
||||
ext,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Descargar archivo binay en tmp
|
||||
* @param {*} url
|
||||
* @returns
|
||||
*/
|
||||
const generalDownload = async (url) => {
|
||||
const handleDownload = () => {
|
||||
const checkProtocol = url.includes('https:')
|
||||
const handleHttp = checkProtocol ? https : http
|
||||
const name = `tmp-${Date.now()}-dat`
|
||||
const fullPath = `${tmpdir()}/${name}`
|
||||
const file = createWriteStream(fullPath)
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
handleHttp.get(url, function (response) {
|
||||
response.pipe(file)
|
||||
file.on('finish', async function () {
|
||||
file.close()
|
||||
res({ response, fullPath })
|
||||
})
|
||||
file.on('error', function () {
|
||||
file.close()
|
||||
rej(null)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const handleFile = (pathInput, ext) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const fullPath = `${pathInput}.${ext}`
|
||||
rename(pathInput, fullPath, (err) => {
|
||||
if (err) reject(null)
|
||||
resolve(fullPath)
|
||||
})
|
||||
})
|
||||
|
||||
const httpResponse = await handleDownload()
|
||||
const { ext } = await fileTypeFromFile(httpResponse.response)
|
||||
const getPath = await handleFile(httpResponse.fullPath, ext)
|
||||
|
||||
return getPath
|
||||
}
|
||||
|
||||
module.exports = { generalDownload }
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@bot-whatsapp/provider",
|
||||
"version": "0.0.68-alpha.0",
|
||||
"version": "0.0.70-alpha.0",
|
||||
"description": "Esto es el conector a Twilio, Meta, etc...",
|
||||
"main": "./lib/mock/index.cjs",
|
||||
"keywords": [],
|
||||
|
||||
@@ -4,7 +4,7 @@ const pino = require('pino')
|
||||
const rimraf = require('rimraf')
|
||||
const mime = require('mime-types')
|
||||
const { join } = require('path')
|
||||
const { existsSync, createWriteStream, readFileSync } = require('fs')
|
||||
const { createWriteStream, readFileSync } = require('fs')
|
||||
const { Console } = require('console')
|
||||
|
||||
const {
|
||||
@@ -13,13 +13,15 @@ const {
|
||||
Browsers,
|
||||
DisconnectReason,
|
||||
} = require('@adiwajshing/baileys')
|
||||
|
||||
const {
|
||||
baileyGenerateImage,
|
||||
baileyCleanNumber,
|
||||
baileyIsValidNumber,
|
||||
baileyDownloadMedia,
|
||||
} = require('./utils')
|
||||
|
||||
const { generalDownload } = require('../../common/download')
|
||||
|
||||
const logger = new Console({
|
||||
stdout: createWriteStream(`${process.cwd()}/baileys.log`),
|
||||
})
|
||||
@@ -169,14 +171,50 @@ class BaileysProvider extends ProviderClass {
|
||||
*/
|
||||
|
||||
sendMedia = async (number, imageUrl, text) => {
|
||||
const fileDownloaded = await baileyDownloadMedia(imageUrl)
|
||||
const fileDownloaded = await generalDownload(imageUrl)
|
||||
const mimeType = mime.lookup(fileDownloaded)
|
||||
|
||||
if (mimeType.includes('image'))
|
||||
return this.sendImage(number, fileDownloaded, text)
|
||||
if (mimeType.includes('video'))
|
||||
return this.sendVideo(number, fileDownloaded, text)
|
||||
if (mimeType.includes('audio'))
|
||||
return this.sendAudio(number, fileDownloaded, text)
|
||||
|
||||
return this.sendFile(number, fileDownloaded)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar imagen
|
||||
* @param {*} number
|
||||
* @param {*} imageUrl
|
||||
* @param {*} text
|
||||
* @returns
|
||||
*/
|
||||
sendImage = async (number, filePath, text) => {
|
||||
return this.vendor.sendMessage(number, {
|
||||
image: readFileSync(fileDownloaded),
|
||||
image: readFileSync(filePath),
|
||||
caption: text,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar video
|
||||
* @param {*} number
|
||||
* @param {*} imageUrl
|
||||
* @param {*} text
|
||||
* @returns
|
||||
*/
|
||||
sendVideo = async (number, filePath, text) => {
|
||||
return this.vendor.sendMessage(number, {
|
||||
video: readFileSync(filePath),
|
||||
caption: text,
|
||||
gifPlayback: true,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar audio
|
||||
* @alpha
|
||||
* @param {string} number
|
||||
* @param {string} message
|
||||
@@ -185,8 +223,7 @@ class BaileysProvider extends ProviderClass {
|
||||
*/
|
||||
|
||||
sendAudio = async (number, audioUrl, voiceNote = false) => {
|
||||
const numberClean = number.replace('+', '')
|
||||
await this.vendor.sendMessage(`${numberClean}@c.us`, {
|
||||
return this.vendor.sendMessage(number, {
|
||||
audio: { url: audioUrl },
|
||||
ptt: voiceNote,
|
||||
})
|
||||
@@ -210,18 +247,14 @@ class BaileysProvider extends ProviderClass {
|
||||
*/
|
||||
|
||||
sendFile = async (number, filePath) => {
|
||||
if (existsSync(filePath)) {
|
||||
const mimeType = mime.lookup(filePath)
|
||||
const numberClean = number.replace('+', '')
|
||||
const fileName = filePath.split('/').pop()
|
||||
|
||||
await this.vendor.sendMessage(`${numberClean}@c.us`, {
|
||||
return this.vendor.sendMessage(number, {
|
||||
document: { url: filePath },
|
||||
mimetype: mimeType,
|
||||
fileName: fileName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@adiwajshing/baileys": "4.4.0",
|
||||
"mime-types": "2.1.35",
|
||||
"wa-sticker-formatter": "4.3.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
const { createWriteStream } = require('fs')
|
||||
const combineImage = require('combine-image')
|
||||
const qr = require('qr-image')
|
||||
const { tmpdir } = require('os')
|
||||
const http = require('http')
|
||||
const https = require('https')
|
||||
|
||||
const baileyCleanNumber = (number, full = false) => {
|
||||
number = number.replace('@s.whatsapp.net', '')
|
||||
@@ -41,38 +38,8 @@ const baileyIsValidNumber = (rawNumber) => {
|
||||
return !exist
|
||||
}
|
||||
|
||||
/**
|
||||
* Incompleta
|
||||
* Descargar archivo multimedia para enviar
|
||||
* @param {*} url
|
||||
* @returns
|
||||
*/
|
||||
const baileyDownloadMedia = (url) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const ext = url.split('.').pop()
|
||||
const checkProtocol = url.includes('https:')
|
||||
const handleHttp = checkProtocol ? https : http
|
||||
const name = `tmp-${Date.now()}.${ext}`
|
||||
const fullPath = `${tmpdir()}/${name}`
|
||||
const file = createWriteStream(fullPath)
|
||||
handleHttp.get(url, function (response) {
|
||||
response.pipe(file)
|
||||
file.on('finish', function () {
|
||||
file.close()
|
||||
resolve(fullPath)
|
||||
})
|
||||
file.on('error', function () {
|
||||
console.log('errro')
|
||||
file.close()
|
||||
reject(null)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
baileyCleanNumber,
|
||||
baileyGenerateImage,
|
||||
baileyIsValidNumber,
|
||||
baileyDownloadMedia,
|
||||
}
|
||||
|
||||
@@ -2,18 +2,20 @@ const { ProviderClass } = require('@bot-whatsapp/bot')
|
||||
const venom = require('venom-bot')
|
||||
const { createWriteStream } = require('fs')
|
||||
const { Console } = require('console')
|
||||
const mime = require('mime-types')
|
||||
|
||||
const {
|
||||
venomCleanNumber,
|
||||
venomGenerateImage,
|
||||
venomisValidNumber,
|
||||
venomDownloadMedia,
|
||||
} = require('./utils')
|
||||
|
||||
const logger = new Console({
|
||||
stdout: createWriteStream(`${process.cwd()}/venom.log`),
|
||||
})
|
||||
|
||||
const { generalDownload } = require('../../common/download')
|
||||
|
||||
/**
|
||||
* ⚙️ VenomProvider: Es una clase tipo adaptor
|
||||
* que extiende clases de ProviderClass (la cual es como interfaz para sber que funciones rqueridas)
|
||||
@@ -134,6 +136,53 @@ class VenomProvider extends ProviderClass {
|
||||
// return this.vendor.sendButtons(number, "Title", buttons1, "Description");
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar audio
|
||||
* @alpha
|
||||
* @param {string} number
|
||||
* @param {string} message
|
||||
* @param {boolean} voiceNote optional
|
||||
* @example await sendMessage('+XXXXXXXXXXX', 'audio.mp3')
|
||||
*/
|
||||
|
||||
sendAudio = async (number, audioPath) => {
|
||||
return this.vendor.sendVoice(number, audioPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar imagen
|
||||
* @param {*} number
|
||||
* @param {*} imageUrl
|
||||
* @param {*} text
|
||||
* @returns
|
||||
*/
|
||||
sendImage = async (number, filePath, text) => {
|
||||
return this.vendor.sendImage(number, filePath, 'image-name', text)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} number
|
||||
* @param {string} filePath
|
||||
* @example await sendMessage('+XXXXXXXXXXX', './document/file.pdf')
|
||||
*/
|
||||
|
||||
sendFile = async (number, filePath, text) => {
|
||||
const fileName = filePath.split('/').pop()
|
||||
return this.vendor.sendFile(number, filePath, fileName, text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar video
|
||||
* @param {*} number
|
||||
* @param {*} imageUrl
|
||||
* @param {*} text
|
||||
* @returns
|
||||
*/
|
||||
sendVideo = async (number, filePath, text) => {
|
||||
return this.vendor.sendVideoAsGif(number, filePath, 'video.gif', text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar imagen o multimedia
|
||||
* @param {*} number
|
||||
@@ -141,10 +190,18 @@ class VenomProvider extends ProviderClass {
|
||||
* @param {*} message
|
||||
* @returns
|
||||
*/
|
||||
sendMedia = async (number, mediaInput, message) => {
|
||||
if (!mediaInput) throw new Error(`NO_SE_ENCONTRO: ${mediaInput}`)
|
||||
const fileDownloaded = await venomDownloadMedia(mediaInput)
|
||||
return this.vendor.sendImage(number, fileDownloaded, '.', message)
|
||||
sendMedia = async (number, mediaUrl, text) => {
|
||||
const fileDownloaded = await generalDownload(mediaUrl)
|
||||
const mimeType = mime.lookup(fileDownloaded)
|
||||
|
||||
if (mimeType.includes('image'))
|
||||
return this.sendImage(number, fileDownloaded, text)
|
||||
if (mimeType.includes('video'))
|
||||
return this.sendVideo(number, fileDownloaded, text)
|
||||
if (mimeType.includes('audio'))
|
||||
return this.sendAudio(number, fileDownloaded)
|
||||
|
||||
return this.sendFile(number, fileDownloaded, text)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
const { Client, LocalAuth, MessageMedia, Buttons } = require('whatsapp-web.js')
|
||||
const { ProviderClass } = require('@bot-whatsapp/bot')
|
||||
const { Console } = require('console')
|
||||
const { createWriteStream } = require('fs')
|
||||
const { createWriteStream, readFileSync } = require('fs')
|
||||
const {
|
||||
wwebCleanNumber,
|
||||
wwebDownloadMedia,
|
||||
wwebGenerateImage,
|
||||
wwebIsValidNumber,
|
||||
} = require('./utils')
|
||||
@@ -13,6 +12,9 @@ const logger = new Console({
|
||||
stdout: createWriteStream('./log'),
|
||||
})
|
||||
|
||||
const { generalDownload } = require('../../common/download')
|
||||
const mime = require('mime-types')
|
||||
|
||||
/**
|
||||
* ⚙️ WebWhatsappProvider: Es una clase tipo adaptor
|
||||
* que extiende clases de ProviderClass (la cual es como interfaz para sber que funciones rqueridas)
|
||||
@@ -35,6 +37,7 @@ class WebWhatsappProvider extends ProviderClass {
|
||||
'--disable-setuid-sandbox',
|
||||
'--unhandled-rejections=strict',
|
||||
],
|
||||
//executablePath: 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -103,23 +106,6 @@ class WebWhatsappProvider extends ProviderClass {
|
||||
},
|
||||
]
|
||||
|
||||
/**
|
||||
* Enviar un archivo multimedia
|
||||
* https://docs.wwebjs.dev/MessageMedia.html
|
||||
* @private
|
||||
* @param {*} number
|
||||
* @param {*} mediaInput
|
||||
* @returns
|
||||
*/
|
||||
sendMedia = async (number, mediaInput = null) => {
|
||||
if (!mediaInput) throw new Error(`NO_SE_ENCONTRO: ${mediaInput}`)
|
||||
const fileDownloaded = await wwebDownloadMedia(mediaInput)
|
||||
const media = MessageMedia.fromFilePath(fileDownloaded)
|
||||
return this.vendor.sendMessage(number, media, {
|
||||
sendAudioAsVoice: true,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar botones
|
||||
* https://docs.wwebjs.dev/Buttons.html
|
||||
@@ -170,6 +156,90 @@ class WebWhatsappProvider extends ProviderClass {
|
||||
return this.vendor.sendMessage(number, message)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar imagen
|
||||
* @param {*} number
|
||||
* @param {*} imageUrl
|
||||
* @param {*} text
|
||||
* @returns
|
||||
*/
|
||||
sendImage = async (number, filePath) => {
|
||||
const base64 = readFileSync(filePath, { encoding: 'base64' })
|
||||
const mimeType = mime.lookup(filePath)
|
||||
const media = new MessageMedia(mimeType, base64)
|
||||
return this.vendor.sendMessage(number, media, {
|
||||
caption: 'soy una imagen',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar audio
|
||||
* @param {*} number
|
||||
* @param {*} imageUrl
|
||||
* @param {*} text
|
||||
* @returns
|
||||
*/
|
||||
|
||||
sendAudio = async (number, filePath) => {
|
||||
const base64 = readFileSync(filePath, { encoding: 'base64' })
|
||||
const mimeType = mime.lookup(filePath)
|
||||
const media = new MessageMedia(mimeType, base64)
|
||||
return this.vendor.sendMessage(number, media, {
|
||||
caption: 'soy un audio',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar video
|
||||
* @param {*} number
|
||||
* @param {*} imageUrl
|
||||
* @param {*} text
|
||||
* @returns
|
||||
*/
|
||||
sendVideo = async (number, filePath) => {
|
||||
const base64 = readFileSync(filePath, { encoding: 'base64' })
|
||||
const mimeType = mime.lookup(filePath)
|
||||
const media = new MessageMedia(mimeType, base64)
|
||||
return this.vendor.sendMessage(number, media, {
|
||||
sendMediaAsDocument: true,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar Arhivos/pdf
|
||||
* @param {*} number
|
||||
* @param {*} imageUrl
|
||||
* @param {*} text
|
||||
* @returns
|
||||
*/
|
||||
sendFile = async (number, filePath) => {
|
||||
const base64 = readFileSync(filePath, { encoding: 'base64' })
|
||||
const mimeType = mime.lookup(filePath)
|
||||
const media = new MessageMedia(mimeType, base64)
|
||||
return this.vendor.sendMessage(number, media)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enviar imagen o multimedia
|
||||
* @param {*} number
|
||||
* @param {*} mediaInput
|
||||
* @param {*} message
|
||||
* @returns
|
||||
*/
|
||||
sendMedia = async (number, mediaUrl, text) => {
|
||||
const fileDownloaded = await generalDownload(mediaUrl)
|
||||
const mimeType = mime.lookup(fileDownloaded)
|
||||
|
||||
if (mimeType.includes('image'))
|
||||
return this.sendImage(number, fileDownloaded, text)
|
||||
if (mimeType.includes('video'))
|
||||
return this.sendVideo(number, fileDownloaded)
|
||||
if (mimeType.includes('audio'))
|
||||
return this.sendAudio(number, fileDownloaded)
|
||||
|
||||
return this.sendFile(number, fileDownloaded)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} userId
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"whatsapp-web.js": "1.19.2"
|
||||
"whatsapp-web.js": "1.19.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1113,6 +1113,7 @@ __metadata:
|
||||
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
|
||||
@@ -13443,7 +13444,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mime-types@npm:^2.1.12, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
|
||||
"mime-types@npm:^2.1.12, mime-types@npm:^2.1.35, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
|
||||
version: 2.1.35
|
||||
resolution: "mime-types@npm:2.1.35"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user