diff --git a/.eslintignore b/.eslintignore index c6f157a..6c4b500 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ -packages/docs/* \ No newline at end of file +packages/docs/* +packages/portal/* \ No newline at end of file diff --git a/.github/workflows/check-provider-major.yml b/.github/workflows/check-provider-major.yml index 5f0227b..ea91659 100644 --- a/.github/workflows/check-provider-major.yml +++ b/.github/workflows/check-provider-major.yml @@ -25,20 +25,28 @@ jobs: run: yarn install --immutable --network-timeout 300000 - name: Check Baileys - run: yarn node ./scripts/checker.js --name=baileys --stable=false + run: yarn node ./scripts/checker.js --name=baileys --stable=true - name: Check Venom - run: yarn node ./scripts/checker.js --name=venom --stable=false + run: yarn node ./scripts/checker.js --name=venom --stable=true - name: Check web-whatsapp - run: yarn node ./scripts/checker.js --name=web-whatsapp --stable=false + run: yarn node ./scripts/checker.js --name=web-whatsapp --stable=true - name: Check Meta - run: yarn node ./scripts/checker.js --name=meta --stable=false + run: yarn node ./scripts/checker.js --name=meta --stable=true - name: Check Twilio - run: yarn node ./scripts/checker.js --name=twilio --stable=false + run: yarn node ./scripts/checker.js --name=twilio --stable=true + + - name: Add and commit changes to gh-pages branch + run: | + git config --local user.email 'action@github.com' + git config --local user.name 'GitHub Action' + git add . - uses: stefanzweifel/git-auto-commit-action@v4 with: - commit_message: 'ci(providers): 🚩 Check BREAKING CHANGE' + commit_message: 'ci(providers): check provider versions' + create_branch: true + branch: feature/providers-major diff --git a/.github/workflows/check-providers.yml b/.github/workflows/check-providers.yml deleted file mode 100644 index 5bdae6c..0000000 --- a/.github/workflows/check-providers.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: Rev Providers - -on: - push: - branches: - - dev - pull_request: - branches: - - dev - -jobs: - check-npm: - name: Install Dependencies - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Node - uses: actions/setup-node@v3 - with: - node-version: 16.x - cache: 'yarn' - registry-url: https://registry.npmjs.org/ - - - run: corepack enable - - - name: Install NPM Dependencies - run: yarn install --immutable --network-timeout 300000 - - - name: Check Baileys - run: yarn node ./scripts/checker.js --name=baileys --stable=true - - - name: Check Venom - run: yarn node ./scripts/checker.js --name=venom --stable=true - - - name: Check web-whatsapp - run: yarn node ./scripts/checker.js --name=web-whatsapp --stable=true - - - name: Check Meta - run: yarn node ./scripts/checker.js --name=meta --stable=true - - - name: Check Twilio - run: yarn node ./scripts/checker.js --name=twilio --stable=true - - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: 'ci(providers): 👍 updated versions stable providers' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82f5ff0..87e42a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,6 @@ name: Build and Test on: - push: - branches: - - dev pull_request: branches: - dev @@ -32,7 +29,7 @@ jobs: run: yarn install --immutable --network-timeout 300000 - name: Build Package - run: yarn build + run: yarn build:full - name: Build Eslint rules run: yarn lint:fix @@ -59,3 +56,55 @@ jobs: - name: Unit Tests run: yarn test + + ############ UNIT TEST ############ + check-providers: + name: Check Providers Versions + runs-on: ubuntu-latest + outputs: + commit: ${{ steps.vars.outputs.commit }} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{github.event.after}} + persist-credentials: false + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 16.x + cache: 'yarn' + registry-url: https://registry.npmjs.org/ + + - run: corepack enable + + - name: Install NPM Dependencies + run: yarn install --immutable --network-timeout 300000 + + - name: Check Baileys + run: yarn node ./scripts/checker.js --name=baileys --stable=true + + - name: Check Venom + run: yarn node ./scripts/checker.js --name=venom --stable=true + + - name: Check web-whatsapp + run: yarn node ./scripts/checker.js --name=web-whatsapp --stable=true + + - name: Check Meta + run: yarn node ./scripts/checker.js --name=meta --stable=true + + - name: Check Twilio + run: yarn node ./scripts/checker.js --name=twilio --stable=true + + - name: Set output + id: vars + run: echo "commit=$(git log --format=%B -n 1 ${{github.event.after}})" >> $GITHUB_OUTPUT + + - name: Commit & Push changes + uses: actions-js/push@master + with: + branch: feature/providers-major + github_token: ${{ secrets.GITHUB_TOKEN }} + force: true diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 86178fd..a1fed1b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,7 +13,7 @@ name: 'CodeQL' on: push: - branches: ['main', dev, next-release] + branches: [release/next] pull_request: # The branches below must be a subset of the branches above branches: ['main'] @@ -22,6 +22,7 @@ on: jobs: analyze: + if: ${{ !github.event.act }} name: Analyze runs-on: ubuntu-latest permissions: diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index a7d4e46..b126e98 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -1,13 +1,8 @@ name: Revisando Colaboradores + on: - push: - branches: - - main - pull_request: - branches: - - dev - - main - types: [closed] + schedule: + - cron: '0 9 * * *' jobs: contrib-readme-job: diff --git a/.github/workflows/netlify-dev.yml b/.github/workflows/netlify-dev.yml index e3e8200..1820da5 100644 --- a/.github/workflows/netlify-dev.yml +++ b/.github/workflows/netlify-dev.yml @@ -13,6 +13,7 @@ on: jobs: ############ DOCUMENTATION BUILD ############ build-documentation: + if: ${{ !github.event.act }} name: Build Package runs-on: ubuntu-latest diff --git a/.github/workflows/netlify.yml b/.github/workflows/netlify.yml index bdb51ab..2dac101 100644 --- a/.github/workflows/netlify.yml +++ b/.github/workflows/netlify.yml @@ -3,12 +3,11 @@ name: 📄 (PROD) Desplegando documentacion on: push: branches: - - main - - next-release + - release/next jobs: ############ DOCUMENTATION BUILD ############ - build-documentation: + build-documentation-prod: name: Build Package runs-on: ubuntu-latest diff --git a/.github/workflows/releases-dev.yml b/.github/workflows/releases-dev.yml index a2d9979..be2e1d6 100644 --- a/.github/workflows/releases-dev.yml +++ b/.github/workflows/releases-dev.yml @@ -3,16 +3,22 @@ name: 🚀 (DEV) Liberando versiones on: push: branches: - - next-release + - release/next jobs: ############ RELEASE ############ release: name: Release runs-on: ubuntu-latest + outputs: + commit: ${{ steps.vars.outputs.commit }} steps: - name: Checkout uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{github.event.after}} + persist-credentials: false - name: Setup Node uses: actions/setup-node@v3 @@ -27,7 +33,7 @@ jobs: run: yarn install --immutable --network-timeout 300000 - name: Build Package - run: yarn build + run: yarn build:full - name: Release @bot-whatsapp/bot run: yarn node ./scripts/release.js --name=bot --version= --token="${{ secrets.NPM_TOKEN }}" @@ -44,8 +50,15 @@ jobs: - name: Release @bot-whatsapp/provider run: yarn node ./scripts/release.js --name=provider --version= --token="${{ secrets.NPM_TOKEN }}" - - name: Commit Versioning & Push changes - uses: stefanzweifel/git-auto-commit-action@v4 + - name: Release @bot-whatsapp/contexts + run: yarn node ./scripts/release.js --name=contexts --version= --token="${{ secrets.NPM_TOKEN }}" + + - name: Release @bot-whatsapp/portal + run: yarn node ./scripts/release.js --name=portal --version= --token="${{ secrets.NPM_TOKEN }}" + + - name: Commit & Push changes + uses: actions-js/push@master with: - commit_message: 'ci(version): :zap: automatic - "${date}" updated versions every packages' - branch: dev + branch: release/next + github_token: ${{ secrets.GITHUB_TOKEN }} + force: true diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index ac91a75..a9b490f 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -2,12 +2,12 @@ name: 🚀⚡ Liberando versiones on: push: - tags: - - 'v*.*.*' - + branches: + - release/production jobs: ############ RELEASE ############ - release: + release-prod: + if: ${{ !github.event.act }} name: Release runs-on: ubuntu-latest steps: @@ -27,29 +27,49 @@ jobs: - run: corepack enable + - name: Set User + run: git config --global user.email "leifer.contacto@gmail.com" && git config --global user.name "Leifer Mendez" + - name: Install NPM Dependencies run: yarn install --immutable --network-timeout 300000 + - name: Set CHANGELOG + run: yarn release + + - name: get-npm-version + id: package-version + uses: martinbeentjes/npm-get-version-action@main + - name: Build Package - run: yarn build + run: yarn build:full - name: Release @bot-whatsapp/bot - run: yarn node ./scripts/release.js --name=bot --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}" + run: yarn node ./scripts/release.js --name=bot --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}" - name: Release @bot-whatsapp/cli - run: yarn node ./scripts/release.js --name=cli --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}" + run: yarn node ./scripts/release.js --name=cli --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}" - name: Release @bot-whatsapp/create-bot-whatsapp - run: yarn node ./scripts/release.js --name=create-bot-whatsapp --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}" + run: yarn node ./scripts/release.js --name=create-bot-whatsapp --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}" - name: Release @bot-whatsapp/database - run: yarn node ./scripts/release.js --name=database --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}" + run: yarn node ./scripts/release.js --name=database --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}" - name: Release @bot-whatsapp/provider - run: yarn node ./scripts/release.js --name=provider --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}" + run: yarn node ./scripts/release.js --name=provider --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}" - - name: Commit Versioning & Push changes - uses: stefanzweifel/git-auto-commit-action@v4 + - name: Release @bot-whatsapp/contexts + run: yarn node ./scripts/release.js --name=contexts --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}" + + - name: Release @bot-whatsapp/portal + run: yarn node ./scripts/release.js --name=portal --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}" + + - name: Release Github + run: yarn node ./scripts/github.js --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.OCTO_TOKEN }}" + + - name: Commit & Push changes + uses: actions-js/push@master with: - commit_message: 'release(version): 🚀 - "${{ steps.vars.outputs.tag }}" release' - branch: dev + branch: release/production + github_token: ${{ secrets.GITHUB_TOKEN }} + force: true diff --git a/.gitignore b/.gitignore index 2a3fcb7..9f6dbbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /node_modules +/packages/repl /packages/*/starters /packages/*/node_modules /packages/*/dist @@ -14,6 +15,10 @@ mediaSend/* !mediaSend/nota-de-voz.mp3 .env .wwebjs_auth +/session +/session/* +/tokens +/tokens/* packages/cli/config.json config.json .yarnrc.yml @@ -38,3 +43,4 @@ yarn-error.log .npmrc # Local Netlify folder .netlify +.secrets \ No newline at end of file diff --git a/.husky/commit-msg b/.husky/commit-msg index 4002db7..3d3fb52 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1,4 @@ #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" -npx --no -- commitlint --edit +npm run lint:fix && npx --no -- commitlint --edit diff --git a/.prettierignore b/.prettierignore index 547ec21..7fffea9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,5 +1,5 @@ packages/**/lib -packages/docs/*.json +packages/docs/ **/.git **/.svn **/.hg diff --git a/.prettierrc.json b/.prettierrc.json index e74ed9f..f0db82f 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,5 +2,6 @@ "trailingComma": "es5", "tabWidth": 4, "semi": false, - "singleQuote": true + "singleQuote": true, + "printWidth": 120 } diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 0d0e52b..1ace260 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["xyc.vscode-mdx-preview"] + "recommendations": ["xyc.vscode-mdx-preview", "vivaxy.vscode-conventional-commits", "mhutchie.git-graph"] } diff --git a/CHANGELOG.md b/CHANGELOG.md index dafc64e..01adb7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,199 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.1.20](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.19...v0.1.20) (2023-02-05) + + +### Features + +* **cli:** :fire: add regex expression in addKeyworkd ([e34560c](https://github.com/leifermendez/bot-whatsapp/commit/e34560c77d4852d2e90930f0858e51aa67d4eeab)) +* **provider:** :zap: possible get class provider ([76ba717](https://github.com/leifermendez/bot-whatsapp/commit/76ba717927a75b3d6299206aa0b8aee2bc25b726)) + + +### Bug Fixes + +* **cli:** :zap: working flowDynamic test ([c0113ca](https://github.com/leifermendez/bot-whatsapp/commit/c0113ca49295aff220d8defcb53f2ba7f2872d75)) +* **cli:** :zap: working flowDynamic test ([aef52d2](https://github.com/leifermendez/bot-whatsapp/commit/aef52d2694fa6616d614338643db198b4f7f1fe8)) +* **cli:** :zap: working flowDynamic test ([f769320](https://github.com/leifermendez/bot-whatsapp/commit/f76932021ce968d93241b55cfcdb8ae0e0e6c934)) +* **cli:** :zap: working flowDynamic test ([23e09ef](https://github.com/leifermendez/bot-whatsapp/commit/23e09efaeccaf51018c55da492edff45b625f0a9)) +* **database:** add support emoji in mysql ([9311aa0](https://github.com/leifermendez/bot-whatsapp/commit/9311aa0a65623a1bf40e96207a281625154dae90)) +* **database:** fix naming ([cd082f2](https://github.com/leifermendez/bot-whatsapp/commit/cd082f235012cd5f5844c6437f51711beee0c865)) +* **database:** fix naming ([1afc3ba](https://github.com/leifermendez/bot-whatsapp/commit/1afc3ba182070713b5bec40eaab0fa1f680830cd)) +* **database:** fix naming ([c9831d2](https://github.com/leifermendez/bot-whatsapp/commit/c9831d202ab2c85f15a0247cd2a2426bc435270c)) +* **provider:** :zap: baily wa.link ([96c2bff](https://github.com/leifermendez/bot-whatsapp/commit/96c2bffd093269be8e39474a84c156938504a6cb)) + +### [0.1.19](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.18...v0.1.19) (2023-01-29) + + +### Features + +* :fire: bailey add media ([eab39e4](https://github.com/leifermendez/bot-whatsapp/commit/eab39e4ac06fd46f1a4671f8c15d1456b4400b97)) +* :zap: more feature ([e19c3a2](https://github.com/leifermendez/bot-whatsapp/commit/e19c3a25a40259c74b4add9635af4844907eed26)) +* **provider:** :rocket: fix issues in providers venom and wwebjs ([cbe438b](https://github.com/leifermendez/bot-whatsapp/commit/cbe438b77854e8df48b9dafaf7a837d21124ac5f)) +* **provider:** :rocket: fix provider ([0ad4c58](https://github.com/leifermendez/bot-whatsapp/commit/0ad4c58457b548dc41c0f9e8470d59c48de7b95a)) +* **provider:** :rocket: fix provider ([f8c7184](https://github.com/leifermendez/bot-whatsapp/commit/f8c7184487065443ab10f77aaf585e8bd63ca441)) +* **provider:** :rocket: fix provider ([b2afa45](https://github.com/leifermendez/bot-whatsapp/commit/b2afa45352a7ab1f5d9775f3c1fde475bd8ca204)) +* **provider:** :rocket: fix provider venom and wwebjs ([dcb0566](https://github.com/leifermendez/bot-whatsapp/commit/dcb0566d2bc3da40cd0c71554bb5ea0ec115d9ca)) +* **provider:** :rocket: implements all send media to venom provider ([9dd7c02](https://github.com/leifermendez/bot-whatsapp/commit/9dd7c02b6a5474aff063f7d6be0ca8519504b93c)) +* **provider:** :rocket: send file wwebjs ([6ff1a3a](https://github.com/leifermendez/bot-whatsapp/commit/6ff1a3a980196c01c66ed04ee07d0e7e57256504)) +* **provider:** :zap: bailey add send file video audio ([14d1a61](https://github.com/leifermendez/bot-whatsapp/commit/14d1a61fa259c09135c37c55bd79e97c9c8367e4)) +* **provider:** :zap: venom wweb ([fd2847a](https://github.com/leifermendez/bot-whatsapp/commit/fd2847aea0db17a0bdf33b5bca67a4cb8db2da16)) +* **provider:** :zap: venom wweb ([f95331d](https://github.com/leifermendez/bot-whatsapp/commit/f95331d3dc70e76a3dfbe4c8d24059f0e7a164ef)) +* **provider:** 🚀 implements all send media to venom provider ([bd7d150](https://github.com/leifermendez/bot-whatsapp/commit/bd7d150c047af41fdbb47f0a50a21e82cd79ee85)) + + +### Bug Fixes + +* **bot:** :fire: endFlow with ctx ([f6114af](https://github.com/leifermendez/bot-whatsapp/commit/f6114affadfbc324536a86167d1fdfe8da3c8de6)) +* **bot:** :fire: endFlow with ctx ([b655ae4](https://github.com/leifermendez/bot-whatsapp/commit/b655ae449e7958ea940d8cc3c678fd66f60b6385)) +* **bot:** :zap: endFlow butons ([87a4203](https://github.com/leifermendez/bot-whatsapp/commit/87a4203cd5b88f566387a76d586248e4265d6e4e)) +* **bot:** :zap: fix fallback refactor ([e22780d](https://github.com/leifermendez/bot-whatsapp/commit/e22780d3faba94f71a70f1f201a20690608fa5bf)) +* **cli:** :zap: endflow ([1c66f17](https://github.com/leifermendez/bot-whatsapp/commit/1c66f178a56d284bb8cb9df5ca17685c7e5d1ddd)) +* **cli:** :zap: refactor fallback in child flow ([b33e346](https://github.com/leifermendez/bot-whatsapp/commit/b33e34692d3abcb6874308a9be79f74be4a2c3a8)) +* **cli:** :zap: refactor fallback in child flow ([8da4b20](https://github.com/leifermendez/bot-whatsapp/commit/8da4b204b41125b5d0fa0aee4fa87c1f5faf5568)) + +### [0.1.18](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.17...v0.1.18) (2023-01-24) + + +### Features + +* **bot:** :zap: add blacklist ([7078dc4](https://github.com/leifermendez/bot-whatsapp/commit/7078dc4c93d01bf90ef08ecb34e89a1abbe16fd2)) +* **bot:** :zap: flowDynamic buttons, media ([3c4b1c0](https://github.com/leifermendez/bot-whatsapp/commit/3c4b1c0fc4b6d98d67c67806d918d3604bb2209b)) + + +### Bug Fixes + +* **bot:** :bug: body undefined ([bb6ed4a](https://github.com/leifermendez/bot-whatsapp/commit/bb6ed4a084ae98070dfdf0c4ba1eca574c4092cc)) +* **bot:** :bug: body undefined ([9234cf1](https://github.com/leifermendez/bot-whatsapp/commit/9234cf1c5d00abdd35e62a826b3c450ab056987a)) +* **bot:** :bug: body undefined ([a118bbb](https://github.com/leifermendez/bot-whatsapp/commit/a118bbbf7f0a7023cb7f33c23f37db72adad151f)) +* **bot:** :bug: body undefined ([f54dea5](https://github.com/leifermendez/bot-whatsapp/commit/f54dea52b01063acd6122eeba1fbbe324aa7805d)) +* **bot:** :bug: body undefined ([72e0a91](https://github.com/leifermendez/bot-whatsapp/commit/72e0a910503e9643db7dfbc6e09c41c96934e1f7)) +* **bot:** :bug: body undefined ([70dd4d7](https://github.com/leifermendez/bot-whatsapp/commit/70dd4d73e814fc5636d19a887f3621c483b837c1)) +* **bot:** :bug: body undefined ([ecf0eef](https://github.com/leifermendez/bot-whatsapp/commit/ecf0eef928917d76c59bd23886cb7a4108b421f1)) +* **bot:** :bug: flowDynamic stranger behaviour ([877252b](https://github.com/leifermendez/bot-whatsapp/commit/877252bd4a8a7bbbbf083c3ceaeaeb952b0a1828)) +* **bot:** :bug: flowDynamic stranger behaviour ([f5a7de3](https://github.com/leifermendez/bot-whatsapp/commit/f5a7de3a003c012e2164e51fff26892cfc3144be)) +* **bot:** :memo: more docs ([98793d0](https://github.com/leifermendez/bot-whatsapp/commit/98793d0cfc1674830beaa3707f933c5a791eec14)) +* **cli:** :zap: refactor ([a29b9d4](https://github.com/leifermendez/bot-whatsapp/commit/a29b9d4e1f85fc163cf1d633c0857f0c8b7f03e1)) +* **cli:** :zap: refactor ([18ef4e9](https://github.com/leifermendez/bot-whatsapp/commit/18ef4e9d726575ca390ca24354825860328d3347)) +* **cli:** :zap: refactor ([3648757](https://github.com/leifermendez/bot-whatsapp/commit/3648757fa083bdb88a16bf6c2e90c828c233bdb1)) +* **cli:** :zap: refactor ([32f6a70](https://github.com/leifermendez/bot-whatsapp/commit/32f6a70f8f6fb26d8ea2a0f1a4aec4827b9d6a93)) +* **cli:** :zap: refactor ([8c825e7](https://github.com/leifermendez/bot-whatsapp/commit/8c825e7f6b7133f7cc7f3041ce331b80a9fe60e0)) +* **cli:** :zap: refactor ([0c0f437](https://github.com/leifermendez/bot-whatsapp/commit/0c0f4375b84549bee809340a85f9ce038ee2739e)) +* **cli:** :zap: refactor ([039ce5d](https://github.com/leifermendez/bot-whatsapp/commit/039ce5dd7cac8115b335ad5de05f7bd871e24140)) +* **cli:** :zap: refactor ([5e87918](https://github.com/leifermendez/bot-whatsapp/commit/5e879188b8bf9d486399b308a9a9c2612607d465)) +* **cli:** :zap: refactor ([21a7270](https://github.com/leifermendez/bot-whatsapp/commit/21a72702817bc6b344223b34ca4513a7ff45fc93)) +* **cli:** :zap: refactor ([82a99b2](https://github.com/leifermendez/bot-whatsapp/commit/82a99b2c80e6738566042ea738bbab8208a17758)) +* **cli:** :zap: refactor ([cc19974](https://github.com/leifermendez/bot-whatsapp/commit/cc19974579379777b05cb69c38cec0fce6740471)) +* **cli:** :zap: refactor ([56fcb8f](https://github.com/leifermendez/bot-whatsapp/commit/56fcb8fb72169bc21fce7c4fcdceccf2acd39c73)) +* **cli:** :zap: refactor ([f36cff1](https://github.com/leifermendez/bot-whatsapp/commit/f36cff1eefdd96be4ab531e1cb2d3b630b1a81c3)) +* **cli:** :zap: refactor ([b393c11](https://github.com/leifermendez/bot-whatsapp/commit/b393c11af6c0ebccb0a690be8b90b9df8877dad1)) +* **cli:** :zap: refactor ([6683715](https://github.com/leifermendez/bot-whatsapp/commit/6683715ad617ea1075654a475a1c62ea607c733f)) +* **contexts:** :bug: fixed [#524](https://github.com/leifermendez/bot-whatsapp/issues/524) issue ([79cc31a](https://github.com/leifermendez/bot-whatsapp/commit/79cc31a96f6a9836447cc4e6bb1e1521c54183fe)) +* **contexts:** :bug: fixed [#524](https://github.com/leifermendez/bot-whatsapp/issues/524) issue ([7067b4a](https://github.com/leifermendez/bot-whatsapp/commit/7067b4a80b7938ccfaf1ed141a37d645a1a3a062)) +* **provider:** wwebjs upgrade ([345f256](https://github.com/leifermendez/bot-whatsapp/commit/345f256a1b4a238519dafc15c9a31bc5e6bad4fe)) +* se agrego @bot-whatsapp/portal a package.json ([46a9fa6](https://github.com/leifermendez/bot-whatsapp/commit/46a9fa6793e06600335de998d2bd9d0691b02ca4)) + +### [0.1.17](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.16...v0.1.17) (2023-01-13) + + +### Features + +* mod de starters para habiltar portal ([eceb170](https://github.com/leifermendez/bot-whatsapp/commit/eceb170df03721dca4183b658c863b94fa04bc84)) + + +### Bug Fixes + +* **ci:** pre-release ([aaec075](https://github.com/leifermendez/bot-whatsapp/commit/aaec0751408ab49483d428810d94aaf7d46acb94)) +* correccion en starters app.js para portal QR ([f430380](https://github.com/leifermendez/bot-whatsapp/commit/f430380b4f23d41702395c96c628bf13bf443278)) +* **starters:** :zap: added dockerfile ([230981e](https://github.com/leifermendez/bot-whatsapp/commit/230981e2676361149cb2a99def7f705e75009260)) + +### [0.1.16](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.15...v0.1.16) (2023-01-11) + +### [0.1.15](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.14...v0.1.15) (2023-01-11) + +### [0.1.14](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.13...v0.1.14) (2023-01-11) + +### [0.1.13](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.12...v0.1.13) (2023-01-11) + +### [0.1.12](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.10...v0.1.12) (2023-01-11) + +### [0.1.9-pre](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.9...v0.1.9-pre) (2023-01-10) + +### [0.1.7-pre-1](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.7-alpha...v0.1.7-pre-1) (2023-01-10) + +### [0.1.7-alpha](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.7-pre...v0.1.7-alpha) (2023-01-10) + +### [0.1.11](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.10...v0.1.11) (2023-01-11) + +### [0.1.9-pre](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.9...v0.1.9-pre) (2023-01-10) + +### [0.1.7-pre-1](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.7-alpha...v0.1.7-pre-1) (2023-01-10) + +### [0.1.7-alpha](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.7-pre...v0.1.7-alpha) (2023-01-10) + +### [0.1.10](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.8...v0.1.10) (2023-01-11) + + +### Bug Fixes + +* :fire: update qr package ([ecde23f](https://github.com/leifermendez/bot-whatsapp/commit/ecde23fdea65def209aa874af35a3f293e6b1a91)) + +### [0.1.8](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.7-pre...v0.1.8) (2023-01-10) + +### [0.1.7](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.7-pre...v0.1.7) (2023-01-10) + +### [0.1.6](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.5...v0.1.6) (2023-01-10) + + +### Features + +* :zap: new portal web for qr scan ([cb2e869](https://github.com/leifermendez/bot-whatsapp/commit/cb2e8692a3f94c8b24993cd11dd564f094b0e4ef)) +* :zap: new portal web for qr scan ([9e93795](https://github.com/leifermendez/bot-whatsapp/commit/9e93795e6fce38890045389da95184fef1fbe0da)) +* :zap: new portal web for qr scan ([3c178ea](https://github.com/leifermendez/bot-whatsapp/commit/3c178ea113b140535a51f5dcd521dbb66251670e)) +* :zap: new portal web for qr scan ([1f1f564](https://github.com/leifermendez/bot-whatsapp/commit/1f1f564f4e2e3aa13b84de500fe215e0c45c2770)) +* :zap: new portal web for qr scan ([3de5f4b](https://github.com/leifermendez/bot-whatsapp/commit/3de5f4b77a10e30632ff7555f5af5d8e93cb2019)) +* :zap: qr code filename ([d794f60](https://github.com/leifermendez/bot-whatsapp/commit/d794f604ac8a835e523709dbf18c9b1609bbd00e)) +* :zap: qr portal ([246ecdc](https://github.com/leifermendez/bot-whatsapp/commit/246ecdc11a8c4e652867c842b612dc4ce73f9828)) +* :zap: qr portal ([af8b401](https://github.com/leifermendez/bot-whatsapp/commit/af8b401d075e1c35065589ede61476461ce86b4d)) +* agregamos dockerfile y webserver a starters ([f9e3bbc](https://github.com/leifermendez/bot-whatsapp/commit/f9e3bbc6655060408e4fdbe1d7e920c2ed4fca53)) + + +### Bug Fixes + +* :zap: add Dockerfile, starter ([4e0d33c](https://github.com/leifermendez/bot-whatsapp/commit/4e0d33c6bb46ad259774f6d0c38c6c0b5f8ca4a9)) +* :zap: fix inject port args ([20f752e](https://github.com/leifermendez/bot-whatsapp/commit/20f752e6c1b1f7d11948fc4f2f8950f7834df7d9)) +* :zap: fix inject port args ([7a23eb0](https://github.com/leifermendez/bot-whatsapp/commit/7a23eb0cc6f93ec21c5ab34e46981ae7a93f42ff)) +* **provider:** :zap: fix send image baileys ([2ddea54](https://github.com/leifermendez/bot-whatsapp/commit/2ddea5468d235035478d4e91e63c821da19da179)) +* **provider:** :zap: fix send image baileys ([391e11c](https://github.com/leifermendez/bot-whatsapp/commit/391e11ce738cd64792b5237d69f3739b0263c198)) +* **provider:** :zap: fix send image baileys ([5d10cb9](https://github.com/leifermendez/bot-whatsapp/commit/5d10cb9026da60043e9a2f86117ebb04d0631a3f)) +* **provider:** fix error docker as root user ([5a033da](https://github.com/leifermendez/bot-whatsapp/commit/5a033da83aee1f614120bccf27c9f330500cc7b0)) + +### [0.1.4](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.5...v0.1.4) (2023-01-10) + + +### Features + +* :zap: new portal web for qr scan ([cb2e869](https://github.com/leifermendez/bot-whatsapp/commit/cb2e8692a3f94c8b24993cd11dd564f094b0e4ef)) +* :zap: new portal web for qr scan ([9e93795](https://github.com/leifermendez/bot-whatsapp/commit/9e93795e6fce38890045389da95184fef1fbe0da)) +* :zap: new portal web for qr scan ([3c178ea](https://github.com/leifermendez/bot-whatsapp/commit/3c178ea113b140535a51f5dcd521dbb66251670e)) +* :zap: new portal web for qr scan ([1f1f564](https://github.com/leifermendez/bot-whatsapp/commit/1f1f564f4e2e3aa13b84de500fe215e0c45c2770)) +* :zap: new portal web for qr scan ([3de5f4b](https://github.com/leifermendez/bot-whatsapp/commit/3de5f4b77a10e30632ff7555f5af5d8e93cb2019)) +* :zap: qr code filename ([d794f60](https://github.com/leifermendez/bot-whatsapp/commit/d794f604ac8a835e523709dbf18c9b1609bbd00e)) +* :zap: qr portal ([246ecdc](https://github.com/leifermendez/bot-whatsapp/commit/246ecdc11a8c4e652867c842b612dc4ce73f9828)) +* :zap: qr portal ([af8b401](https://github.com/leifermendez/bot-whatsapp/commit/af8b401d075e1c35065589ede61476461ce86b4d)) +* agregamos dockerfile y webserver a starters ([f9e3bbc](https://github.com/leifermendez/bot-whatsapp/commit/f9e3bbc6655060408e4fdbe1d7e920c2ed4fca53)) + + +### Bug Fixes + +* :zap: add Dockerfile, starter ([4e0d33c](https://github.com/leifermendez/bot-whatsapp/commit/4e0d33c6bb46ad259774f6d0c38c6c0b5f8ca4a9)) +* :zap: fix inject port args ([20f752e](https://github.com/leifermendez/bot-whatsapp/commit/20f752e6c1b1f7d11948fc4f2f8950f7834df7d9)) +* :zap: fix inject port args ([7a23eb0](https://github.com/leifermendez/bot-whatsapp/commit/7a23eb0cc6f93ec21c5ab34e46981ae7a93f42ff)) +* **provider:** :zap: fix send image baileys ([2ddea54](https://github.com/leifermendez/bot-whatsapp/commit/2ddea5468d235035478d4e91e63c821da19da179)) +* **provider:** :zap: fix send image baileys ([391e11c](https://github.com/leifermendez/bot-whatsapp/commit/391e11ce738cd64792b5237d69f3739b0263c198)) +* **provider:** :zap: fix send image baileys ([5d10cb9](https://github.com/leifermendez/bot-whatsapp/commit/5d10cb9026da60043e9a2f86117ebb04d0631a3f)) +* **provider:** fix error docker as root user ([5a033da](https://github.com/leifermendez/bot-whatsapp/commit/5a033da83aee1f614120bccf27c9f330500cc7b0)) + ### [0.1.3](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.2...v0.1.3) (2023-01-04) diff --git a/README.md b/README.md index b4a2271..af4b124 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # Chatbot Library ![](https://img.shields.io/npm/v/@bot-whatsapp/bot?color=%2300c200&label=%40bot-whatsapp) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) -[![BotWhatsapp Releases(Prod)](https://github.com/codigoencasa/bot-whatsapp/actions/workflows/releases.yml/badge.svg)](https://github.com/codigoencasa/bot-whatsapp/actions/workflows/releases.yml) - +[![](https://img.shields.io/discord/915193197645402142?logo=discord)](https://link.codigoencasa.com/DISCORD)

