Compare commits

..

138 Commits

Author SHA1 Message Date
Leifer Mendez
028f6186b4 Update 2022-12-12 00:38:51 +01:00
Leifer Mendez
aad129ea94 Create FUNDING.yml 2022-12-12 00:31:06 +01:00
Leifer Mendez
c64560bfc3 ci(version): updated versions every packages
ci(version):  updated versions every packages
2022-12-11 22:31:51 +01:00
Leifer Mendez
255ba86506 ci(version): updated versions every packages 2022-12-11 22:31:21 +01:00
Leifer Mendez
cd2dad9d26 ci(version): updated versions every packages
ci(version):  updated versions every packages
2022-12-11 22:27:02 +01:00
Leifer Mendez
c7ff6b4794 ci(version): updated versions every packages 2022-12-11 22:26:29 +01:00
Leifer Mendez
628d55df37 ci(version): updated versions every packages
ci(version):  updated versions every packages
2022-12-11 22:18:46 +01:00
Leifer Mendez
0059a1e7fb ci(version): updated versions every packages 2022-12-11 22:18:01 +01:00
Leifer Mendez
73ea7c0063 ci(version): updated versions every packages 2022-12-11 22:16:09 +01:00
Leifer Mendez
cb9f4befa0 fix/version
fix/version
2022-12-11 22:11:24 +01:00
Leifer Mendez
decccb672a ci(ci): push 2022-12-11 22:10:42 +01:00
Leifer Mendez
51466bf1c7 ci(ci): push 2022-12-11 22:09:29 +01:00
Leifer Mendez
397798790e feat(cli): create-starter
feat(cli): create-starter
2022-12-11 22:00:53 +01:00
Leifer Mendez
d3b8310180 fix(cli): create-starter 2022-12-11 21:58:13 +01:00
Leifer Mendez
7797c2b461 fix(cli): update cli copy 2022-12-11 20:59:28 +01:00
Leifer Mendez
929e74c84b fix(starter): pre-copy fix
pre-copy fix
2022-12-11 13:46:52 +01:00
08e2552907 fix: pre-copy fix 2022-12-11 05:55:31 -06:00
6617107ab8 fix: pre-copy fix 2022-12-11 05:48:51 -06:00
Leifer Mendez
8921959ea0 Merge pull request #165 from leifermendez:fix/version
fix(ci):  balance version
2022-12-10 21:49:33 +01:00
Leifer Mendez
ec46cfdd65 fix(ci): balance version 2022-12-10 21:48:55 +01:00
Leifer Mendez
bfb69d9a95 fix(cli): clean eslinter
fix(cli):  clean eslinter
2022-12-10 21:40:20 +01:00
Leifer Mendez
15f6972257 fix(cli): clean eslinter 2022-12-10 21:38:23 +01:00
Leifer Mendez
2319db3009 fix(cli): 🔥 create script - templates
fix(cli): 🔥 create script - templates
2022-12-10 21:33:07 +01:00
Leifer Mendez
9cb98b5e73 fix(cli): 🔥 create script - templates 2022-12-10 21:27:58 +01:00
Leifer Mendez
2999e0e753 fix(cli): 🔥 create script - templates
fix(cli): 🔥 create script - templates
2022-12-10 21:22:48 +01:00
Leifer Mendez
af716b7537 fix(cli): 🔥 create script - templates 2022-12-10 21:20:57 +01:00
Leifer Mendez
c6999c8493 fix(cli): 🔥 create script - templates
fix(cli): 🔥 create script - templates
2022-12-10 21:20:07 +01:00
Leifer Mendez
d4b49a9bd7 fix(cli): 🔥 create script - templates 2022-12-10 21:19:38 +01:00
Leifer Mendez
eebc3c9806 fix(cli): 🔥 create script - templates 2022-12-10 21:17:29 +01:00
Leifer Mendez
257f1cc12d ci(ci): update ci
ci(ci):  update ci
2022-12-09 15:00:05 +01:00
Leifer Mendez
1036273a28 ci(ci): update ci 2022-12-09 14:59:21 +01:00
Leifer Mendez
1b8ed93367 ci(ci): update ci
ci(ci):  update ci
2022-12-09 14:18:06 +01:00
Leifer Mendez
8c1820c879 ci(ci): update ci 2022-12-09 14:17:26 +01:00
Leifer Mendez
e5a9db7e12 feat(ci): update ci
feat(ci): update ci
2022-12-09 14:13:43 +01:00
Leifer Mendez
9d5aa7db5d ci(ci): update ci 2022-12-09 14:12:38 +01:00
Leifer Mendez
40b0d9691e chore(ci): script releases 2022-12-09 13:50:01 +01:00
Leifer Mendez
2e906bce79 feat(ci): 🎨 relases script 2022-12-09 12:17:29 +01:00
Leifer Mendez
f05ff4cf88 style: ✏️ fixed typos
style: ✏️ fixed typos
2022-12-08 22:48:12 +01:00
Leifer Mendez
5735b49e25 Merge pull request #155 from leifermendez/feat/releases
feat(release): added
2022-12-08 22:23:03 +01:00
Leifer Mendez
1988948c30 feat(release): added 2022-12-08 22:22:04 +01:00
Leifer Mendez
cb33c0df68 Merge pull request #154 from leifermendez/feat/releases
feat(relases): added
2022-12-08 22:19:25 +01:00
Leifer Mendez
f4ad7040ab feat(release): added 2022-12-08 22:15:46 +01:00
Leifer Mendez
b115dc3654 feat(ci): 🎨 add releases 2022-12-08 21:56:34 +01:00
6rak0
8c6023e93b style: ✏️ fixed typos
fixed typos
2022-12-08 14:34:41 -06:00
Leifer Mendez
5d7c297f2f Merge pull request #150 from leifermendez/fix/ci-yarn
ci(action): update
2022-12-08 20:17:46 +01:00
Leifer Mendez
04f99d5ed2 ci(action): update 2022-12-08 20:16:31 +01:00
Leifer Mendez
bb3f21b056 Merge pull request #149 from leifermendez/fix/ci-yarn
fix(ci): update yarn
2022-12-08 20:13:02 +01:00
Leifer Mendez
cf6188d860 ci(action): update 2022-12-08 20:12:11 +01:00
Leifer Mendez
294bfbb35f ci(action): update 2022-12-08 20:11:33 +01:00
github-actions[bot]
230538bcea docs(contributor): contrib-readme-action has updated readme 2022-12-07 22:43:36 +00:00
Leifer Mendez
5e7aa72494 Merge pull request #138 from vicente1992/feat-jsonfile-adapter
feat(adapter): implementation of json file adapter
2022-12-07 23:43:21 +01:00
vicente1992
3159ea5665 Merge branch 'dev' of https://github.com/vicente1992/bot-whatsapp into feat-jsonfile-adapter 2022-12-07 17:18:36 -05:00
vicente1992
4b307efe79 fix(adapter): conflict resolution 2022-12-07 17:14:16 -05:00
github-actions[bot]
0105dab2c4 docs(contributor): contrib-readme-action has updated readme 2022-12-07 22:10:34 +00:00
github-actions[bot]
6e8e16c9a4 docs(contributor): contrib-readme-action has updated readme 2022-12-07 20:33:21 +00:00
Leifer Mendez
8d73c86946 feat(adapter): mysql adapter
feat(adapter): mysql adapter
2022-12-07 21:33:05 +01:00
Leifer Mendez
a7b19d9bff fix(bot): flow improvement + add utils 2022-12-07 21:29:48 +01:00
vicente1992
4b7de0f690 feat(adapter): sql is added to create the table 2022-12-07 15:07:38 -05:00
github-actions[bot]
520145bf7d docs(contributor): contrib-readme-action has updated readme 2022-12-07 19:54:07 +00:00
Leifer Mendez
2253d57fed Merge branch 'dev' of github.com:leifermendez/bot-whatsapp into dev 2022-12-07 18:13:00 +01:00
Leifer Mendez
0fb93f66a3 Merge branch 'fix/webwhatsapp' into dev 2022-12-07 18:12:45 +01:00
Leifer Mendez
501887300d Merge branch 'fix/delay-message' into dev 2022-12-07 18:11:49 +01:00
Leifer Mendez
14b6247106 Merge pull request #137 from vicente1992/feat-mysql-adapter
feat(adapter): added adapter mysql
2022-12-07 18:09:13 +01:00
Leifer Mendez
eda8a67718 refactor(provider): twilio + hook 2022-12-07 18:06:27 +01:00
Leifer Mendez
73caf090ba fix(bot): added delay promises 2022-12-07 16:20:56 +01:00
vicente1992
afa6771903 fix(adapter): corrections are made to the adapter 2022-12-06 15:48:25 -05:00
Leifer Mendez
8dd3be909b feat(provider): added twilio provider 2022-12-06 21:28:52 +01:00
github-actions[bot]
88af2469cb docs(contributor): contrib-readme-action has updated readme 2022-12-06 18:36:55 +00:00
Leifer Mendez
999d6742b4 Merge pull request #141 from leifermendez:feature/twilio
Feature/twilio
2022-12-06 19:36:42 +01:00
Leifer Mendez
24ac9fbf48 fix(bot): fix sensitive case 2022-12-06 19:35:21 +01:00
Leifer Mendez
4350dff22a feat(provider): 🔥 add twilii (weoking) 2022-12-05 20:45:05 +01:00
github-actions[bot]
30e3d443bb docs(contributor): contrib-readme-action has updated readme 2022-12-05 15:47:15 +00:00
Leifer Mendez
f5ea7fe2c4 Merge branch 'dev' of github.com:leifermendez/bot-whatsapp into dev 2022-12-05 16:46:47 +01:00
Leifer Mendez
94d139e484 ci(contributors): add username avatar 2022-12-05 16:46:29 +01:00
github-actions[bot]
8049241f3f docs(contributor): contrib-readme-action has updated readme 2022-12-05 15:45:01 +00:00
Leifer Mendez
28d308ed4b ci(contributors): add username avatar 2022-12-05 16:44:36 +01:00
Leifer Mendez
e0862053d0 Merge branch 'feature/fallback' into dev 2022-12-05 16:35:05 +01:00
Leifer Mendez
e0e76d3a56 ci(contributors): add username avatar 2022-12-05 16:33:22 +01:00
Leifer Mendez
242e44315b Merge pull request #139 from leifermendez/feature/fallback
Feature/fallback
2022-12-05 16:32:02 +01:00
Leifer Mendez
6b53ed13e2 docs(contributors): add username avatar 2022-12-05 16:15:21 +01:00
Leifer Mendez
fafccbcecc Merge commit 'f2533f1ed5c0078be59938720f2ecf96c616b843' into feature/fallback 2022-12-05 16:05:49 +01:00
Leifer Mendez
49698bfda9 fix(bot): update 2022-12-05 16:02:23 +01:00
Leifer Mendez
4154cc2230 feat(bot): 🔥 improvement provider handler 2022-12-05 14:49:23 +01:00
Leifer Mendez
ce8a96b958 feat(bot): add send image function 2022-12-05 13:01:32 +01:00
Leifer Mendez
f373a3abc7 chore(bot): fallback done 2022-12-05 09:59:40 +01:00
Leifer Mendez
371ee0a780 chore(bot): update fallback function 2022-12-04 16:01:52 +01:00
Leifer Mendez
327cf5730b Merge pull request #137 from vicente1992/feat-mysql-adapter
feat(adapter): added adapter mysql
2022-12-03 12:06:03 +01:00
vicente1992
5e1a373730 feat(adapter): implementation of json file adapter 2022-12-02 22:27:31 -05:00
vicente1992
717a7dc95f feat(adapter): added adapter mysql 2022-12-02 09:41:57 -05:00
Leifer Mendez
aa2417af12 chore(bot): added new fallback option 2022-12-02 15:22:13 +01:00
Leifer Mendez
f2533f1ed5 Merge pull request #136 from leifermendez/feature/monorepo
docs(contributing):  update
2022-12-01 21:34:54 +01:00
Leifer Mendez
7d41699207 docs(contributing): update 2022-12-01 21:33:50 +01:00
Leifer Mendez
f9ccfef8e0 Merge pull request #135 from leifermendez/feature/monorepo
chore(cli): 🔥 fix
2022-12-01 21:07:48 +01:00
Leifer Mendez
468a2ba251 chore(cli): 🔥 fix 2022-12-01 21:07:00 +01:00
Leifer Mendez
08dbdcf4ae Merge pull request #134 from leifermendez/feature/monorepo
chore(cli): 🎨 remove uneccesary steps
2022-12-01 20:59:57 +01:00
Leifer Mendez
50d73f7bc8 chore(cli): 🎨 remove uneccesary steps 2022-12-01 20:58:58 +01:00
Leifer Mendez
d7ed9ff592 Merge pull request #133 from leifermendez/feature/monorepo
chore(cli):  added new function
2022-12-01 20:45:34 +01:00
Leifer Mendez
05c6fd4528 chore(cli): added new function 2022-12-01 20:44:12 +01:00
Leifer Mendez
a99f424901 Merge pull request #132 from leifermendez/feature/monorepo
Feature/monorepo
2022-12-01 18:43:03 +01:00
Leifer Mendez
648354500b fix(fix): fix 2022-12-01 18:42:18 +01:00
Leifer Mendez
28c0480b8b fix(fix): fix 2022-12-01 18:39:49 +01:00
Leifer Mendez
f29ed6e29b Merge pull request #131 from leifermendez/feature/monorepo
Merge pull request #130 from leifermendez/dev
2022-12-01 18:38:03 +01:00
Leifer Mendez
903b4d79ac docs(contributing): 📝 added more example 2022-12-01 18:17:19 +01:00
Leifer Mendez
026c189901 Merge pull request #130 from leifermendez/dev
Dev
2022-12-01 12:52:46 +01:00
Leifer
f2b30ee349 docs: 📝 update CONTRIBUTING 2022-12-01 12:46:49 +01:00
Leifer
5c02a9325a chore(release): 0.2.0-alpha.0 2022-12-01 12:38:17 +01:00
Leifer
7645c8642f test 2022-12-01 12:33:18 +01:00
Leifer
c5ebbe319f refactor(hook): added new improvement 2022-12-01 12:32:45 +01:00
Leifer
6f36eb1690 test 2022-12-01 10:35:41 +01:00
Leifer
18f9e006a3 test 2022-12-01 10:34:30 +01:00
Leifer
a7e334ebe9 add banner 2022-12-01 10:27:18 +01:00
Leifer
a5e15d9d84 edit hook 2022-12-01 10:17:19 +01:00
Leifer
b3173517b4 add pretty-quick 2022-12-01 10:16:06 +01:00
Leifer Mendez
06d2963163 Merge pull request #129 from leifermendez/feature/monorepo
Feature/monorepo
2022-12-01 09:53:28 +01:00
Leifer Mendez
df8282015d docs(example-app): add cli crate app 2022-11-30 21:39:30 +01:00
Leifer Mendez
81b0aab850 refactor(io): added new method addChild 2022-11-30 21:06:26 +01:00
Leifer Mendez
a8705c5b44 refactor(io): added new method addChild 2022-11-30 21:05:32 +01:00
Leifer Mendez
efe739f9fc refactor(io): added new method addChild 2022-11-30 21:04:45 +01:00
Leifer Mendez
2e83a0508a docs(contribuiting): update 2022-11-30 17:14:50 +01:00
Leifer Mendez
d66adb2a1f Merge pull request #126 from leifermendez/feature/monorepo
Feature/monorepo
2022-11-29 22:14:56 +01:00
Leifer
e5cecdee03 docs(contributin): added more info 2022-11-29 22:13:58 +01:00
Leifer
9351af16b7 docs(contributing): added more info 2022-11-29 22:04:46 +01:00
Leifer
ad8831a75a f 2022-11-29 22:00:05 +01:00
Leifer
c63018ff08 f 2022-11-29 21:55:39 +01:00
Leifer
131bce3898 . 2022-11-29 21:45:44 +01:00
Leifer
fff9316030 . 2022-11-29 21:44:04 +01:00
Leifer
2b3148dc3c update documenta 2022-11-29 21:43:11 +01:00
Leifer Mendez
13a4202f08 docs(contributing): added main readme 2022-11-29 11:33:33 +01:00
Leifer Mendez
f0df143aaf Merge pull request #125 from leifermendez/feature/monorepo
Feature/monorepo
2022-11-29 11:23:07 +01:00
Leifer Mendez
70a94ab2c6 fix(linter): update linter and commitlint 2022-11-29 10:47:12 +01:00
Leifer Mendez
e6d18d1a72 chore(release): 0.1.1 2022-11-29 10:36:23 +01:00
Leifer Mendez
46cd57fb36 chore(release): 0.1.0 2022-11-29 10:33:25 +01:00
Leifer Mendez
4ae389846d feat: (🎸) add onClick prop to component 2022-11-29 10:22:37 +01:00
Leifer Mendez
37d04e9e89 test: (💍) Is justa test!
nothing

