mirror of
https://github.com/cheveguerra/bot-whatsapp.git
synced 2026-04-20 20:49:15 +00:00
Compare commits
92 Commits
alpha-v2
...
fix/ci-yar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04f99d5ed2 | ||
|
|
cf6188d860 | ||
|
|
294bfbb35f | ||
|
|
230538bcea | ||
|
|
5e7aa72494 | ||
|
|
3159ea5665 | ||
|
|
4b307efe79 | ||
|
|
0105dab2c4 | ||
|
|
6e8e16c9a4 | ||
|
|
8d73c86946 | ||
|
|
a7b19d9bff | ||
|
|
4b7de0f690 | ||
|
|
520145bf7d | ||
|
|
2253d57fed | ||
|
|
0fb93f66a3 | ||
|
|
501887300d | ||
|
|
14b6247106 | ||
|
|
eda8a67718 | ||
|
|
73caf090ba | ||
|
|
afa6771903 | ||
|
|
8dd3be909b | ||
|
|
88af2469cb | ||
|
|
999d6742b4 | ||
|
|
24ac9fbf48 | ||
|
|
4350dff22a | ||
|
|
30e3d443bb | ||
|
|
f5ea7fe2c4 | ||
|
|
94d139e484 | ||
|
|
8049241f3f | ||
|
|
28d308ed4b | ||
|
|
e0862053d0 | ||
|
|
e0e76d3a56 | ||
|
|
242e44315b | ||
|
|
6b53ed13e2 | ||
|
|
fafccbcecc | ||
|
|
49698bfda9 | ||
|
|
4154cc2230 | ||
|
|
ce8a96b958 | ||
|
|
f373a3abc7 | ||
|
|
371ee0a780 | ||
|
|
327cf5730b | ||
|
|
5e1a373730 | ||
|
|
717a7dc95f | ||
|
|
aa2417af12 | ||
|
|
f2533f1ed5 | ||
|
|
7d41699207 | ||
|
|
f9ccfef8e0 | ||
|
|
468a2ba251 | ||
|
|
08dbdcf4ae | ||
|
|
50d73f7bc8 | ||
|
|
d7ed9ff592 | ||
|
|
05c6fd4528 | ||
|
|
a99f424901 | ||
|
|
648354500b | ||
|
|
28c0480b8b | ||
|
|
f29ed6e29b | ||
|
|
903b4d79ac | ||
|
|
026c189901 | ||
|
|
f2b30ee349 | ||
|
|
5c02a9325a | ||
|
|
7645c8642f | ||
|
|
c5ebbe319f | ||
|
|
6f36eb1690 | ||
|
|
18f9e006a3 | ||
|
|
a7e334ebe9 | ||
|
|
a5e15d9d84 | ||
|
|
b3173517b4 | ||
|
|
06d2963163 | ||
|
|
df8282015d | ||
|
|
81b0aab850 | ||
|
|
a8705c5b44 | ||
|
|
efe739f9fc | ||
|
|
2e83a0508a | ||
|
|
d66adb2a1f | ||
|
|
e5cecdee03 | ||
|
|
9351af16b7 | ||
|
|
ad8831a75a | ||
|
|
c63018ff08 | ||
|
|
131bce3898 | ||
|
|
fff9316030 | ||
|
|
2b3148dc3c | ||
|
|
13a4202f08 | ||
|
|
f0df143aaf | ||
|
|
70a94ab2c6 | ||
|
|
e6d18d1a72 | ||
|
|
46cd57fb36 | ||
|
|
4ae389846d | ||
|
|
37d04e9e89 | ||
|
|
39d141ca67 | ||
|
|
befcc169e0 | ||
|
|
6bbb9c1b81 | ||
|
|
bb77afc4d2 |
@@ -4,5 +4,5 @@
|
|||||||
"reporter": ["html"],
|
"reporter": ["html"],
|
||||||
"report-dir": "./coverage",
|
"report-dir": "./coverage",
|
||||||
"check-coverage": true,
|
"check-coverage": true,
|
||||||
"lines": 95
|
"lines": 90
|
||||||
}
|
}
|
||||||
|
|||||||
17
.github/workflows/ci.yml
vendored
17
.github/workflows/ci.yml
vendored
@@ -1,10 +1,10 @@
|
|||||||
name: Test / Coverage
|
name: Bot CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [feature/monorepo]
|
branches: [dev]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main, dev]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -20,7 +20,10 @@ jobs:
|
|||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm install
|
- run: corepack enable
|
||||||
- run: npm run build --if-present
|
- name: Install NPM Dependencies
|
||||||
- run: npm run test.unit
|
run: yarn install --immutable --network-timeout 300000
|
||||||
- run: npm run test.coverage
|
- name: Build
|
||||||
|
run: yarn build
|
||||||
|
- name: Test
|
||||||
|
run: yarn test
|
||||||
|
|||||||
20
.github/workflows/contributors.yml
vendored
Normal file
20
.github/workflows/contributors.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
name: Add contributors
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '20 20 * * *'
|
||||||
|
push:
|
||||||
|
branches: [dev]
|
||||||
|
pull_request:
|
||||||
|
branches: [main, dev]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
contrib-readme-job:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: A job to automate contrib in readme
|
||||||
|
steps:
|
||||||
|
- name: Contribute List
|
||||||
|
uses: akhilmhdh/contributors-readme-action@v2.3.6
|
||||||
|
with:
|
||||||
|
image_size: 50
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -14,12 +14,19 @@ mediaSend/*
|
|||||||
.wwebjs_auth
|
.wwebjs_auth
|
||||||
packages/cli/config.json
|
packages/cli/config.json
|
||||||
config.json
|
config.json
|
||||||
|
.yarnrc.yml
|
||||||
coverage/
|
coverage/
|
||||||
*.lcov
|
*.lcov
|
||||||
log
|
log
|
||||||
|
log/*
|
||||||
|
*.log
|
||||||
lib
|
lib
|
||||||
tmp/
|
tmp/
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/plugins/@yarnpkg/plugin-postinstall.cjs
|
||||||
.fleet/
|
.fleet/
|
||||||
example-app/
|
example-app*/
|
||||||
qr.svg
|
qr.svg
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
yarn-error.log
|
||||||
4
.husky/commit-msg
Executable file
4
.husky/commit-msg
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
npx --no -- commitlint --edit
|
||||||
2
.husky/pre-commit
Normal file → Executable file
2
.husky/pre-commit
Normal file → Executable file
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
yarn run format:check && yarn run format:write && git add .
|
yarn run fmt.staged
|
||||||
|
|||||||
0
.husky/pre-push
Normal file → Executable file
0
.husky/pre-push
Normal file → Executable file
11
.vscode/settings.json
vendored
Normal file
11
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"conventionalCommits.scopes": [
|
||||||
|
"hook",
|
||||||
|
"contributing",
|
||||||
|
"cli",
|
||||||
|
"bot",
|
||||||
|
"provider",
|
||||||
|
"adapter",
|
||||||
|
"ci"
|
||||||
|
]
|
||||||
|
}
|
||||||
8
.yarn/plugins/@yarnpkg/plugin-postinstall.cjs
vendored
Normal file
8
.yarn/plugins/@yarnpkg/plugin-postinstall.cjs
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
module.exports = {
|
||||||
|
name: "@yarnpkg/plugin-postinstall",
|
||||||
|
factory: function (require) {
|
||||||
|
var plugin;(()=>{"use strict";var e={d:(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{default:()=>s});const n=require("@yarnpkg/core"),o=require("clipanion"),a={postinstall:{description:"Postinstall hook that will always run in Yarn v2",type:n.SettingsType.STRING,default:""}},r=require("@yarnpkg/shell"),l=async e=>{if(e){console.log("Running postinstall command...");const t=await r.execute(e);if(0!==t)throw new Error("postinstall command failed with exit code "+t)}};var i=function(e,t,n,o){var a,r=arguments.length,l=r<3?t:null===o?o=Object.getOwnPropertyDescriptor(t,n):o;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)l=Reflect.decorate(e,t,n,o);else for(var i=e.length-1;i>=0;i--)(a=e[i])&&(l=(r<3?a(l):r>3?a(t,n,l):a(t,n))||l);return r>3&&l&&Object.defineProperty(t,n,l),l};class c extends o.Command{async execute(){const e=(await n.Configuration.find(this.context.cwd,this.context.plugins)).get("postinstall");await l(e)}}i([o.Command.Path("postinstall")],c.prototype,"execute",null);const s={configuration:a,commands:[c],hooks:{afterAllInstalled:async e=>{const t=e.configuration.get("postinstall");await l(t)}}};plugin=t})();
|
||||||
|
return plugin;
|
||||||
|
}
|
||||||
|
};
|
||||||
807
.yarn/releases/yarn-3.3.0.cjs
vendored
Normal file
807
.yarn/releases/yarn-3.3.0.cjs
vendored
Normal file
File diff suppressed because one or more lines are too long
10
.yarnrc.yml
Normal file
10
.yarnrc.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
npmPublishRegistry: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- path: .yarn/plugins/@yarnpkg/plugin-postinstall.cjs
|
||||||
|
spec: 'https://raw.githubusercontent.com/gravitywelluk/yarn-plugin-postinstall/master/bundles/%40yarnpkg/plugin-postinstall.js'
|
||||||
|
|
||||||
|
yarnPath: .yarn/releases/yarn-3.3.0.cjs
|
||||||
|
postinstall: npx husky install
|
||||||
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,3 +1,48 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
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.2.0-alpha.0 (2022-12-01)
|
||||||
|
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* 🧨 NO
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* (🎸) add onClick prop to component ([4ae3898](https://github.com/leifermendez/bot-whatsapp/commit/4ae389846d38c133f6bb2129ae373eed39d9d08d))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **ci:** ci ([f55cfae](https://github.com/leifermendez/bot-whatsapp/commit/f55cfae6e4ccc1df949212999406680020d27f9c))
|
||||||
|
* **ci:** ci ([671c5b3](https://github.com/leifermendez/bot-whatsapp/commit/671c5b37f33360e8cb754625b8dd6e83bce9014d))
|
||||||
|
* **linter:** update linter and commitlint ([70a94ab](https://github.com/leifermendez/bot-whatsapp/commit/70a94ab2c6f8e4122780c77bc3a621944883e621))
|
||||||
|
|
||||||
|
|
||||||
|
* (💍) Is justa test! ([37d04e9](https://github.com/leifermendez/bot-whatsapp/commit/37d04e9e89d3f01fdc367654ba60fb11ab2614c4))
|
||||||
|
|
||||||
|
## 0.1.0 (2022-11-29)
|
||||||
|
|
||||||
|
|
||||||
|
### ⚠ BREAKING CHANGES
|
||||||
|
|
||||||
|
* 🧨 NO
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* (🎸) add onClick prop to component ([4ae3898](https://github.com/leifermendez/bot-whatsapp/commit/4ae389846d38c133f6bb2129ae373eed39d9d08d))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **ci:** ci ([f55cfae](https://github.com/leifermendez/bot-whatsapp/commit/f55cfae6e4ccc1df949212999406680020d27f9c))
|
||||||
|
* **ci:** ci ([671c5b3](https://github.com/leifermendez/bot-whatsapp/commit/671c5b37f33360e8cb754625b8dd6e83bce9014d))
|
||||||
|
|
||||||
|
|
||||||
|
* (💍) Is justa test! ([37d04e9](https://github.com/leifermendez/bot-whatsapp/commit/37d04e9e89d3f01fdc367654ba60fb11ab2614c4))
|
||||||
|
|
||||||
#### Actualización 14 Ene 2022
|
#### Actualización 14 Ene 2022
|
||||||
|
|
||||||
- npm update
|
- npm update
|
||||||
|
|||||||
59
CONTRIBUTING.md
Normal file
59
CONTRIBUTING.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# CONTRIBUTING
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
__Requerimientos:__
|
||||||
|
- Node v16 o superior __[descargar node](https://nodejs.org/es/download/)__
|
||||||
|
- __[Yarn](https://classic.yarnpkg.com/lang/en/docs/install/#windows-stable)__ como gestor de paquetes. En el link conseguiras las intrucciones para instalar yarn.
|
||||||
|
- __[VSCode](https://code.visualstudio.com/download)__ (recomendado): Editor de codigo con plugins
|
||||||
|
- __[Conventional Commits](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits&ssr=false#overview)__ (plugin-vscode) este plugin te ayudara a crear commit semantico.
|
||||||
|
- Se usara la rama __dev__ *(https://github.com/leifermendez/bot-whatsapp/tree/dev)* como rama principal hasta que se haga oficialmente el lanzamiento de la V2
|
||||||
|
|
||||||
|
### 🚀 Iniciando
|
||||||
|
|
||||||
|
__Clonar repo rama dev__
|
||||||
|
```
|
||||||
|
git clone --branch dev https://github.com/leifermendez/bot-whatsapp
|
||||||
|
```
|
||||||
|
__Instalar dependencias__
|
||||||
|
```
|
||||||
|
cd bot-whatsapp
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
__Compilar (build)__
|
||||||
|
Para compilar la aplicación es necesario ejecutar, eso te genera dentro de packages del monorepo un directorio `lib`
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
__Example-app__
|
||||||
|
Se ejecuta el CLI (Command Line Interface) para ayudarte a crear un app-bot de ejemplo
|
||||||
|
```
|
||||||
|
yarn run cli
|
||||||
|
```
|
||||||
|
|
||||||
|
Abrir carpeta __example-app-base__ y ejecutar
|
||||||
|
```
|
||||||
|
cd example-app-base
|
||||||
|
npm i
|
||||||
|
npm run pre-copy
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
__Commit y Push__
|
||||||
|
El proyecto tiene implementado __[husky](https://typicode.github.io/husky/#/)__ es una herramienta que dispara unas acciones al momento de hacer commit y hacer push
|
||||||
|
|
||||||
|
__commit:__ Los commit son semanticos esto quiere decir que deben cumplir un standar al momento de escribirlos ejemplo ` feat(adapter): new adapter myqsl ` puede ver más info sobre esto __[aquí](https://github.com/conventional-changelog/commitlint/#what-is-commitlint)__
|
||||||
|
|
||||||
|
__push:__ Cada push ejecutar `yarn run test` el cual ejecuta los test internos que tienen que cumplir con __95% de cobertura__.
|
||||||
|
|
||||||
|
|
||||||
|
> Documento en constaten actualización....
|
||||||
|
|
||||||
|
------
|
||||||
|
- [Discord](https://link.codigoencasa.com/DISCORD)
|
||||||
|
- [Twitter](https://twitter.com/leifermendez)
|
||||||
|
- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
|
||||||
|
- [Telegram](https://t.me/leifermendez)
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
2
GLOSSARY.md
Normal file
2
GLOSSARY.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
CTX: Es el objeto que representa un mensaje, con opciones, id, ref
|
||||||
|
messageInComming: Objeto entrante del provider {body, from,...}
|
||||||
81
README.md
81
README.md
@@ -1,6 +1,6 @@
|
|||||||
[](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml)
|
[](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml)
|
||||||
[](http://commitizen.github.io/cz-cli/)
|
[](http://commitizen.github.io/cz-cli/)
|
||||||
|
--------
|
||||||
🦊 Documentación: [https://bot-whatsapp.pages.dev/](https://bot-whatsapp.pages.dev/)
|
🦊 Documentación: [https://bot-whatsapp.pages.dev/](https://bot-whatsapp.pages.dev/)
|
||||||
Video como hacer PR: https://youtu.be/Lxt8Acob6aU
|
Video como hacer PR: https://youtu.be/Lxt8Acob6aU
|
||||||
|
|
||||||
@@ -8,6 +8,82 @@ Video como hacer PR: https://youtu.be/Lxt8Acob6aU
|
|||||||
|
|
||||||
|
|
||||||
**Comunidad**
|
**Comunidad**
|
||||||
|
<!-- readme: collaborators,contributors -start -->
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/leifermendez">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/15802366?v=4" width="50;" alt="leifermendez"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Leifer Mendez</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/aurik3">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/37228512?v=4" width="50;" alt="aurik3"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Null</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/vicente1992">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/57806030?v=4" width="50;" alt="vicente1992"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Manuel Vicente Ortiz</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/leifermendezfroged">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/97020486?v=4" width="50;" alt="leifermendezfroged"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Leifer Mendez</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"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Null</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/jzvi12">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/10729787?v=4" width="50;" alt="jzvi12"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Null</b></sub>
|
||||||
|
</a>
|
||||||
|
</td></tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/tonyvazgar">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/21047090?v=4" width="50;" alt="tonyvazgar"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Luis Antonio Vázquez García</b></sub>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center">
|
||||||
|
<a href="https://github.com/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 align="center">
|
||||||
|
<a href="https://github.com/yond1994">
|
||||||
|
<img src="https://avatars.githubusercontent.com/u/47557263?v=4" width="50;" alt="yond1994"/>
|
||||||
|
<br />
|
||||||
|
<sub><b>Yonathan Suarez</b></sub>
|
||||||
|
</a>
|
||||||
|
</td></tr>
|
||||||
|
</table>
|
||||||
|
<!-- readme: collaborators,contributors -end -->
|
||||||
|
|
||||||
> Forma parte de este proyecto.
|
> Forma parte de este proyecto.
|
||||||
|
|
||||||
@@ -15,6 +91,3 @@ Video como hacer PR: https://youtu.be/Lxt8Acob6aU
|
|||||||
- [Twitter](https://twitter.com/leifermendez)
|
- [Twitter](https://twitter.com/leifermendez)
|
||||||
- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
|
- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
|
||||||
- [Telegram](https://t.me/leifermendez)
|
- [Telegram](https://t.me/leifermendez)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
32
TODO.md
32
TODO.md
@@ -2,30 +2,50 @@
|
|||||||
- [X] __(doc)__ Video de como colaborar PR
|
- [X] __(doc)__ Video de como colaborar PR
|
||||||
- [ ] __(doc)__ Video implementación de test y cobertura
|
- [ ] __(doc)__ Video implementación de test y cobertura
|
||||||
- [ ] __(doc)__ Video explicacion de github action
|
- [ ] __(doc)__ Video explicacion de github action
|
||||||
|
- [ ] Crear packages list externas
|
||||||
|
|
||||||
### @bot-whatsapp/bot
|
### @bot-whatsapp/bot
|
||||||
- [ ] agregar export package
|
- [X] agregar export package
|
||||||
- [X] Posibilidad de en el capture meter todo un nuevo CTX de FLOW .addAnswer('Marca la opcion',{capture:true, join:CTX})
|
- [X] Posibilidad de en el capture meter todo un nuevo CTX de FLOW .addAnswer('Marca la opcion',{capture:true, join:CTX})
|
||||||
- [X] .addKeyword('1') no funciona con 1 caracter
|
- [X] .addKeyword('1') no funciona con 1 caracter
|
||||||
- [X] sensitivy viene activado por defecto
|
- [X] sensitivy viene activado por defecto
|
||||||
- [ ] fallback respuesta en hijo: Se puede colocar en option el ref de la answer fallback
|
- [X] fallback respuesta en hijo: Se puede colocar en option el ref de la answer fallback
|
||||||
|
- [X] Cuando Envian Sticket devuelve mensaje raro
|
||||||
|
- [x] addAnswer agregar delay
|
||||||
- [ ] colocar mensaje esperando conectando whatsapp (provider)
|
- [ ] colocar mensaje esperando conectando whatsapp (provider)
|
||||||
- [ ] Cuando Envian Sticket devuelve mensaje raro
|
|
||||||
- [ ] createDatabase validar implementacion de funciones
|
- [ ] createDatabase validar implementacion de funciones
|
||||||
|
- [ ] limitar caracteres de mensajes 4000
|
||||||
|
- [X] cuando envias numeros (5 o 1) se dispara el flujo
|
||||||
|
|
||||||
### @bot-whatsapp/database
|
### @bot-whatsapp/database
|
||||||
- [X] agregar export package
|
- [X] agregar export package
|
||||||
- [X] __(doc):__ Video para explicar como implementar nuevos database
|
- [X] __(doc):__ Video para explicar como implementar nuevos database
|
||||||
- [X] Mongo adapter
|
- [X] Mongo adapter
|
||||||
- [ ] MySQL adapter
|
- [X] MySQL adapter
|
||||||
- [ ] JsonFile adapter
|
- [ ] JsonFile adapter
|
||||||
|
|
||||||
### @bot-whatsapp/provider
|
### @bot-whatsapp/provider
|
||||||
- [X] agregar export package
|
- [X] agregar export package
|
||||||
- [ ] __(doc):__ Video para explicar como implementar nuevos providers
|
- [ ] __(doc):__ Video para explicar como implementar nuevos providers
|
||||||
- [ ] WhatsappWeb provider enviar imagenes
|
- [X] WhatsappWeb provider enviar imagenes
|
||||||
- [ ] WhatsappWeb provider enviar audio
|
- [X] WhatsappWeb provider enviar audio
|
||||||
|
- [X] WhatsappWeb botones (Tiene truco) github:leifermendez/whatsapp-web.js
|
||||||
- [ ] Twilio adapter
|
- [ ] Twilio adapter
|
||||||
- [ ] Meta adapter
|
- [ ] Meta adapter
|
||||||
|
|
||||||
### @bot-whatsapp/cli
|
### @bot-whatsapp/cli
|
||||||
|
- [X] Hacer comando para crear `example-app`
|
||||||
|
|
||||||
|
|
||||||
|
### @bot-whatsapp/create-bot
|
||||||
|
- [ ]
|
||||||
|
|
||||||
|
### Starters
|
||||||
|
- [X] Base
|
||||||
|
- [X] Basico
|
||||||
|
- [ ] Enviando Imagen
|
||||||
|
- [ ] Enviando Botones
|
||||||
|
- [ ] Mezclando flujos hijos
|
||||||
|
|
||||||
|
### Extra
|
||||||
|
- [X] Crear CI mantener fork update https://stackoverflow.com/questions/23793062/can-forks-be-synced-automatically-in-github
|
||||||
|
|||||||
91
changelog.config.js
Normal file
91
changelog.config.js
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
module.exports = {
|
||||||
|
disableEmoji: false,
|
||||||
|
format: '{type}{scope}: {emoji}{subject}',
|
||||||
|
list: [
|
||||||
|
'test',
|
||||||
|
'feat',
|
||||||
|
'fix',
|
||||||
|
'chore',
|
||||||
|
'docs',
|
||||||
|
'refactor',
|
||||||
|
'style',
|
||||||
|
'ci',
|
||||||
|
'perf',
|
||||||
|
],
|
||||||
|
maxMessageLength: 64,
|
||||||
|
minMessageLength: 3,
|
||||||
|
questions: [
|
||||||
|
'type',
|
||||||
|
'scope',
|
||||||
|
'subject',
|
||||||
|
'body',
|
||||||
|
'breaking',
|
||||||
|
'issues',
|
||||||
|
'lerna',
|
||||||
|
],
|
||||||
|
scopes: [],
|
||||||
|
types: {
|
||||||
|
chore: {
|
||||||
|
description: 'Build process or auxiliary tool changes',
|
||||||
|
emoji: '(🤖)',
|
||||||
|
value: 'chore',
|
||||||
|
},
|
||||||
|
ci: {
|
||||||
|
description: 'CI related changes',
|
||||||
|
emoji: '(🎡)',
|
||||||
|
value: 'ci',
|
||||||
|
},
|
||||||
|
docs: {
|
||||||
|
description: 'Documentation only changes',
|
||||||
|
emoji: '(✏️)',
|
||||||
|
value: 'docs',
|
||||||
|
},
|
||||||
|
feat: {
|
||||||
|
description: 'A new feature',
|
||||||
|
emoji: '(🎸)',
|
||||||
|
value: 'feat',
|
||||||
|
},
|
||||||
|
fix: {
|
||||||
|
description: 'A bug fix',
|
||||||
|
emoji: '(🐛)',
|
||||||
|
value: 'fix',
|
||||||
|
},
|
||||||
|
perf: {
|
||||||
|
description: 'A code change that improves performance',
|
||||||
|
emoji: '(⚡️)',
|
||||||
|
value: 'perf',
|
||||||
|
},
|
||||||
|
refactor: {
|
||||||
|
description:
|
||||||
|
'A code change that neither fixes a bug or adds a feature',
|
||||||
|
emoji: '(💡)',
|
||||||
|
value: 'refactor',
|
||||||
|
},
|
||||||
|
release: {
|
||||||
|
description: 'Create a release commit',
|
||||||
|
emoji: '(🏹)',
|
||||||
|
value: 'release',
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
description:
|
||||||
|
'Markup, white-space, formatting, missing semi-colons...',
|
||||||
|
emoji: '(💄)',
|
||||||
|
value: 'style',
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
description: 'Adding missing tests',
|
||||||
|
emoji: '(💍)',
|
||||||
|
value: 'test',
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
type: "Select the type of change that you're committing:",
|
||||||
|
customScope: 'Select the scope this component affects:',
|
||||||
|
subject:
|
||||||
|
'Write a short, imperative mood description of the change:\n',
|
||||||
|
body: 'Provide a longer description of the change:\n ',
|
||||||
|
breaking: 'List any breaking changes:\n',
|
||||||
|
footer: 'Issues this commit closes, e.g #123:',
|
||||||
|
confirmCommit: 'The packages that this commit has affected\n',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
1
commitlint.config.js
Normal file
1
commitlint.config.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
module.exports = { extends: ['@commitlint/config-conventional'] }
|
||||||
8
config/banner.rollup.json
Normal file
8
config/banner.rollup.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"banner.output": [
|
||||||
|
"/** \n",
|
||||||
|
"* NO TOCAR ESTE ARCHIVO: Es generado automaticamente, si sabes lo que haces adelante ;)\n",
|
||||||
|
"* de lo contrario mejor ir a la documentacion o al servidor de discord link.codigoencasa.com/DISCORD\n",
|
||||||
|
"*/"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
core.class.log
Normal file
3
core.class.log
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[handleMsg]: { from: 'XXXXXX', body: 'hola', hasMedia: false }
|
||||||
|
[handleMsg]: { from: 'XXXXXX', body: 'hola', hasMedia: false }
|
||||||
|
[handleMsg]: { from: 'XXXXXX', body: 'hola', hasMedia: false }
|
||||||
34
package.json
34
package.json
@@ -1,31 +1,37 @@
|
|||||||
{
|
{
|
||||||
"name": "@bot-whatsapp/root",
|
"name": "@bot-whatsapp/root",
|
||||||
"version": "0.0.1",
|
"version": "0.2.0-alpha.0",
|
||||||
"description": "Bot de wahtsapp open source para MVP o pequeños negocios",
|
"description": "Bot de wahtsapp open source para MVP o pequeños negocios",
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"commit": "git-cz",
|
"commit": "git-cz",
|
||||||
"cli:rollup": "rollup --config ./packages/cli/rollup-cli.config.js ",
|
"cli:rollup": "rollup --config ./packages/cli/rollup-cli.config.js ",
|
||||||
|
"create-bot:rollup": "rollup --config ./packages/create-bot-whatsapp/rollup-create.config.js ",
|
||||||
"bot:rollup": "rollup --config ./packages/bot/rollup-bot.config.js",
|
"bot:rollup": "rollup --config ./packages/bot/rollup-bot.config.js",
|
||||||
"provider:rollup": "rollup --config ./packages/provider/rollup-provider.config.js ",
|
"provider:rollup": "rollup --config ./packages/provider/rollup-provider.config.js ",
|
||||||
"database:rollup": "rollup --config ./packages/database/rollup-database.config.js",
|
"database:rollup": "rollup --config ./packages/database/rollup-database.config.js",
|
||||||
"format:check": "prettier --check ./packages",
|
"format:check": "prettier --check ./packages",
|
||||||
"format:write": "prettier --write ./packages",
|
"format:write": "prettier --write ./packages",
|
||||||
|
"fmt.staged": "pretty-quick --staged",
|
||||||
"lint:check": "eslint ./packages",
|
"lint:check": "eslint ./packages",
|
||||||
"lint:fix": "eslint --fix ./packages",
|
"lint:fix": "eslint --fix ./packages",
|
||||||
"build": "yarn run cli:rollup && yarn run bot:rollup && yarn run provider:rollup && yarn run database:rollup",
|
"build": "yarn run cli:rollup && yarn run bot:rollup && yarn run provider:rollup && yarn run database:rollup",
|
||||||
"link.dist": "cd packages/bot && npm link && cd ../provider && npm link && cd ../cli && npm link",
|
"copy.lib": "node ./scripts/move.js",
|
||||||
"test.unit": "node ./node_modules/uvu/bin.js packages test",
|
"test.unit": "node ./node_modules/uvu/bin.js packages test",
|
||||||
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit",
|
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit",
|
||||||
"test": "npm run test.coverage",
|
"test": "npm run test.coverage",
|
||||||
"cli": "node ./packages/cli/bin/cli.js",
|
"cli": "node ./packages/cli/bin/cli.js",
|
||||||
|
"create": "node ./packages/create-bot-whatsapp/bin/create.js",
|
||||||
"dev:debug": "node --inspect ./example-app/app.js",
|
"dev:debug": "node --inspect ./example-app/app.js",
|
||||||
"dev": "node ./example-app/app.js",
|
"dev": "node ./example-app/app.js",
|
||||||
"prepare": "npx husky install",
|
"prepare": "npx husky install",
|
||||||
"preinstall": "npx only-allow yarn"
|
"preinstall": "npx only-allow yarn",
|
||||||
|
"postinstall": "npx prettier --write .",
|
||||||
|
"release": "standard-version"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
|
"packages/create-bot-whatsapp",
|
||||||
"packages/bot",
|
"packages/bot",
|
||||||
"packages/cli",
|
"packages/cli",
|
||||||
"packages/database",
|
"packages/database",
|
||||||
@@ -52,36 +58,36 @@
|
|||||||
"repository": "https://github.com/leifermendez/bot-whatsapp",
|
"repository": "https://github.com/leifermendez/bot-whatsapp",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^17.3.0",
|
||||||
|
"@commitlint/config-conventional": "^17.3.0",
|
||||||
"@rollup/plugin-commonjs": "^23.0.2",
|
"@rollup/plugin-commonjs": "^23.0.2",
|
||||||
"@rollup/plugin-json": "^5.0.1",
|
"@rollup/plugin-json": "^5.0.1",
|
||||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
"@rollup/plugin-replace": "^5.0.1",
|
"@rollup/plugin-replace": "^5.0.1",
|
||||||
"c8": "^7.12.0",
|
"c8": "^7.12.0",
|
||||||
"commitizen": "^4.2.5",
|
"conventional-changelog": "^3.1.25",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"cz-conventional-changelog": "^3.3.0",
|
|
||||||
"eslint": "^8.26.0",
|
"eslint": "^8.26.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"fs-extra": "^11.1.0",
|
||||||
|
"git-cz": "^4.9.0",
|
||||||
"husky": "^8.0.2",
|
"husky": "^8.0.2",
|
||||||
"only-allow": "^1.1.1",
|
"only-allow": "^1.1.1",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^2.8.0",
|
||||||
|
"pretty-quick": "^3.1.3",
|
||||||
"prompts": "^2.4.2",
|
"prompts": "^2.4.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rollup": "^3.2.3",
|
"rollup": "^3.2.3",
|
||||||
"rollup-plugin-cleanup": "^3.2.1",
|
"rollup-plugin-cleanup": "^3.2.1",
|
||||||
"rollup-plugin-copy": "^3.4.0",
|
"rollup-plugin-copy": "^3.4.0",
|
||||||
|
"standard-version": "^9.5.0",
|
||||||
"uvu": "^0.5.6"
|
"uvu": "^0.5.6"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.19",
|
"packageManager": "yarn@3.3.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16",
|
"node": ">=16",
|
||||||
"npm": "please-use-yarn",
|
"npm": "please-use-yarn",
|
||||||
"yarn": ">=1"
|
"yarn": ">=3"
|
||||||
},
|
},
|
||||||
"author": "Leifer Mendez <leifer33@gmail.com>",
|
"author": "Leifer Mendez <leifer33@gmail.com>"
|
||||||
"config": {
|
|
||||||
"commitizen": {
|
|
||||||
"path": "./node_modules/cz-conventional-changelog"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
const { toCtx } = require('../io/methods')
|
const { toCtx } = require('../io/methods')
|
||||||
const { printer } = require('../utils/interactive')
|
const { printer } = require('../utils/interactive')
|
||||||
|
const { delay } = require('../utils/delay')
|
||||||
|
const Queue = require('../utils/queue')
|
||||||
|
const { Console } = require('console')
|
||||||
|
const { createWriteStream } = require('fs')
|
||||||
|
|
||||||
|
const logger = new Console({
|
||||||
|
stdout: createWriteStream(`${process.cwd()}/core.class.log`),
|
||||||
|
})
|
||||||
/**
|
/**
|
||||||
* [ ] Escuchar eventos del provider asegurarte que los provider emitan eventos
|
* [ ] Escuchar eventos del provider asegurarte que los provider emitan eventos
|
||||||
* [ ] Guardar historial en db
|
* [ ] Guardar historial en db
|
||||||
@@ -21,7 +28,14 @@ class CoreClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manejador de eventos
|
||||||
|
*/
|
||||||
listenerBusEvents = () => [
|
listenerBusEvents = () => [
|
||||||
|
{
|
||||||
|
event: 'preinit',
|
||||||
|
func: () => printer('Iniciando provider espere...'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
event: 'require_action',
|
event: 'require_action',
|
||||||
func: ({ instructions, title = '⚡⚡ ACCION REQUERIDA ⚡⚡' }) =>
|
func: ({ instructions, title = '⚡⚡ ACCION REQUERIDA ⚡⚡' }) =>
|
||||||
@@ -44,16 +58,19 @@ class CoreClass {
|
|||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
*
|
||||||
* @param {*} ctxMessage
|
* @param {*} messageInComming
|
||||||
|
* @returns
|
||||||
*/
|
*/
|
||||||
handleMsg = async (messageInComming) => {
|
handleMsg = async (messageInComming) => {
|
||||||
|
logger.log(`[handleMsg]: `, messageInComming)
|
||||||
const { body, from } = messageInComming
|
const { body, from } = messageInComming
|
||||||
let msgToSend = []
|
let msgToSend = []
|
||||||
|
let fallBackFlag = false
|
||||||
|
|
||||||
|
if (!body.length) return
|
||||||
|
|
||||||
//Consultamos mensaje previo en DB
|
|
||||||
const prevMsg = await this.databaseClass.getPrevByNumber(from)
|
const prevMsg = await this.databaseClass.getPrevByNumber(from)
|
||||||
//Consultamos for refSerializada en el flow actual
|
|
||||||
const refToContinue = this.flowClass.findBySerialize(
|
const refToContinue = this.flowClass.findBySerialize(
|
||||||
prevMsg?.refSerialize
|
prevMsg?.refSerialize
|
||||||
)
|
)
|
||||||
@@ -67,14 +84,24 @@ class CoreClass {
|
|||||||
this.databaseClass.save(ctxByNumber)
|
this.databaseClass.save(ctxByNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Si se tiene un callback se ejecuta
|
// 📄 [options: fallback]: esta funcion se encarga de repetir el ultimo mensaje
|
||||||
if (refToContinue && prevMsg?.options?.callback) {
|
const fallBack = () => {
|
||||||
const indexFlow = this.flowClass.findIndexByRef(refToContinue?.ref)
|
fallBackFlag = true
|
||||||
this.flowClass.allCallbacks[indexFlow].callback(messageInComming)
|
msgToSend = this.flowClass.find(refToContinue?.keyword, true) || []
|
||||||
|
this.sendFlow(msgToSend, from)
|
||||||
|
return refToContinue
|
||||||
}
|
}
|
||||||
|
|
||||||
//Si se tiene anidaciones de flows, si tienes anidados obligatoriamente capture:true
|
// 📄 [options: callback]: Si se tiene un callback se ejecuta
|
||||||
if (prevMsg?.options?.nested?.length) {
|
if (!fallBackFlag && refToContinue && prevMsg?.options?.callback) {
|
||||||
|
const indexFlow = this.flowClass.findIndexByRef(refToContinue?.ref)
|
||||||
|
this.flowClass.allCallbacks[indexFlow].callback(messageInComming, {
|
||||||
|
fallBack,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 📄🤘(tiene return) [options: nested(array)]: Si se tiene flujos hijos los implementa
|
||||||
|
if (!fallBackFlag && prevMsg?.options?.nested?.length) {
|
||||||
const nestedRef = prevMsg.options.nested
|
const nestedRef = prevMsg.options.nested
|
||||||
const flowStandalone = nestedRef.map((f) => ({
|
const flowStandalone = nestedRef.map((f) => ({
|
||||||
...nestedRef.find((r) => r.refSerialize === f.refSerialize),
|
...nestedRef.find((r) => r.refSerialize === f.refSerialize),
|
||||||
@@ -85,28 +112,44 @@ class CoreClass {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Consultamos si se espera respuesta por parte de cliente "Ejemplo: Dime tu nombre"
|
// 📄🤘(tiene return) [options: capture (boolean)]: Si se tiene option boolean
|
||||||
if (!prevMsg?.options?.nested?.length && prevMsg?.options?.capture) {
|
if (!fallBackFlag && !prevMsg?.options?.nested?.length) {
|
||||||
|
const typeCapture = typeof prevMsg?.options?.capture
|
||||||
|
const valueCapture = prevMsg?.options?.capture
|
||||||
|
|
||||||
|
if (['string', 'boolean'].includes(typeCapture) && valueCapture) {
|
||||||
msgToSend = this.flowClass.find(refToContinue?.ref, true) || []
|
msgToSend = this.flowClass.find(refToContinue?.ref, true) || []
|
||||||
} else {
|
this.sendFlow(msgToSend, from)
|
||||||
msgToSend = this.flowClass.find(body) || []
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msgToSend = this.flowClass.find(body) || []
|
||||||
this.sendFlow(msgToSend, from)
|
this.sendFlow(msgToSend, from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviar mensaje con contexto atraves del proveedor de whatsapp
|
||||||
|
* @param {*} numberOrId
|
||||||
|
* @param {*} ctxMessage ver más en GLOSSARY.md
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
sendProviderAndSave = (numberOrId, ctxMessage) => {
|
sendProviderAndSave = (numberOrId, ctxMessage) => {
|
||||||
const { answer } = ctxMessage
|
const { answer } = ctxMessage
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
this.providerClass.sendMessage(numberOrId, answer),
|
this.providerClass.sendMessage(numberOrId, answer, ctxMessage),
|
||||||
this.databaseClass.save({ ...ctxMessage, from: numberOrId }),
|
this.databaseClass.save({ ...ctxMessage, from: numberOrId }),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
sendFlow = (messageToSend, numberOrId) => {
|
sendFlow = async (messageToSend, numberOrId) => {
|
||||||
const queue = []
|
const queue = []
|
||||||
for (const ctxMessage of messageToSend) {
|
for (const ctxMessage of messageToSend) {
|
||||||
queue.push(this.sendProviderAndSave(numberOrId, ctxMessage))
|
const delayMs = ctxMessage?.options?.delay || 0
|
||||||
|
if (delayMs) await delay(delayMs)
|
||||||
|
Queue.enqueue(() =>
|
||||||
|
this.sendProviderAndSave(numberOrId, ctxMessage)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return Promise.all(queue)
|
return Promise.all(queue)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const CoreClass = require('./core/core.class')
|
const CoreClass = require('./core/core.class')
|
||||||
const ProviderClass = require('./provider/provider.class')
|
const ProviderClass = require('./provider/provider.class')
|
||||||
const FlowClass = require('./io/flow.class')
|
const FlowClass = require('./io/flow.class')
|
||||||
const { addKeyword, addAnswer, toSerialize } = require('./io/methods')
|
const { addKeyword, addAnswer, addChild, toSerialize } = require('./io/methods')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Crear instancia de clase Bot
|
* Crear instancia de clase Bot
|
||||||
@@ -22,11 +22,13 @@ const createFlow = (args) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Crear instancia de clase Provider
|
* Crear instancia de clase Provider
|
||||||
|
* Depdendiendo del Provider puedes pasar argumentos
|
||||||
|
* Ver Documentacion
|
||||||
* @param {*} args
|
* @param {*} args
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const createProvider = (providerClass = class {}) => {
|
const createProvider = (providerClass = class {}, args = null) => {
|
||||||
const providerInstance = new providerClass()
|
const providerInstance = new providerClass(args)
|
||||||
if (!providerClass.prototype instanceof ProviderClass)
|
if (!providerClass.prototype instanceof ProviderClass)
|
||||||
throw new Error('El provider no implementa ProviderClass')
|
throw new Error('El provider no implementa ProviderClass')
|
||||||
return providerInstance
|
return providerInstance
|
||||||
@@ -38,6 +40,7 @@ module.exports = {
|
|||||||
createProvider,
|
createProvider,
|
||||||
addKeyword,
|
addKeyword,
|
||||||
addAnswer,
|
addAnswer,
|
||||||
|
addChild,
|
||||||
toSerialize,
|
toSerialize,
|
||||||
ProviderClass,
|
ProviderClass,
|
||||||
CoreClass,
|
CoreClass,
|
||||||
|
|||||||
@@ -21,29 +21,25 @@ class FlowClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
find = (keyOrWord, symbol = false, overFlow = null) => {
|
find = (keyOrWord, symbol = false, overFlow = null) => {
|
||||||
|
keyOrWord = `${keyOrWord}`
|
||||||
let capture = false
|
let capture = false
|
||||||
let messages = []
|
let messages = []
|
||||||
let refSymbol = null
|
let refSymbol = null
|
||||||
overFlow = overFlow ?? this.flowSerialize
|
overFlow = overFlow ?? this.flowSerialize
|
||||||
|
|
||||||
const mapSensitiveString = (str, flag = false) => {
|
/** Retornar expresion regular para buscar coincidencia */
|
||||||
if (!flag && Array.isArray(str)) {
|
const mapSensitive = (str, flag = false) => {
|
||||||
return str.map((c) => c.toLowerCase())
|
const regexSensitive = flag ? 'g' : 'i'
|
||||||
|
if (Array.isArray(str)) {
|
||||||
|
return new RegExp(str.join('|'), regexSensitive)
|
||||||
}
|
}
|
||||||
|
return new RegExp(str, regexSensitive)
|
||||||
if (!flag && typeof str === 'string') {
|
|
||||||
return str.toLowerCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const findIn = (keyOrWord, symbol = false, flow = overFlow) => {
|
const findIn = (keyOrWord, symbol = false, flow = overFlow) => {
|
||||||
const sensitive = refSymbol?.options?.sensitive || false
|
const sensitive = refSymbol?.options?.sensitive || false
|
||||||
capture = refSymbol?.options?.capture || false
|
capture = refSymbol?.options?.capture || false
|
||||||
|
|
||||||
keyOrWord = mapSensitiveString(keyOrWord, sensitive)
|
|
||||||
|
|
||||||
if (capture) return messages
|
if (capture) return messages
|
||||||
|
|
||||||
if (symbol) {
|
if (symbol) {
|
||||||
@@ -51,9 +47,9 @@ class FlowClass {
|
|||||||
if (refSymbol?.answer) messages.push(refSymbol)
|
if (refSymbol?.answer) messages.push(refSymbol)
|
||||||
if (refSymbol?.ref) findIn(refSymbol.ref, true)
|
if (refSymbol?.ref) findIn(refSymbol.ref, true)
|
||||||
} else {
|
} else {
|
||||||
refSymbol = flow.find((c) =>
|
refSymbol = flow.find((c) => {
|
||||||
mapSensitiveString(c.keyword, sensitive).includes(keyOrWord)
|
return mapSensitive(c.keyword, sensitive).test(keyOrWord)
|
||||||
)
|
})
|
||||||
if (refSymbol?.ref) findIn(refSymbol.ref, true)
|
if (refSymbol?.ref) findIn(refSymbol.ref, true)
|
||||||
return messages
|
return messages
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
const { generateRef } = require('../../utils/hash')
|
const { generateRef } = require('../../utils/hash')
|
||||||
const { toJson } = require('./toJson')
|
const { toJson } = require('./toJson')
|
||||||
const { toSerialize } = require('./toSerialize')
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param answer string
|
* @param answer string
|
||||||
* @param options {media:string, buttons:[], capture:true default false}
|
* @param options {media:string, buttons:[{"body":"😎 Cursos"}], delay:ms, capture:true default false}
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const addAnswer =
|
const addAnswer =
|
||||||
(inCtx) =>
|
(inCtx) =>
|
||||||
(answer, options, cb = null, nested = []) => {
|
(answer, options, cb = null, nested = []) => {
|
||||||
|
answer = Array.isArray(answer) ? answer.join('\n') : answer
|
||||||
/**
|
/**
|
||||||
* Todas las opciones referentes a el mensaje en concreto options:{}
|
* Todas las opciones referentes a el mensaje en concreto options:{}
|
||||||
* @returns
|
* @returns
|
||||||
@@ -24,6 +24,7 @@ const addAnswer =
|
|||||||
: false,
|
: false,
|
||||||
child:
|
child:
|
||||||
typeof options?.child === 'string' ? `${options?.child}` : null,
|
typeof options?.child === 'string' ? `${options?.child}` : null,
|
||||||
|
delay: typeof options?.delay === 'number' ? options?.delay : 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
const getNested = () => ({
|
const getNested = () => ({
|
||||||
@@ -78,6 +79,7 @@ const addAnswer =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retornar contexto no colocar nada más abajo de esto
|
||||||
const ctx = ctxAnswer()
|
const ctx = ctxAnswer()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
15
packages/bot/io/methods/addChild.js
Normal file
15
packages/bot/io/methods/addChild.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
const { toSerialize } = require('./toSerialize')
|
||||||
|
/**
|
||||||
|
* @deprecate
|
||||||
|
* @param answer string
|
||||||
|
* @param options {media:string, buttons:[], capture:true default false}
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const addChild = (flowIn = null) => {
|
||||||
|
if (!flowIn?.toJson) {
|
||||||
|
throw new Error('DEBE SER UN FLOW CON toJSON()')
|
||||||
|
}
|
||||||
|
return toSerialize(flowIn.toJson())
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { addChild }
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
const { addAnswer } = require('./addAnswer')
|
const { addAnswer } = require('./addAnswer')
|
||||||
const { addKeyword } = require('./addKeyword')
|
const { addKeyword } = require('./addKeyword')
|
||||||
|
const { addChild } = require('./addChild')
|
||||||
const { toSerialize } = require('./toSerialize')
|
const { toSerialize } = require('./toSerialize')
|
||||||
const { toCtx } = require('./toCtx')
|
const { toCtx } = require('./toCtx')
|
||||||
const { toJson } = require('./toJson')
|
const { toJson } = require('./toJson')
|
||||||
|
|
||||||
module.exports = { addAnswer, addKeyword, toCtx, toJson, toSerialize }
|
module.exports = { addAnswer, addKeyword, addChild, toCtx, toJson, toSerialize }
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class ProviderClass extends EventEmitter {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sendMessage = async (userId, message) => {
|
sendMessage = async (userId, message, sendMessage) => {
|
||||||
if (NODE_ENV !== 'production')
|
if (NODE_ENV !== 'production')
|
||||||
console.log('[sendMessage]', { userId, message })
|
console.log('[sendMessage]', { userId, message })
|
||||||
return message
|
return message
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const banner = require('../../config/banner.rollup.json')
|
||||||
const commonjs = require('@rollup/plugin-commonjs')
|
const commonjs = require('@rollup/plugin-commonjs')
|
||||||
const { nodeResolve } = require('@rollup/plugin-node-resolve')
|
const { nodeResolve } = require('@rollup/plugin-node-resolve')
|
||||||
const { join } = require('path')
|
const { join } = require('path')
|
||||||
@@ -7,6 +8,7 @@ const PATH = join(__dirname, 'lib', 'bundle.bot.cjs')
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
input: join(__dirname, 'index.js'),
|
input: join(__dirname, 'index.js'),
|
||||||
output: {
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
file: PATH,
|
file: PATH,
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,6 +22,15 @@ test('Debere probar las propeidades array', () => {
|
|||||||
assert.is(MAIN_CTX.ctx.keyword, ARRANGE.keyword)
|
assert.is(MAIN_CTX.ctx.keyword, ARRANGE.keyword)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Debere probar las propeidades array en answer', () => {
|
||||||
|
const ARRANGE = {
|
||||||
|
keyword: ['hola!', 'ole'],
|
||||||
|
}
|
||||||
|
const MAIN_CTX = addKeyword(ARRANGE.keyword).addAnswer(['hola', 'chao'])
|
||||||
|
|
||||||
|
assert.is(MAIN_CTX.ctx.keyword, ARRANGE.keyword)
|
||||||
|
})
|
||||||
|
|
||||||
test('Debere probar toSerialize', () => {
|
test('Debere probar toSerialize', () => {
|
||||||
const ARRANGE = {
|
const ARRANGE = {
|
||||||
keyword: ['hola!', 'ole'],
|
keyword: ['hola!', 'ole'],
|
||||||
|
|||||||
4
packages/bot/utils/delay.js
Normal file
4
packages/bot/utils/delay.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
const delay = (miliseconds) =>
|
||||||
|
new Promise((res) => setTimeout(res, miliseconds))
|
||||||
|
|
||||||
|
module.exports = { delay }
|
||||||
46
packages/bot/utils/queue.js
Normal file
46
packages/bot/utils/queue.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
class Queue {
|
||||||
|
static queue = []
|
||||||
|
static pendingPromise = false
|
||||||
|
|
||||||
|
static enqueue(promise) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.queue.push({
|
||||||
|
promise,
|
||||||
|
resolve,
|
||||||
|
reject,
|
||||||
|
})
|
||||||
|
this.dequeue()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static dequeue() {
|
||||||
|
if (this.workingOnPromise) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const item = this.queue.shift()
|
||||||
|
if (!item) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.workingOnPromise = true
|
||||||
|
item.promise()
|
||||||
|
.then((value) => {
|
||||||
|
this.workingOnPromise = false
|
||||||
|
item.resolve(value)
|
||||||
|
this.dequeue()
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this.workingOnPromise = false
|
||||||
|
item.reject(err)
|
||||||
|
this.dequeue()
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
this.workingOnPromise = false
|
||||||
|
item.reject(err)
|
||||||
|
this.dequeue()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Queue
|
||||||
0
packages/cli/bin/cli.js
Normal file → Executable file
0
packages/cli/bin/cli.js
Normal file → Executable file
21
packages/cli/create-app/index.js
Normal file
21
packages/cli/create-app/index.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
const fs = require('fs-extra')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy files
|
||||||
|
*/
|
||||||
|
const copyFiles = async (from, to) => {
|
||||||
|
try {
|
||||||
|
await fs.copy(from, to)
|
||||||
|
console.log('success!')
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const copyBaseApp = async () => {
|
||||||
|
const BASEP_APP_PATH_FROM = `${process.cwd()}/starters/apps/base`
|
||||||
|
const BASEP_APP_PATH_TO = `${process.cwd()}/example-app-base`
|
||||||
|
await copyFiles(BASEP_APP_PATH_FROM, BASEP_APP_PATH_TO)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { copyBaseApp }
|
||||||
@@ -2,6 +2,7 @@ const prompts = require('prompts')
|
|||||||
const { yellow, red } = require('kleur')
|
const { yellow, red } = require('kleur')
|
||||||
const { installAll } = require('../install')
|
const { installAll } = require('../install')
|
||||||
const { cleanSession } = require('../clean')
|
const { cleanSession } = require('../clean')
|
||||||
|
const { copyBaseApp } = require('../create-app')
|
||||||
const { checkNodeVersion, checkOs } = require('../check')
|
const { checkNodeVersion, checkOs } = require('../check')
|
||||||
const { jsonConfig } = require('../configuration')
|
const { jsonConfig } = require('../configuration')
|
||||||
|
|
||||||
@@ -9,10 +10,16 @@ const startInteractive = async () => {
|
|||||||
const questions = [
|
const questions = [
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'dependencies',
|
name: 'exampeOpt',
|
||||||
message:
|
message:
|
||||||
'Quieres actualizar las librerias "whatsapp-web.js"? (Y/n)',
|
'Quieres crear una app de ejemplo "example-app-example"? (Y/n)',
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// type: 'text',
|
||||||
|
// name: 'dependencies',
|
||||||
|
// message:
|
||||||
|
// 'Quieres actualizar las librerias "whatsapp-web.js"? (Y/n)',
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
type: 'text',
|
type: 'text',
|
||||||
name: 'cleanTmp',
|
name: 'cleanTmp',
|
||||||
@@ -57,11 +64,12 @@ const startInteractive = async () => {
|
|||||||
const {
|
const {
|
||||||
dependencies = '',
|
dependencies = '',
|
||||||
cleanTmp = '',
|
cleanTmp = '',
|
||||||
|
exampeOpt = '',
|
||||||
providerDb = [],
|
providerDb = [],
|
||||||
providerWs = [],
|
providerWs = [],
|
||||||
} = response
|
} = response
|
||||||
/**
|
/**
|
||||||
* Question #1
|
* Question
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const installOrUdpateDep = async () => {
|
const installOrUdpateDep = async () => {
|
||||||
@@ -75,7 +83,7 @@ const startInteractive = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Question #2
|
* Question
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const cleanAllSession = async () => {
|
const cleanAllSession = async () => {
|
||||||
@@ -88,6 +96,16 @@ const startInteractive = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createApp = async () => {
|
||||||
|
const answer = exampeOpt.toLowerCase() || 'n'
|
||||||
|
if (answer.includes('n')) return true
|
||||||
|
|
||||||
|
if (answer.includes('y')) {
|
||||||
|
await copyBaseApp()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const vendorProvider = async () => {
|
const vendorProvider = async () => {
|
||||||
if (!providerWs.length) {
|
if (!providerWs.length) {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -117,6 +135,7 @@ const startInteractive = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await createApp()
|
||||||
await installOrUdpateDep()
|
await installOrUdpateDep()
|
||||||
await cleanAllSession()
|
await cleanAllSession()
|
||||||
await vendorProvider()
|
await vendorProvider()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const banner = require('../../config/banner.rollup.json')
|
||||||
const commonjs = require('@rollup/plugin-commonjs')
|
const commonjs = require('@rollup/plugin-commonjs')
|
||||||
const { nodeResolve } = require('@rollup/plugin-node-resolve')
|
const { nodeResolve } = require('@rollup/plugin-node-resolve')
|
||||||
const { join } = require('path')
|
const { join } = require('path')
|
||||||
@@ -7,6 +8,7 @@ const PATH = join(__dirname, 'lib', 'cli', 'bundle.cli.cjs')
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
input: join(__dirname, 'index.js'),
|
input: join(__dirname, 'index.js'),
|
||||||
output: {
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
file: PATH,
|
file: PATH,
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
},
|
},
|
||||||
|
|||||||
3
packages/create-bot-whatsapp/bin/create.js
Normal file
3
packages/create-bot-whatsapp/bin/create.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
const main = require('../lib/bin/bundle.create.cjs')
|
||||||
|
main()
|
||||||
12
packages/create-bot-whatsapp/index.js
Normal file
12
packages/create-bot-whatsapp/index.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Main function
|
||||||
|
*/
|
||||||
|
const main = () => {
|
||||||
|
console.clear()
|
||||||
|
console.log(``)
|
||||||
|
console.log(`[PostInstall]: Este es el main function.`)
|
||||||
|
console.log(`[PostInstall]: 👌 Aqui podrias instalar cosas`)
|
||||||
|
console.log(``)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = main
|
||||||
13
packages/create-bot-whatsapp/package.json
Normal file
13
packages/create-bot-whatsapp/package.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "create-bot-whatsapp",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "./lib/bin/bundle.create.cjs",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@bot-whatsapp/cli": "*"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"bot": "./lib/bin/bundle.create.cjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
16
packages/create-bot-whatsapp/rollup-create.config.js
Normal file
16
packages/create-bot-whatsapp/rollup-create.config.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const banner = require('../../config/banner.rollup.json')
|
||||||
|
const commonjs = require('@rollup/plugin-commonjs')
|
||||||
|
const { nodeResolve } = require('@rollup/plugin-node-resolve')
|
||||||
|
const { join } = require('path')
|
||||||
|
|
||||||
|
const PATH = join(__dirname, 'lib', 'bin', 'bundle.create.cjs')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
input: join(__dirname, 'index.js'),
|
||||||
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
|
file: PATH,
|
||||||
|
format: 'cjs',
|
||||||
|
},
|
||||||
|
plugins: [commonjs(), nodeResolve()],
|
||||||
|
}
|
||||||
@@ -25,6 +25,28 @@ const main = async () => {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
#### CTX
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
ref: 'ans_7d9981e5-5019-422c-a19a-565cbb021391',
|
||||||
|
keyword: 'ans_cfdad31b-ff6d-475f-873a-4ed6f8a79a43',
|
||||||
|
answer: 'Esperando respuesta...',
|
||||||
|
options: {
|
||||||
|
media: null,
|
||||||
|
buttons: [],
|
||||||
|
capture: true,
|
||||||
|
child: null,
|
||||||
|
nested: [Array],
|
||||||
|
keyword: {},
|
||||||
|
callback: true
|
||||||
|
},
|
||||||
|
refSerialize: '81f18f563fd26a6c6d12c62aed98095f',
|
||||||
|
from: 'NUMERO_PERSONA_QUE_ESCRIBE'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
#### Video
|
#### Video
|
||||||
|
|
||||||
> Video explicando como debes de agregar nuevos adaptadores
|
> Video explicando como debes de agregar nuevos adaptadores
|
||||||
|
|||||||
@@ -7,13 +7,16 @@
|
|||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"mongodb": "^4.11.0"
|
"mongodb": "^4.11.0",
|
||||||
|
"mysql2": "^2.3.3",
|
||||||
|
"stormdb": "^0.6.0"
|
||||||
},
|
},
|
||||||
"exports": {
|
"exports": {
|
||||||
"./mock": "./lib/mock/index.cjs",
|
"./mock": "./lib/mock/index.cjs",
|
||||||
"./mongo": "./lib/mongo/index.cjs"
|
"./mongo": "./lib/mongo/index.cjs",
|
||||||
|
"./json-file": "./lib/json-file/index.cjs",
|
||||||
|
"./mysql": "./lib/mysql/index.cjs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const banner = require('../../config/banner.rollup.json')
|
||||||
const commonjs = require('@rollup/plugin-commonjs')
|
const commonjs = require('@rollup/plugin-commonjs')
|
||||||
const { join } = require('path')
|
const { join } = require('path')
|
||||||
|
|
||||||
@@ -5,6 +6,7 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
input: join(__dirname, 'src', 'mock', 'index.js'),
|
input: join(__dirname, 'src', 'mock', 'index.js'),
|
||||||
output: {
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
file: join(__dirname, 'lib', 'mock', 'index.cjs'),
|
file: join(__dirname, 'lib', 'mock', 'index.cjs'),
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
},
|
},
|
||||||
@@ -13,9 +15,27 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
input: join(__dirname, 'src', 'mongo', 'index.js'),
|
input: join(__dirname, 'src', 'mongo', 'index.js'),
|
||||||
output: {
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
file: join(__dirname, 'lib', 'mongo', 'index.cjs'),
|
file: join(__dirname, 'lib', 'mongo', 'index.cjs'),
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
},
|
},
|
||||||
plugins: [commonjs()],
|
plugins: [commonjs()],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: join(__dirname, 'src', 'mysql', 'index.js'),
|
||||||
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
|
file: join(__dirname, 'lib', 'mysql', 'index.cjs'),
|
||||||
|
format: 'cjs',
|
||||||
|
},
|
||||||
|
plugins: [commonjs()],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: join(__dirname, 'src', 'json-file', 'index.js'),
|
||||||
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
|
file: join(__dirname, 'lib', 'json-file', 'index.cjs'),
|
||||||
|
},
|
||||||
|
plugins: [commonjs()],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
48
packages/database/src/json-file/index.js
Normal file
48
packages/database/src/json-file/index.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
const path = require('path')
|
||||||
|
const StormDB = require('stormdb')
|
||||||
|
const engine = new StormDB.localFileEngine(
|
||||||
|
path.join(process.cwd(), './db.stormdb')
|
||||||
|
)
|
||||||
|
|
||||||
|
class JsonFileAdapter {
|
||||||
|
db
|
||||||
|
listHistory = []
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.init().then()
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.db = new StormDB(engine)
|
||||||
|
this.db.default({ history: [] })
|
||||||
|
resolve(this.db)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrevByNumber = async (from) => {
|
||||||
|
const response = await this.db.get('history')
|
||||||
|
const { history } = response.state
|
||||||
|
|
||||||
|
if (!history.length) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = history.filter((res) => res.from === from).pop()
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save = async (ctx) => {
|
||||||
|
await this.db
|
||||||
|
.get('history')
|
||||||
|
.push({ ...ctx })
|
||||||
|
.save()
|
||||||
|
console.log('Guardado en DB...', ctx)
|
||||||
|
this.listHistory.push(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = JsonFileAdapter
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Si necesitas saber que trae el "ctx"
|
||||||
|
* Puedes ver el README.md dentro packages/database
|
||||||
|
*/
|
||||||
|
|
||||||
class MockDatabase {
|
class MockDatabase {
|
||||||
|
db
|
||||||
listHistory = []
|
listHistory = []
|
||||||
|
|
||||||
constructor() {
|
constructor() {}
|
||||||
/**
|
|
||||||
* Se debe cargar listHistory con historial de mensajes
|
getPrevByNumber = (from) => {
|
||||||
* para que se pueda continuar el flow
|
const history = this.listHistory.slice().reverse()
|
||||||
*/
|
return history.find((a) => a.from === from)
|
||||||
}
|
}
|
||||||
|
|
||||||
save = (ctx) => {
|
save = (ctx) => {
|
||||||
console.log('Guardando DB...', ctx)
|
|
||||||
this.listHistory.push(ctx)
|
this.listHistory.push(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
106
packages/database/src/mysql/index.js
Normal file
106
packages/database/src/mysql/index.js
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
const mysql = require('mysql2')
|
||||||
|
|
||||||
|
class MyslAdapter {
|
||||||
|
db
|
||||||
|
listHistory = []
|
||||||
|
credentials = { host: null, user: null, database: null }
|
||||||
|
|
||||||
|
constructor(_credentials) {
|
||||||
|
this.credentials = _credentials
|
||||||
|
this.init().then()
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
this.db = mysql.createConnection(this.credentials)
|
||||||
|
|
||||||
|
await this.db.connect(async (error) => {
|
||||||
|
if (!error) {
|
||||||
|
console.log(`Solicitud de conexión a base de datos exitosa`)
|
||||||
|
await this.checkTableExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.log(`Solicitud de conexión fallida ${error.stack}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getPrevByNumber = (from) =>
|
||||||
|
new Promise((resolve, reject) => {
|
||||||
|
const sql = `SELECT * FROM history WHERE phone=${from} ORDER BY id DESC`
|
||||||
|
this.db.query(sql, (error, rows) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rows.length) {
|
||||||
|
const [row] = rows
|
||||||
|
row.options = JSON.parse(row.options)
|
||||||
|
resolve(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rows.length) {
|
||||||
|
resolve(null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
save = (ctx) => {
|
||||||
|
const values = [
|
||||||
|
[
|
||||||
|
ctx.ref,
|
||||||
|
ctx.keyword,
|
||||||
|
ctx.answer,
|
||||||
|
ctx.refSerialize,
|
||||||
|
ctx.from,
|
||||||
|
JSON.stringify(ctx.options),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
const sql =
|
||||||
|
'INSERT INTO history (ref, keyword, answer, refSerialize, phone, options ) values ?'
|
||||||
|
|
||||||
|
this.db.query(sql, [values], (err) => {
|
||||||
|
if (err) throw err
|
||||||
|
console.log('Guardado en DB...', values)
|
||||||
|
})
|
||||||
|
this.listHistory.push(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
createTable = () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
const tableName = 'history'
|
||||||
|
|
||||||
|
const sql = `CREATE TABLE ${tableName}
|
||||||
|
(id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
ref varchar(255) NOT NULL,
|
||||||
|
keyword varchar(255) NOT NULL,
|
||||||
|
answer longtext NOT NULL,
|
||||||
|
refSerialize varchar(255) NOT NULL,
|
||||||
|
phone varchar(255) NOT NULL,
|
||||||
|
options longtext NOT NULL
|
||||||
|
)`
|
||||||
|
|
||||||
|
this.db.query(sql, (err) => {
|
||||||
|
if (err) throw err
|
||||||
|
console.log(`Tabla ${tableName} creada correctamente `)
|
||||||
|
resolve(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
checkTableExists = () =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
const sql = "SHOW TABLES LIKE 'history'"
|
||||||
|
|
||||||
|
this.db.query(sql, (err, rows) => {
|
||||||
|
if (err) throw err
|
||||||
|
|
||||||
|
if (!rows.length) {
|
||||||
|
this.createTable()
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(!!rows.length)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = MyslAdapter
|
||||||
@@ -29,10 +29,13 @@
|
|||||||
"@types/node": "latest",
|
"@types/node": "latest",
|
||||||
"@typescript-eslint/eslint-plugin": "5.43.0",
|
"@typescript-eslint/eslint-plugin": "5.43.0",
|
||||||
"@typescript-eslint/parser": "5.43.0",
|
"@typescript-eslint/parser": "5.43.0",
|
||||||
|
"autoprefixer": "10.4.11",
|
||||||
"eslint": "8.28.0",
|
"eslint": "8.28.0",
|
||||||
"eslint-plugin-qwik": "0.14.1",
|
"eslint-plugin-qwik": "0.14.1",
|
||||||
"node-fetch": "3.3.0",
|
"node-fetch": "3.3.0",
|
||||||
|
"postcss": "^8.4.16",
|
||||||
"prettier": "2.7.1",
|
"prettier": "2.7.1",
|
||||||
|
"tailwindcss": "^3.1.8",
|
||||||
"typescript": "4.9.3",
|
"typescript": "4.9.3",
|
||||||
"vite": "3.2.4",
|
"vite": "3.2.4",
|
||||||
"vite-tsconfig-paths": "3.5.0",
|
"vite-tsconfig-paths": "3.5.0",
|
||||||
|
|||||||
6
packages/docs/postcss.config.js
Normal file
6
packages/docs/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
21
packages/docs/tailwind.config.js
Normal file
21
packages/docs/tailwind.config.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
|
||||||
|
const defaultTheme = require("tailwindcss/defaultTheme");
|
||||||
|
const colors = require("tailwindcss/colors");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
primary: colors.purple,
|
||||||
|
secondary: colors.sky,
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: ["'Inter'", ...defaultTheme.fontFamily.sans],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
darkMode: "class",
|
||||||
|
};
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# @bot-whatsapp/provider
|
|
||||||
|
|
||||||
```js
|
|
||||||
// bootstrap.js Como iniciar el provider
|
|
||||||
const { inout, provider, database } = require('@bot-whatsapp')
|
|
||||||
|
|
||||||
provider.start()
|
|
||||||
provider.close()
|
|
||||||
```
|
|
||||||
|
|
||||||
- [ ] whatsapp-web.js _verificar update_
|
|
||||||
- [ ] Meta _verificar tokens_
|
|
||||||
- [ ] Twilio _verificar tokens_
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const banner = require('../../config/banner.rollup.json')
|
||||||
const { join } = require('path')
|
const { join } = require('path')
|
||||||
const commonjs = require('@rollup/plugin-commonjs')
|
const commonjs = require('@rollup/plugin-commonjs')
|
||||||
|
|
||||||
@@ -5,6 +6,7 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
input: join(__dirname, 'src', 'web-whatsapp', 'index.js'),
|
input: join(__dirname, 'src', 'web-whatsapp', 'index.js'),
|
||||||
output: {
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
file: join(__dirname, 'lib', 'web-whatsapp', 'index.cjs'),
|
file: join(__dirname, 'lib', 'web-whatsapp', 'index.cjs'),
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
},
|
},
|
||||||
@@ -13,6 +15,7 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
input: join(__dirname, 'src', 'twilio', 'index.js'),
|
input: join(__dirname, 'src', 'twilio', 'index.js'),
|
||||||
output: {
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
file: join(__dirname, 'lib', 'twilio', 'index.cjs'),
|
file: join(__dirname, 'lib', 'twilio', 'index.cjs'),
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
},
|
},
|
||||||
@@ -21,6 +24,7 @@ module.exports = [
|
|||||||
{
|
{
|
||||||
input: join(__dirname, 'src', 'mock', 'index.js'),
|
input: join(__dirname, 'src', 'mock', 'index.js'),
|
||||||
output: {
|
output: {
|
||||||
|
banner: banner['banner.output'].join(''),
|
||||||
file: join(__dirname, 'lib', 'mock', 'index.cjs'),
|
file: join(__dirname, 'lib', 'mock', 'index.cjs'),
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,19 +1,116 @@
|
|||||||
const twilio = require('twilio')
|
const twilio = require('twilio')
|
||||||
const { ProviderClass } = require('@bot-whatsapp/bot')
|
const { ProviderClass } = require('@bot-whatsapp/bot')
|
||||||
|
|
||||||
const TwilioVendor = new twilio(accountSid, authToken)
|
const TwilioWebHookServer = require('./server')
|
||||||
|
const { parseNumber } = require('./utils')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ⚙️TwilioProvider: Es un provedor que te ofrece enviar
|
||||||
|
* mensaje a Whatsapp via API
|
||||||
|
* info: https://www.twilio.com/es-mx/messaging/whatsapp
|
||||||
|
* video: https://youtu.be/KoOmsHylxUw
|
||||||
|
*
|
||||||
|
* Necesitas las siguientes tokens y valores
|
||||||
|
* { accountSid, authToken, vendorNumber }
|
||||||
|
*/
|
||||||
class TwilioProvider extends ProviderClass {
|
class TwilioProvider extends ProviderClass {
|
||||||
constructor() {
|
twilioHook
|
||||||
super(TwilioVendor)
|
vendor
|
||||||
|
vendorNumber
|
||||||
|
constructor({ accountSid, authToken, vendorNumber }, _port = 3000) {
|
||||||
|
super()
|
||||||
|
this.vendor = new twilio(accountSid, authToken)
|
||||||
|
this.twilioHook = new TwilioWebHookServer(_port)
|
||||||
|
this.vendorNumber = parseNumber(vendorNumber)
|
||||||
|
|
||||||
|
this.twilioHook.start()
|
||||||
|
const listEvents = this.busEvents()
|
||||||
|
|
||||||
|
for (const { event, func } of listEvents) {
|
||||||
|
this.twilioHook.on(event, func)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendMessage = (message) =>
|
/**
|
||||||
this.vendor.messages.create({
|
* Mapeamos los eventos nativos de whatsapp-web.js a los que la clase Provider espera
|
||||||
|
* para tener un standar de eventos
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
busEvents = () => [
|
||||||
|
{
|
||||||
|
event: 'auth_failure',
|
||||||
|
func: (payload) => this.emit('error', payload),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'ready',
|
||||||
|
func: () => this.emit('ready', true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
event: 'message',
|
||||||
|
func: (payload) => {
|
||||||
|
this.emit('message', payload)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviar un archivo multimedia
|
||||||
|
* https://www.twilio.com/es-mx/docs/whatsapp/tutorial/send-and-receive-media-messages-whatsapp-nodejs
|
||||||
|
* @private
|
||||||
|
* @param {*} number
|
||||||
|
* @param {*} mediaInput
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
sendMedia = async (number, message, mediaInput = null) => {
|
||||||
|
if (!mediaInput) throw new Error(`MEDIA_INPUT_NULL_: ${mediaInput}`)
|
||||||
|
number = parseNumber(number)
|
||||||
|
return this.vendor.messages.create({
|
||||||
|
mediaUrl: [`${mediaInput}`],
|
||||||
body: message,
|
body: message,
|
||||||
to: '+12345678901', // Text this number
|
from: `whatsapp:+${this.vendorNumber}`,
|
||||||
from: '+12345678901', // From a valid Twilio number
|
to: `whatsapp:+${number}`,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviar botones
|
||||||
|
* https://www.twilio.com/es-mx/docs/whatsapp/buttons
|
||||||
|
* @private
|
||||||
|
* @param {*} number
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} buttons []
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
sendButtons = async (number, message, buttons = []) => {
|
||||||
|
console.log(``)
|
||||||
|
console.log(
|
||||||
|
`[NOTA]: Actualmente enviar botons con Twilio esta en desarrollo`
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
`[NOTA]: https://www.twilio.com/es-mx/docs/whatsapp/buttons`
|
||||||
|
)
|
||||||
|
console.log(``)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} userId
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} param2
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
sendMessage = async (number, message, { options }) => {
|
||||||
|
number = parseNumber(number)
|
||||||
|
if (options?.buttons?.length)
|
||||||
|
this.sendButtons(number, message, options.buttons)
|
||||||
|
if (options?.media)
|
||||||
|
return this.sendMedia(number, message, options.media)
|
||||||
|
return this.vendor.messages.create({
|
||||||
|
body: message,
|
||||||
|
from: `whatsapp:+${this.vendorNumber}`,
|
||||||
|
to: `whatsapp:+${number}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = TwilioProvider
|
module.exports = TwilioProvider
|
||||||
|
|||||||
64
packages/provider/src/twilio/server.js
Normal file
64
packages/provider/src/twilio/server.js
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
const { EventEmitter } = require('node:events')
|
||||||
|
const polka = require('polka')
|
||||||
|
const { urlencoded } = require('body-parser')
|
||||||
|
const { parseNumber } = require('./utils')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encargado de levantar un servidor HTTP con una hook url
|
||||||
|
* [POST] /twilio-hook
|
||||||
|
*/
|
||||||
|
class TwilioWebHookServer extends EventEmitter {
|
||||||
|
twilioServer
|
||||||
|
twilioPort
|
||||||
|
constructor(_twilioPort) {
|
||||||
|
super()
|
||||||
|
this.twilioServer = this.buildHTTPServer()
|
||||||
|
this.twilioPort = _twilioPort
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mensaje entrante
|
||||||
|
* emit: 'message'
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
*/
|
||||||
|
incomingMsg = (req, res) => {
|
||||||
|
const { body } = req
|
||||||
|
this.emit('message', {
|
||||||
|
from: parseNumber(body.From),
|
||||||
|
to: parseNumber(body.To),
|
||||||
|
body: body.Body,
|
||||||
|
})
|
||||||
|
const json = JSON.stringify({ body })
|
||||||
|
res.end(json)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contruir HTTP Server
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
buildHTTPServer = () => {
|
||||||
|
return polka()
|
||||||
|
.use(urlencoded({ extended: true }))
|
||||||
|
.post('/twilio-hook', this.incomingMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puerto del HTTP
|
||||||
|
* @param {*} port default 3000
|
||||||
|
*/
|
||||||
|
start = () => {
|
||||||
|
this.twilioServer.listen(this.twilioPort, () => {
|
||||||
|
console.log(``)
|
||||||
|
console.log(`[Twilio]: Agregar esta url "WHEN A MESSAGE COMES IN"`)
|
||||||
|
console.log(
|
||||||
|
`[Twilio]: POST http://localhost:${this.twilioPort}/twilio-hook`
|
||||||
|
)
|
||||||
|
console.log(`[Twilio]: Más información en la documentacion`)
|
||||||
|
console.log(``)
|
||||||
|
})
|
||||||
|
this.emit('ready')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = TwilioWebHookServer
|
||||||
5
packages/provider/src/twilio/utils.js
Normal file
5
packages/provider/src/twilio/utils.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const parseNumber = (number) => {
|
||||||
|
return `${number}`.replace('whatsapp:', '').replace('+', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { parseNumber }
|
||||||
@@ -1,13 +1,29 @@
|
|||||||
const { Client, LocalAuth } = require('whatsapp-web.js')
|
const {
|
||||||
|
Client,
|
||||||
|
LocalAuth,
|
||||||
|
MessageMedia,
|
||||||
|
Buttons,
|
||||||
|
List,
|
||||||
|
} = require('whatsapp-web.js')
|
||||||
const { ProviderClass } = require('@bot-whatsapp/bot')
|
const { ProviderClass } = require('@bot-whatsapp/bot')
|
||||||
const { Console } = require('console')
|
const { Console } = require('console')
|
||||||
const { createWriteStream } = require('fs')
|
const { createWriteStream, existsSync } = require('fs')
|
||||||
const { cleanNumber, generateImage, isValidNumber } = require('./utils')
|
const {
|
||||||
|
cleanNumber,
|
||||||
|
generateImage,
|
||||||
|
isValidNumber,
|
||||||
|
downloadMedia,
|
||||||
|
} = require('./utils')
|
||||||
|
|
||||||
const logger = new Console({
|
const logger = new Console({
|
||||||
stdout: createWriteStream('./log'),
|
stdout: createWriteStream('./log'),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ⚙️ WebWhatsappProvider: Es una clase tipo adaptor
|
||||||
|
* que extiende clases de ProviderClass (la cual es como interfaz para sber que funciones rqueridas)
|
||||||
|
* https://github.com/pedroslopez/whatsapp-web.js
|
||||||
|
*/
|
||||||
class WebWhatsappProvider extends ProviderClass {
|
class WebWhatsappProvider extends ProviderClass {
|
||||||
vendor
|
vendor
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -21,13 +37,14 @@ class WebWhatsappProvider extends ProviderClass {
|
|||||||
for (const { event, func } of listEvents) {
|
for (const { event, func } of listEvents) {
|
||||||
this.vendor.on(event, func)
|
this.vendor.on(event, func)
|
||||||
}
|
}
|
||||||
|
this.vendor.emit('preinit')
|
||||||
this.vendor.initialize().catch((e) => {
|
this.vendor.initialize().catch((e) => {
|
||||||
logger.log(e)
|
logger.log(e)
|
||||||
this.emit('require_action', {
|
this.emit('require_action', {
|
||||||
instructions: [
|
instructions: [
|
||||||
`Debes eliminar la carpeta .wwebjs_auth`,
|
`(Opcion 1): Debes eliminar la carpeta .wwebjs_auth y reiniciar nuevamente el bot. `,
|
||||||
`y reiniciar nuevamente el bot `,
|
`(Opcion 2): Intenta actualizar el paquete [npm install whatsapp-web.js] `,
|
||||||
|
`(Opcion 3): Ir FORO de discord https://link.codigoencasa.com/DISCORD `,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -60,10 +77,6 @@ class WebWhatsappProvider extends ProviderClass {
|
|||||||
event: 'ready',
|
event: 'ready',
|
||||||
func: () => this.emit('ready', true),
|
func: () => this.emit('ready', true),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
event: 'authenticated',
|
|
||||||
func: () => this.emit('ready', true),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
event: 'message',
|
event: 'message',
|
||||||
func: (payload) => {
|
func: (payload) => {
|
||||||
@@ -80,10 +93,87 @@ class WebWhatsappProvider extends ProviderClass {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
sendMessage = async (userId, message) => {
|
/**
|
||||||
const number = cleanNumber(userId)
|
* 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 downloadMedia(mediaInput)
|
||||||
|
const media = MessageMedia.fromFilePath(fileDownloaded)
|
||||||
|
return this.vendor.sendMessage(number, media, {
|
||||||
|
sendAudioAsVoice: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviar botones
|
||||||
|
* https://docs.wwebjs.dev/Buttons.html
|
||||||
|
* @private
|
||||||
|
* @param {*} number
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} buttons []
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
sendButtons = async (number, message, buttons = []) => {
|
||||||
|
const buttonMessage = new Buttons(message, buttons, '', '')
|
||||||
|
return this.vendor.sendMessage(number, buttonMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviar lista
|
||||||
|
* https://docs.wwebjs.dev/List.html
|
||||||
|
* @private
|
||||||
|
* @alpha No funciona en whatsapp bussines
|
||||||
|
* @param {*} number
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} buttons []
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
sendList = async (number, message, listInput = []) => {
|
||||||
|
let sections = [
|
||||||
|
{
|
||||||
|
title: 'sectionTitle',
|
||||||
|
rows: [
|
||||||
|
{ title: 'ListItem1', description: 'desc' },
|
||||||
|
{ title: 'ListItem2' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
let list = new List('List body', 'btnText', sections, 'Title', 'footer')
|
||||||
|
return this.vendor.sendMessage(number, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enviar un mensaje solo texto
|
||||||
|
* https://docs.wwebjs.dev/Message.html
|
||||||
|
* @private
|
||||||
|
* @param {*} number
|
||||||
|
* @param {*} message
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
sendText = async (number, message) => {
|
||||||
return this.vendor.sendMessage(number, message)
|
return this.vendor.sendMessage(number, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} userId
|
||||||
|
* @param {*} message
|
||||||
|
* @param {*} param2
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
sendMessage = async (userId, message, { options }) => {
|
||||||
|
const number = cleanNumber(userId)
|
||||||
|
if (options?.buttons?.length)
|
||||||
|
return this.sendButtons(number, message, options.buttons)
|
||||||
|
if (options?.media) return this.sendMedia(number, options.media)
|
||||||
|
return this.sendText(number, message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = WebWhatsappProvider
|
module.exports = WebWhatsappProvider
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
const { createWriteStream } = require('fs')
|
const { createWriteStream } = require('fs')
|
||||||
const qr = require('qr-image')
|
const qr = require('qr-image')
|
||||||
|
const { tmpdir } = require('os')
|
||||||
|
const http = require('http')
|
||||||
|
const https = require('https')
|
||||||
|
|
||||||
const cleanNumber = (number, full = false) => {
|
const cleanNumber = (number, full = false) => {
|
||||||
number = number.replace('@c.us', '')
|
number = number.replace('@c.us', '')
|
||||||
@@ -18,4 +21,33 @@ const isValidNumber = (rawNumber) => {
|
|||||||
return !exist
|
return !exist
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { cleanNumber, generateImage, isValidNumber }
|
/**
|
||||||
|
* Incompleta
|
||||||
|
* Descargar archivo multimedia para enviar
|
||||||
|
* @param {*} url
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const downloadMedia = (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 = { cleanNumber, generateImage, isValidNumber, downloadMedia }
|
||||||
|
|||||||
18
scripts/move.js
Normal file
18
scripts/move.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
const fs = require('fs-extra')
|
||||||
|
const PACKAGES_PATH = `${process.cwd()}/packages`
|
||||||
|
const NAME_PREFIX = `@bot-whatsapp`
|
||||||
|
|
||||||
|
const [, , appDir] = process.argv || []
|
||||||
|
|
||||||
|
const copyLibPkg = async (pkgName, to) => {
|
||||||
|
const FROM = `${PACKAGES_PATH}/${pkgName}`
|
||||||
|
const TO = `${process.cwd()}/${to}/node_modules/${NAME_PREFIX}/${pkgName}`
|
||||||
|
await fs.copy(FROM, TO)
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
copyLibPkg('create-bot-whatsapp', appDir),
|
||||||
|
copyLibPkg('bot', appDir),
|
||||||
|
copyLibPkg('database', appDir),
|
||||||
|
copyLibPkg('provider', appDir),
|
||||||
|
]).then(() => console.log('Todas las lib copiadas'))
|
||||||
12
starters/apps/base/README.md
Normal file
12
starters/apps/base/README.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
### BASE APP
|
||||||
|
|
||||||
|
Este bot contiene un flujo basico en el cual una persona (cliente) escribe **"hola"** y el bot responde.
|
||||||
|
- Bienvenido a mi tienda
|
||||||
|
- Como puedo ayudarte?
|
||||||
|
- Tengo: Zapatos Bolsos etc..
|
||||||
|
|
||||||
|
------
|
||||||
|
- [Discord](https://link.codigoencasa.com/DISCORD)
|
||||||
|
- [Twitter](https://twitter.com/leifermendez)
|
||||||
|
- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
|
||||||
|
- [Telegram](https://t.me/leifermendez)
|
||||||
28
starters/apps/base/app.js
Normal file
28
starters/apps/base/app.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
const {
|
||||||
|
createBot,
|
||||||
|
createProvider,
|
||||||
|
createFlow,
|
||||||
|
addKeyword,
|
||||||
|
} = require('@bot-whatsapp/bot')
|
||||||
|
|
||||||
|
const WebWhatsappProvider = require('@bot-whatsapp/provider/web-whatsapp')
|
||||||
|
const MockAdapter = require('@bot-whatsapp/database/mock')
|
||||||
|
|
||||||
|
const flowPrincipal = addKeyword(['hola', 'ole', 'HOLA'])
|
||||||
|
.addAnswer('Bienvenido a mi tienda')
|
||||||
|
.addAnswer('Como puedo ayudarte?')
|
||||||
|
.addAnswer(['Tengo:', 'Zapatos', 'Bolsos', 'etc..'])
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
|
const adapterDB = new MockAdapter()
|
||||||
|
const adapterFlow = createFlow([flowPrincipal])
|
||||||
|
const adapterProvider = createProvider(WebWhatsappProvider)
|
||||||
|
|
||||||
|
createBot({
|
||||||
|
flow: adapterFlow,
|
||||||
|
provider: adapterProvider,
|
||||||
|
database: adapterDB,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
16
starters/apps/base/package.json
Normal file
16
starters/apps/base/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "bot-whatsapp-base",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "app.js",
|
||||||
|
"scripts": {
|
||||||
|
"pre-copy": "cd .. && yarn run copy.lib example-app-base",
|
||||||
|
"start": "node app.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"dependencies": {
|
||||||
|
"whatsapp-web.js": "^1.18.3"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
12
starters/apps/basic/README.md
Normal file
12
starters/apps/basic/README.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
### BASIC APP
|
||||||
|
|
||||||
|
Este bot contiene un flujo basico en el cual una persona (cliente) escribe **"hola"** y el bot responde.
|
||||||
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
------
|
||||||
|
- [Discord](https://link.codigoencasa.com/DISCORD)
|
||||||
|
- [Twitter](https://twitter.com/leifermendez)
|
||||||
|
- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
|
||||||
|
- [Telegram](https://t.me/leifermendez)
|
||||||
55
starters/apps/basic/app.js
Normal file
55
starters/apps/basic/app.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
const {
|
||||||
|
createBot,
|
||||||
|
createProvider,
|
||||||
|
createFlow,
|
||||||
|
addKeyword,
|
||||||
|
addChild,
|
||||||
|
} = require('@bot-whatsapp/bot')
|
||||||
|
|
||||||
|
const WebWhatsappProvider = require('@bot-whatsapp/provider/web-whatsapp')
|
||||||
|
const MockAdapter = require('@bot-whatsapp/database/mock')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declarando flujo hijo
|
||||||
|
*/
|
||||||
|
|
||||||
|
const flowZapatos = addKeyword(['zapatos', 'ZAPATOS'])
|
||||||
|
.addAnswer('🤯 Veo que elegiste zapatos')
|
||||||
|
.addAnswer('Tengo muchos zapatos...bla bla')
|
||||||
|
|
||||||
|
const flowBolsos = addKeyword(['bolsos', 'BOLSOS'])
|
||||||
|
.addAnswer('🙌 Veo que elegiste bolsos')
|
||||||
|
.addAnswer('Tengo muchos bolsos...bla bla')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declarando flujo principal
|
||||||
|
*/
|
||||||
|
const flowPrincipal = addKeyword(['hola', 'ole', 'HOLA'])
|
||||||
|
.addAnswer('Bienvenido a mi tienda')
|
||||||
|
.addAnswer('Como puedo ayudarte?')
|
||||||
|
.addAnswer(['Tengo:', 'Zapatos', 'Bolsos', 'etc..'])
|
||||||
|
.addAnswer('Escribe zapatos o bolsos')
|
||||||
|
.addAnswer(
|
||||||
|
'esperando respuesta...',
|
||||||
|
{ capture: true },
|
||||||
|
(ctx) => {
|
||||||
|
console.log('Aqui puedes ver más info del usuario...')
|
||||||
|
console.log('Puedes enviar un mail, hook, etc..')
|
||||||
|
console.log(ctx)
|
||||||
|
},
|
||||||
|
[...addChild(flowBolsos), ...addChild(flowZapatos)]
|
||||||
|
)
|
||||||
|
|
||||||
|
const main = async () => {
|
||||||
|
const adapterDB = new MockAdapter()
|
||||||
|
const adapterFlow = createFlow([flowPrincipal])
|
||||||
|
const adapterProvider = createProvider(WebWhatsappProvider)
|
||||||
|
|
||||||
|
createBot({
|
||||||
|
flow: adapterFlow,
|
||||||
|
provider: adapterProvider,
|
||||||
|
database: adapterDB,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
16
starters/apps/basic/package.json
Normal file
16
starters/apps/basic/package.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "bot-whatsapp-basic",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "app.js",
|
||||||
|
"scripts": {
|
||||||
|
"pre-copy": "cd .. && yarn run copy.lib example-app-basic",
|
||||||
|
"start": "node app.js"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"dependencies": {
|
||||||
|
"whatsapp-web.js": "^1.18.3"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC"
|
||||||
|
}
|
||||||
8944
yarn-error.log
Normal file
8944
yarn-error.log
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user