@@ -34,6 +33,13 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación. + + + + - + + + + + + + + - + +
+ + cheveguerra +
+ Jose Alberto Guerra Ugalde +
+
leifermendez @@ -62,6 +68,21 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación. Leifer Mendez + + danielcasta0398 +
+ Juan Daniel Castaño +
+
+ + marianarolfo +
+ Null +
+
HKong31 @@ -75,8 +96,14 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
Zvi
-
+ + JosephVTX +
+ Joseph Vega Callupe +
+
Gonzalito87 @@ -84,6 +111,42 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación. Null + + devrlbusiness +
+ Developer RL Business +
+
+ + Gregoriotecnico +
+ Null +
+
+ + jlferrete +
+ Jose Luis Ferrete Olarte +
+
+ + lisandroprada +
+ Null +
+
+ + 6rak0 +
+ Null +
+
tonyvazgar @@ -91,20 +154,14 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación. Luis Antonio Vázquez García - - ulisesvina -
- Ulises Viña -
-
rrruuuyyy
Rodrigo Mendoza Cabrera
-
yond1994 diff --git a/__test__/01-case.test.js b/__test__/01-case.test.js new file mode 100644 index 0000000..57ae08c --- /dev/null +++ b/__test__/01-case.test.js @@ -0,0 +1,36 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const MOCK_DB = require('../packages/database/src/mock') +const PROVIDER_DB = require('../packages/provider/src/mock') +const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') + +test(`[Caso - 01] Flow Basico`, async () => { + const [VALUE_A, VALUE_B] = ['hola', 'buenas'] + + const flow = addKeyword(VALUE_A).addAnswer(VALUE_B) + const provider = createProvider(PROVIDER_DB) + const database = new MOCK_DB() + + createBot({ + database, + flow: createFlow([flow]), + provider, + }) + + provider.delaySendMessage(100, 'message', { + from: '000', + body: VALUE_A, + }) + + await delay(100) + + const prevMsg = database.getPrevByNumber('000') + + assert.is(prevMsg.answer, VALUE_B) +}) + +test.run() + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/__test__/02-case.test.js b/__test__/02-case.test.js new file mode 100644 index 0000000..855a566 --- /dev/null +++ b/__test__/02-case.test.js @@ -0,0 +1,94 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const MOCK_DB = require('../packages/database/src/mock') +const PROVIDER_DB = require('../packages/provider/src/mock') +const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') + +/** + * Falsear peticion async + * @param {*} fakeData + * @returns + */ +const fakeHTTP = async (fakeData = []) => { + console.log('⚡ Server request!') + await delay(50) + console.log('⚡ Server return!') + const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` })) + console.log(data) + return Promise.resolve(data) +} + +test(`[Caso - 02] Flow (flowDynamic)`, async () => { + const MOCK_VALUES = [ + 'Bienvenido te envio muchas marcas (5510)', + 'Seleccione marca del auto a cotizar, con el *número* correspondiente', + 'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:', + 'Los precios rondan:', + ] + const provider = createProvider(PROVIDER_DB) + const database = new MOCK_DB() + + const flujoPrincipal = addKeyword(['hola']) + .addAnswer(MOCK_VALUES[0], null, async (ctx, { flowDynamic }) => { + console.log('execute...') + const data = await fakeHTTP(['Ford', 'GM', 'BMW']) + return flowDynamic(data) + }) + .addAnswer(MOCK_VALUES[1], null, async (ctx, { flowDynamic }) => { + const data = await fakeHTTP(['Ranger', 'Explorer']) + return flowDynamic(data) + }) + .addAnswer(MOCK_VALUES[2], null, async (ctx, { flowDynamic }) => { + const data = await fakeHTTP(['Usado', 'Nuevos']) + return flowDynamic(data) + }) + .addAnswer(MOCK_VALUES[3], null, async (ctx, { flowDynamic }) => { + const data = await fakeHTTP(['1000', '2000', '3000']) + return flowDynamic(data) + }) + + createBot({ + database, + flow: createFlow([flujoPrincipal]), + provider, + }) + + provider.delaySendMessage(0, 'message', { + from: '000', + body: 'hola', + }) + + await delay(1200) + const getHistory = database.listHistory.map((i) => i.answer) + assert.is(MOCK_VALUES[0], getHistory[0]) + + //FlowDynamic + assert.is('1 Ford', getHistory[1]) + assert.is('2 GM', getHistory[2]) + assert.is('3 BMW', getHistory[3]) + + assert.is(MOCK_VALUES[1], getHistory[4]) + + //FlowDynamic + assert.is('1 Ranger', getHistory[5]) + assert.is('2 Explorer', getHistory[6]) + + assert.is(MOCK_VALUES[2], getHistory[7]) + + //FlowDynamic + assert.is('1 Usado', getHistory[8]) + assert.is('2 Nuevos', getHistory[9]) + + assert.is(MOCK_VALUES[3], getHistory[10]) + + //FlowDynamic + assert.is('1 1000', getHistory[11]) + assert.is('2 2000', getHistory[12]) + assert.is('3 3000', getHistory[13]) +}) + +test.run() + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/__test__/03-case.test.js b/__test__/03-case.test.js new file mode 100644 index 0000000..646b5d5 --- /dev/null +++ b/__test__/03-case.test.js @@ -0,0 +1,37 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const MOCK_DB = require('../packages/database/src/mock') +const PROVIDER_DB = require('../packages/provider/src/mock') +const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') + +test(`[Caso - 03] Flow puro`, async () => { + const MOCK_VALUES = ['Bienvenido a mi tienda', 'Como estas?'] + + const provider = createProvider(PROVIDER_DB) + const database = new MOCK_DB() + + const flujoPrincipal = addKeyword(['hola']).addAnswer(MOCK_VALUES[0]).addAnswer(MOCK_VALUES[1]) + + createBot({ + database, + flow: createFlow([flujoPrincipal]), + provider, + }) + + provider.delaySendMessage(0, 'message', { + from: '000', + body: 'hola', + }) + + await delay(10) + const getHistory = database.listHistory.map((i) => i.answer) + + assert.is(MOCK_VALUES[0], getHistory[0]) + assert.is(MOCK_VALUES[1], getHistory[1]) +}) + +test.run() + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/__test__/04-case.test.js b/__test__/04-case.test.js new file mode 100644 index 0000000..8fcaeda --- /dev/null +++ b/__test__/04-case.test.js @@ -0,0 +1,77 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const MOCK_DB = require('../packages/database/src/mock') +const PROVIDER_DB = require('../packages/provider/src/mock') +const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') + +/** + * Falsear peticion async + * @param {*} fakeData + * @returns + */ +const fakeHTTP = async (fakeData = []) => { + console.log('⚡ Server request!') + await delay(50) + console.log('⚡ Server return!') + const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` })) + console.log(data) + return Promise.resolve(data) +} + +test(`[Caso - 04] Romper flujo (endFlow)`, async () => { + const MOCK_VALUES = [ + 'Bienvenido te envio muchas marcas (5510)', + 'Seleccione marca del auto a cotizar, con el *número* correspondiente', + 'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:', + 'Los precios rondan:', + ] + const provider = createProvider(PROVIDER_DB) + const database = new MOCK_DB() + + const flujoPrincipal = addKeyword(['hola']) + .addAnswer(MOCK_VALUES[0], null, async (ctx, { flowDynamic }) => { + console.log('execute...') + const data = await fakeHTTP(['Ford', 'GM', 'BMW']) + return flowDynamic(data) + }) + .addAnswer(MOCK_VALUES[1], null, async (ctx, { endFlow }) => { + return endFlow() + }) + .addAnswer(MOCK_VALUES[2], null, async (ctx, { flowDynamic }) => { + const data = await fakeHTTP(['Usado', 'Nuevos']) + return flowDynamic(data) + }) + .addAnswer(MOCK_VALUES[3], null, async (ctx, { flowDynamic }) => { + const data = await fakeHTTP(['1000', '2000', '3000']) + return flowDynamic(data) + }) + + createBot({ + database, + flow: createFlow([flujoPrincipal]), + provider, + }) + + provider.delaySendMessage(0, 'message', { + from: '000', + body: 'hola', + }) + + await delay(1200) + const getHistory = database.listHistory.map((i) => i.answer) + assert.is(MOCK_VALUES[0], getHistory[0]) + + //FlowDynamic + assert.is('1 Ford', getHistory[1]) + assert.is('2 GM', getHistory[2]) + assert.is('3 BMW', getHistory[3]) + + assert.is(MOCK_VALUES[1], getHistory[4]) + assert.is(undefined, getHistory[5]) +}) + +test.run() + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/__test__/05-case.test.js b/__test__/05-case.test.js new file mode 100644 index 0000000..f61d114 --- /dev/null +++ b/__test__/05-case.test.js @@ -0,0 +1,100 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const MOCK_DB = require('../packages/database/src/mock') +const PROVIDER_DB = require('../packages/provider/src/mock') +const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') + +/** + * Falsear peticion async + * @param {*} fakeData + * @returns + */ +const fakeHTTP = async (fakeData = []) => { + await delay(5) + const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` })) + return Promise.resolve(data) +} + +test(`[Caso - 05] Continuar Flujo (continueFlow)`, async () => { + const MOCK_VALUES = ['¿CUal es tu email?', 'Continuamos....', '¿Cual es tu edad?'] + const provider = createProvider(PROVIDER_DB) + const database = new MOCK_DB() + + const flujoPrincipal = addKeyword(['hola']) + .addAnswer( + MOCK_VALUES[0], + { + capture: true, + }, + async (ctx, { flowDynamic, fallBack }) => { + const validation = ctx.body.includes('@') + + if (validation) { + const getDataFromApi = await fakeHTTP(['Gracias por tu email se ha validado de manera correcta']) + return flowDynamic(getDataFromApi) + } + return fallBack(validation) + } + ) + .addAnswer(MOCK_VALUES[1]) + .addAnswer(MOCK_VALUES[2], { capture: true }, async (ctx, { flowDynamic, fallBack }) => { + if (ctx.body !== '18') { + await delay(50) + return fallBack(false, 'Ups creo que no eres mayor de edad') + } + return flowDynamic('Bien tu edad es correcta!') + }) + .addAnswer('Puedes pasar') + + createBot({ + database, + flow: createFlow([flujoPrincipal]), + provider, + }) + + provider.delaySendMessage(0, 'message', { + from: '000', + body: 'hola', + }) + + provider.delaySendMessage(10, 'message', { + from: '000', + body: 'this is not email value', + }) + + provider.delaySendMessage(20, 'message', { + from: '000', + body: 'test@test.com', + }) + + provider.delaySendMessage(90, 'message', { + from: '000', + body: '20', + }) + + provider.delaySendMessage(200, 'message', { + from: '000', + body: '18', + }) + + await delay(1200) + const getHistory = database.listHistory.map((i) => i.answer) + assert.is(MOCK_VALUES[0], getHistory[0]) + assert.is('this is not email value', getHistory[1]) + assert.is(MOCK_VALUES[0], getHistory[2]) + assert.is('test@test.com', getHistory[3]) + assert.is('1 Gracias por tu email se ha validado de manera correcta', getHistory[4]) + assert.is(MOCK_VALUES[1], getHistory[5]) + assert.is(MOCK_VALUES[2], getHistory[6]) + assert.is('20', getHistory[7]) + assert.is('Ups creo que no eres mayor de edad', getHistory[8]) + assert.is('18', getHistory[9]) + assert.is('Bien tu edad es correcta!', getHistory[10]) + assert.is('Puedes pasar', getHistory[11]) +}) + +test.run() + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/__test__/06-case.test.js b/__test__/06-case.test.js new file mode 100644 index 0000000..355cbd4 --- /dev/null +++ b/__test__/06-case.test.js @@ -0,0 +1,93 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const MOCK_DB = require('../packages/database/src/mock') +const PROVIDER_DB = require('../packages/provider/src/mock') +const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') + +/** + * Falsear peticion async + * @param {*} fakeData + * @returns + */ +const fakeHTTP = async (fakeData = []) => { + await delay(5) + const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` })) + return Promise.resolve(data) +} + +test(`[Caso - 06] Finalizar Flujo (endFlow)`, async () => { + const MOCK_VALUES = ['¿CUal es tu email?', 'Continuamos....', '¿Cual es tu edad?'] + const provider = createProvider(PROVIDER_DB) + const database = new MOCK_DB() + + const flujoPrincipal = addKeyword(['hola']) + .addAnswer( + MOCK_VALUES[0], + { + capture: true, + }, + async (ctx, { flowDynamic, fallBack }) => { + const validation = ctx.body.includes('@') + + if (validation) { + const getDataFromApi = await fakeHTTP(['Gracias por tu email se ha validado de manera correcta']) + return flowDynamic(getDataFromApi) + } + return fallBack(validation) + } + ) + .addAnswer(MOCK_VALUES[1], null, async (_, { endFlow }) => { + return endFlow() + }) + .addAnswer(MOCK_VALUES[2], { capture: true }, async (ctx, { flowDynamic, fallBack }) => { + if (ctx.body !== '18') { + await delay(50) + return fallBack(false, 'Ups creo que no eres mayor de edad') + } + return flowDynamic('Bien tu edad es correcta!') + }) + .addAnswer('Puedes pasar') + + createBot({ + database, + flow: createFlow([flujoPrincipal]), + provider, + }) + + provider.delaySendMessage(0, 'message', { + from: '000', + body: 'hola', + }) + + provider.delaySendMessage(10, 'message', { + from: '000', + body: 'this is not email value', + }) + + provider.delaySendMessage(20, 'message', { + from: '000', + body: 'test@test.com', + }) + + provider.delaySendMessage(90, 'message', { + from: '000', + body: '20', + }) + + await delay(1200) + const getHistory = database.listHistory.map((i) => i.answer) + assert.is(MOCK_VALUES[0], getHistory[0]) + assert.is('this is not email value', getHistory[1]) + assert.is(MOCK_VALUES[0], getHistory[2]) + assert.is('test@test.com', getHistory[3]) + assert.is('1 Gracias por tu email se ha validado de manera correcta', getHistory[4]) + assert.is(MOCK_VALUES[1], getHistory[5]) + assert.is('20', getHistory[6]) + assert.is(undefined, getHistory[7]) +}) + +test.run() + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/__test__/07-case.test.js b/__test__/07-case.test.js new file mode 100644 index 0000000..6eb8e42 --- /dev/null +++ b/__test__/07-case.test.js @@ -0,0 +1,92 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const MOCK_DB = require('../packages/database/src/mock') +const PROVIDER_DB = require('../packages/provider/src/mock') +const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') + +/** + * Falsear peticion async + * @param {*} fakeData + * @returns + */ +const fakeHTTP = async (fakeData = []) => { + await delay(5) + const data = fakeData.map((u, i) => ({ body: `${i + 1} ${u}` })) + return Promise.resolve(data) +} + +let STATE_APP = {} + +test(`[Caso - 07] Retornar estado`, async () => { + const MOCK_VALUES = ['¿Cual es tu nombre?', '¿Cual es tu edad?', 'Tu datos son:'] + const provider = createProvider(PROVIDER_DB) + const database = new MOCK_DB() + + const flujoPrincipal = addKeyword(['hola']) + .addAnswer( + MOCK_VALUES[0], + { + capture: true, + }, + async (ctx, { flowDynamic, fallBack }) => { + STATE_APP[ctx.from] = { ...STATE_APP[ctx.from], name: ctx.body } + + flowDynamic('Gracias por tu nombre!') + } + ) + .addAnswer( + MOCK_VALUES[1], + { + capture: true, + }, + async (ctx, { flowDynamic, endFlow }) => { + STATE_APP[ctx.from] = { ...STATE_APP[ctx.from], age: ctx.body } + + await flowDynamic('Gracias por tu edad!') + } + ) + .addAnswer(MOCK_VALUES[2], null, async (ctx, { flowDynamic }) => { + flowDynamic(`Nombre: ${STATE_APP[ctx.from].name} Edad: ${STATE_APP[ctx.from].age}`) + }) + .addAnswer('🤖🤖 Gracias por tu participacion') + + createBot({ + database, + flow: createFlow([flujoPrincipal]), + provider, + }) + + provider.delaySendMessage(0, 'message', { + from: '000', + body: 'hola', + }) + + provider.delaySendMessage(20, 'message', { + from: '000', + body: 'Leifer', + }) + + provider.delaySendMessage(40, 'message', { + from: '000', + body: '90', + }) + + await delay(1200) + const getHistory = database.listHistory.map((i) => i.answer) + assert.is(MOCK_VALUES[0], getHistory[0]) + assert.is('Leifer', getHistory[1]) + assert.is('Gracias por tu nombre!', getHistory[2]) + assert.is('¿Cual es tu edad?', getHistory[3]) + assert.is('90', getHistory[4]) + assert.is('Gracias por tu edad!', getHistory[5]) + assert.is('Tu datos son:', getHistory[6]) + assert.is('Nombre: Leifer Edad: 90', getHistory[7]) + assert.is('🤖🤖 Gracias por tu participacion', getHistory[8]) + assert.is(undefined, getHistory[9]) +}) + +test.run() + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/__test__/08-case.test.js b/__test__/08-case.test.js new file mode 100644 index 0000000..82fc46a --- /dev/null +++ b/__test__/08-case.test.js @@ -0,0 +1,43 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const MOCK_DB = require('../packages/database/src/mock') +const PROVIDER_DB = require('../packages/provider/src/mock') +const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') + +test(`[Caso - 08] Regular expression on keyword`, async () => { + const provider = createProvider(PROVIDER_DB) + const database = new MOCK_DB() + + const REGEX_CREDIT_NUMBER = `/(^4[0-9]{12}(?:[0-9]{3})?$)|(^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$)|(3[47][0-9]{13})|(^3(?:0[0-5]|[68][0-9])[0-9]{11}$)|(^6(?:011|5[0-9]{2})[0-9]{12}$)|(^(?:2131|1800|35\d{3})\d{11}$)/gm` + const flujoPrincipal = addKeyword(REGEX_CREDIT_NUMBER, { regex: true }) + .addAnswer(`Gracias por proporcionar un numero de tarjeta valido`) + .addAnswer('Fin!') + + createBot({ + database, + flow: createFlow([flujoPrincipal]), + provider, + }) + + provider.delaySendMessage(0, 'message', { + from: '000', + body: 'hola', + }) + + provider.delaySendMessage(20, 'message', { + from: '000', + body: '374245455400126', + }) + + await delay(40) + const getHistory = database.listHistory.map((i) => i.answer) + assert.is('Gracias por proporcionar un numero de tarjeta valido', getHistory[0]) + assert.is('Fin!', getHistory[1]) + assert.is(undefined, getHistory[2]) +}) + +test.run() + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/__test__/09-case.test.js b/__test__/09-case.test.js new file mode 100644 index 0000000..417ce31 --- /dev/null +++ b/__test__/09-case.test.js @@ -0,0 +1,41 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const MOCK_DB = require('../packages/database/src/mock') +const PROVIDER_MOCK = require('../packages/provider/src/mock') +const { addKeyword, createBot, createFlow, createProvider } = require('../packages/bot/index') + +let PROVIDER = undefined + +test(`[Caso - 09] Check provider WS`, async () => { + const [VALUE_A, VALUE_B] = ['hola', 'buenas'] + + const flow = addKeyword(VALUE_A).addAnswer(VALUE_B, null, async (_, { provider }) => { + PROVIDER = provider + }) + const provider = createProvider(PROVIDER_MOCK) + const database = new MOCK_DB() + + createBot({ + database, + flow: createFlow([flow]), + provider, + }) + + provider.delaySendMessage(100, 'message', { + from: '000', + body: VALUE_A, + }) + + await delay(100) + + const prevMsg = database.getPrevByNumber('000') + + assert.is(prevMsg.answer, VALUE_B) + assert.is(typeof PROVIDER.sendMessage, 'function') +}) + +test.run() + +function delay(ms) { + return new Promise((res) => setTimeout(res, ms)) +} diff --git a/changelog.config.js b/changelog.config.js index 70a500d..38c6f74 100644 --- a/changelog.config.js +++ b/changelog.config.js @@ -1,28 +1,10 @@ module.exports = { disableEmoji: false, format: '{type}{scope}: {emoji}{subject}', - list: [ - 'test', - 'feat', - 'fix', - 'chore', - 'docs', - 'refactor', - 'style', - 'ci', - 'perf', - ], + list: ['test', 'feat', 'fix', 'chore', 'docs', 'refactor', 'style', 'ci', 'perf'], maxMessageLength: 64, minMessageLength: 3, - questions: [ - 'type', - 'scope', - 'subject', - 'body', - 'breaking', - 'issues', - 'lerna', - ], + questions: ['type', 'scope', 'subject', 'body', 'breaking', 'issues', 'lerna'], scopes: [], types: { chore: { @@ -56,8 +38,7 @@ module.exports = { value: 'perf', }, refactor: { - description: - 'A code change that neither fixes a bug or adds a feature', + description: 'A code change that neither fixes a bug or adds a feature', emoji: '(💡)', value: 'refactor', }, @@ -67,8 +48,7 @@ module.exports = { value: 'release', }, style: { - description: - 'Markup, white-space, formatting, missing semi-colons...', + description: 'Markup, white-space, formatting, missing semi-colons...', emoji: '(💄)', value: 'style', }, @@ -80,8 +60,7 @@ module.exports = { messages: { type: "Select the type of change that you're committing:", customScope: 'Select the scope this component affects:', - subject: - 'Write a short, imperative mood description of the change:\n', + subject: 'Write a short, imperative mood description of the change:\n', body: 'Provide a longer description of the change:\n ', breaking: 'List any breaking changes:\n', footer: 'Issues this commit closes, e.g #123:', diff --git a/package.json b/package.json index cd6eb54..cc0b21a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bot-whatsapp/root", - "version": "0.1.3", + "version": "0.1.20", "description": "Bot de wahtsapp open source para MVP o pequeños negocios", "main": "app.js", "private": true, @@ -13,15 +13,19 @@ "contexts:rollup": "rollup --config ./packages/contexts/rollup-contexts.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", + "portal:rollup": "rollup --config ./packages/portal/rollup-portal.config.js", "format:check": "prettier --check ./packages", "format:write": "prettier --write ./packages", "fmt.staged": "pretty-quick --staged", "lint:check": "eslint ./packages", "lint:fix": "eslint --fix ./packages", - "build": "yarn run cli:rollup && yarn run bot:rollup && yarn run provider:rollup && yarn run database:rollup && yarn run contexts:rollup && yarn run create-bot-whatsapp:rollup", + "build:portal-web": "cd ./packages/portal/ && yarn run build.types && yarn run build.client && yarn run build.server && yarn run lint --fix", + "build:full": "yarn run build:portal-web && yarn run cli:rollup && yarn run bot:rollup && yarn run provider:rollup && yarn run database:rollup && yarn run contexts:rollup && yarn run create-bot-whatsapp:rollup && yarn run portal:rollup", + "build": "yarn run cli:rollup && yarn run bot:rollup && yarn run provider:rollup && yarn run database:rollup && yarn run contexts:rollup && yarn run create-bot-whatsapp:rollup && yarn run portal:rollup", "copy.lib": "node ./scripts/move.js", "test.unit": "node ./node_modules/uvu/bin.js packages test", - "test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit", + "test.e2e": "node ./node_modules/uvu/bin.js __test__", + "test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit && npm run test.e2e", "test": "npm run test.coverage", "cli": "node ./packages/cli/bin/cli.js", "create": "node ./packages/create-bot-whatsapp/bin/create.js", @@ -30,7 +34,7 @@ "prepare": "npx husky install", "preinstall": "npx only-allow yarn", "postinstall": "npx prettier --write .", - "release": "standard-version -- --prerelease" + "release": "standard-version -- --prerelease --global" }, "workspaces": [ "packages/create-bot-whatsapp", @@ -39,6 +43,7 @@ "packages/database", "packages/provider", "packages/contexts", + "packages/portal", "packages/docs" ], "keywords": [ @@ -63,6 +68,7 @@ "devDependencies": { "@commitlint/cli": "^17.3.0", "@commitlint/config-conventional": "^17.3.0", + "@octokit/core": "^4.1.0", "@rollup/plugin-commonjs": "^23.0.2", "@rollup/plugin-json": "^5.0.1", "@rollup/plugin-node-resolve": "^15.0.1", @@ -75,6 +81,7 @@ "fs-extra": "^11.1.0", "git-cz": "^4.9.0", "husky": "^8.0.2", + "mime-types": "^2.1.35", "only-allow": "^1.1.1", "prettier": "^2.8.0", "pretty-quick": "^3.1.3", diff --git a/packages/bot/core/core.class.js b/packages/bot/core/core.class.js index 7550dd0..29cf5d1 100644 --- a/packages/bot/core/core.class.js +++ b/packages/bot/core/core.class.js @@ -8,6 +8,9 @@ const { createWriteStream } = require('fs') const logger = new Console({ stdout: createWriteStream(`${process.cwd()}/core.class.log`), }) + +const QueuePrincipal = new Queue() + /** * [ ] Escuchar eventos del provider asegurarte que los provider emitan eventos * [ ] Guardar historial en db @@ -18,10 +21,12 @@ class CoreClass { flowClass databaseClass providerClass - constructor(_flow, _database, _provider) { + generalArgs = { blackList: [] } + constructor(_flow, _database, _provider, _args) { this.flowClass = _flow this.databaseClass = _database this.providerClass = _provider + this.generalArgs = { ...this.generalArgs, ..._args } for (const { event, func } of this.listenerBusEvents()) { this.providerClass.on(event, func) @@ -38,8 +43,7 @@ class CoreClass { }, { event: 'require_action', - func: ({ instructions, title = '⚡⚡ ACCIÓN REQUERIDA ⚡⚡' }) => - printer(instructions, title), + func: ({ instructions, title = '⚡⚡ ACCIÓN REQUERIDA ⚡⚡' }) => printer(instructions, title), }, { event: 'ready', @@ -47,8 +51,7 @@ class CoreClass { }, { event: 'auth_failure', - func: ({ instructions }) => - printer(instructions, '⚡⚡ ERROR AUTH ⚡⚡'), + func: ({ instructions }) => printer(instructions, '⚡⚡ ERROR AUTH ⚡⚡'), }, { @@ -66,14 +69,14 @@ class CoreClass { logger.log(`[handleMsg]: `, messageCtxInComming) const { body, from } = messageCtxInComming let msgToSend = [] + let endFlowFlag = false let fallBackFlag = false - + if (this.generalArgs.blackList.includes(from)) return + if (!body) return if (!body.length) return - const prevMsg = await this.databaseClass.getPrevByNumber(from) - const refToContinue = this.flowClass.findBySerialize( - prevMsg?.refSerialize - ) + let prevMsg = await this.databaseClass.getPrevByNumber(from) + const refToContinue = this.flowClass.findBySerialize(prevMsg?.refSerialize) if (prevMsg?.ref) { const ctxByNumber = toCtx({ @@ -84,53 +87,128 @@ class CoreClass { this.databaseClass.save(ctxByNumber) } + // 📄 Crar CTX de mensaje (uso private) + const createCtxMessage = (payload = {}, index = 0) => { + const body = typeof payload === 'string' ? payload : payload?.body ?? payload?.answer + const media = payload?.media ?? null + const buttons = payload?.buttons ?? [] + const capture = payload?.capture ?? false + + return toCtx({ + body, + from, + keyword: null, + index, + options: { media, buttons, capture }, + }) + } + + // 📄 Limpiar cola de procesos + const clearQueue = () => { + QueuePrincipal.pendingPromise = false + QueuePrincipal.queue = [] + } + + // 📄 Finalizar flujo + const endFlow = async (message = null) => { + endFlowFlag = true + if (message) this.sendProviderAndSave(from, createCtxMessage(message)) + clearQueue() + sendFlow([]) + return + } + + // 📄 Esta funcion se encarga de enviar un array de mensajes dentro de este ctx + const sendFlow = async (messageToSend, numberOrId, options = { prev: prevMsg }) => { + if (options.prev?.options?.capture) await cbEveryCtx(options.prev?.ref) + + const queue = [] + for (const ctxMessage of messageToSend) { + if (endFlowFlag) return + const delayMs = ctxMessage?.options?.delay || 0 + if (delayMs) await delay(delayMs) + QueuePrincipal.enqueue(() => + Promise.all([ + this.sendProviderAndSave(numberOrId, ctxMessage).then(() => resolveCbEveryCtx(ctxMessage)), + ]) + ) + } + return Promise.all(queue) + } + // 📄 [options: fallBack]: esta funcion se encarga de repetir el ultimo mensaje - const fallBack = () => { - fallBackFlag = true - msgToSend = this.flowClass.find(refToContinue?.keyword, true) || [] - this.sendFlow(msgToSend, from) - return refToContinue + const fallBack = async (validation = false, message = null) => { + QueuePrincipal.queue = [] + + if (validation) { + const currentPrev = await this.databaseClass.getPrevByNumber(from) + const nextFlow = await this.flowClass.find(refToContinue?.ref, true) + const filterNextFlow = nextFlow.filter((msg) => msg.refSerialize !== currentPrev?.refSerialize) + + return sendFlow(filterNextFlow, from, { prev: undefined }) + } + + await this.sendProviderAndSave(from, { + ...prevMsg, + answer: typeof message === 'string' ? message : message?.body ?? prevMsg.answer, + options: { + ...prevMsg.options, + buttons: prevMsg.options?.buttons, + }, + }) + return } // 📄 [options: flowDynamic]: esta funcion se encarga de responder un array de respuesta esta limitado a 5 mensajes // para evitar bloque de whatsapp - const flowDynamic = (listMsg = [], optListMsg = { limit: 3 }) => { - if (!Array.isArray(listMsg)) - throw new Error('Esto debe ser un ARRAY') - const parseListMsg = listMsg - .map(({ body }, index) => - toCtx({ - body, - from, - keyword: null, - index, - }) - ) - .slice(0, optListMsg.limit) - msgToSend = parseListMsg - this.sendFlow(msgToSend, from) + const flowDynamic = async (listMsg = []) => { + if (!Array.isArray(listMsg)) listMsg = [listMsg] + + const parseListMsg = listMsg.map((opt, index) => createCtxMessage(opt, index)) + const currentPrev = await this.databaseClass.getPrevByNumber(from) + + const skipContinueFlow = async () => { + const nextFlow = await this.flowClass.find(refToContinue?.ref, true) + const filterNextFlow = nextFlow.filter((msg) => msg.refSerialize !== currentPrev?.refSerialize) + const isContinueFlow = filterNextFlow.map((i) => i.keyword).includes(currentPrev?.ref) + return { + continue: !isContinueFlow, + contexts: filterNextFlow, + } + } + + if (endFlowFlag) return + for (const msg of parseListMsg) { + await this.sendProviderAndSave(from, msg) + } + + const continueFlowData = await skipContinueFlow() + + if (continueFlowData.continue) return sendFlow(continueFlowData.contexts, from, { prev: undefined }) return } + // 📄 Se encarga de revisar si el contexto del mensaje tiene callback o fallback + const resolveCbEveryCtx = async (ctxMessage) => { + if (!ctxMessage?.options?.capture) return await cbEveryCtx(ctxMessage?.ref) + } + // 📄 Se encarga de revisar si el contexto del mensaje tiene callback y ejecutarlo - const cbEveryCtx = (inRef) => { - this.flowClass.allCallbacks[inRef](messageCtxInComming, { + const cbEveryCtx = async (inRef) => { + const provider = this.providerClass + + if (!this.flowClass.allCallbacks[inRef]) return Promise.resolve() + return this.flowClass.allCallbacks[inRef](messageCtxInComming, { + provider, fallBack, flowDynamic, + endFlow, }) } - // 📄 [options: callback]: Si se tiene un callback se ejecuta - if (!fallBackFlag) { - if (refToContinue?.options?.capture) cbEveryCtx(refToContinue?.ref) - for (const ite of this.flowClass.find(body)) { - if (!ite?.options?.capture) cbEveryCtx(ite?.ref) - } - } - // 📄🤘(tiene return) [options: nested(array)]: Si se tiene flujos hijos los implementa - if (!fallBackFlag && prevMsg?.options?.nested?.length) { + if (!endFlowFlag && prevMsg?.options?.nested?.length) { const nestedRef = prevMsg.options.nested const flowStandalone = nestedRef.map((f) => ({ ...nestedRef.find((r) => r.refSerialize === f.refSerialize), @@ -138,28 +216,23 @@ class CoreClass { msgToSend = this.flowClass.find(body, false, flowStandalone) || [] - for (const ite of msgToSend) { - cbEveryCtx(ite?.ref) - } - - this.sendFlow(msgToSend, from) + sendFlow(msgToSend, from) return } - // 📄🤘(tiene return) [options: capture (boolean)]: Si se tiene option boolean - if (!fallBackFlag && !prevMsg?.options?.nested?.length) { + // 📄🤘(tiene return) Si el mensaje previo implementa capture + if (!endFlowFlag && !prevMsg?.options?.nested?.length) { const typeCapture = typeof prevMsg?.options?.capture - const valueCapture = prevMsg?.options?.capture - if (['string', 'boolean'].includes(typeCapture) && valueCapture) { + if (typeCapture === 'boolean' && fallBackFlag) { msgToSend = this.flowClass.find(refToContinue?.ref, true) || [] - this.sendFlow(msgToSend, from) + sendFlow(msgToSend, from) return } } msgToSend = this.flowClass.find(body) || [] - this.sendFlow(msgToSend, from) + sendFlow(msgToSend, from) } /** @@ -170,25 +243,13 @@ class CoreClass { */ sendProviderAndSave = (numberOrId, ctxMessage) => { const { answer } = ctxMessage - return Promise.all([ - this.providerClass.sendMessage(numberOrId, answer, ctxMessage), - this.databaseClass.save({ ...ctxMessage, from: numberOrId }), - ]) - } - - sendFlow = async (messageToSend, numberOrId) => { - const queue = [] - for (const ctxMessage of messageToSend) { - const delayMs = ctxMessage?.options?.delay || 0 - if (delayMs) await delay(delayMs) - Queue.enqueue(() => - this.sendProviderAndSave(numberOrId, ctxMessage) - ) - } - return Promise.all(queue) + return this.providerClass + .sendMessage(numberOrId, answer, ctxMessage) + .then(() => this.databaseClass.save({ ...ctxMessage, from: numberOrId })) } /** + * @deprecated * @private * @param {*} message * @param {*} ref @@ -201,5 +262,22 @@ class CoreClass { this.continue(null, responde.ref) } } + + /** + * Funcion dedicada a enviar el mensaje sin pasar por el flow + * (dialogflow) + * @param {*} messageToSend + * @param {*} numberOrId + * @returns + */ + sendFlowSimple = async (messageToSend, numberOrId) => { + const queue = [] + for (const ctxMessage of messageToSend) { + const delayMs = ctxMessage?.options?.delay || 0 + if (delayMs) await delay(delayMs) + QueuePrincipal.enqueue(() => this.sendProviderAndSave(numberOrId, ctxMessage)) + } + return Promise.all(queue) + } } module.exports = CoreClass diff --git a/packages/bot/index.js b/packages/bot/index.js index eb9df24..fe03d60 100644 --- a/packages/bot/index.js +++ b/packages/bot/index.js @@ -8,8 +8,7 @@ const { addKeyword, addAnswer, addChild, toSerialize } = require('./io/methods') * @param {*} args * @returns */ -const createBot = async ({ flow, database, provider }) => - new CoreClass(flow, database, provider) +const createBot = async ({ flow, database, provider }, args = {}) => new CoreClass(flow, database, provider, args) /** * Crear instancia de clase Io (Flow) @@ -29,8 +28,7 @@ const createFlow = (args) => { */ const createProvider = (providerClass = class {}, args = null) => { const providerInstance = new providerClass(args) - if (!providerClass.prototype instanceof ProviderClass) - throw new Error('El provider no implementa ProviderClass') + if (!providerClass.prototype instanceof ProviderClass) throw new Error('El provider no implementa ProviderClass') return providerInstance } diff --git a/packages/bot/io/flow.class.js b/packages/bot/io/flow.class.js index 458c423..13de732 100644 --- a/packages/bot/io/flow.class.js +++ b/packages/bot/io/flow.class.js @@ -25,9 +25,17 @@ class FlowClass { let refSymbol = null overFlow = overFlow ?? this.flowSerialize + const customRegex = (str = null) => { + if (typeof str !== 'string') return + const instanceRegex = new RegExp(str) + return instanceRegex.test(str) + } + /** Retornar expresion regular para buscar coincidencia */ - const mapSensitive = (str, flag = false) => { - const regexSensitive = flag ? 'g' : 'i' + const mapSensitive = (str, mapOptions = { sensitive: false, regex: false }) => { + if (mapOptions.regex) return customRegex(str) + + const regexSensitive = mapOptions.sensitive ? 'g' : 'i' if (Array.isArray(str)) { return new RegExp(str.join('|'), regexSensitive) } @@ -36,6 +44,7 @@ class FlowClass { const findIn = (keyOrWord, symbol = false, flow = overFlow) => { const sensitive = refSymbol?.options?.sensitive || false + const regex = refSymbol?.options?.regex || false capture = refSymbol?.options?.capture || false if (capture) return messages @@ -46,7 +55,7 @@ class FlowClass { if (refSymbol?.ref) findIn(refSymbol.ref, true) } else { refSymbol = flow.find((c) => { - return mapSensitive(c.keyword, sensitive).test(keyOrWord) + return mapSensitive(c.keyword, { sensitive, regex }).test(keyOrWord) }) if (refSymbol?.ref) findIn(refSymbol.ref, true) return messages @@ -56,8 +65,7 @@ class FlowClass { return messages } - findBySerialize = (refSerialize) => - this.flowSerialize.find((r) => r.refSerialize === refSerialize) + findBySerialize = (refSerialize) => this.flowSerialize.find((r) => r.refSerialize === refSerialize) findIndexByRef = (ref) => this.flowSerialize.findIndex((r) => r.ref === ref) } diff --git a/packages/bot/io/methods/addAnswer.js b/packages/bot/io/methods/addAnswer.js index f0ccdad..5236c14 100644 --- a/packages/bot/io/methods/addAnswer.js +++ b/packages/bot/io/methods/addAnswer.js @@ -17,15 +17,10 @@ const addAnswer = * @returns */ const getAnswerOptions = () => ({ - media: - typeof options?.media === 'string' ? `${options?.media}` : null, + media: typeof options?.media === 'string' ? `${options?.media}` : null, buttons: Array.isArray(options?.buttons) ? options.buttons : [], - capture: - typeof options?.capture === 'boolean' - ? options?.capture - : false, - child: - typeof options?.child === 'string' ? `${options?.child}` : null, + capture: typeof options?.capture === 'boolean' ? options?.capture : false, + child: typeof options?.child === 'string' ? `${options?.child}` : null, delay: typeof options?.delay === 'number' ? options?.delay : 0, }) @@ -49,8 +44,7 @@ const addAnswer = * Esta funcion aplana y busca los callback anidados de los hijos * @returns */ - const getCbFromNested = () => - flatObject(Array.isArray(nested) ? nested : [nested]) + const getCbFromNested = () => flatObject(Array.isArray(nested) ? nested : [nested]) const callback = typeof cb === 'function' ? cb : () => null diff --git a/packages/bot/io/methods/addKeyword.js b/packages/bot/io/methods/addKeyword.js index e17f9be..50a5c0c 100644 --- a/packages/bot/io/methods/addKeyword.js +++ b/packages/bot/io/methods/addKeyword.js @@ -8,12 +8,14 @@ const { toJson } = require('./toJson') * @param {*} options {sensitive:boolean} default false */ const addKeyword = (keyword, options) => { + if (typeof keyword !== 'string' && !Array.isArray(keyword)) { + throw new Error('DEBE_SER_STRING_ARRAY_REGEX') + } + const parseOptions = () => { const defaultProperties = { - sensitive: - typeof options?.sensitive === 'boolean' - ? options?.sensitive - : false, + sensitive: typeof options?.sensitive === 'boolean' ? options?.sensitive : false, + regex: typeof options?.regex === 'boolean' ? options?.regex : false, } return defaultProperties diff --git a/packages/bot/io/methods/toCtx.js b/packages/bot/io/methods/toCtx.js index d29295e..980cbfb 100644 --- a/packages/bot/io/methods/toCtx.js +++ b/packages/bot/io/methods/toCtx.js @@ -5,12 +5,12 @@ const { generateRef, generateRefSerialize } = require('../../utils/hash') * @param options {media:string, buttons:[], capture:true default false} * @returns */ -const toCtx = ({ body, from, prevRef, index }) => { +const toCtx = ({ body, from, prevRef, options = {}, index }) => { return { ref: generateRef(), keyword: prevRef, answer: body, - options: {}, + options: options ?? {}, from, refSerialize: generateRefSerialize({ index, answer: body }), } diff --git a/packages/bot/package.json b/packages/bot/package.json index 94ea8ad..563cd74 100644 --- a/packages/bot/package.json +++ b/packages/bot/package.json @@ -1,6 +1,6 @@ { "name": "@bot-whatsapp/bot", - "version": "0.0.29-alpha.0", + "version": "0.0.96-alpha.0", "description": "", "main": "./lib/bundle.bot.cjs", "scripts": { @@ -28,5 +28,9 @@ }, "dependencies": { "dotenv": "^16.0.3" + }, + "repository": { + "type": "git", + "url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/bot" } } diff --git a/packages/bot/provider/provider.class.js b/packages/bot/provider/provider.class.js index 212f042..57d7185 100644 --- a/packages/bot/provider/provider.class.js +++ b/packages/bot/provider/provider.class.js @@ -20,8 +20,7 @@ class ProviderClass extends EventEmitter { */ sendMessage = async (userId, message) => { - if (NODE_ENV !== 'production') - console.log('[sendMessage]', { userId, message }) + if (NODE_ENV !== 'production') console.log('[sendMessage]', { userId, message }) return message } } diff --git a/packages/bot/rollup-bot.config.js b/packages/bot/rollup-bot.config.js index f8ffa2a..36bbb87 100644 --- a/packages/bot/rollup-bot.config.js +++ b/packages/bot/rollup-bot.config.js @@ -10,6 +10,7 @@ module.exports = [ banner: banner['banner.output'].join(''), file: join(__dirname, 'lib', 'bundle.bot.cjs'), format: 'cjs', + sourcemap: true, }, plugins: [commonjs(), nodeResolve()], }, diff --git a/packages/bot/tests/bot.class.test.js b/packages/bot/tests/bot.class.test.js index d74fdad..4db4ca8 100644 --- a/packages/bot/tests/bot.class.test.js +++ b/packages/bot/tests/bot.class.test.js @@ -2,13 +2,7 @@ const { test } = require('uvu') const assert = require('uvu/assert') const FlowClass = require('../io/flow.class') const MockProvider = require('../../../__mocks__/mock.provider') -const { - createBot, - CoreClass, - createFlow, - createProvider, - ProviderClass, -} = require('../index') +const { createBot, CoreClass, createFlow, createProvider, ProviderClass } = require('../index') class MockFlow { allCallbacks = { ref: () => 1 } @@ -100,20 +94,13 @@ test(`[Bot] Eventos 'require_action,ready,auth_failure,message '`, async () => { await createBot(setting) /// Escuchamos eventos - mockProvider.on( - 'require_action', - (r) => (responseEvents['require_action'] = r) - ) + mockProvider.on('require_action', (r) => (responseEvents['require_action'] = r)) mockProvider.on('ready', (r) => (responseEvents['ready'] = r)) mockProvider.on('auth_failure', (r) => (responseEvents['auth_failure'] = r)) mockProvider.on('message', (r) => (responseEvents['message'] = r)) /// Emitimos eventos - mockProvider.delaySendMessage( - 0, - 'require_action', - MOCK_EVENTS.require_action - ) + mockProvider.delaySendMessage(0, 'require_action', MOCK_EVENTS.require_action) mockProvider.delaySendMessage(0, 'ready', MOCK_EVENTS.ready) mockProvider.delaySendMessage(0, 'auth_failure', MOCK_EVENTS.auth_failure) mockProvider.delaySendMessage(0, 'message', MOCK_EVENTS.message) @@ -121,21 +108,12 @@ test(`[Bot] Eventos 'require_action,ready,auth_failure,message '`, async () => { await delay(0) /// Testeamos eventos - assert.is( - JSON.stringify(responseEvents.require_action), - JSON.stringify(MOCK_EVENTS.require_action) - ) + assert.is(JSON.stringify(responseEvents.require_action), JSON.stringify(MOCK_EVENTS.require_action)) assert.is(responseEvents.ready, MOCK_EVENTS.ready) - assert.is( - JSON.stringify(responseEvents.auth_failure), - JSON.stringify(MOCK_EVENTS.auth_failure) - ) + assert.is(JSON.stringify(responseEvents.auth_failure), JSON.stringify(MOCK_EVENTS.auth_failure)) - assert.is( - JSON.stringify(responseEvents.message), - JSON.stringify(MOCK_EVENTS.message) - ) + assert.is(JSON.stringify(responseEvents.message), JSON.stringify(MOCK_EVENTS.message)) }) test(`[Bot] Probando Flujos Internos`, async () => { @@ -166,20 +144,13 @@ test(`[Bot] Probando Flujos Internos`, async () => { await createBot(setting) /// Escuchamos eventos - mockProvider.on( - 'require_action', - (r) => (responseEvents['require_action'] = r) - ) + mockProvider.on('require_action', (r) => (responseEvents['require_action'] = r)) mockProvider.on('ready', (r) => (responseEvents['ready'] = r)) mockProvider.on('auth_failure', (r) => (responseEvents['auth_failure'] = r)) mockProvider.on('message', (r) => (responseEvents['message'] = r)) /// Emitimos eventos - mockProvider.delaySendMessage( - 0, - 'require_action', - MOCK_EVENTS.require_action - ) + mockProvider.delaySendMessage(0, 'require_action', MOCK_EVENTS.require_action) mockProvider.delaySendMessage(0, 'ready', MOCK_EVENTS.ready) mockProvider.delaySendMessage(0, 'auth_failure', MOCK_EVENTS.auth_failure) mockProvider.delaySendMessage(0, 'message', MOCK_EVENTS.message) @@ -187,21 +158,12 @@ test(`[Bot] Probando Flujos Internos`, async () => { await delay(0) /// Testeamos eventos - assert.is( - JSON.stringify(responseEvents.require_action), - JSON.stringify(MOCK_EVENTS.require_action) - ) + assert.is(JSON.stringify(responseEvents.require_action), JSON.stringify(MOCK_EVENTS.require_action)) assert.is(responseEvents.ready, MOCK_EVENTS.ready) - assert.is( - JSON.stringify(responseEvents.auth_failure), - JSON.stringify(MOCK_EVENTS.auth_failure) - ) + assert.is(JSON.stringify(responseEvents.auth_failure), JSON.stringify(MOCK_EVENTS.auth_failure)) - assert.is( - JSON.stringify(responseEvents.message), - JSON.stringify(MOCK_EVENTS.message) - ) + assert.is(JSON.stringify(responseEvents.message), JSON.stringify(MOCK_EVENTS.message)) }) test(`[Bot] Probando Flujos Nested`, async () => { @@ -234,20 +196,13 @@ test(`[Bot] Probando Flujos Nested`, async () => { botInstance.sendProviderAndSave('xxxxx', 'xxxxx') botInstance.continue('xxxxx', 'xxxxx') /// Escuchamos eventos - mockProvider.on( - 'require_action', - (r) => (responseEvents['require_action'] = r) - ) + mockProvider.on('require_action', (r) => (responseEvents['require_action'] = r)) mockProvider.on('ready', (r) => (responseEvents['ready'] = r)) mockProvider.on('auth_failure', (r) => (responseEvents['auth_failure'] = r)) mockProvider.on('message', (r) => (responseEvents['message'] = r)) /// Emitimos eventos - mockProvider.delaySendMessage( - 0, - 'require_action', - MOCK_EVENTS.require_action - ) + mockProvider.delaySendMessage(0, 'require_action', MOCK_EVENTS.require_action) mockProvider.delaySendMessage(0, 'ready', MOCK_EVENTS.ready) mockProvider.delaySendMessage(0, 'auth_failure', MOCK_EVENTS.auth_failure) mockProvider.delaySendMessage(0, 'message', MOCK_EVENTS.message) @@ -255,21 +210,12 @@ test(`[Bot] Probando Flujos Nested`, async () => { await delay(0) /// Testeamos eventos - assert.is( - JSON.stringify(responseEvents.require_action), - JSON.stringify(MOCK_EVENTS.require_action) - ) + assert.is(JSON.stringify(responseEvents.require_action), JSON.stringify(MOCK_EVENTS.require_action)) assert.is(responseEvents.ready, MOCK_EVENTS.ready) - assert.is( - JSON.stringify(responseEvents.auth_failure), - JSON.stringify(MOCK_EVENTS.auth_failure) - ) + assert.is(JSON.stringify(responseEvents.auth_failure), JSON.stringify(MOCK_EVENTS.auth_failure)) - assert.is( - JSON.stringify(responseEvents.message), - JSON.stringify(MOCK_EVENTS.message) - ) + assert.is(JSON.stringify(responseEvents.message), JSON.stringify(MOCK_EVENTS.message)) }) test.run() diff --git a/packages/bot/tests/flow.class.test.js b/packages/bot/tests/flow.class.test.js new file mode 100644 index 0000000..e1c93d7 --- /dev/null +++ b/packages/bot/tests/flow.class.test.js @@ -0,0 +1,28 @@ +const { test } = require('uvu') +const assert = require('uvu/assert') +const FlowClass = require('../io/flow.class') +const { addKeyword } = require('../index') + +test(`[FlowClass] Probando instanciamiento de clase`, async () => { + const MOCK_FLOW = addKeyword('hola').addAnswer('Buenas!') + const flowClass = new FlowClass([MOCK_FLOW]) + assert.is(flowClass instanceof FlowClass, true) +}) + +test(`[FlowClass] Probando find`, async () => { + const MOCK_FLOW = addKeyword('hola').addAnswer('Buenas!') + const flowClass = new FlowClass([MOCK_FLOW]) + + flowClass.find('hola') + assert.is(flowClass instanceof FlowClass, true) +}) + +test(`[FlowClass] Probando findBySerialize`, async () => { + const MOCK_FLOW = addKeyword('hola').addAnswer('Buenas!') + const flowClass = new FlowClass([MOCK_FLOW]) + + flowClass.findBySerialize('') + assert.is(flowClass instanceof FlowClass, true) +}) + +test.run() diff --git a/packages/bot/tests/methods.test.js b/packages/bot/tests/methods.test.js index 10b0977..f030731 100644 --- a/packages/bot/tests/methods.test.js +++ b/packages/bot/tests/methods.test.js @@ -35,10 +35,7 @@ test('Debere probar toSerialize', () => { const ARRANGE = { keyword: ['hola!', 'ole'], } - const MAIN_CTX = addKeyword(ARRANGE.keyword) - .addAnswer('Segundo!') - .addAnswer('Segundo!') - .toJson() + const MAIN_CTX = addKeyword(ARRANGE.keyword).addAnswer('Segundo!').addAnswer('Segundo!').toJson() const [ANSWER_A] = MAIN_CTX @@ -71,9 +68,7 @@ test('Debere probar la anidación', () => { answer_A: 'Bienvenido', answer_B: 'Continuar', } - const MAIN_CTX = addKeyword(ARRANGE.keyword) - .addAnswer(ARRANGE.answer_A) - .addAnswer(ARRANGE.answer_B) + const MAIN_CTX = addKeyword(ARRANGE.keyword).addAnswer(ARRANGE.answer_A).addAnswer(ARRANGE.answer_B) assert.is(MAIN_CTX.ctx.answer, ARRANGE.answer_B) }) @@ -107,10 +102,7 @@ test('Debere probar error las addAnswer', () => { }) test('Obtener toJson', () => { - const [ctxA, ctxB, ctxC] = addKeyword('hola') - .addAnswer('pera!') - .addAnswer('chao') - .toJson() + const [ctxA, ctxB, ctxC] = addKeyword('hola').addAnswer('pera!').addAnswer('chao').toJson() assert.is(ctxA.keyword, 'hola') assert.match(ctxA.ref, /^key_/) diff --git a/packages/bot/utils/delay.js b/packages/bot/utils/delay.js index 021fafe..a6534c0 100644 --- a/packages/bot/utils/delay.js +++ b/packages/bot/utils/delay.js @@ -1,4 +1,3 @@ -const delay = (miliseconds) => - new Promise((res) => setTimeout(res, miliseconds)) +const delay = (miliseconds) => new Promise((res) => setTimeout(res, miliseconds)) module.exports = { delay } diff --git a/packages/bot/utils/flattener.js b/packages/bot/utils/flattener.js index 875736d..ddc823f 100644 --- a/packages/bot/utils/flattener.js +++ b/packages/bot/utils/flattener.js @@ -3,9 +3,7 @@ const flatObject = (listArray = []) => { if (!listArray.length) return {} - const cbNestedObj = cbNestedList - .map(({ ctx }) => ctx?.callbacks) - .filter((i) => !!i) + const cbNestedObj = cbNestedList.map(({ ctx }) => ctx?.callbacks).filter((i) => !!i) const queueCb = cbNestedObj.reduce((acc, current) => { const getKeys = Object.keys(current) const parse = getKeys.map((icb, i) => ({ diff --git a/packages/bot/utils/hash.js b/packages/bot/utils/hash.js index 84a25db..6902d58 100644 --- a/packages/bot/utils/hash.js +++ b/packages/bot/utils/hash.js @@ -16,9 +16,6 @@ const generateRef = (prefix = false) => { * @returns */ const generateRefSerialize = ({ index, answer, keyword }) => - crypto - .createHash('md5') - .update(JSON.stringify({ index, answer, keyword })) - .digest('hex') + crypto.createHash('md5').update(JSON.stringify({ index, answer, keyword })).digest('hex') module.exports = { generateRef, generateRefSerialize } diff --git a/packages/bot/utils/interactive.js b/packages/bot/utils/interactive.js index c317a7f..44905cf 100644 --- a/packages/bot/utils/interactive.js +++ b/packages/bot/utils/interactive.js @@ -4,9 +4,7 @@ const printer = (message, title) => { if (NODE_ENV !== 'test') { // console.clear() if (title) console.log(bgRed(`${title}`)) - console.log( - yellow(Array.isArray(message) ? message.join('\n') : message) - ) + console.log(yellow(Array.isArray(message) ? message.join('\n') : message)) console.log(``) } } diff --git a/packages/bot/utils/queue.js b/packages/bot/utils/queue.js index 1f610e9..873024a 100644 --- a/packages/bot/utils/queue.js +++ b/packages/bot/utils/queue.js @@ -1,8 +1,8 @@ class Queue { - static queue = [] - static pendingPromise = false + queue = [] + pendingPromise = false - static enqueue(promise) { + enqueue(promise) { return new Promise((resolve, reject) => { this.queue.push({ promise, @@ -13,7 +13,7 @@ class Queue { }) } - static dequeue() { + dequeue() { if (this.workingOnPromise) { return false } diff --git a/packages/cli/check/index.js b/packages/cli/check/index.js index 73d5924..5befb8a 100644 --- a/packages/cli/check/index.js +++ b/packages/cli/check/index.js @@ -1,38 +1,59 @@ const { red, yellow, green, bgCyan } = require('kleur') +const { exec } = require('node:child_process') const checkNodeVersion = () => { - console.log(bgCyan('🚀 Revisando tu Node.js')) - const version = process.version - const majorVersion = parseInt(version.replace('v', '').split('.').shift()) - if (majorVersion < 16) { - console.error( - red( - `🔴 Se require Node.js 16 o superior. Actualmente esta ejecutando Node.js ${version}` - ) - ) - process.exit(1) - } - console.log(green(`Node.js compatible ${version}`)) - console.log(``) + return new Promise((resolve, reject) => { + console.log(bgCyan('🚀 Revisando tu Node.js')) + const version = process.version + const majorVersion = parseInt(version.replace('v', '').split('.').shift()) + if (majorVersion < 16) { + console.error(red(`🔴 Se require Node.js 16 o superior. Actualmente esta ejecutando Node.js ${version}`)) + console.log(``) + reject('ERROR_NODE') + } + console.log(green(`Node.js: ${version} compatible ✅`)) + console.log(``) + resolve() + }) } const checkOs = () => { - console.log(bgCyan('🙂 Revisando tu sistema operativo')) - const os = process.platform - if (!os.includes('win32')) { - const messages = [ - `El sistema operativo actual (${os}) posiblemente requiera`, - `una configuración adicional referente al puppeteer`, - ``, - `Recuerda pasar por el WIKI`, - `🔗 https://github.com/leifermendez/bot-whatsapp/wiki/Instalación`, - ``, - ] + return new Promise((resolve) => { + console.log(bgCyan('🙂 Revisando tu sistema operativo')) + const os = process.platform + if (!os.includes('win32')) { + const messages = [ + `El sistema operativo actual (${os}) posiblemente requiera`, + `una configuración adicional referente al puppeteer`, + ``, + `Recuerda pasar por el WIKI`, + `🔗 https://github.com/leifermendez/bot-whatsapp/wiki/Instalación`, + ``, + ] - console.log(yellow(messages.join(' \n'))) - } - - console.log(``) + console.log(yellow(messages.join(' \n'))) + } + console.log(green(`OS: compatible ✅`)) + console.log(``) + resolve() + }) } -module.exports = { checkNodeVersion, checkOs } +const checkGit = () => { + return new Promise((resolve, reject) => { + console.log(bgCyan('🤓 Revisando GIT')) + exec('git --version', (error) => { + if (error) { + console.error(red(`🔴 Se require instalar GIT`)) + console.log(``) + reject('ERROR_GIT') + } else { + console.log(green(`Git: Compatible ✅`)) + console.log(``) + resolve() + } + }) + }) +} + +module.exports = { checkNodeVersion, checkOs, checkGit } diff --git a/packages/cli/clean/index.js b/packages/cli/clean/index.js index 6b7f6fd..60b8092 100644 --- a/packages/cli/clean/index.js +++ b/packages/cli/clean/index.js @@ -2,10 +2,7 @@ const rimraf = require('rimraf') const { yellow } = require('kleur') const { join } = require('path') -const PATH_WW = [ - join(process.cwd(), '.wwebjs_auth'), - join(process.cwd(), 'session.json'), -] +const PATH_WW = [join(process.cwd(), '.wwebjs_auth'), join(process.cwd(), 'session.json')] const cleanSession = () => { const queue = [] diff --git a/packages/cli/configuration/index.js b/packages/cli/configuration/index.js index 50565d9..2ce6077 100644 --- a/packages/cli/configuration/index.js +++ b/packages/cli/configuration/index.js @@ -23,11 +23,7 @@ const JSON_TEMPLATE = { const PATH_CONFIG = join(process.cwd(), 'config.json') const jsonConfig = () => { - return writeFile( - PATH_CONFIG, - JSON.stringify(JSON_TEMPLATE, null, 2), - 'utf-8' - ) + return writeFile(PATH_CONFIG, JSON.stringify(JSON_TEMPLATE, null, 2), 'utf-8') } module.exports = { jsonConfig } diff --git a/packages/cli/install/tool.js b/packages/cli/install/tool.js index 8095cd0..a74cec6 100644 --- a/packages/cli/install/tool.js +++ b/packages/cli/install/tool.js @@ -20,13 +20,9 @@ const installDeps = (pkgManager, packageList) => { const installSingle = (pkgInstall) => () => { new Promise((resolve) => { try { - childProcess = spawn( - pkgManager, - [PKG_OPTION[pkgManager], pkgInstall], - { - stdio: 'inherit', - } - ) + childProcess = spawn(pkgManager, [PKG_OPTION[pkgManager], pkgInstall], { + stdio: 'inherit', + }) childProcess.on('error', (e) => { console.error(e) diff --git a/packages/cli/interactive/index.js b/packages/cli/interactive/index.js index a0a4034..d65ca19 100644 --- a/packages/cli/interactive/index.js +++ b/packages/cli/interactive/index.js @@ -1,9 +1,9 @@ const prompts = require('prompts') const { join } = require('path') -const { yellow, red, cyan, bgMagenta } = require('kleur') +const { yellow, red, cyan, bgMagenta, bgRed } = require('kleur') const { existsSync } = require('fs') const { copyBaseApp } = require('../create-app') -const { checkNodeVersion, checkOs } = require('../check') +const { checkNodeVersion, checkOs, checkGit } = require('../check') const bannerDone = () => { console.log(``) @@ -21,6 +21,20 @@ const bannerDone = () => { } const startInteractive = async () => { + try { + console.clear() + await checkNodeVersion() + checkOs() + await checkGit() + console.clear() + await nextSteps() + } catch (e) { + console.error(bgRed(`Ups! 🙄 algo no va bien.`)) + console.error(bgRed(`Revisa los requerimientos minimos en la documentacion`)) + } +} + +const nextSteps = async () => { const questions = [ { type: 'text', @@ -32,11 +46,11 @@ const startInteractive = async () => { name: 'providerWs', message: '¿Cuál proveedor de whatsapp quieres utilizar?', choices: [ - { title: 'whatsapp-web.js (gratis)', value: 'wweb' }, - { title: 'Venom (gratis)', value: 'venom' }, { title: 'Baileys (gratis)', value: 'baileys' }, + { title: 'Venom (gratis)', value: 'venom' }, + { title: 'whatsapp-web.js (gratis)', value: 'wweb' }, { title: 'Twilio', value: 'twilio' }, - { title: 'API Oficial (Meta)', value: 'meta' }, + { title: 'Meta', value: 'meta' }, ], max: 1, hint: 'Espacio para seleccionar', @@ -58,9 +72,6 @@ const startInteractive = async () => { }, ] - console.clear() - checkNodeVersion() - checkOs() const onCancel = () => { console.log('¡Proceso cancelado!') return true @@ -69,8 +80,7 @@ const startInteractive = async () => { const { outDir = '', providerDb = [], providerWs = [] } = response const createApp = async (templateName = null) => { - if (!templateName) - throw new Error('TEMPLATE_NAME_INVALID: ', templateName) + if (!templateName) throw new Error('TEMPLATE_NAME_INVALID: ', templateName) const possiblesPath = [ join(__dirname, '..', '..', 'starters', 'apps', templateName), @@ -102,11 +112,7 @@ const startInteractive = async () => { const vendorProvider = async () => { const [answer] = providerWs if (!providerWs.length) { - console.log( - red( - `Debes seleccionar un proveedor de whatsapp. Tecla [Space] para seleccionar` - ) - ) + console.log(red(`Debes seleccionar un proveedor de whatsapp. Tecla [Space] para seleccionar`)) process.exit(1) } return answer @@ -119,11 +125,7 @@ const startInteractive = async () => { const dbProvider = async () => { const [answer] = providerDb if (!providerDb.length) { - console.log( - red( - `Debes seleccionar un proveedor de base de datos. Tecla [Space] para seleccionar` - ) - ) + console.log(red(`Debes seleccionar un proveedor de base de datos. Tecla [Space] para seleccionar`)) process.exit(1) } return answer diff --git a/packages/cli/package.json b/packages/cli/package.json index 3f86f72..60361be 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@bot-whatsapp/cli", - "version": "0.0.36-alpha.0", + "version": "0.0.72-alpha.0", "description": "", "main": "index.js", "devDependencies": { @@ -15,5 +15,9 @@ ], "bin": { "bot": "./bin/cli.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/cli" } } diff --git a/packages/contexts/package.json b/packages/contexts/package.json index 29c2dcd..c186a5f 100644 --- a/packages/contexts/package.json +++ b/packages/contexts/package.json @@ -1,6 +1,6 @@ { "name": "@bot-whatsapp/contexts", - "version": "0.0.1", + "version": "0.0.16-alpha.0", "description": "", "main": "./lib/bundle.contexts.cjs", "files": [ @@ -13,5 +13,9 @@ }, "dependencies": { "@bot-whatsapp/bot": "*" + }, + "repository": { + "type": "git", + "url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/contexts" } } diff --git a/packages/contexts/src/dialogflow-cx/dialogflow-cx.class.js b/packages/contexts/src/dialogflow-cx/dialogflow-cx.class.js index 25bd2a6..6be4f9e 100644 --- a/packages/contexts/src/dialogflow-cx/dialogflow-cx.class.js +++ b/packages/contexts/src/dialogflow-cx/dialogflow-cx.class.js @@ -38,10 +38,8 @@ class DialogFlowCXContext extends CoreClass { * */ } - if (!this.optionsDX.location.length) - throw new Error('LOCATION_NO_ENCONTRADO') - if (!this.optionsDX.agentId.length) - throw new Error('AGENTID_NO_ENCONTRADO') + if (!this.optionsDX.location.length) throw new Error('LOCATION_NO_ENCONTRADO') + if (!this.optionsDX.agentId.length) throw new Error('AGENTID_NO_ENCONTRADO') const rawJson = readFileSync(GOOGLE_ACCOUNT_PATH, 'utf-8') const { project_id, private_key, client_email } = JSON.parse(rawJson) @@ -86,9 +84,7 @@ class DialogFlowCXContext extends CoreClass { }, } - const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [ - null, - ] + const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [null] const listMessages = single.queryResult.responseMessages.map((res) => { if (res.message == 'text') { @@ -96,17 +92,11 @@ class DialogFlowCXContext extends CoreClass { } if (res.message == 'payload') { - const { - media = null, - buttons = [], - answer = '', - } = res.payload.fields - const buttonsArray = buttons?.listValue?.values?.map( - (btnValue) => { - const { stringValue } = btnValue.structValue.fields.body - return { body: stringValue } - } - ) + const { media = null, buttons = [], answer = '' } = res.payload.fields + const buttonsArray = buttons?.listValue?.values?.map((btnValue) => { + const { stringValue } = btnValue.structValue.fields.body + return { body: stringValue } + }) return { answer: answer?.stringValue, options: { @@ -117,7 +107,7 @@ class DialogFlowCXContext extends CoreClass { } }) - this.sendFlow(listMessages, from) + this.sendFlowSimple(listMessages, from) } } diff --git a/packages/contexts/src/dialogflow-cx/index.js b/packages/contexts/src/dialogflow-cx/index.js index 2a561c4..13c479e 100644 --- a/packages/contexts/src/dialogflow-cx/index.js +++ b/packages/contexts/src/dialogflow-cx/index.js @@ -5,8 +5,7 @@ const DialogCXFlowClass = require('./dialogflow-cx.class') * @param {*} args * @returns */ -const createBotDialog = async ({ database, provider }, _options) => - new DialogCXFlowClass(database, provider, _options) +const createBotDialog = async ({ database, provider }, _options) => new DialogCXFlowClass(database, provider, _options) module.exports = { createBotDialog, diff --git a/packages/contexts/src/dialogflow/dialogflow.class.js b/packages/contexts/src/dialogflow/dialogflow.class.js index ffe50e7..33df66b 100644 --- a/packages/contexts/src/dialogflow/dialogflow.class.js +++ b/packages/contexts/src/dialogflow/dialogflow.class.js @@ -65,10 +65,7 @@ class DialogFlowContext extends CoreClass { * para evitar este problema. * https://github.com/codigoencasa/bot-whatsapp/pull/140 */ - const session = this.sessionClient.projectAgentSessionPath( - this.projectId, - from - ) + const session = this.sessionClient.projectAgentSessionPath(this.projectId, from) const reqDialog = { session, queryInput: { @@ -79,15 +76,11 @@ class DialogFlowContext extends CoreClass { }, } - const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [ - null, - ] + const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [null] const { queryResult } = single - const msgPayload = queryResult?.fulfillmentMessages?.find( - (a) => a.message === 'payload' - ) + const msgPayload = queryResult?.fulfillmentMessages?.find((a) => a.message === 'payload') // Revisamos si el dialogFlow tiene multimedia if (msgPayload && msgPayload?.payload) { @@ -97,17 +90,25 @@ class DialogFlowContext extends CoreClass { }) customPayload = { - media: fields?.media?.stringValue, - buttons: mapButtons, + options: { + media: fields?.media?.stringValue, + buttons: mapButtons, + }, } + + const ctxFromDX = { + ...customPayload, + answer: fields?.answer?.stringValue, + } + this.sendFlowSimple([ctxFromDX], from) + return } const ctxFromDX = { - ...customPayload, answer: queryResult?.fulfillmentText, } - this.sendFlow([ctxFromDX], from) + this.sendFlowSimple([ctxFromDX], from) } } diff --git a/packages/contexts/src/dialogflow/index.js b/packages/contexts/src/dialogflow/index.js index e988f50..377fd88 100644 --- a/packages/contexts/src/dialogflow/index.js +++ b/packages/contexts/src/dialogflow/index.js @@ -5,8 +5,7 @@ const DialogFlowClass = require('./dialogflow.class') * @param {*} args * @returns */ -const createBotDialog = async ({ database, provider }) => - new DialogFlowClass(database, provider) +const createBotDialog = async ({ database, provider }) => new DialogFlowClass(database, provider) module.exports = { createBotDialog, diff --git a/packages/contexts/src/mock/index.js b/packages/contexts/src/mock/index.js index b609be5..4e2e10f 100644 --- a/packages/contexts/src/mock/index.js +++ b/packages/contexts/src/mock/index.js @@ -5,8 +5,7 @@ const MockClass = require('./mock.class') * @param {*} args * @returns */ -const createBotMock = async ({ database, provider }) => - new MockClass(database, provider) +const createBotMock = async ({ database, provider }) => new MockClass(database, provider) module.exports = { createBotMock, diff --git a/packages/create-bot-whatsapp/package.json b/packages/create-bot-whatsapp/package.json index 9c2358f..3b372ca 100644 --- a/packages/create-bot-whatsapp/package.json +++ b/packages/create-bot-whatsapp/package.json @@ -1,6 +1,6 @@ { "name": "create-bot-whatsapp", - "version": "0.0.47-alpha.0", + "version": "0.0.93-alpha.0", "description": "", "main": "./lib/bundle.create-bot-whatsapp.cjs", "files": [ @@ -11,5 +11,9 @@ "bin": "./bin/create.js", "dependencies": { "@bot-whatsapp/cli": "*" + }, + "repository": { + "type": "git", + "url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/create-bot-whatsapp" } } diff --git a/packages/database/package.json b/packages/database/package.json index 71404ba..f69d0c9 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,6 +1,6 @@ { "name": "@bot-whatsapp/database", - "version": "0.0.28-alpha.0", + "version": "0.0.64-alpha.0", "description": "Esto es el conector a mysql, pg, mongo", "main": "./lib/mock/index.cjs", "keywords": [], @@ -19,5 +19,9 @@ "./mongo": "./lib/mongo/index.cjs", "./json": "./lib/json/index.cjs", "./mysql": "./lib/mysql/index.cjs" + }, + "repository": { + "type": "git", + "url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/database" } } diff --git a/packages/database/src/mock/index.js b/packages/database/src/mock/index.js index 4c07c03..2ab91a0 100644 --- a/packages/database/src/mock/index.js +++ b/packages/database/src/mock/index.js @@ -10,7 +10,10 @@ class MockDatabase { constructor() {} getPrevByNumber = (from) => { - const history = this.listHistory.slice().reverse() + const history = this.listHistory + .slice() + .reverse() + .filter((i) => !!i.keyword) return history.find((a) => a.from === from) } diff --git a/packages/database/src/mongo/index.js b/packages/database/src/mongo/index.js index 6af68f0..9ea3fa5 100644 --- a/packages/database/src/mongo/index.js +++ b/packages/database/src/mongo/index.js @@ -24,12 +24,7 @@ class MongoAdapter { } getPrevByNumber = async (from) => { - const result = await this.db - .collection('history') - .find({ from }) - .sort({ _id: -1 }) - .limit(1) - .toArray() + const result = await this.db.collection('history').find({ from }).sort({ _id: -1 }).limit(1).toArray() return result[0] } diff --git a/packages/database/src/mysql/index.js b/packages/database/src/mysql/index.js index e01b55a..0beea56 100644 --- a/packages/database/src/mysql/index.js +++ b/packages/database/src/mysql/index.js @@ -46,18 +46,8 @@ class MyslAdapter { }) save = (ctx) => { - const values = [ - [ - ctx.ref, - ctx.keyword, - ctx.answer, - ctx.refSerialize, - ctx.from, - JSON.stringify(ctx.options), - ], - ] - const sql = - 'INSERT INTO history (ref, keyword, answer, refSerialize, phone, options ) values ?' + const values = [[ctx.ref, ctx.keyword, ctx.answer, ctx.refSerialize, ctx.from, JSON.stringify(ctx.options)]] + const sql = 'INSERT INTO history (ref, keyword, answer, refSerialize, phone, options ) values ?' this.db.query(sql, [values], (err) => { if (err) throw err @@ -71,14 +61,14 @@ class MyslAdapter { const tableName = 'history' const sql = `CREATE TABLE ${tableName} - (id INT AUTO_INCREMENT PRIMARY KEY, - ref varchar(255) NOT NULL, - keyword varchar(255) NOT NULL, - answer longtext NOT NULL, - refSerialize varchar(255) NOT NULL, - phone varchar(255) NOT NULL, - options longtext NOT NULL - )` + (id INT AUTO_INCREMENT PRIMARY KEY, + ref varchar(255) NOT NULL, + keyword varchar(255) NOT NULL, + answer longtext NOT NULL, + refSerialize varchar(255) NOT NULL, + phone varchar(255) NOT NULL, + options longtext NOT NULL) + CHARACTER SET utf8mb4 COLLATE utf8mb4_General_ci` this.db.query(sql, (err) => { if (err) throw err diff --git a/packages/docs/src/assets/fonts/generated/IBMPlexMono-Regular.module.css b/packages/docs/src/assets/fonts/generated/IBMPlexMono-Regular.module.css index deddf7c..f0ab1c0 100644 --- a/packages/docs/src/assets/fonts/generated/IBMPlexMono-Regular.module.css +++ b/packages/docs/src/assets/fonts/generated/IBMPlexMono-Regular.module.css @@ -8,6 +8,5 @@ font-family: IBMPlexMono-Regular; src: url(IBMPlexMono-Regular-subset.woff2) format('woff2'), url(IBMPlexMono-Regular-subset.zopfli.woff) format('woff'); - unicode-range: U+20, U+2C, U+2E, U+41-43, U+46, U+49, U+4B-4F, U+53-55, U+58, - U+61-65, U+67-69, U+6C-76; + unicode-range: U+20, U+2C, U+2E, U+41-43, U+46, U+49, U+4B-4F, U+53-55, U+58, U+61-65, U+67-69, U+6C-76; } diff --git a/packages/docs/src/assets/fonts/generated/IBMPlexMono-SemiBold.module.css b/packages/docs/src/assets/fonts/generated/IBMPlexMono-SemiBold.module.css index e2fdf72..6b47965 100644 --- a/packages/docs/src/assets/fonts/generated/IBMPlexMono-SemiBold.module.css +++ b/packages/docs/src/assets/fonts/generated/IBMPlexMono-SemiBold.module.css @@ -8,6 +8,6 @@ font-family: IBMPlexMono-SemiBold; src: url(IBMPlexMono-SemiBold-subset.woff2) format('woff2'), url(IBMPlexMono-SemiBold-subset.zopfli.woff) format('woff'); - unicode-range: U+20, U+24, U+2E, U+30, U+38, U+39, U+41, U+42, U+44, U+47, - U+4E, U+4F, U+52-55, U+57, U+59, U+65, U+68, U+6F, U+72, U+74; + unicode-range: U+20, U+24, U+2E, U+30, U+38, U+39, U+41, U+42, U+44, U+47, U+4E, U+4F, U+52-55, U+57, U+59, U+65, + U+68, U+6F, U+72, U+74; } diff --git a/packages/docs/src/assets/fonts/generated/Pally-Variable.module.css b/packages/docs/src/assets/fonts/generated/Pally-Variable.module.css index 74aa400..e899bbf 100644 --- a/packages/docs/src/assets/fonts/generated/Pally-Variable.module.css +++ b/packages/docs/src/assets/fonts/generated/Pally-Variable.module.css @@ -6,9 +6,8 @@ @font-face { font-family: Pally-Variable; - src: url(Pally-Variable-subset.woff2) format('woff2'), - url(Pally-Variable-subset.zopfli.woff) format('woff'); - unicode-range: U+20, U+24, U+2C, U+2E, U+30, U+33, U+39, U+41-43, U+46, - U+49-4D, U+53, U+55, U+58, U+61-65, U+67-69, U+6B-77, U+79; + src: url(Pally-Variable-subset.woff2) format('woff2'), url(Pally-Variable-subset.zopfli.woff) format('woff'); + unicode-range: U+20, U+24, U+2C, U+2E, U+30, U+33, U+39, U+41-43, U+46, U+49-4D, U+53, U+55, U+58, U+61-65, U+67-69, + U+6B-77, U+79; font-weight: 400 700; } diff --git a/packages/docs/src/assets/fonts/generated/SourceSerifPro-Regular.module.css b/packages/docs/src/assets/fonts/generated/SourceSerifPro-Regular.module.css index b1a323f..a491d8c 100644 --- a/packages/docs/src/assets/fonts/generated/SourceSerifPro-Regular.module.css +++ b/packages/docs/src/assets/fonts/generated/SourceSerifPro-Regular.module.css @@ -8,6 +8,5 @@ font-family: SourceSerifPro-Regular; src: url(SourceSerifPro-Regular-subset.woff2) format('woff2'), url(SourceSerifPro-Regular-subset.zopfli.woff) format('woff'); - unicode-range: U+20, U+2C, U+2E, U+41-44, U+49, U+4A, U+4C, U+53, U+55, - U+61-65, U+67-69, U+6B-76, U+79; + unicode-range: U+20, U+2C, U+2E, U+41-44, U+49, U+4A, U+4C, U+53, U+55, U+61-65, U+67-69, U+6B-76, U+79; } diff --git a/packages/docs/src/assets/fonts/generated/Synonym-Variable.module.css b/packages/docs/src/assets/fonts/generated/Synonym-Variable.module.css index b834dbb..00cb8df 100644 --- a/packages/docs/src/assets/fonts/generated/Synonym-Variable.module.css +++ b/packages/docs/src/assets/fonts/generated/Synonym-Variable.module.css @@ -6,9 +6,8 @@ @font-face { font-family: Synonym-Variable; - src: url(Synonym-Variable-subset.woff2) format('woff2'), - url(Synonym-Variable-subset.zopfli.woff) format('woff'); - unicode-range: U+20, U+24, U+2C, U+2E, U+30, U+33, U+35, U+41-44, U+46, U+47, - U+49, U+4B-4F, U+53-55, U+57-59, U+61, U+63-65, U+67-69, U+6C-76; + src: url(Synonym-Variable-subset.woff2) format('woff2'), url(Synonym-Variable-subset.zopfli.woff) format('woff'); + unicode-range: U+20, U+24, U+2C, U+2E, U+30, U+33, U+35, U+41-44, U+46, U+47, U+49, U+4B-4F, U+53-55, U+57-59, U+61, + U+63-65, U+67-69, U+6C-76; font-weight: 400 700; } diff --git a/packages/docs/src/assets/fonts/generated/TenorSans-Regular.module.css b/packages/docs/src/assets/fonts/generated/TenorSans-Regular.module.css index 819cfa3..b3dbfc6 100644 --- a/packages/docs/src/assets/fonts/generated/TenorSans-Regular.module.css +++ b/packages/docs/src/assets/fonts/generated/TenorSans-Regular.module.css @@ -6,8 +6,7 @@ @font-face { font-family: TenorSans-Regular; - src: url(TenorSans-Regular-subset.woff2) format('woff2'), - url(TenorSans-Regular-subset.zopfli.woff) format('woff'); - unicode-range: U+20, U+24, U+2E, U+30, U+36, U+46, U+49, U+4A, U+53, U+54, - U+61, U+63, U+65, U+69, U+6B, U+6E, U+6F, U+72-75, U+79; + src: url(TenorSans-Regular-subset.woff2) format('woff2'), url(TenorSans-Regular-subset.zopfli.woff) format('woff'); + unicode-range: U+20, U+24, U+2E, U+30, U+36, U+46, U+49, U+4A, U+53, U+54, U+61, U+63, U+65, U+69, U+6B, U+6E, U+6F, + U+72-75, U+79; } diff --git a/packages/docs/src/assets/images/digital-ocean.png b/packages/docs/src/assets/images/digital-ocean.png new file mode 100644 index 0000000..0d0163b Binary files /dev/null and b/packages/docs/src/assets/images/digital-ocean.png differ diff --git a/packages/docs/src/components/atoms/DigitalOcean.tsx b/packages/docs/src/components/atoms/DigitalOcean.tsx new file mode 100644 index 0000000..fc97537 --- /dev/null +++ b/packages/docs/src/components/atoms/DigitalOcean.tsx @@ -0,0 +1,189 @@ +export const DigitalOcean = () => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/packages/docs/src/components/atoms/Logo.tsx b/packages/docs/src/components/atoms/Logo.tsx index b5e1199..97a7fa1 100644 --- a/packages/docs/src/components/atoms/Logo.tsx +++ b/packages/docs/src/components/atoms/Logo.tsx @@ -5,14 +5,7 @@ import logoSrc from '~/assets/images/chatbot-whatsapp.png?width=64&height=64&png export default component$(() => ( - Qwind Logo + Qwind Logo Chatbot )) diff --git a/packages/docs/src/components/atoms/Netlify.tsx b/packages/docs/src/components/atoms/Netlify.tsx index 9047eab..3090113 100644 --- a/packages/docs/src/components/atoms/Netlify.tsx +++ b/packages/docs/src/components/atoms/Netlify.tsx @@ -1,11 +1,5 @@ export const Netlify = () => ( - + { {head.title} - + {head.meta.map((m) => ( diff --git a/packages/docs/src/components/core/Social.tsx b/packages/docs/src/components/core/Social.tsx index 43d6ac2..f620441 100644 --- a/packages/docs/src/components/core/Social.tsx +++ b/packages/docs/src/components/core/Social.tsx @@ -34,14 +34,8 @@ export const Social = () => { content="https://campaign.codigoencasa.com" /> */} - - + + @@ -52,10 +46,7 @@ export const Social = () => { name="twitter:title" content="💻 Conviértete en un Programador Backend aprendiendo todo de Cloud y Nodejs" /> - + ) } diff --git a/packages/docs/src/components/core/ToggleMenu.tsx b/packages/docs/src/components/core/ToggleMenu.tsx index 51779fc..567fd46 100644 --- a/packages/docs/src/components/core/ToggleMenu.tsx +++ b/packages/docs/src/components/core/ToggleMenu.tsx @@ -27,9 +27,7 @@ export default component$((props: ItemProps) => { // TODO: document.body.classList.toggle('overflow-hidden') document.getElementById('header')?.classList.toggle('h-screen') - document - .querySelector('#header nav') - ?.classList.toggle('hidden') + document.querySelector('#header nav')?.classList.toggle('hidden') }} > diff --git a/packages/docs/src/components/core/ToggleTheme.tsx b/packages/docs/src/components/core/ToggleTheme.tsx index 00de98e..6ef1196 100644 --- a/packages/docs/src/components/core/ToggleTheme.tsx +++ b/packages/docs/src/components/core/ToggleTheme.tsx @@ -10,16 +10,13 @@ interface ItemProps { export default component$((props: ItemProps) => { const { iconClass } = props const store = useStore({ - theme: - (typeof window !== 'undefined' && window?.localStorage?.theme) || - undefined, + theme: (typeof window !== 'undefined' && window?.localStorage?.theme) || undefined, }) useClientEffect$(() => { store.theme = window.localStorage.theme === 'dark' || - (!('theme' in window.localStorage) && - window.matchMedia('(prefers-color-scheme: dark)').matches) + (!('theme' in window.localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches) ? 'dark' : 'light' }) @@ -42,11 +39,7 @@ export default component$((props: ItemProps) => { } }} > - {store.theme == 'dark' ? ( - - ) : ( - - )} + {store.theme == 'dark' ? : } ) }) diff --git a/packages/docs/src/components/icons/IconArrowDownRight.tsx b/packages/docs/src/components/icons/IconArrowDownRight.tsx index 8adfab2..5fd5087 100644 --- a/packages/docs/src/components/icons/IconArrowDownRight.tsx +++ b/packages/docs/src/components/icons/IconArrowDownRight.tsx @@ -7,9 +7,7 @@ export const IconArrowDownRight = (props: ItemProps) => { return ( { diff --git a/packages/docs/src/components/icons/IconMoon.tsx b/packages/docs/src/components/icons/IconMoon.tsx index fa65ec0..296951f 100644 --- a/packages/docs/src/components/icons/IconMoon.tsx +++ b/packages/docs/src/components/icons/IconMoon.tsx @@ -7,9 +7,7 @@ export const IconMoon = (props: ItemProps) => { return ( {props.user.login}
-
{props.user.login}
+
{props.user.login}
diff --git a/packages/docs/src/components/widgets/Collaborators.tsx b/packages/docs/src/components/widgets/Collaborators.tsx index 23f2844..cfd2d24 100644 --- a/packages/docs/src/components/widgets/Collaborators.tsx +++ b/packages/docs/src/components/widgets/Collaborators.tsx @@ -32,8 +32,7 @@ export default component$((props: { users: User[] }) => { Super estrellas

- Todo es posible gracias a el mayor recursos de todos, el - recurso humano. Tu tambien puedes{' '} + Todo es posible gracias a el mayor recursos de todos, el recurso humano. Tu tambien puedes{' '} formar parte diff --git a/packages/docs/src/components/widgets/ExtraBar.tsx b/packages/docs/src/components/widgets/ExtraBar.tsx index fda47b2..0d9ae34 100644 --- a/packages/docs/src/components/widgets/ExtraBar.tsx +++ b/packages/docs/src/components/widgets/ExtraBar.tsx @@ -15,8 +15,8 @@ export default component$(() => { 📄 Editar esta pagina

- Forma parte de esta comunidad mejorando la documentación - siente libre de poder agregar o editar lo que quieras + Forma parte de esta comunidad mejorando la documentación siente libre de poder agregar o editar + lo que quieras

diff --git a/packages/docs/src/components/widgets/FAQs.tsx b/packages/docs/src/components/widgets/FAQs.tsx index 28aa2c1..5834e24 100644 --- a/packages/docs/src/components/widgets/FAQs.tsx +++ b/packages/docs/src/components/widgets/FAQs.tsx @@ -56,13 +56,9 @@ export default component$(() => { {question} - {answer - .split('\n\n') - .map((paragraph) => ( -

- {paragraph} -

- ))} + {answer.split('\n\n').map((paragraph) => ( +

{paragraph}

+ ))} ))} diff --git a/packages/docs/src/components/widgets/Features.tsx b/packages/docs/src/components/widgets/Features.tsx index 4888086..ddd3dc8 100644 --- a/packages/docs/src/components/widgets/Features.tsx +++ b/packages/docs/src/components/widgets/Features.tsx @@ -50,13 +50,11 @@ export default component$(() => { Caracteristicas

- Nuestras principales{' '} - funciones + Nuestras principales funciones

- El secreto es mantener los procesos repetitivos en - procesos automatizados simples, por eso te mostramos en - que destacamos. + El secreto es mantener los procesos repetitivos en procesos automatizados simples, por eso te + mostramos en que destacamos.

@@ -70,12 +68,8 @@ export default component$(() => {
-

- {title} -

-

- {description} -

+

{title}

+

{description}

))} diff --git a/packages/docs/src/components/widgets/Hero.tsx b/packages/docs/src/components/widgets/Hero.tsx index 4700421..01ee042 100644 --- a/packages/docs/src/components/widgets/Hero.tsx +++ b/packages/docs/src/components/widgets/Hero.tsx @@ -9,36 +9,24 @@ import { src as placeholder } from '~/assets/images/chatbot-whatsapp.png?width=4 export default component$(() => { return ( -
+

- Crear chatbot{' '} - - WhatsApp - - {' '} - en minutos + Crear chatbot WhatsApp + en minutos

- - Con esta libreria,{' '} - + Con esta libreria, - puedes configurar respuestas - automatizadas para preguntas frecuentes + puedes configurar respuestas automatizadas para preguntas frecuentes {' '} - , recibir y responder mensajes de manera - automatizada, y hacer un seguimiento de las - interacciones con los clientes. Además, - nuestro Chatbot se integra fácilmente con - otros sistemas y herramientas que ya esté - utilizando en su negocio. + , recibir y responder mensajes de manera automatizada, y hacer un seguimiento de las + interacciones con los clientes. Además, nuestro Chatbot se integra fácilmente con + otros sistemas y herramientas que ya esté utilizando en su negocio.

@@ -47,12 +35,16 @@ export default component$(() => { npm create bot-whatsapp@latest
- diff --git a/packages/docs/src/components/widgets/Members.tsx b/packages/docs/src/components/widgets/Members.tsx new file mode 100644 index 0000000..8ca5ddb --- /dev/null +++ b/packages/docs/src/components/widgets/Members.tsx @@ -0,0 +1,49 @@ +import { component$ } from '@builder.io/qwik' +import { RequestHandlerCloudflarePages } from '@builder.io/qwik-city/middleware/cloudflare-pages' +import { User } from '~/contexts' +import Collaborator from './Collaborator' + +export const onRequest: RequestHandlerCloudflarePages = async () => { + console.log('??heree') +} + +export const TaleUsers = component$((props: { users: User[] }) => { + return ( + <> + {props.users.map((user) => ( +
+ {' '} + +
+ ))} + + ) +}) + +export default component$((props: { users: User[] }) => { + return ( +
+
+
+

+ Premium +

+

+ Miembros +

+

+ Conviértete en un miembro destacado y forma parte del proyecto y disfruta de manera adelantada + de las actualizaciones{' '} + + Únete + +

+
+ +
+ +
+
+
+ ) +}) diff --git a/packages/docs/src/components/widgets/NavBar.tsx b/packages/docs/src/components/widgets/NavBar.tsx index 56915c9..5b332bc 100644 --- a/packages/docs/src/components/widgets/NavBar.tsx +++ b/packages/docs/src/components/widgets/NavBar.tsx @@ -5,55 +5,45 @@ import { DocumentationCtx } from '~/contexts' /** * options = [] array con la lista de opciones de la documentacion */ -export default component$( - ({ options = [] }: { options: DocumentationCtx[] }) => { - return ( -
- {options.map((item, i) => ( - - ))} -
- ) - } -) +export default component$(({ options = [] }: { options: DocumentationCtx[] }) => { + return ( +
+ {options.map((item, i) => ( + + ))} +
+ ) +}) -export const UlCompoent = component$( - (porps: { title: string; list: { link: string; name: string }[] }) => { - return ( -
    -
  • -
    - {porps.title} -
    - +export const UlCompoent = component$((porps: { title: string; list: { link: string; name: string }[] }) => { + return ( +
      +
    • +
      {porps.title}
      + +
    • +
    + ) +}) + +export const LiComponent = component$((porps: { list: { link: string; name: string }[] }) => { + const location = useLocation() + const currentPage = location.pathname + return ( +
      + {porps.list.map((opt) => ( +
    • + + {opt.name} +
    • -
    - ) - } -) - -export const LiComponent = component$( - (porps: { list: { link: string; name: string }[] }) => { - const location = useLocation() - const currentPage = location.pathname - return ( -
      - {porps.list.map((opt) => ( -
    • - - {opt.name} - -
    • - ))} -
    - ) - } -) + ))} +
+ ) +}) diff --git a/packages/docs/src/components/widgets/Navigation.tsx b/packages/docs/src/components/widgets/Navigation.tsx index 779a787..092ed93 100644 --- a/packages/docs/src/components/widgets/Navigation.tsx +++ b/packages/docs/src/components/widgets/Navigation.tsx @@ -1,74 +1,63 @@ import { component$ } from '@builder.io/qwik' -export const ButtonLink = component$( - (props: { name: string; link: string; direction: 'left' | 'right' }) => { - const ArrowRight = () => ( - - - - ) +export const ButtonLink = component$((props: { name: string; link: string; direction: 'left' | 'right' }) => { + const ArrowRight = () => ( + + + + ) - const ArrowLeft = () => ( - - - - ) + const ArrowLeft = () => ( + + + + ) - return ( - - {props.direction === 'left' ? ( - <> - - {props.name} - - ) : ( - <> - {props.name} - - - )} - - ) - } -) + return ( + + {props.direction === 'left' ? ( + <> + + {props.name} + + ) : ( + <> + {props.name} + + + )} + + ) +}) -export default component$( - (props: { pages: ({ name: string; link: string } | null)[] }) => { - const { pages } = props - return ( -
-
- {pages[0] ? ( - - ) : null} - {pages[1] ? ( - - ) : null} -
+export default component$((props: { pages: ({ name: string; link: string } | null)[] }) => { + const { pages } = props + return ( +
+
+ {pages[0] ? : null} + {pages[1] ? : null}
- ) - } -) +
+ ) +}) diff --git a/packages/docs/src/components/widgets/SearchModal.tsx b/packages/docs/src/components/widgets/SearchModal.tsx new file mode 100644 index 0000000..287a28b --- /dev/null +++ b/packages/docs/src/components/widgets/SearchModal.tsx @@ -0,0 +1,20 @@ +import { component$ } from '@builder.io/qwik' + +export const SearchModal = component$(() => { + // const state = useStore({ + // open: false, + // src: '', + // }) + + return ( +
+
+ +
+
+ ) +}) + +export const SingleModal = component$(() => { + return
Modal singlke
+}) diff --git a/packages/docs/src/components/widgets/SponsorBar.tsx b/packages/docs/src/components/widgets/SponsorBar.tsx index c0d8183..f355691 100644 --- a/packages/docs/src/components/widgets/SponsorBar.tsx +++ b/packages/docs/src/components/widgets/SponsorBar.tsx @@ -7,6 +7,8 @@ import { src as qwik } from '~/assets/images/qwik.png?width=100&metadata' import { src as leanga } from '~/assets/images/leanga.png?width=40&metadata' // @ts-ignore import { src as netlify } from '~/assets/images/full-logo-light.png?width=100&metadata' +// @ts-ignore +import { src as digitalOcean } from '~/assets/images/digital-ocean.png?width=100&metadata' /** * options = [] array con la lista de opciones de la documentacion @@ -41,7 +43,20 @@ export default component$(() => { Qwind Hero Image (Cool dog) + + + +
  • + + + DigitalOcean diff --git a/packages/docs/src/components/widgets/Stats.tsx b/packages/docs/src/components/widgets/Stats.tsx index c4dc31a..0721ba7 100644 --- a/packages/docs/src/components/widgets/Stats.tsx +++ b/packages/docs/src/components/widgets/Stats.tsx @@ -5,33 +5,25 @@ export default component$(() => {
    -
    - 132K -
    +
    132K

    Downloads

    -
    - 24.8K -
    +
    24.8K

    Stars

    -
    - 10.3K -
    +
    10.3K

    Forks

    -
    - 48.4K -
    +
    48.4K

    Users

    diff --git a/packages/docs/src/contexts/index.tsx b/packages/docs/src/contexts/index.tsx index 7282539..ca33495 100644 --- a/packages/docs/src/contexts/index.tsx +++ b/packages/docs/src/contexts/index.tsx @@ -13,5 +13,4 @@ export interface User { avatar_url: string } -export const GlobalStore = - createContext('documentation-site') +export const GlobalStore = createContext('documentation-site') diff --git a/packages/docs/src/root.tsx b/packages/docs/src/root.tsx index 7d6db36..61c3906 100644 --- a/packages/docs/src/root.tsx +++ b/packages/docs/src/root.tsx @@ -1,14 +1,5 @@ -import { - component$, - useContextProvider, - useStore, - useStyles$, -} from '@builder.io/qwik' -import { - QwikCityProvider, - RouterOutlet, - ServiceWorkerRegister, -} from '@builder.io/qwik-city' +import { component$, useContextProvider, useStore, useStyles$ } from '@builder.io/qwik' +import { QwikCityProvider, RouterOutlet, ServiceWorkerRegister } from '@builder.io/qwik-city' import { RouterHead } from '~/components/core/RouterHead' import { DarkThemeLauncher } from '~/components/core/DarkThemeLauncher' @@ -52,16 +43,22 @@ export default component$(() => { title: 'Avanzado', list: [ { name: 'Migración', link: '/docs/migration' }, - { name: 'Extender funcionalidades', link: '/docs/custom' }, + { name: 'MasterClass', link: '/docs/masterclass' }, + ], + }, + { + title: 'Despliegue', + list: [ + { name: 'Local', link: '/docs/deploy/local' }, + { name: 'Docker', link: '/docs/deploy/docker' }, + { name: 'Cloud', link: '/docs/deploy/cloud' }, ], }, { title: 'Comunidad', list: [ - { name: 'MasterClass', link: '/docs/masterclass' }, { name: 'Colabores', link: '/docs/contributing' }, { name: 'Unirme al proyecto', link: '/docs/join' }, - { name: 'Sponsors', link: '/docs/sponsors' }, ], }, ]) @@ -72,10 +69,7 @@ export default component$(() => { - + diff --git a/packages/docs/src/routes/docs/database/index.mdx b/packages/docs/src/routes/docs/database/index.mdx index dab499f..01eb796 100644 --- a/packages/docs/src/routes/docs/database/index.mdx +++ b/packages/docs/src/routes/docs/database/index.mdx @@ -4,11 +4,10 @@ import Navigation from '../../../components/widgets/Navigation' # DataBase (Base de datos) - ⚡ Dependiendo del tipo de conector que utlices puede que necesites pasar - algunas configuracion adicional como **user, host, password** para esos - casos te recomendamos guiarte de los - **[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** - o si gustas puedes editar esta documentación para ir agregando más info + ⚡ Dependiendo del tipo de conector que utlices puede que necesites pasar algunas configuracion adicional como + **user, host, password** para esos casos te recomendamos guiarte de los + **[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** o si gustas puedes editar esta + documentación para ir agregando más info Es la pieza encargada de mantener el **"estado"** de una conversación, para mayor facilidad la libreria te proporcia diferentes conectores que se de adapten mejor a tu desarrollo diff --git a/packages/docs/src/routes/docs/deploy/cloud/index.mdx b/packages/docs/src/routes/docs/deploy/cloud/index.mdx new file mode 100644 index 0000000..2e38ff3 --- /dev/null +++ b/packages/docs/src/routes/docs/deploy/cloud/index.mdx @@ -0,0 +1,108 @@ +import Alert from '../../../../components/widgets/Alert' +import Navigation from '../../../../components/widgets/Navigation' + +# Entorno Cloud + +Si deseas tener tu chatbot en ejecución en un servidor en la nueba esta, guía te ayudará. +El servidor deberá cumplir con los requisitos mínimos, puedes ver en [este enlace.](/docs/requirements) + +--- + +Dependiendo de tu proveedor de **servicio Cloud** debes crear una instancia (máquina virtual), este ejemplo iremos orientando en un entorno de AWS. +En nuestro ejemplo creamos una maquina virtual con **Ubuntu 20.04** + +![](https://i.imgur.com/5zRCz9q.png) + +--- + +Posterior al proceso de crear la máquina esperamos unos minutos hasta que ya está operativo y tomamos nota del usuario y la IP pública para proceder a conectarnos vía SSH + +## ![](https://i.imgur.com/ljyJPBm.png) + +## Conectarse via SSH + +Luego de obtener los datos necesarios para conectarnos a nuestra máquina, procedemos a hacerlo + +```shell +ssh -i llaveBot.pem ubutnu@34.228.208.104 +``` + +--- + +Luego puede aparecer un mensaje como el siguiente donde solo debes de responder **yes** + +![](https://i.imgur.com/rUBASqR.png) + +--- + +Una vez conectado ya estás dentro de la máquina virtual, te aconsejamos la primera vez hacer una actualización de dependencias de Ubuntu con los siguientes comandos + +```shell +sudo apt-get update +``` + +```shell +sudo apt-get upgrade +``` + +--- + +## Recuerda instalar Node 16 o superior + +Puedes ver más a detalle los pasos de la instalacion en este [blog](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04-es) + +```shell +curl -sL https://deb.nodesource.com/setup_18.x -o nodesource_setup.sh +``` + +```shell +sudo bash nodesource_setup.sh +``` + +```shell +sudo apt-get install -y nodejs +``` + +--- + +## Implementar el bot + +Si tienes el código de tu chatbot en un repositorio, solo falta que clones el repo en el servidor y ejecutes `npm start` + +Para escanear el **QR** puedes hacerlo vía WEB accediendo a la URL `http://[TU_IP_PUBLICA]:3000` en este ejemplo seria `http://34.228.208.10:3000` + +![](https://i.imgur.com/xcovczm.png) + +--- + +## Firewall + +Si no te abre la pagina web asegurate de tener el puerto abierto en tu firewall. +Ejemplo permitir el puerto **3000** + +![](https://i.imgur.com/0dAz0B1.png) + +--- + +## Escanear QR + +![](https://i.imgur.com/2m3NbXC.png) + +--- + +## Ejecutar en Producción + +Debes ubicarte en el directorio donde tienes el codigo fuente de tu chatbot. + +Independientemente de tu sistema operativo deberás ejecutar el chatbot con el comando atrevés de un sistema que mantenga el proceso en ejecución. +Recomendamos [Pm2](https://pm2.keymetrics.io/) + +```shell +pm2 start app.js --name=bot1 +``` + +![](https://i.imgur.com/ilPS75H.png) + +La consola de devolver un mensaje con una lista de procesos, en el ejemplo puedes observar, que tenemos un proceso llamado `bot1` + +De esta manera ya puedes cerrar la terminal y tu bot seguirá en ejecución sin problema diff --git a/packages/docs/src/routes/docs/deploy/docker/index.mdx b/packages/docs/src/routes/docs/deploy/docker/index.mdx new file mode 100644 index 0000000..9db9e2e --- /dev/null +++ b/packages/docs/src/routes/docs/deploy/docker/index.mdx @@ -0,0 +1,32 @@ +import Alert from '../../../../components/widgets/Alert' +import Navigation from '../../../../components/widgets/Navigation' + +# Entorno Docker + +Previamente, necesitas tener instalado Docker en tu servidor dependiendo del sistema operativo, los procesos cambian, +puedes encontrar toda la información oficial de docker en [este enlace.](https://docs.docker.com/get-docker/) + +--- + +Dependiendo del proveedor que has elegido necesitaras una implementación de Docker específica, pero no te preocupes, ya que viene implementada automáticamente en un archivo llamado **Dockerfile**, también puedes ver los otros Dockerfile en el apartado de [plantillas.](https://github.com/codigoencasa/bot-whatsapp/tree/main/starters/apps) + +![](https://i.imgur.com/cDspa0R.png) + +--- + +## Contruir imagen + +Solo es necesario construir la imagen del docker lo puedes hacer con el siguiente comando + +```shell +docker build . -t botwhatsapp:latest +``` + +## Iniciar contenedor + +Para iniciar el contenedor con la imagen previamente construida puedes realizarlo ejecutando el siguiente comando. +Se utiliza el puerto **3001** solo com un ejemplo puedes usar el puerto que tu quieras + +```shell +docker run -e PORT=3001 -p 3001:3001 botwhatsapp:latest +``` diff --git a/packages/docs/src/routes/docs/deploy/local/index.mdx b/packages/docs/src/routes/docs/deploy/local/index.mdx new file mode 100644 index 0000000..394af82 --- /dev/null +++ b/packages/docs/src/routes/docs/deploy/local/index.mdx @@ -0,0 +1,26 @@ +import Alert from '../../../../components/widgets/Alert' +import Navigation from '../../../../components/widgets/Navigation' + +# Entorno Local + +Si deseas tener tu chatbot en ejecución en un servidor local (computadora personal, etc.) esta, guía te ayudará. +El servidor local deberá cumplir con los requisitos mínimos, puedes ver en [este enlace.](/docs/requirements) + +--- + +Si deseas instalar pm2 puedes ejecutar `npm install pm2 --global` + +Debes ubicarte en el directorio donde tienes el codigo fuente de tu chatbot. + +Independientemente de tu sistema operativo deberás ejecutar el chatbot con el comando atrevés de un sistema que mantenga el proceso en ejecución. +Recomendamos [Pm2](https://pm2.keymetrics.io/) + +```shell +pm2 start app.js --name=bot1 +``` + +![](https://i.imgur.com/ilPS75H.png) + +La consola de devolver un mensaje con una lista de procesos, en el ejemplo puedes observar, que tenemos un proceso llamado `bot1` + +De esta manera ya puedes cerrar la terminal y tu bot seguirá en ejecución sin problema diff --git a/packages/docs/src/routes/docs/essential/index.mdx b/packages/docs/src/routes/docs/essential/index.mdx index 8187544..0f0dc45 100644 --- a/packages/docs/src/routes/docs/essential/index.mdx +++ b/packages/docs/src/routes/docs/essential/index.mdx @@ -22,12 +22,7 @@ Tan sencillo como decir **palabra/s clave** y **mensaje a responder** Ambos metodos **[addKeyword](https://github.com/codigoencasa/bot-whatsapp/blob/dev/packages/bot/io/methods/addKeyword.js)** y el **[addAnswer](https://github.com/codigoencasa/bot-whatsapp/blob/dev/packages/bot/io/methods/addAnswer.js)** tienen una serie opciones disponibles ```js -const { - createBot, - createProvider, - createFlow, - addKeyword, -} = require('@bot-whatsapp/bot') +const { createBot, createProvider, createFlow, addKeyword } = require('@bot-whatsapp/bot') const flowPrincipal = addKeyword(['hola', 'alo']) .addAnswer(['Hola, bienvenido a mi tienda', '¿Como puedo ayudarte?']) @@ -39,11 +34,10 @@ const flowPrincipal = addKeyword(['hola', 'alo']) ## Provider (Proveedor) - ⚡ Dependiendo del tipo de proveedor que utlices puede que necesites pasar - algunas configuracion adicional como **token, api, etc.** para esos casos te - recomendamos guiarte de los - **[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** - o si gustas puedes editar esta documentación para ir agregando más info + ⚡ Dependiendo del tipo de proveedor que utlices puede que necesites pasar algunas configuracion adicional como + **token, api, etc.** para esos casos te recomendamos guiarte de los + **[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** o si gustas puedes editar esta + documentación para ir agregando más info Es la pieza que conectara tu flujo con Whatsapp. En este chatbot tenemos varios proveedores disponibles la mayoria gratis pero tambien tenemos integracion la api oficial de whatsapp o twilio @@ -71,11 +65,10 @@ Los proveedores disponibles hasta el momento son los siguientes: ## DataBase (Base de datos) - ⚡ Dependiendo del tipo de conector que utlices puede que necesites pasar - algunas configuracion adicional como **user, host, password** para esos - casos te recomendamos guiarte de los - **[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** - o si gustas puedes editar esta documentación para ir agregando más info + ⚡ Dependiendo del tipo de conector que utlices puede que necesites pasar algunas configuracion adicional como + **user, host, password** para esos casos te recomendamos guiarte de los + **[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** o si gustas puedes editar esta + documentación para ir agregando más info Es la pieza encargada de mantener el **"estado"** de una conversación, para mayor facilidad la libreria te proporcia diferentes conectores que se de adapten mejor a tu desarrollo diff --git a/packages/docs/src/routes/docs/example/index.mdx b/packages/docs/src/routes/docs/example/index.mdx index 8daded3..057fcc6 100644 --- a/packages/docs/src/routes/docs/example/index.mdx +++ b/packages/docs/src/routes/docs/example/index.mdx @@ -5,12 +5,7 @@ import Navigation from '../../../components/widgets/Navigation' Si copias y pegas este codigo y tu entorno de trabajo cumple con todos los requesitos te debe funcionar abajo explico muy por encima ```js -const { - createBot, - createProvider, - createFlow, - addKeyword, -} = require('@bot-whatsapp/bot') +const { createBot, createProvider, createFlow, addKeyword } = require('@bot-whatsapp/bot') const WebWhatsappProvider = require('@bot-whatsapp/provider/web-whatsapp') const MockAdapter = require('@bot-whatsapp/database/mock') @@ -42,12 +37,7 @@ main() En esta parte solo estamos declaramos las dependencias que vamos a utilizar. Si quieres saber a fondo cada una de las funciones te recomiendo pasarte por la seccion de **[conceptos](/docs/concepts)** ```js -const { - createBot, - createProvider, - createFlow, - addKeyword, -} = require('@bot-whatsapp/bot') +const { createBot, createProvider, createFlow, addKeyword } = require('@bot-whatsapp/bot') const WebWhatsappProvider = require('@bot-whatsapp/provider/web-whatsapp') const MockAdapter = require('@bot-whatsapp/database/mock') diff --git a/packages/docs/src/routes/docs/flows/index.mdx b/packages/docs/src/routes/docs/flows/index.mdx index 027c793..0c2cf0b 100644 --- a/packages/docs/src/routes/docs/flows/index.mdx +++ b/packages/docs/src/routes/docs/flows/index.mdx @@ -1,6 +1,6 @@ import Navigation from '../../../components/widgets/Navigation' -# Flow (Flujos) +# Flow Los flujos hace referencia al hecho de construir un flujo de conversion. Esto es un flow podemos observar que estan presente dos metodos importantes **addKeyword** y el **addAnswer**. @@ -9,12 +9,7 @@ Tan sencillo como decir **palabra/s clave** y **mensaje a responder** Ambos metodos **[addKeyword](https://github.com/codigoencasa/bot-whatsapp/blob/dev/packages/bot/io/methods/addKeyword.js)** y el **[addAnswer](https://github.com/codigoencasa/bot-whatsapp/blob/dev/packages/bot/io/methods/addAnswer.js)** tienen una serie opciones disponibles ```js -const { - createBot, - createProvider, - createFlow, - addKeyword, -} = require('@bot-whatsapp/bot') +const { createBot, createProvider, createFlow, addKeyword } = require('@bot-whatsapp/bot') const flowPrincipal = addKeyword(['hola', 'alo']) .addAnswer(['Hola, bienvenido a mi tienda', '¿Como puedo ayudarte?']) @@ -23,6 +18,206 @@ const flowPrincipal = addKeyword(['hola', 'alo']) --- +## blackList + +Éste argumento se utiliza para **evitar que el bot se active** cuando los números de la lista activen el bot. +Es importante que el número **vaya acompañado de su prefijo**, en el caso de España "34". + +```js +createBot( + { + flow: adapterFlow, + provider: adapterProvider, + database: adapterDB, + }, + { + blackList: ['34XXXXXXXXX', '34XXXXXXXXX', '34XXXXXXXXX', '34XXXXXXXXX'], + } +) +``` + +--- + +## addKeyword() + +Esta funcion se utliza para iniciar un flujo de conversion.
    Recibe un `string` o un `array` +de string `['hola','buenas']`. + +**Opciones** + +- sensitive: Sensible a mayusculas y minusculas por defecto `false` + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola') + +const flowArray = addKeyword(['hola', 'alo']) + +const flowSensitive = addKeyword(['hola', 'alo'], { + sensitive: true, +}) +``` + +--- + +## addAnswer() + +Esta funcion se utliza para responder un mensaje despues del `addKeyword()` + +**Opciones** + +- delay: 0 (milisegundos) +- media: url de imagen +- buttons: array `[{body:'Boton1'}, {body:'Boton2'}, {body:'Boton3'}]` +- capture: true (para esperar respuesta) +- child: Objecto tipo flujo o arra de flujos hijos + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola').addAnswer('Este mensaje se enviara 1 segundo despues', { + delay: 1000, +}) + +const flowString = addKeyword('hola').addAnswer('Este mensaje envia una imagen', { + media: 'https://i.imgur.com/0HpzsEm.png', +}) + +const flowString = addKeyword('hola').addAnswer('Este mensaje envia tres botones', { + buttons: [{ body: 'Boton 1' }, { body: 'Boton 2' }, { body: 'Boton 3' }], +}) + +const flowString = addKeyword('hola').addAnswer('Este mensaje espera una respueta del usuario', { + capture: true, +}) +``` + +--- + +## ctx + +Este argumento se utiliza para obtener el contexto de la conversación + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola').addAnswer('Indica cual es tu email', null, (ctx) => { + console.log('👉 Informacion del contexto: ', ctx) +}) +``` + +--- + +## fallBack() + +Esta funcion se utliza para volver a enviar el ultimo mensaje abajo un ejemplo. +En el ejemplo de abajo esperamos que el usuario ingrese un mensaje que contenga `@` sino contiene +se repetira el mensaje `Indica cual es tu email` + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola').addAnswer('Indica cual es tu email', null, (ctx, { fallBack }) => { + if (!ctx.body.includes('@')) return fallBack() +}) +``` + +--- + +## flowDynamic() + +Esta funcion se utliza para devolver mensajes dinamicos que pueden venir de una API o Base de datos. +La funcion recibe un array que debe contener la siguiente estrucutura: + +`[{body:'Mensaje}, {body:'Mensaje2}]` + +```js +const { addKeyword } = require('@bot-whatsapp/bot') + +const flowString = addKeyword('hola') + .addAnswer('Indica cual es tu email', null, async (ctx, {flowDynamic}) => { + const mensajesDB = () => { + const categories = db.find(...) + const mapDatos = categories.map((c) => ({body:c.name})) + return mapDatos + } + await flowDynamic(mensajesDB()) + }) + + +``` + +--- + +## endFlow() + +Esta funcion se utliza para finalizar un flujo con dos o más addAnswer. Un ejemplo de uso sería registrar 3 datos de un usuario en 3 preguntas distinas y +que el usuario pueda finalizar por él mismo el flujo. +Como podrás comprobar en el ejemplo siguiente, se puede vincular flowDynamic y todas sus funciones; como por ejemplo botones. + +```js +const flowFormulario = addKeyword(['Hola']) + .addAnswer( + ['Hola!', 'Escriba su *Nombre* para generar su solicitud'], + { capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] }, + async (ctx, { flowDynamic, endFlow }) => { + if (ctx.body == '❌ Cancelar solicitud') { + await flowDynamic([ + { + body: '❌ *Su solicitud de cita ha sido cancelada* ❌', + buttons: [{ body: '⬅️ Volver al Inicio' }], + }, + ]) + return endFlow() + } + } + ) + .addAnswer( + ['También necesito tus dos apellidos'], + { capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] }, + async (ctx, { flowDynamic, endFlow }) => { + if (ctx.body == '❌ Cancelar solicitud') { + await flowDynamic([ + { + body: '❌ *Su solicitud de cita ha sido cancelada* ❌', + buttons: [{ body: '⬅️ Volver al Inicio' }], + }, + ]) + return endFlow() + } + } + ) + .addAnswer( + ['Dejeme su número de teléfono y le llamaré lo antes posible.'], + { capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] }, + async (ctx, { flowDynamic, endFlow }) => { + if (ctx.body == '❌ Cancelar solicitud') { + await flowDynamic([ + { + body: '❌ *Su solicitud de cita ha sido cancelada* ❌', + buttons: [{ body: '⬅️ Volver al Inicio' }], + }, + ]) + return endFlow() + } + } + ) +``` + +--- + +# QRPortalWeb + +Argumento para asignar nombre y puerto al BOT + +```js +const BOTNAME = 'bot' +QRPortalWeb({ name: BOTNAME, port: 3005 }) +``` + +--- + - **Atención** estás leyendo la documentación de la **versión v2** de esta - librería, si vienes de la versión anterior te recomendamos pasarte por la - sección de **[migración](/docs/migration/)** para que puedas disfrutar de - las nuevas características. + **Atención** estás leyendo la documentación de la **versión v2** de esta librería, si vienes de la versión anterior + te recomendamos pasarte por la sección de **[migración](/docs/migration/)** para que puedas disfrutar de las nuevas + características. ## ¿Qué es esto? @@ -34,10 +33,7 @@ npm create bot-whatsapp@latest muted playsinline > - +
    diff --git a/packages/docs/src/routes/docs/install/index.mdx b/packages/docs/src/routes/docs/install/index.mdx index 342043a..4fa8575 100644 --- a/packages/docs/src/routes/docs/install/index.mdx +++ b/packages/docs/src/routes/docs/install/index.mdx @@ -28,10 +28,7 @@ El **CLI** te hace una revisión previa, de versión de Node y sistema operativo muted playsinline > - +
    @@ -54,10 +51,8 @@ Cada plantilla tiene sus dependencias necesarias basadas en tu previa selección ``` - 📄 Si deseas cambiar tu **proveedor o tu motor** de base de datos no es - necesario volver ejecutar el CLI (lo puedes hacer sin problema) aunque - tambien basta con solo modificar un par de lineas. [Ver - explicación](/docs/essential) + 📄 Si deseas cambiar tu **proveedor o tu motor** de base de datos no es necesario volver ejecutar el CLI (lo puedes + hacer sin problema) aunque tambien basta con solo modificar un par de lineas. [Ver explicación](/docs/essential) --- diff --git a/packages/docs/src/routes/docs/layout!.tsx b/packages/docs/src/routes/docs/layout!.tsx index cb34220..8fe70cb 100644 --- a/packages/docs/src/routes/docs/layout!.tsx +++ b/packages/docs/src/routes/docs/layout!.tsx @@ -3,6 +3,7 @@ import type { DocumentHead } from '@builder.io/qwik-city' import ExtraBar from '~/components/widgets/ExtraBar' import Header from '~/components/widgets/Header' import NavBar from '~/components/widgets/NavBar' +// import { SearchModal } from '~/components/widgets/SearchModal' import SponsorBar from '~/components/widgets/SponsorBar' import { GlobalStore } from '~/contexts' // import Navigation from '~/components/widgets/Navigation' @@ -14,6 +15,7 @@ export default component$(() => { return ( <> + {/* */}
    @@ -25,11 +27,7 @@ export default component$(() => {
    -
    +
    @@ -52,8 +50,7 @@ export const head: DocumentHead = { meta: [ { name: 'description', - content: - 'Crear chatbot WhatsApp en minutos — Servicio de chatbot para whatspp gratis proyecto OpenSource', + content: 'Crear chatbot WhatsApp en minutos — Servicio de chatbot para whatspp gratis proyecto OpenSource', }, ], } diff --git a/packages/docs/src/routes/docs/masterclass/index.md b/packages/docs/src/routes/docs/masterclass/index.md deleted file mode 100644 index 9f94b4e..0000000 --- a/packages/docs/src/routes/docs/masterclass/index.md +++ /dev/null @@ -1,24 +0,0 @@ -[![hackmd-github-sync-badge](https://hackmd.io/79xQyVSgRD6RsTpqtMPPdw/badge)](https://hackmd.io/79xQyVSgRD6RsTpqtMPPdw) -### Preguntas Frecuentes para Master Class BOT v2 -> Anota aqui las preguntas o dudas que tengas -> Pronto estare publicando fecha y hora para la masterclass - -1.- Si necesito correr dos bots al mismo tiempo ¿donde puedo cambiar el puerto? - -2.- Si necesito agregar o modificar funciones del bot, ¿como puedo hacerlo? - -3.- Si quiero mi bot con otra base de datos diferente a MySQL ¿como lo puedo hacer? - -4.- Quiero conectarme a tal o cual API con JSON/XML/etc, ¿se puede hacer? - -5.- ¿Como integrar listas? - -6.- Preguntas y respuestas con el Bot - -7.- Guardar conversaciones en Excel. - -8.- ¿Puedo usar 2 o mas sesiones (códigos QR) al mismo tiempo? - -9.- ¿Puede ser que al usar el provider bailey, al leer el qr.png, que sea desde una url en el navegador, y no desde visual studio? Gracias - -10.- ¿Cómo tomo los datos que me envían en un mensaje para utilizarlo internamente en la búsqueda de datos propios y devolver la respuesta? diff --git a/packages/docs/src/routes/docs/masterclass/index.mdx b/packages/docs/src/routes/docs/masterclass/index.mdx new file mode 100644 index 0000000..97612b4 --- /dev/null +++ b/packages/docs/src/routes/docs/masterclass/index.mdx @@ -0,0 +1,26 @@ +# MasterClass + + + +--- + +### Preguntas de la masterclass + +- Si necesito correr dos bots al mismo tiempo ¿donde puedo cambiar el puerto? +- Si necesito agregar o modificar funciones del bot, ¿como puedo hacerlo? +- Si quiero mi bot con otra base de datos diferente a MySQL ¿como lo puedo hacer? +- Quiero conectarme a tal o cual API con JSON/XML/etc, ¿se puede hacer? +- ¿Como integrar listas? +- Preguntas y respuestas con el Bot +- Guardar conversaciones en Excel. +- ¿Puedo usar 2 o mas sesiones (códigos QR) al mismo tiempo? +- ¿Puede ser que al usar el provider bailey, al leer el qr.png, que sea desde una url en el navegador, y no desde visual studio? Gracias +- ¿Cómo tomo los datos que me envían en un mensaje para utilizarlo internamente en la búsqueda de datos propios y devolver la respuesta? diff --git a/packages/docs/src/routes/docs/migration/index.mdx b/packages/docs/src/routes/docs/migration/index.mdx index 84a1658..2e09f97 100644 --- a/packages/docs/src/routes/docs/migration/index.mdx +++ b/packages/docs/src/routes/docs/migration/index.mdx @@ -49,11 +49,7 @@ En la **_versión (legacy)_** se implementas los flujos de esta manera, en dos a "title": "¿Que te interesa ver?", "message": "Abajo unos botons", "footer": "", - "buttons": [ - { "body": "Telefonos" }, - { "body": "Computadoras" }, - { "body": "Otros" } - ] + "buttons": [{ "body": "Telefonos" }, { "body": "Computadoras" }, { "body": "Otros" }] } }, "catalogo": { @@ -70,51 +66,28 @@ En esta versión es mucho más sencillo, abajo encontrarás un ejemplo del mismo ```js //app.js -const { - createBot, - createProvider, - createFlow, - addKeyword, - addChild, -} = require('@bot-whatsapp/bot') +const { createBot, createProvider, createFlow, addKeyword, addChild } = require('@bot-whatsapp/bot') const BaileysProvider = require('@bot-whatsapp/provider/baileys') //Provider const MockAdapter = require('@bot-whatsapp/database/mock') //Base de datos /** * Declarando flujos principales. */ -const flowHola = addKeyword(['hola', 'ola', 'alo']).addAnswer( - 'Bienvenido a tu tienda online!' -) +const flowHola = addKeyword(['hola', 'ola', 'alo']).addAnswer('Bienvenido a tu tienda online!') -const flowAdios = addKeyword(['adios', 'bye']) - .addAnswer('Que te vaya bien!!') - .addAnswer('Hasta luego!') +const flowAdios = addKeyword(['adios', 'bye']).addAnswer('Que te vaya bien!!').addAnswer('Hasta luego!') -const flowProductos = addKeyword(['productos', 'info']).addAnswer( - 'Te envio una imagen', - { - buttons: [ - { body: 'Telefonos' }, - { body: 'Computadoras' }, - { body: 'Otros' }, - ], - } -) +const flowProductos = addKeyword(['productos', 'info']).addAnswer('Te envio una imagen', { + buttons: [{ body: 'Telefonos' }, { body: 'Computadoras' }, { body: 'Otros' }], +}) -const flowCatalogo = addKeyword(['imagen', 'foto']).addAnswer( - 'Te envio una imagen', - { media: 'https://media2.giphy.com/media/VQJu0IeULuAmCwf5SL/giphy.gif' } -) +const flowCatalogo = addKeyword(['imagen', 'foto']).addAnswer('Te envio una imagen', { + media: 'https://media2.giphy.com/media/VQJu0IeULuAmCwf5SL/giphy.gif', +}) const main = async () => { const adapterDB = new MockAdapter() - const adapterFlow = createFlow([ - flowHola, - flowAdios, - flowProductos, - flowCatalogo, - ]) //Se crean los flujos. + const adapterFlow = createFlow([flowHola, flowAdios, flowProductos, flowCatalogo]) //Se crean los flujos. const adapterProvider = createProvider(BaileysProvider) createBot({ flow: adapterFlow, diff --git a/packages/docs/src/routes/docs/overview/index.mdx b/packages/docs/src/routes/docs/overview/index.mdx index 79830ff..7d2e57c 100644 --- a/packages/docs/src/routes/docs/overview/index.mdx +++ b/packages/docs/src/routes/docs/overview/index.mdx @@ -49,17 +49,15 @@ Qwik is a new kind of web framework that can deliver instant loading web applica

    Instant-on

    - Unlike other frameworks, Qwik is resumable which means Qwik - applications require 0 hydration. This allows Qwik apps to have - instant-on interactivity, regardless of size or complexity + Unlike other frameworks, Qwik is resumable which means Qwik applications require 0 hydration. This allows + Qwik apps to have instant-on interactivity, regardless of size or complexity

    Optimized for speed

    - Qwik has unprecedented performance, offering sub-second full page - loads even on mobile devices. Qwik achieves this by delivering pure - HTML, and incrementally loading JS only as-needed. + Qwik has unprecedented performance, offering sub-second full page loads even on mobile devices. Qwik + achieves this by delivering pure HTML, and incrementally loading JS only as-needed.

    diff --git a/packages/docs/src/routes/docs/providers/index.mdx b/packages/docs/src/routes/docs/providers/index.mdx index 285e5f5..e534576 100644 --- a/packages/docs/src/routes/docs/providers/index.mdx +++ b/packages/docs/src/routes/docs/providers/index.mdx @@ -1,14 +1,13 @@ import Alert from '../../../components/widgets/Alert' import Navigation from '../../../components/widgets/Navigation' -# Provider (Proveedor) +# Proveedores - ⚡ Dependiendo del tipo de proveedor que utlices puede que necesites pasar - algunas configuracion adicional como **token, api, etc.** para esos casos te - recomendamos guiarte de los - **[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** - o si gustas puedes editar esta documentación para ir agregando más info + ⚡ Dependiendo del tipo de proveedor que utlices puede que necesites pasar algunas configuracion adicional como + **token, api, etc.** para esos casos te recomendamos guiarte de los + **[starters](https://github.com/codigoencasa/bot-whatsapp/tree/dev/starters/apps)** o si gustas puedes editar esta + documentación para ir agregando más info Es la pieza que conectara tu flujo con Whatsapp. En este chatbot tenemos varios proveedores disponibles la mayoria gratis pero tambien tenemos integracion la api oficial de whatsapp o twilio @@ -33,9 +32,21 @@ Los proveedores disponibles hasta el momento son los siguientes: --- +### Twilio: Configuración + +Estamos trabajando en el apartado de la documentación lo más claro posible. Puedes encontrar los [detalles aquí](/docs/providers/twilio) + +--- + +### Meta: Configuración + +Estamos trabajando en el apartado de la documentación lo más claro posible. Puedes encontrar los [detalles aquí](/docs/providers/meta) + +--- + diff --git a/packages/docs/src/routes/docs/providers/meta/index.mdx b/packages/docs/src/routes/docs/providers/meta/index.mdx new file mode 100644 index 0000000..d0f1d6f --- /dev/null +++ b/packages/docs/src/routes/docs/providers/meta/index.mdx @@ -0,0 +1,156 @@ +import Alert from '../../../../components/widgets/Alert' +import Navigation from '../../../../components/widgets/Navigation' + +# Meta + +La Plataforma de WhatsApp Business permite a medianas y grandes empresas comunicarse con sus clientes a gran escala. +Puedes iniciar conversaciones con clientes en apenas unos minutos, enviarles notificaciones de atención al cliente o actualizaciones de compras, ofrecerles un nivel de servicio personalizado y +prestarles ayuda a través del canal que ellos prefieran. + +### Requerimientos + +- Registrar una cuenta de [facebook developers](https://developers.facebook.com/apps) + +--- + +### Requerimientos + +Debes crear una aplicación nueva. Para ello haz clic en el botón **Crear aplicación** + +![](https://i.imgur.com/DKgjwj9.png) + +--- + +En esta pantalla debes de seleccionar **Empresa o Business** + +![](https://i.imgur.com/tapmpMk.png) + +--- + +En el siguiente paso debes escribir un nombre al tu aplicación también colocar un email de contacto y seleccionar la aplicación de Test Business + +![](https://i.imgur.com/cfHLJTJ.png) + +--- + +Ahora dirígete a la sección de Ajustes de WhatsApp. + +![](https://i.imgur.com/37gMMM5.png) + +--- + +Está la parte donde encontraras el token acceso y también puedes ver el número **desde donde** se envía los mensajes de WhatsApp +y **a donde** se va a enviar. +Como estás en la versión de prueba, deberás dar de alta los números a los que quieres enviar. + +![](https://i.imgur.com/bkSAZn2.png) + +--- + +En esta misma página puedes encontrar el apartado de **Webhook** que pronto necesitaremos usar + +![](https://i.imgur.com/L3PRcj7.png) + +--- + +En el **archivo principal** del bot donde estás implementando la función del adaptador de meta vas a colocar los siguientes datos: + +- **numberId:** Lo puedes encontrar en la pagina anterior +- **jwtToken:** Lo puedes encontrar en la pagina anterior +- **verifyToken:** Puedes escribir lo que quieras es como una palabra clave + +En el ejemplo de abajo puedes ver como una sugerencia de como puede ser utilizando variables de entorno + +```js +const main = async () => { + const adapterDB = new MockAdapter() + const adapterFlow = createFlow([flowPrincipal]) + + const adapterProvider = createProvider(MetaProvider, { + jwtToken: process.env.JWTOKEN, //EAARBW3ZBGU0UBAACDjtQIzI8JuEa............. + numberId: process.env.NUMBER_ID, //103975305758520 + verifyToken: process.env.VERIFY_TOKEN, //LO_QUE_SEA + }) + + createBot({ + flow: adapterFlow, + provider: adapterProvider, + database: adapterDB, + }) +} + +main() +``` + +--- + +Luego de ejecutar el bot encontraras un mensaje en la consola similar al siguiente. +Donde podrás encontrar la URL para tu **webhook** + +```shell +$ npm start + +> bot-whatsapp-base-meta-memory@1.0.0 start +> node app.js + + +[meta]: Agregar esta url "WHEN A MESSAGE COMES IN" +[meta]: POST http://localhost:3000/webhook +[meta]: Más información en la documentacion +``` + +--- + +## ¿Ahora que hago? + +Podrás observar que el bot inicia un servicio HTTP (endpoint) que debe estar en un servidor en linea para que puedas conectarlo con Meta. + +--- + +### Opción 1: + +Puedes hacer pruebas en local atrevés de un servidor proxy tunnel.  +Descarga **[ngork](https://ngrok.com/download)** es una herramienta gratuita que nos ayudara con esto. + +![](https://i.imgur.com/TjjBtRh.png) + +--- + +Esto genera una URL en línea que podemos usar en la parte de **WebHook** de Meta + +![](https://i.imgur.com/NXHMDsf.png) + +--- + +![](https://i.imgur.com/tpov3D1.png) + +--- + +![](https://i.imgur.com/haRGylR.png) + +--- + +![](https://i.imgur.com/cMaIzeC.png) + +--- + +### Opción 2: + +Si ya tienes desplegado tu bot en un servidor tienes que obtener la IP publica o subdominio que te proporcionaron. **Ejemplo** si estas usando +[Railway](https://railway.app/) puedes ir a la seccion de ajustes y generar un subdominio. Ya tendriamos el **WebHook** +`https://base-twilio-memory-production.up.railway.app/twilio-hook` + +![](https://i.imgur.com/Yg2BYqB.png) + +--- + +![](https://i.imgur.com/dIbyEwp.png) + +--- + + diff --git a/packages/docs/src/routes/docs/providers/twilio/index.mdx b/packages/docs/src/routes/docs/providers/twilio/index.mdx new file mode 100644 index 0000000..37fc6dd --- /dev/null +++ b/packages/docs/src/routes/docs/providers/twilio/index.mdx @@ -0,0 +1,146 @@ +import Alert from '../../../../components/widgets/Alert' +import Navigation from '../../../../components/widgets/Navigation' + +# Twilio + +Twilio es una plataforma de desarrollo que permite a los desarrolladores construir aplicaciones de comunicación en la nube y sistemas web. Las API de comunicaciones de Twilio permiten a las empresas proporcionar la experiencia de comunicación adecuada para sus clientes dentro de la web y las aplicaciones móviles. Al usar las API de Twilio, los desarrolladores pueden agregar rápidamente esta funcionalidad a una aplicación, como mensajes de voz, videollamadas, mensajes de texto y más. + +Twilio te proporciona una cuenta **Sandbox** para que puedas probar gratuitamente el servicio + +### Requerimientos + +- Registrar una cuenta de [twilio](https://www.twilio.com/try-twilio) + +--- + +### Requerimientos + +Debemos aceptar los términos y condiciones y luego activar la cuenta sandbox + +![](https://i.imgur.com/53RyhZD.png) + +--- + +Observamos que ahora tenemos un número de WhatsApp y una frase. El número proporcionado es un **número de pruebas** que te ofrece Twilio, luego que actives un plan de pago puedes comprar un número para tu uso.  +Guarda ese número como un contacto de WhatsApp en tu móvil y después envíale el mensaje que te asignan. En el ejemplo de la pantalla sale **join score-state** + +![](https://i.imgur.com/Eb69grk.png) + +--- + +Luego Twilio te responde con un mensaje confirmando la verificación de la conexión. **Este paso solo es necesario cuando estás en modo Sandbox.** + +![](https://i.imgur.com/cTWNxBF.png) + +--- + +Ahora dirígete a la sección de Ajustes de WhatsApp ubicada en la consola de Twilio. + +![](https://i.imgur.com/UQG8LkW.png) + +--- + +En esta sección puedes configurar los **Webhook** que conectaran con el chatbot. + +**¿No sabes cuál es tu link?** continúa leyendo esta guía más adelante entenderás + +![](https://i.imgur.com/WovZGPm.png) + +--- + +También necesitarás el **Account SID y Auth Token** estos datos los consigues [console.twilio.com](https://console.twilio.com). + +**¿Que hago con estos datos?** guardalos o tenlos ubicados porque los necesitaremos en las siguientes pantallas + +![](https://i.imgur.com/RxlxEEm.png) + +--- + +En el **archivo principal** del bot donde estás implementando la función del adaptador de twilio vas a colocar los siguientes datos: + +- **ACC_SID:** Lo encontraras en console.twilio puedes ver la pantalla anterior +- **ACC_TOKEN:** Lo encontraras en console.twilio puedes ver la pantalla anterior +- **ACC_VENDOR:** Es el numero de whatsapp (si ya tienes el plan de pago de Twilio usa el numero que compraste), si aun estas en modo + sandbox utliza el numero proporcionado en el paso numero 2 + +En el ejemplo de abajo puedes ver como una sugerencia de como puede ser utilizando variables de entorno + +```js +const main = async () => { + const adapterDB = new MockAdapter() + const adapterFlow = createFlow([flowPrincipal]) + + const adapterProvider = createProvider(TwilioProvider, { + accountSid: process.env.ACC_SID, //AC4695aa720b4d700a*************** + authToken: process.env.ACC_TOKEN, //3f6fae09f7a1c3534*************** + vendorNumber: process.env.ACC_VENDOR, //+14155238886 + }) + + createBot({ + flow: adapterFlow, + provider: adapterProvider, + database: adapterDB, + }) +} +``` + +--- + +Luego de ejecutar el bot encontraras un mensaje en la consola similar al siguiente. +Donde podrás encontrar la URL para tu **webhook** + +```shell +$ npm start + +> bot-whatsapp-base-twilio-memory@1.0.0 start +> node app.js + + +[Twilio]: Agregar esta url "WHEN A MESSAGE COMES IN" +[Twilio]: POST http://localhost:3000/twilio-hook +[Twilio]: Más información en la documentacion +``` + +--- + +## ¿Ahora que hago? + +Podrás observar que el bot inicia un servicio HTTP (endpoint) que debe estar en un servidor en linea para que puedas conectarlo con Twilio. + +--- + +### Opción 1: + +Puedes hacer pruebas en local atrevés de un servidor proxy tunnel.  +Descarga **[ngork](https://ngrok.com/download)** es una herramienta gratuita que nos ayudara con esto. + +![](https://i.imgur.com/TjjBtRh.png) + +--- + +Esto genera una URL en línea que podemos usar en la parte de **WebHook** de Twilio + +![](https://i.imgur.com/S9zXROt.png) + +--- + +### Opción 2: + +Si ya tienes desplegado tu bot en un servidor tienes que obtener la IP publica o subdominio que te proporcionaron. **Ejemplo** si estas usando +[Railway](https://railway.app/) puedes ir a la seccion de ajustes y generar un subdominio. Ya tendriamos el **WebHook** +`https://base-twilio-memory-production.up.railway.app/twilio-hook` + +![](https://i.imgur.com/Yg2BYqB.png) + +--- + +![](https://i.imgur.com/dpv6RTR.png) + +--- + + diff --git a/packages/docs/src/routes/docs/requirements/index.mdx b/packages/docs/src/routes/docs/requirements/index.mdx index 03718d2..8f1ef96 100644 --- a/packages/docs/src/routes/docs/requirements/index.mdx +++ b/packages/docs/src/routes/docs/requirements/index.mdx @@ -4,14 +4,8 @@ import Navigation from '../../../components/widgets/Navigation' A continuación se describen los puntos técnicos que debes de tener en cuenta antes de trabajar con esta herramienta -- Node v16 o superior **[descargar node](https://nodejs.org/es/download/)** - ---- - -## ¿Como instalar Node? - -- **Windows**: [Ver video](https://youtu.be/xRXHQlqA3Ak?t=376). Necesita ayuda para instalar Node en Windows. A continuación te comparto un video en el minuto exacto donde explico como instalar. -- **Ubuntu**: Te comparto un recurso de **[Digital Ocean](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04-es)** donde explica como instalar node en Ubuntu. +- Node v16 o superior - **[descargar node](https://nodejs.org/es/download/)** +- Git - **[descargar Git](https://git-scm.com/download/win)** --- @@ -26,6 +20,34 @@ v18.12.1 --- +## ¿Como instalar Node? + +- **Windows**: [Ver video](https://youtu.be/xRXHQlqA3Ak?t=376). Si necesitas ayuda para instalar Node en Windows. A continuación te comparto un video en el minuto exacto donde explico como instalar. +- **Ubuntu**: Te comparto un recurso de **[Digital Ocean](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04-es)** donde explica como instalar node en Ubuntu. + +--- + +## ¿Como saber que tengo Git? + +Solo debes ejecutar el siguiente comando y esperar que te mande la versión que tienes instalada, si te manda un error de comando no reconocido es que no lo tienes instalado. + +```shell +$ git -v +git +``` + +--- + +## ¿Como instalar Git? + +- Solo es necesario instalar Git si estás usando **Windows**, ya que Mac y Linux lo traen preinstalado. +- Lo puedes descargar desde esta **[liga](https://git-scm.com/download/win)** . +- Descarga la versión necesaria para tu sistema operativo (32-bit o 64-bit). +- Una vez terminada la descarga, ejecuta el archivo descargado y dale "Siguiente" en todas las pantallas. +- Haz clic en el botón de "Finalizar". + +--- + { - const CHECK_GITHUB_TOKEN = - (platform as any)?.['GITHUB_TOKEN'] ?? GITHUB_TOKEN - console.log(`[🚩 platform]: `, GITHUB_TOKEN) - const data = await fetchGithub(CHECK_GITHUB_TOKEN) - return data + const CHECK_GITHUB_TOKEN = (platform as any)?.['GITHUB_TOKEN'] ?? GITHUB_TOKEN + const dataGithub = await fetchGithub(CHECK_GITHUB_TOKEN) + const dataOpenCollective = await fetchOpenCollective() + return { + dataGithub, + dataOpenCollective, + } } export default component$(() => { @@ -27,9 +32,16 @@ export default component$(() => { } + onResolved={(data: any) => { + return ( + <> + + + + + ) + }} > - ) }) @@ -39,8 +51,7 @@ export const head: DocumentHead = { meta: [ { name: 'description', - content: - 'Crear chatbot WhatsApp en minutos — Servicio de chatbot para whatspp gratis proyecto OpenSource', + content: 'Crear chatbot WhatsApp en minutos — Servicio de chatbot para whatspp gratis proyecto OpenSource', }, ], } diff --git a/packages/docs/src/services/github.ts b/packages/docs/src/services/github.ts index 05d2b8f..6ef1186 100644 --- a/packages/docs/src/services/github.ts +++ b/packages/docs/src/services/github.ts @@ -3,17 +3,17 @@ * @returns */ export const fetchGithub = async (token: string) => { - const data = await fetch( - `https://api.github.com/repos/codigoencasa/bot-whatsapp/contributors`, - { - method: 'GET', - headers: { - Accept: 'application/vnd.github+json', - 'X-GitHub-Api-Version': '2022-11-28', - Authorization: `Bearer ${token}`, - }, - } - ) - const listUsers = data.json() - return listUsers + const data = await fetch(`https://api.github.com/repos/codigoencasa/bot-whatsapp/contributors`, { + method: 'GET', + headers: { + Accept: 'application/vnd.github+json', + 'X-GitHub-Api-Version': '2022-11-28', + Authorization: `Bearer ${token}`, + }, + }) + const listUsers = await data.json() + return listUsers.map((u: any) => ({ + ...u, + avatar_url: `${u.avatar_url}&s=80`, + })) } diff --git a/packages/docs/src/services/opencollective.ts b/packages/docs/src/services/opencollective.ts new file mode 100644 index 0000000..55e7e75 --- /dev/null +++ b/packages/docs/src/services/opencollective.ts @@ -0,0 +1,16 @@ +/** + * GET API from OpenCollective + * @returns + */ +export const fetchOpenCollective = async () => { + const data = await fetch(`https://opencollective.com/bot-whatsapp/members/users.json?limit=22&offset=0`, { + method: 'GET', + }) + const listUsers = await data.json() + return listUsers.map((u: any) => ({ + html_url: u.profile, + avatar_url: u.image ?? 'https://i.imgur.com/HhiYKwN.png', + login: u.name, + id: u.MemberId, + })) +} diff --git a/packages/docs/tailwind.config.js b/packages/docs/tailwind.config.js index 1614b2e..a92fde1 100644 --- a/packages/docs/tailwind.config.js +++ b/packages/docs/tailwind.config.js @@ -76,9 +76,7 @@ module.exports = { a: { fontWeight: theme('fontWeight.semibold'), textDecoration: 'none', - borderBottom: `1px solid ${theme( - 'colors.sky.300' - )}`, + borderBottom: `1px solid ${theme('colors.sky.300')}`, }, 'a:hover': { borderBottomWidth: '2px', diff --git a/packages/portal/.eslintignore b/packages/portal/.eslintignore new file mode 100644 index 0000000..73d2683 --- /dev/null +++ b/packages/portal/.eslintignore @@ -0,0 +1,33 @@ +**/*.log +**/.DS_Store +*. +.vscode/settings.json +.history +.yarn +bazel-* +bazel-bin +bazel-out +bazel-qwik +bazel-testlogs +dist +dist-dev +lib +lib-types +etc +external +node_modules +temp +tsc-out +tsdoc-metadata.json +target +output +rollup.config.js +build +.cache +.vscode +.rollup.cache +dist +tsconfig.tsbuildinfo +vite.config.ts +*.spec.tsx +*.spec.ts diff --git a/packages/portal/.eslintrc.cjs b/packages/portal/.eslintrc.cjs new file mode 100644 index 0000000..039b99a --- /dev/null +++ b/packages/portal/.eslintrc.cjs @@ -0,0 +1,41 @@ +module.exports = { + root: true, + env: { + browser: true, + es2021: true, + node: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:qwik/recommended', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + ecmaVersion: 2021, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + plugins: ['@typescript-eslint'], + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-this-alias': 'off', + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + 'prefer-spread': 'off', + 'no-case-declarations': 'off', + 'no-console': 'off', + '@typescript-eslint/no-unused-vars': ['error'], + '@typescript-eslint/consistent-type-imports': 'warn', + }, +}; diff --git a/packages/portal/.gitignore b/packages/portal/.gitignore new file mode 100644 index 0000000..e95b829 --- /dev/null +++ b/packages/portal/.gitignore @@ -0,0 +1,38 @@ +# Build +/dist +/lib +/lib-types +/server + +# Development +node_modules + +# Cache +.cache +.mf +.vscode +.rollup.cache +tsconfig.tsbuildinfo + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Editor +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Yarn +.yarn/* +!.yarn/releases diff --git a/packages/portal/.prettierignore b/packages/portal/.prettierignore new file mode 100644 index 0000000..1592248 --- /dev/null +++ b/packages/portal/.prettierignore @@ -0,0 +1,6 @@ +# Files Prettier should not format +**/*.log +**/.DS_Store +*. +dist +node_modules diff --git a/packages/portal/.vscode/extensions.json b/packages/portal/.vscode/extensions.json new file mode 100644 index 0000000..1396082 --- /dev/null +++ b/packages/portal/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["dbaeumer.vscode-eslint", "unifiedjs.vscode-mdx"], + "unwantedRecommendations": [] +} diff --git a/packages/portal/.vscode/qwik-city.code-snippets b/packages/portal/.vscode/qwik-city.code-snippets new file mode 100644 index 0000000..361ddf7 --- /dev/null +++ b/packages/portal/.vscode/qwik-city.code-snippets @@ -0,0 +1,28 @@ +{ + "onGet": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:onGet", + "description": "onGet function for a route index", + "body": ["export const onGet: RequestHandler = (request) => {", " $0", "};"] + }, + "onGet (typed)": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:onGet typed", + "description": "onGet function for a route index", + "body": [ + "export interface ${1:PageData} {", + " $2", + "};", + "", + "export const onGet: RequestHandler<$1> = (request) => {", + " $4", + "};" + ] + }, + "useEndpoint": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:useEndpoint", + "description": "useEndpoint declaration", + "body": "const $1 = useEndpoint();" + } +} diff --git a/packages/portal/.vscode/qwik.code-snippets b/packages/portal/.vscode/qwik.code-snippets new file mode 100644 index 0000000..c2a8a8f --- /dev/null +++ b/packages/portal/.vscode/qwik.code-snippets @@ -0,0 +1,73 @@ +{ + "Qwik component (simple)": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:component", + "description": "Simple Qwik component", + "body": [ + "export const ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}} = component$(() => {", + " return <${2:div}>$4", + "});" + ] + }, + "Qwik component (props)": { + "scope": "typescriptreact", + "prefix": "q:component w/props", + "description": "Qwik component w/ props", + "body": [ + "export interface ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}}Props {", + " $2", + "}", + "", + "export const $1 = component$<$1Props>((props) => {", + " const ${2:count} = useSignal(0);", + " return (", + " <${3:div} on${4:Click}$={(ev) => {$5}}>", + " $6", + " ", + " );", + "});" + ] + }, + "Qwik signal": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:useSignal", + "description": "useSignal() declaration", + "body": ["const ${1:foo} = useSignal($2);", "$0"] + }, + "Qwik store": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:useStore", + "description": "useStore() declaration", + "body": ["const ${1:state} = useStore({", " $2", "});", "$0"] + }, + "$ hook": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:$", + "description": "$() function hook", + "body": ["$(() => {", " $0", "});", ""] + }, + "useClientEffect": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:useClientEffect", + "description": "useClientEffect$() function hook", + "body": ["useClientEffect$(({ track }) => {", " $0", "});", ""] + }, + "useTask": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:useTask", + "description": "useTask$() function hook", + "body": ["useTask$(({ track }) => {", " track(() => $1);", " $0", "});", ""] + }, + "useResource": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:useResource", + "description": "useResource$() declaration", + "body": ["const $1 = useResource$(({ track, cleanup }) => {", " $0", "});", ""] + }, + "useServerMount": { + "scope": "javascriptreact,typescriptreact", + "prefix": "q:useServerMount", + "description": "useServerMount$() function hook", + "body": ["useServerMount$(() => {", " $0", "});", ""] + } +} diff --git a/packages/portal/README.md b/packages/portal/README.md new file mode 100644 index 0000000..0b9a2a9 --- /dev/null +++ b/packages/portal/README.md @@ -0,0 +1,71 @@ +# Qwik City App ⚡️ + +- [Qwik Docs](https://qwik.builder.io/) +- [Discord](https://qwik.builder.io/chat) +- [Qwik GitHub](https://github.com/BuilderIO/qwik) +- [@QwikDev](https://twitter.com/QwikDev) +- [Vite](https://vitejs.dev/) + +--- + +## Project Structure + +This project is using Qwik with [QwikCity](https://qwik.builder.io/qwikcity/overview/). QwikCity is just a extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more. + +Inside your project, you'll see the following directory structure: + +``` +├── public/ +│ └── ... +└── src/ + ├── components/ + │ └── ... + └── routes/ + └── ... +``` + +- `src/routes`: Provides the directory based routing, which can include a hierarchy of `layout.tsx` layout files, and an `index.tsx` file as the page. Additionally, `index.ts` files are endpoints. Please see the [routing docs](https://qwik.builder.io/qwikcity/routing/overview/) for more info. + +- `src/components`: Recommended directory for components. + +- `public`: Any static assets, like images, can be placed in the public directory. Please see the [Vite public directory](https://vitejs.dev/guide/assets.html#the-public-directory) for more info. + +## Add Integrations and deployment + +Use the `npm run qwik add` command to add additional integrations. Some examples of integrations include: Cloudflare, Netlify or Express server, and the [Static Site Generator (SSG)](https://qwik.builder.io/qwikcity/static-site-generation/static-site-config/). + +```shell +npm run qwik add # or `yarn qwik add` +``` + +## Development + +Development mode uses [Vite's development server](https://vitejs.dev/). During development, the `dev` command will server-side render (SSR) the output. + +```shell +npm start # or `yarn start` +``` + +> Note: during dev mode, Vite may request a significant number of `.js` files. This does not represent a Qwik production build. + +## Preview + +The preview command will create a production build of the client modules, a production build of `src/entry.preview.tsx`, and run a local server. The preview server is only for convenience to locally preview a production build, and it should not be used as a production server. + +```shell +npm run preview # or `yarn preview` +``` + +## Production + +The production build will generate client and server modules by running both client and server build commands. Additionally, the build command will use Typescript to run a type check on the source code. + +```shell +npm run build # or `yarn build` +``` + +## Static Site Generator (Node.js) + +``` +npm run build.server +``` diff --git a/packages/portal/adaptors/static/vite.config.ts b/packages/portal/adaptors/static/vite.config.ts new file mode 100644 index 0000000..dba968a --- /dev/null +++ b/packages/portal/adaptors/static/vite.config.ts @@ -0,0 +1,19 @@ +import { staticAdaptor } from '@builder.io/qwik-city/adaptors/static/vite' +import { extendConfig } from '@builder.io/qwik-city/vite' +import baseConfig from '../../vite.config' + +export default extendConfig(baseConfig, () => { + return { + build: { + ssr: true, + rollupOptions: { + input: ['@qwik-city-plan'], + }, + }, + plugins: [ + staticAdaptor({ + origin: 'https://bot-whatsapp.netlify.app', + }), + ], + } +}) diff --git a/packages/portal/package.json b/packages/portal/package.json new file mode 100644 index 0000000..c26ab32 --- /dev/null +++ b/packages/portal/package.json @@ -0,0 +1,52 @@ +{ + "name": "@bot-whatsapp/portal", + "version": "0.0.22-alpha.0", + "description": "Portal WEB para escanear QR", + "main": "./lib/portal.http.cjs", + "scripts": { + "build": "qwik build", + "build.client": "vite build", + "build.preview": "vite build --ssr src/entry.preview.tsx", + "build.server": "vite build -c adaptors/static/vite.config.ts", + "build.types": "tsc --incremental --noEmit", + "deploy": "echo 'Run \"npm run qwik add\" to install a server adaptor'", + "dev": "vite --mode ssr", + "dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force", + "fmt": "prettier --write .", + "fmt.check": "prettier --check .", + "lint": "eslint \"src/**/*.ts*\"", + "preview": "qwik build preview && vite preview --open", + "start": "vite --open --mode ssr", + "qwik": "qwik" + }, + "files": [ + "./dist/*", + "./lib/portal.http.cjs" + ], + "devDependencies": { + "@builder.io/qwik": "0.16.2", + "@builder.io/qwik-city": "0.0.128", + "@types/eslint": "8.4.10", + "@types/node": "^18.11.18", + "@types/node-fetch": "latest", + "@typescript-eslint/eslint-plugin": "5.48.0", + "@typescript-eslint/parser": "5.48.0", + "eslint": "8.31.0", + "eslint-plugin-qwik": "0.16.2", + "node-fetch": "3.3.0", + "prettier": "2.8.1", + "typescript": "4.9.4", + "undici": "5.14.0", + "vite": "4.0.3", + "vite-tsconfig-paths": "3.5.0" + }, + "dependencies": { + "kleur": "^4.1.5", + "polka": "^0.5.2", + "serve-static": "^1.13.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/portal" + } +} diff --git a/packages/portal/portal.http.js b/packages/portal/portal.http.js new file mode 100644 index 0000000..122f2de --- /dev/null +++ b/packages/portal/portal.http.js @@ -0,0 +1,56 @@ +const { join } = require('path') +const { createReadStream, existsSync } = require('fs') +const { bgYellow, cyan, yellow } = require('kleur') +const polka = require('polka') + +const HTTP_PORT = process.env.PORT || 3000 +const QR_FILE = process.env.QR_FILE ?? 'bot' +const PUBLIC_URL = process.env.PUBLIC_URL ?? process.env.RAILWAY_STATIC_URL ?? 'http://localhost' + +const dir = [join(__dirname, 'dist'), join(__dirname, '..', 'dist')].find((i) => existsSync(i)) +const serve = require('serve-static')(dir) + +/** + * Iniciamos Portal WEB para escanear QR + * @param {port:3000, publicSite:'http://mistio.com', qrFile:'qr.png', dir:__dirname} + */ +const start = (args) => { + const injectArgs = { + port: HTTP_PORT, + publicSite: PUBLIC_URL, + name: QR_FILE, + ...args, + } + const { port, publicSite, name } = injectArgs + + const banner = () => { + console.log(``) + console.log(bgYellow(`🚩 ESCANEAR QR 🚩`)) + console.log(cyan(`Existen varias maneras de escanear el QR code`)) + console.log(cyan(`- Tambien puedes visitar `), yellow(`${publicSite}:${port}`)) + console.log(cyan(`- Se ha creado un archivo que finaliza `), yellow('qr.png')) + console.log(``) + } + + polka() + .use(serve) + .get('qr.png', (_, res) => { + const qrSource = [ + join(process.cwd(), `${name}.qr.png`), + join(__dirname, '..', `${name}.qr.png`), + join(__dirname, `${name}.qr.png`), + ].find((i) => existsSync(i)) + + const qrMark = [ + join(__dirname, 'dist', 'water-mark.png'), + join(__dirname, '..', 'dist', 'water-mark.png'), + ].find((i) => existsSync(i)) + const fileStream = createReadStream(qrSource ?? qrMark) + + res.writeHead(200, { 'Content-Type': 'image/png' }) + fileStream.pipe(res) + }) + .listen(port, () => banner()) +} + +module.exports = start diff --git a/packages/portal/public/favicon.svg b/packages/portal/public/favicon.svg new file mode 100644 index 0000000..0ded7c1 --- /dev/null +++ b/packages/portal/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/portal/public/manifest.json b/packages/portal/public/manifest.json new file mode 100644 index 0000000..44825fa --- /dev/null +++ b/packages/portal/public/manifest.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/web-manifest-combined.json", + "name": "qwik-project-name", + "short_name": "Welcome to Qwik", + "start_url": ".", + "display": "standalone", + "background_color": "#fff", + "description": "A Qwik project app." +} diff --git a/packages/portal/public/robots.txt b/packages/portal/public/robots.txt new file mode 100644 index 0000000..e69de29 diff --git a/packages/portal/public/water-mark.png b/packages/portal/public/water-mark.png new file mode 100644 index 0000000..e44ada3 Binary files /dev/null and b/packages/portal/public/water-mark.png differ diff --git a/packages/portal/rollup-portal.config.js b/packages/portal/rollup-portal.config.js new file mode 100644 index 0000000..ee955ff --- /dev/null +++ b/packages/portal/rollup-portal.config.js @@ -0,0 +1,13 @@ +const banner = require('../../config/banner.rollup.json') +const { join } = require('path') +const commonjs = require('@rollup/plugin-commonjs') + +module.exports = { + input: join(__dirname, 'portal.http.js'), + output: { + banner: banner['banner.output'].join(''), + file: join(__dirname, 'lib', 'portal.http.cjs'), + format: 'cjs', + }, + plugins: [commonjs()], +} diff --git a/packages/portal/server/@qwik-city-not-found-paths.js b/packages/portal/server/@qwik-city-not-found-paths.js new file mode 100644 index 0000000..473076c --- /dev/null +++ b/packages/portal/server/@qwik-city-not-found-paths.js @@ -0,0 +1,15 @@ +const notFounds = [ + [ + '/', + '\n\n \n \n \n 404 Resource Not Found\n \n \n \n \n

    404 Resource Not Found

    \n \n\n', + ], +] +function getNotFound(p) { + for (const r of notFounds) { + if (p.startsWith(r[0])) { + return r[1] + } + } + return 'Resource Not Found' +} +export { getNotFound } diff --git a/packages/portal/server/@qwik-city-plan.mjs b/packages/portal/server/@qwik-city-plan.mjs new file mode 100644 index 0000000..4ae6623 --- /dev/null +++ b/packages/portal/server/@qwik-city-plan.mjs @@ -0,0 +1,5 @@ +import{componentQrl as o,inlinedQrl as i,useStylesScopedQrl as n,Slot as s,useStore as c,useClientEffectQrl as r,_noopQrl as l}from"@builder.io/qwik";import{jsxs as t,jsx as e,Fragment as d}from"@builder.io/qwik/jsx-runtime";const h=`.logo{display:flex;gap:.5rem;align-items:center;align-content:center;color:#1a1a1a}.logo h1{font-size:1.5rem;margin:0} +`,g=o(i(()=>(n(i(h,"s_a8xI1ebvPf4")),t("div",{class:"logo",children:[e("img",{width:"40",height:"40",src:"data:image/png;base64,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",alt:""}),e("h1",{children:"Chatbot"})]})),"s_PjfpaY6FOTY")),P=`header{display:flex;background:white}header .logo a{display:inline-block;padding:10px 10px 7px 20px;text-decoration:none}header ul{margin:0;padding:3px 10px 0 0;list-style:none;flex:1;text-align:right}header li{display:inline-block;margin:0;padding:0}header li a{display:inline-block;padding:15px 10px;text-decoration:none;color:#1a1a1a;font-weight:500}header li a:hover{text-decoration:underline} +`,p=o(i(()=>(n(i(P,"s_DUMnr602HUE")),t("header",{children:[e("div",{class:"logo",children:e("a",{href:"https://github.com/codigoencasa/bot-whatsapp",target:"_blank",title:"qwik",children:e(g,{},"k2_0")})}),t("ul",{children:[e("li",{children:e("a",{href:"https://github.com/codigoencasa/bot-whatsapp",target:"_blank",children:"Docs"})}),e("li",{children:e("a",{href:"https://github.com/codigoencasa/bot-whatsapp/tree/main/starters/apps",target:"_blank",children:"Examples"})}),e("li",{children:e("a",{href:"https://www.youtube.com/watch?v=1u0TTbjK5bo&list=PL_WGMLcL4jzViIlmyDZPnhAdkc8RXGkFh",target:"_blank",children:"Tutorials"})})]})]})),"s_rUlpxt0UkP8")),A=o(i(()=>t(d,{children:[t("main",{children:[e(p,{},"3N_0"),e("section",{children:e(s,{},"3N_1")})]}),e("footer",{children:t("a",{href:"https://qwik.builder.io/",target:"_blank",children:["Made with ",e("strong",{children:"Qwik"})]})})]},"3N_2"),"s_dyI7p8LxEE8")),u=Object.freeze(Object.defineProperty({__proto__:null,default:A},Symbol.toStringTag,{value:"Module"})),w=`div img{display:block;background-color:#f0f0f0;width:350px;height:350px;object-fit:contain;border-radius:10px}div{display:flex;justify-content:center} +`,j=o(i(()=>{n(i(w,"s_C1zLAObj2Tc"));const a=c({count:0});return r(l("s_9iPc0ZNOdxU",[a])),e("div",{children:e("img",{width:350,height:350,src:"qr.png?time="+a.count,alt:"QR"})})},"s_rghJxmpKVus")),m=`.page{display:grid;padding:1rem;gap:2rem}.page .qr-section.links{display:flex;gap:1rem;flex-wrap:wrap}.page .btn-link{background:white;box-shadow:#00000029 0 10px 36px,#0000000f 0 0 0 1px;padding:10px;border-radius:5px;font-weight:600;text-decoration:none;color:#1a1a1a;border:solid 1px #afafaf} +`,y=o(i(()=>(n(i(m,"s_A20fm0z03Yw")),t("div",{class:"page",children:[e("div",{class:"qr-section",children:e(j,{},"UZ_0")}),t("div",{class:"qr-section intructions",children:[e("h1",{children:"Whatsapp QR"}),t("p",{children:["Con esta libreria, puedes configurar respuestas automatizadas para preguntas frecuentes, recibir y responder mensajes de manera automatizada, y hacer un seguimiento de las interacciones con los clientes. ",e("br",{})," Además, nuestro Chatbot se integra fácilmente con otros sistemas y herramientas que ya esté utilizando en su negocio."]}),t("div",{class:"qr-section links",children:[e("a",{class:"btn-link ",target:"_blank",href:"https://bot-whatsapp.netlify.app/",children:"Ver documentación"}),e("a",{class:"btn-link ",target:"_blank",href:"https://www.youtube.com/watch?v=1u0TTbjK5bo&list=PL_WGMLcL4jzViIlmyDZPnhAdkc8RXGkFh",children:"Ver videos"}),e("a",{class:"btn-link ",target:"_blank",href:"https://opencollective.com/bot-whatsapp",children:"Comprar café"})]})]})]})),"s_4uCQXYaqoXY")),f={title:"🤖 Crear chatbot WhatsApp en minutos",meta:[{name:"description",content:"🤖 Crear chatbot WhatsApp en minutos"}]},b=Object.freeze(Object.defineProperty({__proto__:null,default:y,head:f},Symbol.toStringTag,{value:"Module"})),x=()=>u,z=[[/^\/$/,[x,()=>b],void 0,"/",["q-eaa41d1e.js","q-9cbd2586.js"]]],B=[],I=!0,E="/",L=!0,v={routes:z,menus:B,trailingSlash:I,basePathname:E,cacheModules:L};export{E as basePathname,L as cacheModules,v as default,B as menus,z as routes,I as trailingSlash}; diff --git a/packages/portal/server/@qwik-city-static-paths.js b/packages/portal/server/@qwik-city-static-paths.js new file mode 100644 index 0000000..1835f6b --- /dev/null +++ b/packages/portal/server/@qwik-city-static-paths.js @@ -0,0 +1,22 @@ +const staticPaths = new Set([ + '/favicon.svg', + '/manifest.json', + '/q-manifest.json', + '/robots.txt', + '/service-worker.js', + '/sitemap.xml', + '/water-mark.png', +]) +function isStaticPath(p) { + if (p.startsWith('/build/')) { + return true + } + if (p.startsWith('/assets/')) { + return true + } + if (staticPaths.has(p)) { + return true + } + return false +} +export { isStaticPath } diff --git a/packages/portal/server/entry.ssr.js b/packages/portal/server/entry.ssr.js new file mode 100644 index 0000000..eed19e1 --- /dev/null +++ b/packages/portal/server/entry.ssr.js @@ -0,0 +1 @@ +export * from './entry.ssr.mjs' diff --git a/packages/portal/server/entry.ssr.mjs b/packages/portal/server/entry.ssr.mjs new file mode 100644 index 0000000..c4fbac2 --- /dev/null +++ b/packages/portal/server/entry.ssr.mjs @@ -0,0 +1,2 @@ +import{jsx as r,jsxs as z,Fragment as qe}from"@builder.io/qwik/jsx-runtime";import{renderToStream as je}from"@builder.io/qwik/server";import{componentQrl as j,inlinedQrl as p,useContext as E,jsx as be,SkipRender as we,useEnvData as te,useStore as m,useContextProvider as u,useWatchQrl as ke,useLexicalScope as ne,getLocale as ve,noSerialize as se,Slot as U,useOnDocument as Ce,createContext as k,withLocale as H,_wrapSignal as P,_IMMUTABLE as W,useStylesQrl as Le}from"@builder.io/qwik";const Ne={symbols:{s_kzjavhDI3L0:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"Link_component_a_onClick",canonicalFilename:"s_kzjavhdi3l0",hash:"kzjavhDI3L0",ctxKind:"event",ctxName:"onClick$",captures:!0,parent:"s_8gdLBszqbaM"},s_yiXwCC0m3jY:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"Link_component_a_onMouseOver",canonicalFilename:"s_yixwcc0m3jy",hash:"yiXwCC0m3jY",ctxKind:"event",ctxName:"onMouseOver$",captures:!1,parent:"s_8gdLBszqbaM"},s_EpaZ5qQ4Lg4:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"Link_component_a_onQVisible",canonicalFilename:"s_epaz5qq4lg4",hash:"EpaZ5qQ4Lg4",ctxKind:"event",ctxName:"onQVisible$",captures:!1,parent:"s_8gdLBszqbaM"},s_9iPc0ZNOdxU:{origin:"components/qr/qr.tsx",displayName:"QR_component_useClientEffect",canonicalFilename:"s_9ipc0znodxu",hash:"9iPc0ZNOdxU",ctxKind:"function",ctxName:"useClientEffect$",captures:!0,parent:"s_rghJxmpKVus"},s_4uCQXYaqoXY:{origin:"routes/index.tsx",displayName:"routes_component",canonicalFilename:"s_4ucqxyaqoxy",hash:"4uCQXYaqoXY",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_8gdLBszqbaM:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"Link_component",canonicalFilename:"s_8gdlbszqbam",hash:"8gdLBszqbaM",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_AKetNByE5TM:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"RouterOutlet_component",canonicalFilename:"s_aketnbye5tm",hash:"AKetNByE5TM",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_BVs20TDbvq4:{origin:"root.tsx",displayName:"root_component",canonicalFilename:"s_bvs20tdbvq4",hash:"BVs20TDbvq4",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_D0p4a09aSQg:{origin:"components/router-head/router-head.tsx",displayName:"RouterHead_component",canonicalFilename:"s_d0p4a09asqg",hash:"D0p4a09aSQg",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_PjfpaY6FOTY:{origin:"components/icons/bot.tsx",displayName:"BotLogo_component",canonicalFilename:"s_pjfpay6foty",hash:"PjfpaY6FOTY",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_TxCFOy819ag:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"QwikCityProvider_component",canonicalFilename:"s_txcfoy819ag",hash:"TxCFOy819ag",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_WmYC5H00wtI:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"QwikCityMockProvider_component",canonicalFilename:"s_wmyc5h00wti",hash:"WmYC5H00wtI",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_dyI7p8LxEE8:{origin:"routes/layout.tsx",displayName:"layout_component",canonicalFilename:"s_dyi7p8lxee8",hash:"dyI7p8LxEE8",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_rUlpxt0UkP8:{origin:"components/header/header.tsx",displayName:"header_component",canonicalFilename:"s_rulpxt0ukp8",hash:"rUlpxt0UkP8",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_rghJxmpKVus:{origin:"components/qr/qr.tsx",displayName:"QR_component",canonicalFilename:"s_rghjxmpkvus",hash:"rghJxmpKVus",ctxKind:"function",ctxName:"component$",captures:!1,parent:null},s_xHG1bWPFcUU:{origin:"root.tsx",displayName:"root_component_useStyles",canonicalFilename:"s_xhg1bwpfcuu",hash:"xHG1bWPFcUU",ctxKind:"function",ctxName:"useStyles$",captures:!1,parent:"s_BVs20TDbvq4"},s_A20fm0z03Yw:{origin:"routes/index.tsx",displayName:"routes_component_useStylesScoped",canonicalFilename:"s_a20fm0z03yw",hash:"A20fm0z03Yw",ctxKind:"function",ctxName:"useStylesScoped$",captures:!1,parent:"s_4uCQXYaqoXY"},s_C1zLAObj2Tc:{origin:"components/qr/qr.tsx",displayName:"QR_component_useStylesScoped",canonicalFilename:"s_c1zlaobj2tc",hash:"C1zLAObj2Tc",ctxKind:"function",ctxName:"useStylesScoped$",captures:!1,parent:"s_rghJxmpKVus"},s_DUMnr602HUE:{origin:"components/header/header.tsx",displayName:"header_component_useStylesScoped",canonicalFilename:"s_dumnr602hue",hash:"DUMnr602HUE",ctxKind:"function",ctxName:"useStylesScoped$",captures:!1,parent:"s_rUlpxt0UkP8"},s_a8xI1ebvPf4:{origin:"components/icons/bot.tsx",displayName:"BotLogo_component_useStylesScoped",canonicalFilename:"s_a8xi1ebvpf4",hash:"a8xI1ebvPf4",ctxKind:"function",ctxName:"useStylesScoped$",captures:!1,parent:"s_PjfpaY6FOTY"},s_00bFc4tHmxA:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"useEndpoint_useResource",canonicalFilename:"s_00bfc4thmxa",hash:"00bFc4tHmxA",ctxKind:"function",ctxName:"useResource$",captures:!0,parent:null},s_2Eo7WCpaqI8:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"QwikCityProvider_component_useWatch",canonicalFilename:"s_2eo7wcpaqi8",hash:"2Eo7WCpaqI8",ctxKind:"function",ctxName:"useWatch$",captures:!0,parent:"s_TxCFOy819ag"},s_u0YVoxt2aTY:{origin:"../../../node_modules/@builder.io/qwik-city/index.qwik.mjs",displayName:"Link_component_useOnDocument",canonicalFilename:"s_u0yvoxt2aty",hash:"u0YVoxt2aTY",ctxKind:"function",ctxName:"useOnDocument",captures:!1,parent:"s_8gdLBszqbaM"}},mapping:{s_kzjavhDI3L0:"q-a72d609e.js",s_yiXwCC0m3jY:"q-a72d609e.js",s_EpaZ5qQ4Lg4:"q-a72d609e.js",s_9iPc0ZNOdxU:"q-a72d609e.js",s_4uCQXYaqoXY:"q-a72d609e.js",s_8gdLBszqbaM:"q-a72d609e.js",s_AKetNByE5TM:"q-a72d609e.js",s_BVs20TDbvq4:"q-a72d609e.js",s_D0p4a09aSQg:"q-a72d609e.js",s_PjfpaY6FOTY:"q-a72d609e.js",s_TxCFOy819ag:"q-a72d609e.js",s_WmYC5H00wtI:"q-a72d609e.js",s_dyI7p8LxEE8:"q-a72d609e.js",s_rUlpxt0UkP8:"q-a72d609e.js",s_rghJxmpKVus:"q-a72d609e.js",s_xHG1bWPFcUU:"q-a72d609e.js",s_A20fm0z03Yw:"q-a72d609e.js",s_C1zLAObj2Tc:"q-a72d609e.js",s_DUMnr602HUE:"q-a72d609e.js",s_a8xI1ebvPf4:"q-a72d609e.js",s_00bFc4tHmxA:"q-a72d609e.js",s_2Eo7WCpaqI8:"q-a72d609e.js",s_u0YVoxt2aTY:"q-a72d609e.js"},bundles:{"q-0d007131.js":{size:507,imports:["q-3e21ace4.js"],dynamicImports:["q-9cbd2586.js","q-eaa41d1e.js","q-f1036226.js"],origins:["@qwik-city-plan"]},"q-3e21ace4.js":{size:43312,origins:["\0vite/preload-helper","node_modules/@builder.io/qwik/core.min.mjs"]},"q-45aab3b6.js":{size:2536,origins:["../../node_modules/@builder.io/qwik-city/service-worker.mjs","src/routes/service-worker.ts"]},"q-9cbd2586.js":{size:319,imports:["q-3e21ace4.js"],dynamicImports:["q-a72d609e.js"],origins:["src/routes/index.tsx"]},"q-a72d609e.js":{size:25263,imports:["q-3e21ace4.js"],dynamicImports:["q-0d007131.js"],origins:["../../node_modules/@builder.io/qwik-city/index.qwik.mjs","src/components/header/header.css?used&inline","src/components/header/header.tsx","src/components/icons/bot.css?used&inline","src/components/icons/bot.tsx","src/components/qr/qr.css?used&inline","src/components/qr/qr.tsx","src/components/router-head/router-head.tsx","src/entry_hooks.js","src/global.css?used&inline","src/routes/index.css?used&inline","src/s_00bfc4thmxa.js","src/s_2eo7wcpaqi8.js","src/s_4ucqxyaqoxy.js","src/s_8gdlbszqbam.js","src/s_9ipc0znodxu.js","src/s_a20fm0z03yw.js","src/s_a8xi1ebvpf4.js","src/s_aketnbye5tm.js","src/s_bvs20tdbvq4.js","src/s_c1zlaobj2tc.js","src/s_d0p4a09asqg.js","src/s_dumnr602hue.js","src/s_dyi7p8lxee8.js","src/s_epaz5qq4lg4.js","src/s_kzjavhdi3l0.js","src/s_pjfpay6foty.js","src/s_rghjxmpkvus.js","src/s_rulpxt0ukp8.js","src/s_txcfoy819ag.js","src/s_u0yvoxt2aty.js","src/s_wmyc5h00wti.js","src/s_xhg1bwpfcuu.js","src/s_yixwcc0m3jy.js"],symbols:["s_00bFc4tHmxA","s_2Eo7WCpaqI8","s_4uCQXYaqoXY","s_8gdLBszqbaM","s_9iPc0ZNOdxU","s_A20fm0z03Yw","s_a8xI1ebvPf4","s_AKetNByE5TM","s_BVs20TDbvq4","s_C1zLAObj2Tc","s_D0p4a09aSQg","s_DUMnr602HUE","s_dyI7p8LxEE8","s_EpaZ5qQ4Lg4","s_kzjavhDI3L0","s_PjfpaY6FOTY","s_rghJxmpKVus","s_rUlpxt0UkP8","s_TxCFOy819ag","s_u0YVoxt2aTY","s_WmYC5H00wtI","s_xHG1bWPFcUU","s_yiXwCC0m3jY"]},"q-df6dc34c.js":{size:185,imports:["q-3e21ace4.js"],dynamicImports:["q-a72d609e.js"],origins:["src/root.tsx"]},"q-eaa41d1e.js":{size:185,imports:["q-3e21ace4.js"],dynamicImports:["q-a72d609e.js"],origins:["src/routes/layout.tsx"]},"q-f1036226.js":{size:112,imports:["q-3e21ace4.js"],dynamicImports:["q-45aab3b6.js"],origins:["@qwik-city-entries"]}},injections:[],version:"1",options:{target:"client",buildMode:"production",forceFullBuild:!0,entryStrategy:{type:"single"}},platform:{qwik:"0.16.2",vite:"",rollup:"3.9.1",env:"node",os:"win32",node:"18.12.1"}},Ee=!0,Ie=!1,oe=k("qc-c"),D=k("qc-ic"),O=k("qc-h"),Q=k("qc-l"),M=k("qc-n"),Pe=j(p(()=>{const{contents:e}=E(D);if(e&&e.length>0){const t=e.length;let n=null;for(let s=t-1;s>=0;s--)n=be(e[s].default,{children:n});return n}return we},"RouterOutlet_component_AKetNByE5TM")),R=new WeakMap,N=Symbol(),F=Symbol(),w=new Map,Se=async(e,t,n,s)=>{if(Array.isArray(e))for(const o of e){const a=o[0].exec(s);if(a){const i=o[1],c=Ye(o[2],a),l=o[4],f=new Array(i.length),y=[],_=Te(t,s);let v;return i.forEach((d,g)=>{$(d,y,I=>f[g]=I,n)}),$(_,y,d=>v=d==null?void 0:d.default,n),y.length>0&&await Promise.all(y),[c,f,v,l]}}return null},$=(e,t,n,s)=>{if(typeof e=="function"){const o=R.get(e);if(o)n(o);else{const a=e();typeof a.then=="function"?t.push(a.then(i=>{s!==!1&&R.set(e,i),n(i)})):a&&n(a)}}},Te=(e,t)=>{if(e){t=t.endsWith("/")?t:t+"/";const n=e.find(s=>s[0]===t||t.startsWith(s[0]+(t.endsWith("/")?"":"/")));if(n)return n[1]}},Ye=(e,t)=>{const n={};if(e)for(let s=0;s{const o=K(),a={data:e?e.body:null,head:o,withLocale:i=>H(s,i),...t};for(let i=n.length-1;i>=0;i--){const c=n[i]&&n[i].head;c&&(typeof c=="function"?V(o,H(s,()=>c(a))):typeof c=="object"&&V(o,c))}return a.head},V=(e,t)=>{typeof t.title=="string"&&(e.title=t.title),S(e.meta,t.meta),S(e.links,t.links),S(e.styles,t.styles),Object.assign(e.frontmatter,t.frontmatter)},S=(e,t)=>{if(Array.isArray(t))for(const n of t){if(typeof n.key=="string"){const s=e.findIndex(o=>o.key===n.key);if(s>-1){e[s]=n;continue}}e.push(n)}},K=()=>({title:"",meta:[],links:[],styles:[],frontmatter:{}}),Fe=()=>E(O),ae=()=>E(Q),Ue=()=>E(M),De=()=>se(te("qwikcity")),x=e=>e.pathname+e.search+e.hash,q=(e,t)=>new URL(e,t.href),ie=(e,t)=>e.origin===t.origin,ce=(e,t)=>e.pathname+e.search===t.pathname+t.search,Oe=(e,t)=>e.pathname===t.pathname,X=(e,t)=>ie(e,t)&&!ce(e,t),Qe=(e,t)=>e+(e.endsWith("/")?"":"/")+"q-data.json"+(t??""),Me=(e,t)=>{const n=e.href;if(typeof n=="string"&&n.trim()!==""&&typeof e.target!="string")try{const s=q(n,t),o=q("",t);if(ie(s,o))return x(s)}catch(s){console.error(s)}return null},Ke=(e,t,n)=>{if(e.prefetch!==!1&&t){const s=q(t,n);if(!Oe(s,q("",n)))return""}return null},Ae=(e,t)=>{const n=e.location,s=q(t.path,n);X(n,s)&&(Z(e,n,s),e.history.pushState("","",x(s))),e[F]||(e[F]=1,e.addEventListener("popstate",()=>{const o=e.location,a=q(t.path,o);X(o,a)&&(Z(e,a,o),t.path=x(o))}),e.removeEventListener("popstate",e[N]))},Z=async(e,t,n)=>{const s=e.document,o=n.hash;if(ce(t,n))t.hash!==o&&(await T(),o?J(s,o):e.scrollTo(0,0));else if(o)for(let a=0;a<24&&(await T(),!J(s,o));a++);else await T(),e.scrollTo(0,0)},T=()=>new Promise(e=>setTimeout(e,12)),J=(e,t)=>{const n=t.slice(1),s=e.getElementById(n);return s&&s.scrollIntoView(),s},G=e=>{typeof document<"u"&&document.dispatchEvent(new CustomEvent("qprefetch",{detail:e}))},re=async(e,t)=>{const n=new URL(e),s=n.pathname,o=n.search,a=Qe(s,o);let i=w.get(a);return G({links:[s]}),i||(i=fetch(a).then(c=>{if(c.ok&&(c.headers.get("content-type")||"").includes("json"))return c.json().then(l=>(G({bundles:l.prefetch}),t&&w.delete(a),l));w.delete(a)}),w.set(a,i)),i},Be=j(p(()=>{const e=De();if(!(e!=null&&e.params))throw new Error("Missing Qwik City Env Data");const t=te("url");if(!t)throw new Error("Missing Qwik URL Env Data");const n=new URL(t),s=m({href:n.href,pathname:n.pathname,query:Object.fromEntries(n.searchParams.entries()),params:e.params}),o=m({path:x(n)}),a=m(K),i=m({headings:void 0,menu:void 0}),c=m({contents:void 0});return u(oe,i),u(D,c),u(O,a),u(Q,s),u(M,o),ke(p(async({track:l})=>{const[f,y,_,v,d,g]=ne(),I=ve(""),{routes:le,menus:de,cacheModules:pe,trailingSlash:A}=await import("./@qwik-city-plan.mjs"),me=l(()=>g.path),h=new URL(me,d.href),C=h.pathname,ue=Se(le,de,pe,C),he=Ee?v.response:re(h.href,!0),B=await ue;if(B){const[fe,_e,xe]=B,L=_e,ye=L[L.length-1];if(C.endsWith("/")){if(!A){h.pathname=C.slice(0,-1),g.path=x(h);return}}else if(A){h.pathname+="/",g.path=x(h);return}d.href=h.href,d.pathname=C,d.params={...fe},d.query=Object.fromEntries(h.searchParams.entries()),f.headings=ye.headings,f.menu=xe,y.contents=se(L);const ge=await he,b=ze(ge,d,L,I);w.clear(),_.links=b.links,_.meta=b.meta,_.styles=b.styles,_.title=b.title,_.frontmatter=b.frontmatter,Ie&&Ae(window,g)}},"QwikCityProvider_component_useWatch_2Eo7WCpaqI8",[i,c,a,e,s,o])),r(U,{},"qY_0")},"QwikCityProvider_component_TxCFOy819ag"));p(e=>{const t=e.url??"http://localhost/",n=new URL(t),s=m({href:n.href,pathname:n.pathname,query:Object.fromEntries(n.searchParams.entries()),params:e.params??{}}),o=m({path:x(n)}),a=m(K),i=m({headings:void 0,menu:void 0}),c=m({contents:void 0});return u(oe,i),u(D,c),u(O,a),u(Q,s),u(M,o),r(U,{},"qY_1")},"QwikCityMockProvider_component_WmYC5H00wtI");p(e=>{const t=Ue(),n=ae(),s=e.href,o={...e},a=Me(o,n),i=Ke(e,a,n);return o["preventdefault:click"]=!!a,o.href=a||s,Ce("qinit",p(()=>{window[N]||(window[N]=()=>{window[F]||location.reload()},setTimeout(()=>{addEventListener("popstate",window[N])},0))},"Link_component_useOnDocument_u0YVoxt2aTY")),r("a",{...o,onClick$:p(()=>{const[c,l,f]=ne();c&&(f.path=l.href)},"Link_component_a_onClick_kzjavhDI3L0",[a,o,t]),"data-prefetch":i,onMouseOver$:p((c,l)=>ee(l),"Link_component_a_onMouseOver_yiXwCC0m3jY"),onQVisible$:p((c,l)=>ee(l,!0),"Link_component_a_onQVisible_EpaZ5qQ4Lg4"),children:r(U,{},"AD_0")})},"Link_component_8gdLBszqbaM");const ee=(e,t)=>{e&&e.href&&e.hasAttribute("data-prefetch")&&(Y||(Y=innerWidth),(!t||t&&Y<520)&&re(e.href))};let Y=0;const He=j(p(()=>{const e=Fe(),t=ae();return z(qe,{children:[r("title",{children:P(e,"title")}),r("link",{rel:"canonical",get href(){return t.href},[W]:{href:P(t,"href")}}),r("meta",{name:"viewport",content:"width=device-width, initial-scale=1.0"}),r("link",{rel:"icon",type:"image/svg+xml",href:"/favicon.svg"}),e.meta.map(n=>r("meta",{...n})),e.links.map(n=>r("link",{...n})),e.styles.map(n=>r("style",{...n.props,get dangerouslySetInnerHTML(){return n.style},[W]:{dangerouslySetInnerHTML:P(n,"style")}}))]},"Qn_0")},"s_D0p4a09aSQg")),We=`:root{--qwik-dark-blue: #1a1a1a;--qwik-light-blue: #349713;--qwik-light-purple: #73da51;--qwik-dark-purple: #3f9622}body{background-color:#fafafa;font-family:Inter,sans-serif,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,sans-serif;padding:20px 20px 40px}main{max-width:760px;margin:0 auto;background-color:#fff;border-radius:5px;box-shadow:#110c2e26 0 48px 100px;overflow:hidden}h1,h2{margin:0 0 5px}.lightning{filter:hue-rotate(180deg)}section{padding:20px}ul{list-style-type:square;margin:5px 0 30px;padding-left:25px}li{padding:8px 0}li::marker{color:var(--qwik-light-blue)}a,a:visited{color:var(--qwik-dark-blue)}a:hover{text-decoration:none}table.commands{margin:0 0 30px}.commands td{padding:5px}.commands td:first-child{white-space:nowrap;padding-right:20px}code{font-family:Menlo,Monaco,Courier New,monospace;font-size:.9em;background-color:#e0e0e0;padding:2px 4px;border-radius:3px;border-bottom:2px solid #bfbfbf}footer{padding:15px;text-align:center;font-size:.8em}footer a{text-decoration:none}footer a:hover{text-decoration:underline}a.mindblow{margin:0 auto;display:block;background:var(--qwik-dark-purple);padding:10px 20px;border-radius:10px;border:0;color:#fff;text-decoration:none;font-size:20px;width:fit-content;border-bottom:4px solid black;cursor:url("data:image/svg+xml;utf8,\\1f92f") 16 0,auto}a.mindblow:hover{border-bottom-width:0px;margin-bottom:4px;transform:translateY(4px)} +`,Re=j(p(()=>(Le(p(We,"s_xHG1bWPFcUU")),z(Be,{children:[z("head",{children:[r("meta",{charSet:"utf-8"}),r("link",{rel:"preconnect",href:"https://rsms.me/"}),r("link",{rel:"stylesheet",href:"https://rsms.me/inter/inter.css"}),r(He,{},"pq_0")]}),r("body",{lang:"en",children:r(Pe,{},"pq_1")})]},"pq_2")),"s_BVs20TDbvq4"));function Ze(e){return je(r(Re,{},"we_0"),{manifest:Ne,...e,containerAttributes:{lang:"en-us",...e.containerAttributes}})}export{Ze as default}; diff --git a/packages/portal/server/package.json b/packages/portal/server/package.json new file mode 100644 index 0000000..4720025 --- /dev/null +++ b/packages/portal/server/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/packages/portal/src/components/header/header.css b/packages/portal/src/components/header/header.css new file mode 100644 index 0000000..b88f3da --- /dev/null +++ b/packages/portal/src/components/header/header.css @@ -0,0 +1,36 @@ +header { + display: flex; + background: white; +} + +header .logo a { + display: inline-block; + padding: 10px 10px 7px 20px; + text-decoration: none; +} + +header ul { + margin: 0; + padding: 3px 10px 0 0; + list-style: none; + flex: 1; + text-align: right; +} + +header li { + display: inline-block; + margin: 0; + padding: 0; +} + +header li a { + display: inline-block; + padding: 15px 10px; + text-decoration: none; + color: #1a1a1a; + font-weight: 500; +} + +header li a:hover { + text-decoration: underline; +} diff --git a/packages/portal/src/components/header/header.tsx b/packages/portal/src/components/header/header.tsx new file mode 100644 index 0000000..ebaa11b --- /dev/null +++ b/packages/portal/src/components/header/header.tsx @@ -0,0 +1,37 @@ +import { component$, useStylesScoped$ } from '@builder.io/qwik' +import { BotLogo } from '../icons/bot' +import styles from './header.css?inline' + +export default component$(() => { + useStylesScoped$(styles) + + return ( +
    + + +
    + ) +}) diff --git a/packages/portal/src/components/icons/bot.css b/packages/portal/src/components/icons/bot.css new file mode 100644 index 0000000..481f167 --- /dev/null +++ b/packages/portal/src/components/icons/bot.css @@ -0,0 +1,12 @@ +.logo { + display: flex; + gap: 0.5rem; + align-items: center; + align-content: center; + color: #1a1a1a; +} + +.logo h1 { + font-size: 1.5rem; + margin: 0; +} diff --git a/packages/portal/src/components/icons/bot.tsx b/packages/portal/src/components/icons/bot.tsx new file mode 100644 index 0000000..3c69ef7 --- /dev/null +++ b/packages/portal/src/components/icons/bot.tsx @@ -0,0 +1,18 @@ +import { component$, useStylesScoped$ } from '@builder.io/qwik' + +import styles from './bot.css?inline' + +export const BotLogo = component$(() => { + useStylesScoped$(styles) + return ( +
    + +

    Chatbot

    +
    + ) +}) diff --git a/packages/portal/src/components/icons/qwik.tsx b/packages/portal/src/components/icons/qwik.tsx new file mode 100644 index 0000000..fc113f6 --- /dev/null +++ b/packages/portal/src/components/icons/qwik.tsx @@ -0,0 +1,32 @@ +export const QwikLogo = () => ( + + + + + + + + + +) diff --git a/packages/portal/src/components/qr/qr.css b/packages/portal/src/components/qr/qr.css new file mode 100644 index 0000000..37d8779 --- /dev/null +++ b/packages/portal/src/components/qr/qr.css @@ -0,0 +1,12 @@ +div img { + display: block; + background-color: #f0f0f0; + width: 350px; + height: 350px; + object-fit: contain; + border-radius: 10px; +} +div { + display: flex; + justify-content: center; +} diff --git a/packages/portal/src/components/qr/qr.tsx b/packages/portal/src/components/qr/qr.tsx new file mode 100644 index 0000000..f1aea4b --- /dev/null +++ b/packages/portal/src/components/qr/qr.tsx @@ -0,0 +1,21 @@ +import { component$, useClientEffect$, useStore, useStylesScoped$ } from '@builder.io/qwik' +import style from './qr.css?inline' + +export const QR = component$(() => { + useStylesScoped$(style) + const state = useStore({ + count: 0, + }) + + useClientEffect$(() => { + setInterval(() => { + state.count++ + }, 800) + }) + + return ( +
    + QR +
    + ) +}) diff --git a/packages/portal/src/components/router-head/router-head.tsx b/packages/portal/src/components/router-head/router-head.tsx new file mode 100644 index 0000000..dee9459 --- /dev/null +++ b/packages/portal/src/components/router-head/router-head.tsx @@ -0,0 +1,29 @@ +import { component$ } from '@builder.io/qwik' +import { useDocumentHead, useLocation } from '@builder.io/qwik-city' + +export const RouterHead = component$(() => { + const head = useDocumentHead() + const loc = useLocation() + + return ( + <> + {head.title} + + + + + + {head.meta.map((m) => ( + + ))} + + {head.links.map((l) => ( + + ))} + + {head.styles.map((s) => ( +