BREAKING CHANGE: 🧨 NO
2022-11-29 10:17:32 +01:00
Leifer Mendez
39d141ca67 refactor: (💡) change emoji 2022-11-28 21:20:48 +01:00
Leifer Mendez
befcc169e0 refactor: 💡 pkgmanager
Upgrade Yarn v3
2022-11-28 21:19:00 +01:00
Leifer Mendez
6bbb9c1b81 docs(main documentation): es una mejora 2022-11-28 19:44:20 +01:00
Leifer Mendez
bb77afc4d2 test 2022-11-28 19:40:53 +01:00
114 changed files with 21135 additions and 5836 deletions

View File

@@ -4,5 +4,5 @@
"reporter": ["html"], "reporter": ["html"],
"report-dir": "./coverage", "report-dir": "./coverage",
"check-coverage": true, "check-coverage": true,
"lines": 95 "lines": 90
} }

1
.eslintignore Normal file
View File

@@ -0,0 +1 @@
packages/docs/*

View File

@@ -10,5 +10,9 @@ module.exports = {
parserOptions: { parserOptions: {
ecmaVersion: 'latest', ecmaVersion: 'latest',
}, },
rules: {}, rules: {
'no-unsafe-negation': 'off',
'no-prototype-builtins': 'off',
'no-useless-escape': 'off',
},
} }

9
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: #
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
custom: "https://www.buymeacoffee.com/leifermendez"

View File

@@ -1,26 +1,111 @@
name: Test / Coverage name: BotWhatsapp CI
on: on:
push: push:
branches: [feature/monorepo] branches:
- main
pull_request: pull_request:
branches: [main] branches:
- main
- dev
jobs: jobs:
build: ############ BUILD PACKAGE ############
build-package:
name: Build Package
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs:
strategy: - test-unit
matrix:
node-version: [16.x]
steps: steps:
- uses: actions/checkout@v3 - name: Checkout
- name: Use Node.js ${{ matrix.node-version }} uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: ${{ matrix.node-version }} node-version: 16.x
- run: npm install cache: 'yarn'
- run: npm run build --if-present registry-url: https://registry.npmjs.org/
- run: npm run test.unit
- run: npm run test.coverage - run: corepack enable
- name: Install NPM Dependencies
run: yarn install --immutable --network-timeout 300000
- name: Build Package
run: yarn build
- name: Build Eslint rules
run: yarn lint:fix
############ RELEASE ############
release:
name: Release
runs-on: ubuntu-latest
needs:
- build-package
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}
- 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: Build Package
run: yarn build
- name: Release @bot-whatsapp/bot
run: yarn node ./scripts/release.js --name=bot --version= --token="${{ secrets.NPM_TOKEN }}"
- name: Release @bot-whatsapp/cli
run: yarn node ./scripts/release.js --name=cli --version= --token="${{ secrets.NPM_TOKEN }}"
- name: Release @bot-whatsapp/create-bot-whatsapp
run: yarn node ./scripts/release.js --name=create-bot-whatsapp --version= --token="${{ secrets.NPM_TOKEN }}"
- name: Release @bot-whatsapp/database
run: yarn node ./scripts/release.js --name=database --version= --token="${{ secrets.NPM_TOKEN }}"
- name: Release @bot-whatsapp/provider
run: yarn node ./scripts/release.js --name=provider --version= --token="${{ secrets.NPM_TOKEN }}"
- name: Commit Versioning
run: |
git config --global user.name 'Leifer Mendez (githubaction)'
git config --global user.email 'leifermendez@users.noreply.github.com'
git commit -am "ci(version): :zap: updated versions every packages"
git push
############ UNIT TEST ############
test-unit:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 16.x
cache: 'yarn'
registry-url: https://registry.npmjs.org/
- run: corepack enable
- name: Install NPM Dependencies
run: yarn install --immutable --network-timeout 300000
- name: Unit Tests
run: yarn test

19
.github/workflows/contributors.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: Add contributors
on:
pull_request:
branches:
- dev
- main
types: [closed]
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 }}

16
.gitignore vendored
View File

@@ -1,4 +1,5 @@
/node_modules /node_modules
/packages/*/starters
/packages/*/node_modules /packages/*/node_modules
/packages/*/dist /packages/*/dist
/packages/*/docs/dist /packages/*/docs/dist
@@ -14,12 +15,23 @@ 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
*.tgz
lib lib
tmp/ tmp/
.yarn/*
!.yarn/releases
!.yarn/plugins/@yarnpkg/plugin-postinstall.cjs
.fleet/ .fleet/
example-app/ example-app*/
base-*/
!starters/apps/base-*/
qr.svg qr.svg
package-lock.json package-lock.json
yarn-error.log
.npmrc

4
.husky/commit-msg Executable file
View 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
View 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
View File

View File

@@ -1,5 +1,5 @@
packages/**/lib packages/**/lib
packages/docs packages/docs/*.json
**/.git **/.git
**/.svn **/.svn
**/.hg **/.hg

11
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"conventionalCommits.scopes": [
"hook",
"contributing",
"cli",
"bot",
"provider",
"adapter",
"ci"
]
}

View 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

File diff suppressed because one or more lines are too long

10
.yarnrc.yml Normal file
View 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

View File

@@ -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

60
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,60 @@
# CONTRIBUTING
![](https://i.giphy.com/media/ntMt6TvalpstTIx7Ak/giphy.webp)
__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 conseguirás las intrucciones para instalar yarn.
- __[VSCode](https://code.visualstudio.com/download)__ (recomendado): Editor de código con plugins.
- __[Conventional Commits](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits&ssr=false#overview)__ (plugin-vscode) este plugin te ayudará a crear commit semántico.
- Se usará 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 este comando, el cual genera un directorio `lib` dentro de los paquetes del monorepo.
```
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 semánticos, 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 realiza los test internos que tienen que cumplir con __95% de cobertura__.
> Documento en constante 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)

View File

@@ -1,3 +1,4 @@
```js ```js
const { const {

2
GLOSSARY.md Normal file
View File

@@ -0,0 +1,2 @@
CTX: Es el objeto que representa un mensaje, con opciones, id, ref
messageInComming: Objeto entrante del provider {body, from,...}

View File

@@ -1,6 +1,6 @@
[![Test / Coverage](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml/badge.svg)](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml) [![Test / Coverage](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml/badge.svg)](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](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,13 +8,86 @@ 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.
- [Discord](https://link.codigoencasa.com/DISCORD) - [Discord](https://link.codigoencasa.com/DISCORD)
- [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
View File

@@ -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
View 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
View File

@@ -0,0 +1 @@
module.exports = { extends: ['@commitlint/config-conventional'] }

View 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
View 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 }

View File

@@ -1,31 +1,38 @@
{ {
"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",
"create-bot-whatsapp:rollup": "rollup --config ./packages/create-bot-whatsapp/rollup-create.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 && yarn run create-bot-whatsapp: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 +59,37 @@
"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",
"semver": "^7.3.8",
"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"
}
}
} }

View File

@@ -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,15 +28,22 @@ class CoreClass {
} }
} }
/**
* Manejador de eventos
*/
listenerBusEvents = () => [ listenerBusEvents = () => [
{
event: 'preinit',
func: () => printer('Iniciando proveedor, espere...'),
},
{ {
event: 'require_action', event: 'require_action',
func: ({ instructions, title = '⚡⚡ ACCION REQUERIDA ⚡⚡' }) => func: ({ instructions, title = '⚡⚡ ACCIÓN REQUERIDA ⚡⚡' }) =>
printer(instructions, title), printer(instructions, title),
}, },
{ {
event: 'ready', event: 'ready',
func: () => printer('Provider conectado y listo'), func: () => printer('Proveedor conectado y listo'),
}, },
{ {
event: 'auth_failure', event: 'auth_failure',
@@ -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) {
msgToSend = this.flowClass.find(refToContinue?.ref, true) || [] const typeCapture = typeof prevMsg?.options?.capture
} else { const valueCapture = prevMsg?.options?.capture
msgToSend = this.flowClass.find(body) || []
if (['string', 'boolean'].includes(typeCapture) && valueCapture) {
msgToSend = this.flowClass.find(refToContinue?.ref, true) || []
this.sendFlow(msgToSend, from)
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)
} }

View File

@@ -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,

View File

@@ -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
} }

View File

@@ -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 {

View 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 }

View File

@@ -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 }

View File

@@ -1,9 +1,8 @@
{ {
"name": "@bot-whatsapp/bot", "name": "@bot-whatsapp/bot",
"version": "0.0.1", "version": "0.0.12-alpha.0",
"description": "", "description": "",
"main": "./lib/bundle.bot.cjs", "main": "./lib/bundle.bot.cjs",
"private": true,
"scripts": { "scripts": {
"bot:rollup": "node ../../node_modules/.bin/rollup index.js --config ./rollup-cli.config.js", "bot:rollup": "node ../../node_modules/.bin/rollup index.js --config ./rollup-cli.config.js",
"format:check": "prettier --check .", "format:check": "prettier --check .",

View File

@@ -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',
}, },

View File

@@ -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'],

View File

@@ -0,0 +1,4 @@
const delay = (miliseconds) =>
new Promise((res) => setTimeout(res, miliseconds))
module.exports = { delay }

View 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
View File

View File

@@ -12,20 +12,20 @@ const checkNodeVersion = () => {
) )
process.exit(1) process.exit(1)
} }
console.log(green(`Node.js combatible ${version}`)) console.log(green(`Node.js compatible ${version}`))
console.log(``) console.log(``)
} }
const checkOs = () => { const checkOs = () => {
console.log(bgCyan('🙂 Revisando tu Sistema Operativo')) console.log(bgCyan('🙂 Revisando tu sistema operativo'))
const os = process.platform const os = process.platform
if (!os.includes('win32')) { if (!os.includes('win32')) {
const messages = [ const messages = [
`El sistema operativo actual (${os}) posiblemente requiera`, `El sistema operativo actual (${os}) posiblemente requiera`,
`una confiuración adicional referente al puppeter`, `una configuración adicional referente al puppeteer`,
``, ``,
`Recuerda pasar por el WIKI`, `Recuerda pasar por el WIKI`,
`🔗 https://github.com/leifermendez/bot-whatsapp/wiki/Instalaci%C3%B3n`, `🔗 https://github.com/leifermendez/bot-whatsapp/wiki/Instalación`,
``, ``,
] ]

View File

@@ -0,0 +1,25 @@
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)
}
}
/**
* Copiar directorio con archivos
* @param {*} templateName
*/
const copyBaseApp = async (fromDir = process.cwd(), toDir = process.cwd()) => {
const BASEP_APP_PATH_FROM = `${fromDir}`
const BASEP_APP_PATH_TO = `${toDir}`
await copyFiles(BASEP_APP_PATH_FROM, BASEP_APP_PATH_TO)
}
module.exports = { copyBaseApp }

View File

@@ -14,7 +14,7 @@ const getPkgManage = async () => {
} }
const installDeps = (pkgManager, packageList) => { const installDeps = (pkgManager, packageList) => {
const errorMessage = `Ocurrio un error instalando ${packageList}` const errorMessage = `Ocurrió un error instalando ${packageList}`
let childProcess = [] let childProcess = []
const installSingle = (pkgInstall) => () => { const installSingle = (pkgInstall) => () => {

View File

@@ -1,47 +1,58 @@
const prompts = require('prompts') const prompts = require('prompts')
const { yellow, red } = require('kleur') const { yellow, red, cyan } = require('kleur')
const { installAll } = require('../install') const { copyBaseApp } = require('../create-app')
const { cleanSession } = require('../clean') const { join } = require('path')
const { existsSync } = require('fs')
const { checkNodeVersion, checkOs } = require('../check') const { checkNodeVersion, checkOs } = require('../check')
const { jsonConfig } = require('../configuration')
const bannerDone = () => {
console.log(``)
console.log(
cyan(
[
`[Agradecimientos]: Este es un proyecto OpenSource, si tienes intenciones de colaborar puedes hacerlo:`,
`[😉] Comprando un cafe https://www.buymeacoffee.com/leifermendez`,
`[⭐] Dar estrella https://github.com/leifermendez/bot-whatsapp`,
`[🚀] Realizando mejoras en el codigo`,
].join('\n')
)
)
console.log(``)
}
const startInteractive = async () => { const startInteractive = async () => {
const questions = [ const questions = [
{ {
type: 'text', type: 'text',
name: 'dependencies', name: 'outDir',
message: message: 'Quieres crear un bot? (Y/n)',
'Quieres actualizar las librerias "whatsapp-web.js"? (Y/n)',
},
{
type: 'text',
name: 'cleanTmp',
message: 'Quieres limpiar la session del bot? (Y/n)',
}, },
{ {
type: 'multiselect', type: 'multiselect',
name: 'providerWs', name: 'providerWs',
message: 'Proveedor de Whatsapp', message: '¿Cuál proveedor de whatsapp quieres utilizar?',
choices: [ choices: [
{ title: 'whatsapp-web.js', value: 'whatsapp-web.js' }, { title: 'whatsapp-web.js (gratis)', value: 'wweb' },
{ title: 'Twilio', value: 'twilio' },
{ title: 'Baileys (gratis)', value: 'bailey', disabled: true },
{ title: 'API Oficial (Meta)', value: 'meta', disabled: true }, { title: 'API Oficial (Meta)', value: 'meta', disabled: true },
{ title: 'Twilio', value: 'twilio', disabled: true },
], ],
max: 1, max: 1,
hint: 'Espacio para selecionar', hint: 'Espacio para seleccionar',
instructions: '↑/↓', instructions: '↑/↓',
}, },
{ {
type: 'multiselect', type: 'multiselect',
name: 'providerDb', name: 'providerDb',
message: 'Cual base de datos quieres usar', message: '¿Cuál base de datos quieres utilizar?',
choices: [ choices: [
{ title: 'JSONFile', value: 'json' }, { title: 'Memory', value: 'memory' },
{ title: 'MySQL', value: 'mysql', disabled: true }, { title: 'Mongo', value: 'mongo' },
{ title: 'Mongo', value: 'mongo', disabled: true }, { title: 'MySQL', value: 'mysql' },
{ title: 'Json', value: 'json', disabled: true },
], ],
max: 1, max: 1,
hint: 'Espacio para selecionar', hint: 'Espacio para seleccionar',
instructions: '↑/↓', instructions: '↑/↓',
}, },
] ]
@@ -50,78 +61,77 @@ const startInteractive = async () => {
checkNodeVersion() checkNodeVersion()
checkOs() checkOs()
const onCancel = () => { const onCancel = () => {
console.log('Proceso cancelado!') console.log('¡Proceso cancelado!')
return true return true
} }
const response = await prompts(questions, { onCancel }) const response = await prompts(questions, { onCancel })
const { const { outDir = '', providerDb = [], providerWs = [] } = response
dependencies = '',
cleanTmp = '', const createApp = async (templateName = null) => {
providerDb = [], if (!templateName)
providerWs = [], throw new Error('TEMPLATE_NAME_INVALID: ', templateName)
} = response
/** const possiblesPath = [
* Question #1 join(__dirname, 'starters', 'apps', templateName),
* @returns join(__dirname, '..', 'starters', 'apps', templateName),
*/ join(__dirname, '..', '..', 'starters', 'apps', templateName),
const installOrUdpateDep = async () => { ]
const answer = dependencies.toLowerCase() || 'n'
const answer = outDir.toLowerCase() || 'n'
if (answer.includes('n')) return true if (answer.includes('n')) return true
if (answer.includes('y')) { if (answer.includes('y')) {
await installAll() const indexOfPath = possiblesPath.find((a) => existsSync(a))
return true await copyBaseApp(indexOfPath, join(process.cwd(), templateName))
console.log(``)
console.log(yellow(`cd ${templateName}`))
console.log(yellow(`npm install`))
console.log(yellow(`npm start`))
console.log(``)
return outDir
} }
} }
/** /**
* Question #2 * Selccionar Provider (meta, twilio, etc...)
* @returns * @returns
*/ */
const cleanAllSession = async () => {
const answer = cleanTmp.toLowerCase() || 'n'
if (answer.includes('n')) return true
if (answer.includes('y')) {
await cleanSession()
return true
}
}
const vendorProvider = async () => { const vendorProvider = async () => {
const [answer] = providerWs
if (!providerWs.length) { if (!providerWs.length) {
console.log( console.log(
red( red(
`Debes de seleccionar una WS Provider. Tecla [Space] para seleccionar` `Debes seleccionar un proveedor de whatsapp. Tecla [Space] para seleccionar`
) )
) )
process.exit(1) process.exit(1)
} }
console.log(yellow(`'Deberia crer una carpeta en root/provider'`)) return answer
return true
} }
/**
* Selecionar adaptador de base de datos
* @returns
*/
const dbProvider = async () => { const dbProvider = async () => {
const answer = providerDb const [answer] = providerDb
if (!providerDb.length) { if (!providerDb.length) {
console.log( console.log(
red( red(
`Debes de seleccionar una DB Provider. Tecla [Space] para seleccionar` `Debes seleccionar un proveedor de base de datos. Tecla [Space] para seleccionar`
) )
) )
process.exit(1) process.exit(1)
} }
if (answer === 'json') { return answer
console.log('Deberia crer una carpeta en root/data')
return 1
}
} }
await installOrUdpateDep() const providerAdapter = await vendorProvider()
await cleanAllSession() const dbAdapter = await dbProvider()
await vendorProvider() const NAME_DIR = ['base', providerAdapter, dbAdapter].join('-')
await dbProvider() await createApp(NAME_DIR)
await jsonConfig() bannerDone()
} }
module.exports = { startInteractive } module.exports = { startInteractive }

View File

@@ -1,9 +1,8 @@
{ {
"name": "@bot-whatsapp/cli", "name": "@bot-whatsapp/cli",
"version": "0.0.1", "version": "0.0.19-alpha.0",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"private": true,
"devDependencies": { "devDependencies": {
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cross-spawn": "^7.0.3", "cross-spawn": "^7.0.3",
@@ -11,6 +10,7 @@
"kleur": "^4.1.5" "kleur": "^4.1.5"
}, },
"files": [ "files": [
"./starters/",
"./lib/cli/bundle.cli.cjs" "./lib/cli/bundle.cli.cjs"
], ],
"bin": { "bin": {

View File

@@ -1,4 +1,6 @@
const banner = require('../../config/banner.rollup.json')
const commonjs = require('@rollup/plugin-commonjs') const commonjs = require('@rollup/plugin-commonjs')
const copy = require('rollup-plugin-copy')
const { nodeResolve } = require('@rollup/plugin-node-resolve') const { nodeResolve } = require('@rollup/plugin-node-resolve')
const { join } = require('path') const { join } = require('path')
@@ -7,8 +9,15 @@ 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',
}, },
plugins: [commonjs(), nodeResolve()], plugins: [
copy({
targets: [{ src: 'starters/*', dest: join(__dirname, 'starters') }],
}),
commonjs(),
nodeResolve(),
],
} }

View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
const main = require('../lib/bundle.create-bot-whatsapp.cjs')
main()

View File

@@ -0,0 +1,10 @@
const { startInteractive } = require('../cli')
/**
* Voy a llamar directo a CLI
* Temporalmente luego mejoro esta
* parte
* @returns
*/
const main = () => startInteractive()
module.exports = main

View File

@@ -0,0 +1,15 @@
{
"name": "create-bot-whatsapp",
"version": "0.0.25-alpha.0",
"description": "",
"main": "./lib/bundle.create-bot-whatsapp.cjs",
"files": [
"./starters/",
"./bin/create.js",
"./lib/bundle.create-bot-whatsapp.cjs"
],
"bin": "./bin/create.js",
"dependencies": {
"@bot-whatsapp/cli": "*"
}
}

View File

@@ -0,0 +1,17 @@
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', 'bundle.create-bot-whatsapp.cjs')
module.exports = {
input: join(__dirname, 'index.js'),
output: {
banner: banner['banner.output'].join(''),
file: PATH,
format: 'cjs',
},
plugins: [commonjs(), nodeResolve()],
}

View File

@@ -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

View File

@@ -1,19 +1,24 @@
{ {
"name": "@bot-whatsapp/database", "name": "@bot-whatsapp/database",
"version": "0.0.1", "version": "0.0.11-alpha.0",
"description": "Esto es el conector a mysql, pg, mongo", "description": "Esto es el conector a mysql, pg, mongo",
"main": "./lib/mock/index.cjs", "main": "./lib/mock/index.cjs",
"private": true,
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": {}, "files": [
"./lib/"
],
"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"
} }
} }

View File

@@ -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()],
},
] ]

View 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

View File

@@ -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)
} }
} }

View 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

View File

@@ -1,19 +1,19 @@
import { cloudflarePagesAdaptor } from '@builder.io/qwik-city/adaptors/cloudflare-pages/vite'; import { cloudflarePagesAdaptor } from '@builder.io/qwik-city/adaptors/cloudflare-pages/vite'
import { extendConfig } from '@builder.io/qwik-city/vite'; import { extendConfig } from '@builder.io/qwik-city/vite'
import baseConfig from '../../vite.config'; import baseConfig from '../../vite.config'
export default extendConfig(baseConfig, () => { export default extendConfig(baseConfig, () => {
return { return {
build: { build: {
ssr: true, ssr: true,
rollupOptions: { rollupOptions: {
input: ['src/entry.cloudflare-pages.tsx', '@qwik-city-plan'], input: ['src/entry.cloudflare-pages.tsx', '@qwik-city-plan'],
}, },
}, },
plugins: [ plugins: [
cloudflarePagesAdaptor({ cloudflarePagesAdaptor({
staticGenerate: true, staticGenerate: true,
}), }),
], ],
}; }
}); })

View File

@@ -2,4 +2,4 @@
// Cloudflare Pages Functions // Cloudflare Pages Functions
// https://developers.cloudflare.com/pages/platform/functions/ // https://developers.cloudflare.com/pages/platform/functions/
export { onRequest } from '../server/entry.cloudflare-pages'; export { onRequest } from '../server/entry.cloudflare-pages'

View File

@@ -1,41 +1,44 @@
{ {
"name": "bot-whatsapp-docs", "name": "bot-whatsapp-docs",
"version": "0.0.1", "version": "0.0.1",
"description": "Basic start point to build a docs site with Qwik", "description": "Basic start point to build a docs site with Qwik",
"engines": { "engines": {
"node": ">=15.0.0" "node": ">=15.0.0"
}, },
"private": true, "private": true,
"scripts": { "scripts": {
"build": "qwik build", "build": "qwik build",
"build.client": "vite build", "build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx", "build.preview": "vite build --ssr src/entry.preview.tsx",
"build.server": "vite build -c adaptors/cloudflare-pages/vite.config.ts", "build.server": "vite build -c adaptors/cloudflare-pages/vite.config.ts",
"build.types": "tsc --incremental --noEmit", "build.types": "tsc --incremental --noEmit",
"deploy": "wrangler pages dev ./dist", "deploy": "wrangler pages dev ./dist",
"dev": "vite --mode ssr", "dev": "vite --mode ssr",
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force", "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
"fmt": "prettier --write .", "fmt": "prettier --write .",
"fmt.check": "prettier --check .", "fmt.check": "prettier --check .",
"lint": "eslint \"src/**/*.ts*\"", "lint": "eslint \"src/**/*.ts*\"",
"preview": "qwik build preview && vite preview --open", "preview": "qwik build preview && vite preview --open",
"start": "vite --open --mode ssr", "start": "vite --open --mode ssr",
"qwik": "qwik" "qwik": "qwik"
}, },
"devDependencies": { "devDependencies": {
"@builder.io/qwik": "0.14.1", "@builder.io/qwik": "0.14.1",
"@builder.io/qwik-city": "0.0.127", "@builder.io/qwik-city": "0.0.127",
"@types/eslint": "8.4.10", "@types/eslint": "8.4.10",
"@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",
"eslint": "8.28.0", "autoprefixer": "10.4.11",
"eslint-plugin-qwik": "0.14.1", "eslint": "8.28.0",
"node-fetch": "3.3.0", "eslint-plugin-qwik": "0.14.1",
"prettier": "2.7.1", "node-fetch": "3.3.0",
"typescript": "4.9.3", "postcss": "^8.4.16",
"vite": "3.2.4", "prettier": "2.7.1",
"vite-tsconfig-paths": "3.5.0", "tailwindcss": "^3.1.8",
"wrangler": "latest" "typescript": "4.9.3",
} "vite": "3.2.4",
"vite-tsconfig-paths": "3.5.0",
"wrangler": "latest"
}
} }

View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -1,9 +1,9 @@
{ {
"$schema": "https://json.schemastore.org/web-manifest-combined.json", "$schema": "https://json.schemastore.org/web-manifest-combined.json",
"name": "qwik-project-name", "name": "qwik-project-name",
"short_name": "Welcome to Qwik", "short_name": "Welcome to Qwik",
"start_url": ".", "start_url": ".",
"display": "standalone", "display": "standalone",
"background_color": "#fff", "background_color": "#fff",
"description": "A Qwik project app." "description": "A Qwik project app."
} }

View File

@@ -1,25 +1,25 @@
nav.breadcrumbs { nav.breadcrumbs {
padding: 5px; padding: 5px;
border-bottom: 1px solid #ddd; border-bottom: 1px solid #ddd;
} }
nav.breadcrumbs > span { nav.breadcrumbs > span {
display: inline-block; display: inline-block;
padding: 5px 0; padding: 5px 0;
font-size: 12px; font-size: 12px;
} }
nav.breadcrumbs > span a { nav.breadcrumbs > span a {
text-decoration: none; text-decoration: none;
color: inherit; color: inherit;
} }
nav.breadcrumbs > span::after { nav.breadcrumbs > span::after {
content: '>'; content: '>';
padding: 0 5px; padding: 0 5px;
opacity: 0.4; opacity: 0.4;
} }
nav.breadcrumbs > span:last-child::after { nav.breadcrumbs > span:last-child::after {
display: none; display: none;
} }

View File

@@ -1,74 +1,77 @@
import { component$, useStyles$ } from '@builder.io/qwik'; import { component$, useStyles$ } from '@builder.io/qwik'
import { useContent, useLocation, ContentMenu } from '@builder.io/qwik-city'; import { useContent, useLocation, ContentMenu } from '@builder.io/qwik-city'
import styles from './breadcrumbs.css?inline'; import styles from './breadcrumbs.css?inline'
export const Breadcrumbs = component$(() => { export const Breadcrumbs = component$(() => {
useStyles$(styles); useStyles$(styles)
const { menu } = useContent(); const { menu } = useContent()
const loc = useLocation(); const loc = useLocation()
const breadcrumbs = createBreadcrumbs(menu, loc.pathname); const breadcrumbs = createBreadcrumbs(menu, loc.pathname)
if (breadcrumbs.length === 0) { if (breadcrumbs.length === 0) {
return null; return null
}
return (
<nav class="breadcrumbs">
{breadcrumbs.map((b) => (
<span>{b.href ? <a href={b.href}>{b.text}</a> : b.text}</span>
))}
</nav>
);
});
export function createBreadcrumbs(menu: ContentMenu | undefined, pathname: string) {
if (menu?.items) {
for (const indexA of menu.items) {
const breadcrumbA: ContentBreadcrumb = {
text: indexA.text,
};
if (typeof indexA.href === 'string') {
breadcrumbA.href = indexA.href;
}
if (indexA.href === pathname) {
return [breadcrumbA];
}
if (indexA.items) {
for (const indexB of indexA.items) {
const breadcrumbB: ContentBreadcrumb = {
text: indexB.text,
};
if (typeof indexB.href === 'string') {
breadcrumbB.href = indexB.href;
}
if (indexB.href === pathname) {
return [breadcrumbA, breadcrumbB];
}
if (indexB.items) {
for (const indexC of indexB.items) {
const breadcrumbC: ContentBreadcrumb = {
text: indexC.text,
};
if (typeof indexC.href === 'string') {
breadcrumbC.href = indexC.href;
}
if (indexC.href === pathname) {
return [breadcrumbA, breadcrumbB, breadcrumbC];
}
}
}
}
}
} }
}
return []; return (
<nav class="breadcrumbs">
{breadcrumbs.map((b) => (
<span>{b.href ? <a href={b.href}>{b.text}</a> : b.text}</span>
))}
</nav>
)
})
export function createBreadcrumbs(
menu: ContentMenu | undefined,
pathname: string
) {
if (menu?.items) {
for (const indexA of menu.items) {
const breadcrumbA: ContentBreadcrumb = {
text: indexA.text,
}
if (typeof indexA.href === 'string') {
breadcrumbA.href = indexA.href
}
if (indexA.href === pathname) {
return [breadcrumbA]
}
if (indexA.items) {
for (const indexB of indexA.items) {
const breadcrumbB: ContentBreadcrumb = {
text: indexB.text,
}
if (typeof indexB.href === 'string') {
breadcrumbB.href = indexB.href
}
if (indexB.href === pathname) {
return [breadcrumbA, breadcrumbB]
}
if (indexB.items) {
for (const indexC of indexB.items) {
const breadcrumbC: ContentBreadcrumb = {
text: indexC.text,
}
if (typeof indexC.href === 'string') {
breadcrumbC.href = indexC.href
}
if (indexC.href === pathname) {
return [breadcrumbA, breadcrumbB, breadcrumbC]
}
}
}
}
}
}
}
return []
} }
interface ContentBreadcrumb { interface ContentBreadcrumb {
text: string; text: string
href?: string; href?: string
} }

View File

@@ -1,22 +1,22 @@
footer { footer {
border-top: 0.5px solid #ddd; border-top: 0.5px solid #ddd;
margin-top: 40px; margin-top: 40px;
padding: 20px; padding: 20px;
text-align: center; text-align: center;
} }
footer a { footer a {
color: #9e9e9e; color: #9e9e9e;
font-size: 12px; font-size: 12px;
} }
footer ul { footer ul {
list-style: none; list-style: none;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
footer li { footer li {
display: inline-block; display: inline-block;
padding: 6px 12px; padding: 6px 12px;
} }

View File

@@ -1,36 +1,40 @@
import { component$, useStyles$ } from '@builder.io/qwik'; import { component$, useStyles$ } from '@builder.io/qwik'
import styles from './footer.css?inline'; import styles from './footer.css?inline'
export default component$(() => { export default component$(() => {
useStyles$(styles); useStyles$(styles)
return ( return (
<footer> <footer>
<ul> <ul>
<li> <li>
<a href="/docs">Docs</a> <a href="/docs">Docs</a>
</li> </li>
<li> <li>
<a href="/about-us">About Us</a> <a href="/about-us">About Us</a>
</li> </li>
<li> <li>
<a href="https://qwik.builder.io/">Qwik</a> <a href="https://qwik.builder.io/">Qwik</a>
</li> </li>
<li> <li>
<a href="https://twitter.com/QwikDev">Twitter</a> <a href="https://twitter.com/QwikDev">Twitter</a>
</li> </li>
<li> <li>
<a href="https://github.com/BuilderIO/qwik">GitHub</a> <a href="https://github.com/BuilderIO/qwik">GitHub</a>
</li> </li>
<li> <li>
<a href="https://qwik.builder.io/chat">Chat</a> <a href="https://qwik.builder.io/chat">Chat</a>
</li> </li>
</ul> </ul>
<div> <div>
<a href="https://www.builder.io/" target="_blank" class="builder"> <a
Made with by Builder.io href="https://www.builder.io/"
</a> target="_blank"
</div> class="builder"
</footer> >
); Made with by Builder.io
}); </a>
</div>
</footer>
)
})

View File

@@ -1,34 +1,34 @@
header { header {
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 11; z-index: 11;
display: grid; display: grid;
grid-template-columns: minmax(130px, auto) 1fr; grid-template-columns: minmax(130px, auto) 1fr;
gap: 30px; gap: 30px;
height: 80px; height: 80px;
width: 100%; width: 100%;
padding: 10px; padding: 10px;
background-color: white; background-color: white;
overflow: hidden; overflow: hidden;
} }
header a.logo { header a.logo {
display: block; display: block;
} }
header a { header a {
text-decoration: none; text-decoration: none;
} }
header nav { header nav {
text-align: right; text-align: right;
} }
header nav a { header nav a {
display: inline-block; display: inline-block;
padding: 5px 15px; padding: 5px 15px;
} }
header nav a:hover { header nav a:hover {
text-decoration: underline; text-decoration: underline;
} }

View File

@@ -1,26 +1,32 @@
import { component$, useStyles$ } from '@builder.io/qwik'; import { component$, useStyles$ } from '@builder.io/qwik'
import { useLocation } from '@builder.io/qwik-city'; import { useLocation } from '@builder.io/qwik-city'
import { QwikLogo } from '../icons/qwik'; import { QwikLogo } from '../icons/qwik'
import styles from './header.css?inline'; import styles from './header.css?inline'
export default component$(() => { export default component$(() => {
useStyles$(styles); useStyles$(styles)
const { pathname } = useLocation(); const { pathname } = useLocation()
return ( return (
<header> <header>
<a class="logo" href="/"> <a class="logo" href="/">
<QwikLogo /> <QwikLogo />
</a> </a>
<nav> <nav>
<a href="/docs" class={{ active: pathname.startsWith('/docs') }}> <a
Docs href="/docs"
</a> class={{ active: pathname.startsWith('/docs') }}
<a href="/about-us" class={{ active: pathname.startsWith('/about-us') }}> >
About Us Docs
</a> </a>
</nav> <a
</header> href="/about-us"
); class={{ active: pathname.startsWith('/about-us') }}
}); >
About Us
</a>
</nav>
</header>
)
})

View File

@@ -1,20 +1,20 @@
export const QwikLogo = () => ( export const QwikLogo = () => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 167 53"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 167 53">
<path <path
fill="#000" fill="#000"
d="M81.95 46.59h-6.4V35.4a12.25 12.25 0 0 1-7.06 2.17c-3.47 0-6.06-.94-7.67-2.92-1.6-1.96-2.42-5.45-2.42-10.43 0-5.1.95-8.62 2.87-10.67 1.96-2.08 5.1-3.09 9.43-3.09 4.1 0 7.82.57 11.25 1.67V46.6Zm-6.4-30.31a16.6 16.6 0 0 0-4.85-.66c-2.17 0-3.73.56-4.6 1.7-.85 1.17-1.32 3.38-1.32 6.65 0 3.08.41 5.14 1.26 6.26.86 1.1 2.33 1.67 4.5 1.67 2.84 0 5.01-1.17 5.01-2.62v-13Zm15.58-5.14c2.27 6.3 4.2 12.6 5.86 18.95 2.22-6.5 4.1-12.8 5.55-18.95h5.61a187.5 187.5 0 0 1 5.3 18.95c2.52-6.9 4.5-13.21 5.95-18.95h6.31a285.68 285.68 0 0 1-8.92 25.76h-7.53c-.86-4.6-2.22-10.14-4.04-16.75a151.51 151.51 0 0 1-4.89 16.75H92.8a287.88 287.88 0 0 0-8.17-25.76h6.5Zm41.7-3.58c-2.83 0-3.63-.7-3.63-3.59 0-2.57.82-3.18 3.63-3.18 2.83 0 3.63.6 3.63 3.18 0 2.89-.8 3.59-3.63 3.59Zm-3.18 3.58h6.4V36.9h-6.4V11.14Zm36.65 0c-4.54 6.46-7.72 10.39-9.49 11.8 1.46.95 5.36 5.95 10.2 13.98h-7.38c-6.02-9.13-8.89-13.07-10.3-13.67v13.67h-6.4V0h6.4v23.23c1.45-1.06 4.63-5.1 9.54-12.09h7.43Z" d="M81.95 46.59h-6.4V35.4a12.25 12.25 0 0 1-7.06 2.17c-3.47 0-6.06-.94-7.67-2.92-1.6-1.96-2.42-5.45-2.42-10.43 0-5.1.95-8.62 2.87-10.67 1.96-2.08 5.1-3.09 9.43-3.09 4.1 0 7.82.57 11.25 1.67V46.6Zm-6.4-30.31a16.6 16.6 0 0 0-4.85-.66c-2.17 0-3.73.56-4.6 1.7-.85 1.17-1.32 3.38-1.32 6.65 0 3.08.41 5.14 1.26 6.26.86 1.1 2.33 1.67 4.5 1.67 2.84 0 5.01-1.17 5.01-2.62v-13Zm15.58-5.14c2.27 6.3 4.2 12.6 5.86 18.95 2.22-6.5 4.1-12.8 5.55-18.95h5.61a187.5 187.5 0 0 1 5.3 18.95c2.52-6.9 4.5-13.21 5.95-18.95h6.31a285.68 285.68 0 0 1-8.92 25.76h-7.53c-.86-4.6-2.22-10.14-4.04-16.75a151.51 151.51 0 0 1-4.89 16.75H92.8a287.88 287.88 0 0 0-8.17-25.76h6.5Zm41.7-3.58c-2.83 0-3.63-.7-3.63-3.59 0-2.57.82-3.18 3.63-3.18 2.83 0 3.63.6 3.63 3.18 0 2.89-.8 3.59-3.63 3.59Zm-3.18 3.58h6.4V36.9h-6.4V11.14Zm36.65 0c-4.54 6.46-7.72 10.39-9.49 11.8 1.46.95 5.36 5.95 10.2 13.98h-7.38c-6.02-9.13-8.89-13.07-10.3-13.67v13.67h-6.4V0h6.4v23.23c1.45-1.06 4.63-5.1 9.54-12.09h7.43Z"
/> />
<path <path
fill="#18B6F6" fill="#18B6F6"
d="M40.97 52.54 32.1 43.7l-.14.02v-.1l-18.9-18.66 4.66-4.5-2.74-15.7L2 20.87a7.14 7.14 0 0 0-1.03 8.52l8.11 13.45a6.81 6.81 0 0 0 5.92 3.3l4.02-.05 21.96 6.46Z" d="M40.97 52.54 32.1 43.7l-.14.02v-.1l-18.9-18.66 4.66-4.5-2.74-15.7L2 20.87a7.14 7.14 0 0 0-1.03 8.52l8.11 13.45a6.81 6.81 0 0 0 5.92 3.3l4.02-.05 21.96 6.46Z"
/> />
<path <path
fill="#AC7EF4" fill="#AC7EF4"
d="m45.82 20.54-1.78-3.3-.93-1.68-.37-.66-.04.04-4.9-8.47a6.85 6.85 0 0 0-5.99-3.43l-4.28.12-12.8.04a6.85 6.85 0 0 0-5.85 3.37L1.1 21.99 15 4.73l18.24 20.04L30 28.04l1.94 15.68.02-.04v.04h-.04l.04.04 1.51 1.47 7.36 7.19c.3.29.81-.06.6-.43l-4.54-8.93 7.91-14.63.26-.3a6.73 6.73 0 0 0 .76-7.6Z" d="m45.82 20.54-1.78-3.3-.93-1.68-.37-.66-.04.04-4.9-8.47a6.85 6.85 0 0 0-5.99-3.43l-4.28.12-12.8.04a6.85 6.85 0 0 0-5.85 3.37L1.1 21.99 15 4.73l18.24 20.04L30 28.04l1.94 15.68.02-.04v.04h-.04l.04.04 1.51 1.47 7.36 7.19c.3.29.81-.06.6-.43l-4.54-8.93 7.91-14.63.26-.3a6.73 6.73 0 0 0 .76-7.6Z"
/> />
<path <path
fill="#fff" fill="#fff"
d="M33.3 24.69 15.02 4.75l2.6 15.62-4.66 4.51L31.91 43.7l-1.7-15.62 3.1-3.4Z" d="M33.3 24.69 15.02 4.75l2.6 15.62-4.66 4.51L31.91 43.7l-1.7-15.62 3.1-3.4Z"
/> />
</svg> </svg>
); )

View File

@@ -1,13 +1,13 @@
.menu { .menu {
background: #eee; background: #eee;
padding: 20px 10px; padding: 20px 10px;
} }
.menu h5 { .menu h5 {
margin: 0; margin: 0;
} }
.menu ul { .menu ul {
padding-left: 20px; padding-left: 20px;
margin: 5px 0 25px 0; margin: 5px 0 25px 0;
} }

View File

@@ -1,36 +1,37 @@
import { component$, useStyles$ } from '@builder.io/qwik'; import { component$, useStyles$ } from '@builder.io/qwik'
import { useContent, Link, useLocation } from '@builder.io/qwik-city'; import { useContent, Link, useLocation } from '@builder.io/qwik-city'
import styles from './menu.css?inline'; import styles from './menu.css?inline'
export default component$(() => { export default component$(() => {
useStyles$(styles); useStyles$(styles)
const { menu } = useContent(); const { menu } = useContent()
const loc = useLocation(); const loc = useLocation()
return ( return (
<aside class="menu"> <aside class="menu">
{menu {menu
? menu.items?.map((item) => ( ? menu.items?.map((item) => (
<> <>
<h5>{item.text}</h5> <h5>{item.text}</h5>
<ul> <ul>
{item.items?.map((item) => ( {item.items?.map((item) => (
<li> <li>
<Link <Link
href={item.href} href={item.href}
class={{ class={{
'is-active': loc.pathname === item.href, 'is-active':
}} loc.pathname === item.href,
> }}
{item.text} >
</Link> {item.text}
</li> </Link>
))} </li>
</ul> ))}
</> </ul>
)) </>
: null} ))
</aside> : null}
); </aside>
}); )
})

View File

@@ -1,33 +1,33 @@
.on-this-page { .on-this-page {
padding-bottom: 20px; padding-bottom: 20px;
font-size: 0.9em; font-size: 0.9em;
} }
.on-this-page h6 { .on-this-page h6 {
margin: 10px 0; margin: 10px 0;
font-weight: bold; font-weight: bold;
text-transform: uppercase; text-transform: uppercase;
} }
.on-this-page ul { .on-this-page ul {
margin: 0; margin: 0;
padding: 0 0 20px 0; padding: 0 0 20px 0;
list-style: none; list-style: none;
} }
.on-this-page a { .on-this-page a {
position: relative; position: relative;
display: block; display: block;
border: 0 solid #ddd; border: 0 solid #ddd;
border-left-width: 2px; border-left-width: 2px;
padding: 4px 2px 4px 8px; padding: 4px 2px 4px 8px;
text-decoration: none; text-decoration: none;
} }
.on-this-page a.indent { .on-this-page a.indent {
padding-left: 30px; padding-left: 30px;
} }
.on-this-page a:hover { .on-this-page a:hover {
border-color: var(--theme-accent); border-color: var(--theme-accent);
} }

View File

@@ -1,62 +1,63 @@
import { useContent, useLocation } from '@builder.io/qwik-city'; import { useContent, useLocation } from '@builder.io/qwik-city'
import { component$, useStyles$ } from '@builder.io/qwik'; import { component$, useStyles$ } from '@builder.io/qwik'
import styles from './on-this-page.css?inline'; import styles from './on-this-page.css?inline'
export default component$(() => { export default component$(() => {
useStyles$(styles); useStyles$(styles)
const { headings } = useContent(); const { headings } = useContent()
const contentHeadings = headings?.filter((h) => h.level === 2 || h.level === 3) || []; const contentHeadings =
headings?.filter((h) => h.level === 2 || h.level === 3) || []
const { pathname } = useLocation(); const { pathname } = useLocation()
const editUrl = `#update-your-edit-url-for-${pathname}`; const editUrl = `#update-your-edit-url-for-${pathname}`
return ( return (
<aside class="on-this-page"> <aside class="on-this-page">
{contentHeadings.length > 0 ? ( {contentHeadings.length > 0 ? (
<> <>
<h6>On This Page</h6> <h6>On This Page</h6>
<ul> <ul>
{contentHeadings.map((h) => ( {contentHeadings.map((h) => (
<li> <li>
<a <a
href={`#${h.id}`} href={`#${h.id}`}
class={{ class={{
block: true, block: true,
indent: h.level > 2, indent: h.level > 2,
}} }}
> >
{h.text} {h.text}
</a> </a>
</li> </li>
))} ))}
</ul> </ul>
</> </>
) : null} ) : null}
<h6>More</h6> <h6>More</h6>
<ul> <ul>
<li> <li>
<a href={editUrl} target="_blank"> <a href={editUrl} target="_blank">
Edit this page Edit this page
</a> </a>
</li> </li>
<li> <li>
<a href="https://qwik.builder.io/chat" target="_blank"> <a href="https://qwik.builder.io/chat" target="_blank">
Join our community Join our community
</a> </a>
</li> </li>
<li> <li>
<a href="https://github.com/BuilderIO/qwik" target="_blank"> <a href="https://github.com/BuilderIO/qwik" target="_blank">
GitHub GitHub
</a> </a>
</li> </li>
<li> <li>
<a href="https://twitter.com/QwikDev" target="_blank"> <a href="https://twitter.com/QwikDev" target="_blank">
@QwikDev @QwikDev
</a> </a>
</li> </li>
</ul> </ul>
</aside> </aside>
); )
}); })

View File

@@ -1,32 +1,35 @@
import { component$ } from '@builder.io/qwik'; import { component$ } from '@builder.io/qwik'
import { useDocumentHead, useLocation } from '@builder.io/qwik-city'; import { useDocumentHead, useLocation } from '@builder.io/qwik-city'
/** /**
* The RouterHead component is placed inside of the document `<head>` element. * The RouterHead component is placed inside of the document `<head>` element.
*/ */
export const RouterHead = component$(() => { export const RouterHead = component$(() => {
const head = useDocumentHead(); const head = useDocumentHead()
const loc = useLocation(); const loc = useLocation()
return ( return (
<> <>
<title>{head.title}</title> <title>{head.title}</title>
<link rel="canonical" href={loc.href} /> <link rel="canonical" href={loc.href} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
{head.meta.map((m) => ( {head.meta.map((m) => (
<meta {...m} /> <meta {...m} />
))} ))}
{head.links.map((l) => ( {head.links.map((l) => (
<link {...l} /> <link {...l} />
))} ))}
{head.styles.map((s) => ( {head.styles.map((s) => (
<style {...s.props} dangerouslySetInnerHTML={s.style} /> <style {...s.props} dangerouslySetInnerHTML={s.style} />
))} ))}
</> </>
); )
}); })

View File

@@ -7,10 +7,10 @@
* - https://qwik.builder.io/qwikcity/adaptors/cloudflare-pages/ * - https://qwik.builder.io/qwikcity/adaptors/cloudflare-pages/
* *
*/ */
import { createQwikCity } from '@builder.io/qwik-city/middleware/cloudflare-pages'; import { createQwikCity } from '@builder.io/qwik-city/middleware/cloudflare-pages'
import qwikCityPlan from '@qwik-city-plan'; import qwikCityPlan from '@qwik-city-plan'
import render from './entry.ssr'; import render from './entry.ssr'
const onRequest = createQwikCity({ render, qwikCityPlan }); const onRequest = createQwikCity({ render, qwikCityPlan })
export { onRequest }; export { onRequest }

View File

@@ -9,9 +9,9 @@
* - More code is transferred to the browser than in SSR mode. * - More code is transferred to the browser than in SSR mode.
* - Optimizer/Serialization/Deserialization code is not exercised! * - Optimizer/Serialization/Deserialization code is not exercised!
*/ */
import { render, RenderOptions } from '@builder.io/qwik'; import { render, RenderOptions } from '@builder.io/qwik'
import Root from './root'; import Root from './root'
export default function (opts: RenderOptions) { export default function (opts: RenderOptions) {
return render(document, <Root />, opts); return render(document, <Root />, opts)
} }

View File

@@ -10,11 +10,11 @@
* - https://vitejs.dev/config/preview-options.html#preview-options * - https://vitejs.dev/config/preview-options.html#preview-options
* *
*/ */
import { createQwikCity } from '@builder.io/qwik-city/middleware/node'; import { createQwikCity } from '@builder.io/qwik-city/middleware/node'
import render from './entry.ssr'; import render from './entry.ssr'
import qwikCityPlan from '@qwik-city-plan'; import qwikCityPlan from '@qwik-city-plan'
/** /**
* The default export is the QwikCity adaptor used by Vite preview. * The default export is the QwikCity adaptor used by Vite preview.
*/ */
export default createQwikCity({ render, qwikCityPlan }); export default createQwikCity({ render, qwikCityPlan })

View File

@@ -10,18 +10,18 @@
* - npm run build * - npm run build
* *
*/ */
import { renderToStream, RenderToStreamOptions } from '@builder.io/qwik/server'; import { renderToStream, RenderToStreamOptions } from '@builder.io/qwik/server'
import { manifest } from '@qwik-client-manifest'; import { manifest } from '@qwik-client-manifest'
import Root from './root'; import Root from './root'
export default function (opts: RenderToStreamOptions) { export default function (opts: RenderToStreamOptions) {
return renderToStream(<Root />, { return renderToStream(<Root />, {
manifest, manifest,
...opts, ...opts,
// Use container attributes to set attributes on the html tag. // Use container attributes to set attributes on the html tag.
containerAttributes: { containerAttributes: {
lang: 'en-us', lang: 'en-us',
...opts.containerAttributes, ...opts.containerAttributes,
}, },
}); })
} }

View File

@@ -1,66 +1,67 @@
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
:root { :root {
--user-font-scale: 1rem - 16px; --user-font-scale: 1rem - 16px;
--max-width: calc(100% - 1rem); --max-width: calc(100% - 1rem);
--font-body: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, --font-body: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI,
Apple Color Emoji, Segoe UI Emoji; Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
--font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', --font-mono: 'IBM Plex Mono', Consolas, 'Andale Mono WT', 'Andale Mono',
'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono',
'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace; 'Bitstream Vera Sans Mono', 'Liberation Mono', 'Nimbus Mono L', Monaco,
'Courier New', Courier, monospace;
color-scheme: light; color-scheme: light;
--theme-accent: #006ce9; --theme-accent: #006ce9;
--theme-text: #181818; --theme-text: #181818;
} }
@media (min-width: 50em) { @media (min-width: 50em) {
:root { :root {
--max-width: 46em; --max-width: 46em;
} }
} }
body { body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 100vh; min-height: 100vh;
font-family: var(--font-body); font-family: var(--font-body);
font-size: 1rem; font-size: 1rem;
font-size: clamp(0.9rem, 0.75rem + 0.375vw + var(--user-font-scale), 1rem); font-size: clamp(0.9rem, 0.75rem + 0.375vw + var(--user-font-scale), 1rem);
line-height: 1.5; line-height: 1.5;
max-width: 100vw; max-width: 100vw;
background: var(--theme-bg); background: var(--theme-bg);
color: var(--theme-text); color: var(--theme-text);
} }
main { main {
padding: 10px 20px; padding: 10px 20px;
max-width: 960px; max-width: 960px;
margin: 0 auto; margin: 0 auto;
} }
a { a {
color: var(--theme-accent); color: var(--theme-accent);
} }
a:hover { a:hover {
text-decoration: none; text-decoration: none;
} }
code, code,
kbd, kbd,
samp, samp,
pre { pre {
font-family: var(--font-mono); font-family: var(--font-mono);
} }
code { code {
background-color: rgb(224, 224, 224); background-color: rgb(224, 224, 224);
padding: 2px 4px; padding: 2px 4px;
border-radius: 3px; border-radius: 3px;
font-size: 0.9em; font-size: 0.9em;
border-bottom: 2px solid #bfbfbf; border-bottom: 2px solid #bfbfbf;
} }

View File

@@ -1,26 +1,30 @@
import { component$ } from '@builder.io/qwik'; import { component$ } from '@builder.io/qwik'
import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city'; import {
import { RouterHead } from './components/router-head/router-head'; QwikCityProvider,
RouterOutlet,
ServiceWorkerRegister,
} from '@builder.io/qwik-city'
import { RouterHead } from './components/router-head/router-head'
import './global.css'; import './global.css'
export default component$(() => { export default component$(() => {
/* /*
* The root of a QwikCity site always start with the <QwikCityProvider> component, * The root of a QwikCity site always start with the <QwikCityProvider> component,
* immediately followed by the document's <head> and <body>. * immediately followed by the document's <head> and <body>.
* *
* Dont remove the `<head>` and `<body>` elements. * Dont remove the `<head>` and `<body>` elements.
*/ */
return ( return (
<QwikCityProvider> <QwikCityProvider>
<head> <head>
<meta charSet="utf-8" /> <meta charSet="utf-8" />
<RouterHead /> <RouterHead />
</head> </head>
<body lang="en"> <body lang="en">
<RouterOutlet /> <RouterOutlet />
<ServiceWorkerRegister /> <ServiceWorkerRegister />
</body> </body>
</QwikCityProvider> </QwikCityProvider>
); )
}); })

View File

@@ -1,22 +1,22 @@
.docs { .docs {
display: grid; display: grid;
grid-template-columns: 210px auto 190px; grid-template-columns: 210px auto 190px;
grid-template-areas: 'menu article on-this-page'; grid-template-areas: 'menu article on-this-page';
gap: 40px; gap: 40px;
} }
.docs h1 { .docs h1 {
margin-top: 0; margin-top: 0;
} }
.docs .menu { .docs .menu {
grid-area: menu; grid-area: menu;
} }
.docs article { .docs article {
grid-area: article; grid-area: article;
} }
.docs .on-this-page { .docs .on-this-page {
grid-area: on-this-page; grid-area: on-this-page;
} }

View File

@@ -1,25 +1,25 @@
import { component$, Slot, useStyles$ } from '@builder.io/qwik'; import { component$, Slot, useStyles$ } from '@builder.io/qwik'
import type { DocumentHead } from '@builder.io/qwik-city'; import type { DocumentHead } from '@builder.io/qwik-city'
import Menu from '~/components/menu/menu'; import Menu from '~/components/menu/menu'
import OnThisPage from '~/components/on-this-page/on-this-page'; import OnThisPage from '~/components/on-this-page/on-this-page'
import styles from './docs.css?inline'; import styles from './docs.css?inline'
export default component$(() => { export default component$(() => {
useStyles$(styles); useStyles$(styles)
return ( return (
<div class="docs"> <div class="docs">
<Menu /> <Menu />
<article> <article>
<Slot /> <Slot />
</article> </article>
<OnThisPage /> <OnThisPage />
</div> </div>
); )
}); })
export const head: DocumentHead = ({ head }) => { export const head: DocumentHead = ({ head }) => {
return { return {
title: `${head.title} - Documentation`, title: `${head.title} - Documentation`,
}; }
}; }

View File

@@ -1,15 +1,15 @@
import { component$, Slot } from '@builder.io/qwik'; import { component$, Slot } from '@builder.io/qwik'
import Footer from '~/components/footer/footer'; import Footer from '~/components/footer/footer'
import Header from '~/components/header/header'; import Header from '~/components/header/header'
export default component$(() => { export default component$(() => {
return ( return (
<> <>
<Header /> <Header />
<main> <main>
<Slot /> <Slot />
</main> </main>
<Footer /> <Footer />
</> </>
); )
}); })

View File

@@ -7,12 +7,12 @@
* Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline. * Qwik uses a service worker to speed up your site and reduce latency, ie, not used in the traditional way of offline.
* You can also use this file to add more functionality that runs in the service worker. * You can also use this file to add more functionality that runs in the service worker.
*/ */
import { setupServiceWorker } from '@builder.io/qwik-city/service-worker'; import { setupServiceWorker } from '@builder.io/qwik-city/service-worker'
setupServiceWorker(); setupServiceWorker()
addEventListener('install', () => self.skipWaiting()); addEventListener('install', () => self.skipWaiting())
addEventListener('activate', () => self.clients.claim()); addEventListener('activate', () => self.clients.claim())
declare const self: ServiceWorkerGlobalScope; declare const self: ServiceWorkerGlobalScope

View 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',
}

View File

@@ -1,15 +1,15 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite'
import { qwikVite } from '@builder.io/qwik/optimizer'; import { qwikVite } from '@builder.io/qwik/optimizer'
import { qwikCity } from '@builder.io/qwik-city/vite'; import { qwikCity } from '@builder.io/qwik-city/vite'
import tsconfigPaths from 'vite-tsconfig-paths'; import tsconfigPaths from 'vite-tsconfig-paths'
export default defineConfig(() => { export default defineConfig(() => {
return { return {
plugins: [qwikCity(), qwikVite(), tsconfigPaths()], plugins: [qwikCity(), qwikVite(), tsconfigPaths()],
preview: { preview: {
headers: { headers: {
'Cache-Control': 'public, max-age=600', 'Cache-Control': 'public, max-age=600',
}, },
}, },
}; }
}); })

View File

@@ -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_

View File

@@ -1,12 +1,14 @@
{ {
"name": "@bot-whatsapp/provider", "name": "@bot-whatsapp/provider",
"version": "0.0.1", "version": "0.0.17-alpha.0",
"description": "Esto es el conector a Twilio, Meta, etc...", "description": "Esto es el conector a Twilio, Meta, etc...",
"main": "./lib/mock/index.cjs", "main": "./lib/mock/index.cjs",
"private": true,
"keywords": [], "keywords": [],
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"files": [
"./lib/"
],
"dependencies": { "dependencies": {
"@bot-whatsapp/bot": "*", "@bot-whatsapp/bot": "*",
"qr-image": "^3.2.0" "qr-image": "^3.2.0"

View File

@@ -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',
}, },

View File

@@ -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 () => {
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

View 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

View File

@@ -0,0 +1,5 @@
const parseNumber = (number) => {
return `${number}`.replace('whatsapp:', '').replace('+', '')
}
module.exports = { parseNumber }

View File

@@ -1,13 +1,23 @@
const { Client, LocalAuth } = require('whatsapp-web.js') const { Client, LocalAuth, MessageMedia, Buttons } = 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 } = 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 +31,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 +71,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 +87,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

View File

@@ -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
View 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 librerías copiadas'))

123
scripts/release.js Normal file
View File

@@ -0,0 +1,123 @@
const { writeFile, readFileSync } = require('fs')
const { join } = require('path')
const { execFile } = require('node:child_process')
const process = require('node:process')
const util = require('node:util')
const OS_ENVIROMENT_WIN = process.platform.includes('win32')
const semver = require('semver')
const NPM_COMMAND = OS_ENVIROMENT_WIN ? 'npm.cmd' : 'npm'
const [PKG_ARG, PKG_ARG_VERSION, NPM_TOKEN] = process.argv.slice(2) || [null]
const PATH_PACKAGES = join(__dirname, '..', `packages`)
const cmd = util.promisify(execFile)
/**
* Create Token
*/
const npmToken = (token = null) =>
new Promise((resolve, reject) => {
writeFile(
`${process.cwd()}/.npmrc`,
`//registry.npmjs.org/:_authToken=${token}`,
(error) => {
if (error) reject(error)
resolve()
}
)
})
/**
* Leer package json
* @param {*} packageName
* @returns
*/
const readPackage = (packageName = null) => {
const pkgJson = join(PATH_PACKAGES, packageName, 'package.json')
const rawFile = readFileSync(pkgJson, 'utf-8')
if (!rawFile) throw new Error(`ERROR_FILE_READ`)
return JSON.parse(rawFile)
}
/**
* Actualizar package json
* @param {*} packageName
* @param {*} newPkgJson
*/
const updatePackage = (packageName = null, newPkgJson) => {
return new Promise((resolve, reject) => {
const pkgJson = join(PATH_PACKAGES, packageName, 'package.json')
if (!Object.keys(newPkgJson).length) throw new Error(`ERROR_FILE_READ`)
writeFile(pkgJson, newPkgJson, (err) => {
if (err) reject(err)
resolve(true)
})
})
}
/**
* Actualizar version
* @param {*} packageName
*/
const updateVersion = async (packageName = null, number = null) => {
if (!packageName) throw new Error(`PATH_ERROR_PACKAGE: ${packageName}`)
const pkgJsonObject = readPackage(packageName)
const { version } = pkgJsonObject
const newVersion = !number
? semver.inc(version, 'prepatch', 'alpha')
: `${number}`
if (!semver.valid(newVersion))
throw new Error(`VERSION_ERROR: ${newVersion}`)
const newPkgJson = JSON.stringify(
{ ...pkgJsonObject, version: newVersion },
null,
2
)
await updatePackage(packageName, newPkgJson)
return { version: newVersion }
}
const packRelease = async (packageName) => {
const pkgJson = join(PATH_PACKAGES, packageName)
const { stdout } = await cmd(NPM_COMMAND, ['pack'], {
stdio: 'inherit',
cwd: pkgJson,
})
return stdout
}
const publishRelease = async (packageName, latest = null) => {
const args = !latest ? ['--tag', 'dev'] : ['--access', 'public']
const pkgJson = join(PATH_PACKAGES, packageName)
const { stdout } = await cmd(NPM_COMMAND, ['publish'].concat(args), {
stdio: 'inherit',
cwd: pkgJson,
})
console.log(stdout)
return stdout
}
/**
* Recibe los argumentos entrantes
*/
const main = async () => {
if (PKG_ARG) {
const tokenNpm = NPM_TOKEN ? NPM_TOKEN.split('=').at(1) : null
const pkgName = PKG_ARG ? PKG_ARG.split('=').at(1) : null
const pkgNumber = PKG_ARG_VERSION
? PKG_ARG_VERSION.split('=').at(1)
: null
if (tokenNpm) await npmToken(tokenNpm)
await updateVersion(pkgName, pkgNumber)
await packRelease(pkgName)
await publishRelease(pkgName, pkgNumber)
}
}
main()

View 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)

View 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()

View 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 base-twilio-memory",
"start": "node app.js"
},
"keywords": [],
"dependencies": {
"whatsapp-web.js": "^1.18.3"
},
"author": "",
"license": "ISC"
}

View 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)

View 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()

View 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 base-twilio-mongo",
"start": "node app.js"
},
"keywords": [],
"dependencies": {
"whatsapp-web.js": "^1.18.3"
},
"author": "",
"license": "ISC"
}

Some files were not shown because too many files have changed in this diff Show More