Compare commits

..

1 Commits

Author SHA1 Message Date
Leifer Mendez
7c8742735c docs(bot): 📝 new desing 2022-12-14 20:35:58 +01:00
346 changed files with 12523 additions and 21809 deletions

View File

@@ -1,2 +1 @@
packages/docs/*
packages/portal/*
packages/docs/*

7
.github/FUNDING.yml vendored
View File

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

View File

@@ -1,58 +0,0 @@
name: 🐛 Reporte Bug
description: Algo no va bien?. Hazlo saber
labels: [bug, triage]
title: '[🐛]'
body:
- type: markdown
attributes:
value: |
Gracias por tomarte el tiempo de reportar este problema
- type: dropdown
id: version
attributes:
label: ¿Que versión estas usando?
description: '__INFO:__ Recuerda que puedes consultar dudas directamente en [discord](https://link.codigoencasa.com/DISCORD)'
options:
- v2
- v1
validations:
required: true
- type: dropdown
id: component
attributes:
label: ¿Sobre que afecta?
options:
- Flujo de palabras (Flow)
- DialogFlow
- Base de datos
- Otro
validations:
required: true
- type: textarea
id: description
attributes:
description: 'Trata de ser lo más claro posible, de esa manera podemos entender el contexto de tu problema y darte una mejor solución'
label: Describe tu problema
placeholder: Yo tengo un problema....
validations:
required: true
- type: input
id: reproduction
attributes:
label: Reproducir error
description: __(Recomendación)__ trata de grabar un video puedes usar algunas de las siguientes herramientas [https://www.vidyard.com/](https://www.vidyard.com/) [https://www.loom.com/](https://www.loom.com/) y en lo posbile apoyate en [https://stackblitz.com/](https://stackblitz.com/) para compartir el código de ser necesario
placeholder: URL video o stackblitz
validations:
required: false
- type: textarea
id: additional_information
attributes:
label: Información Adicional
validations:
required: false

View File

@@ -1,4 +0,0 @@
contact_links:
- name: 🤔 Core Team
url: https://link.codigoencasa.com/DISCORD
about: Si quieres formar parte del CoreTeam, patrocinar el proyecto o propuesta profesionales

View File

@@ -1,79 +0,0 @@
name: 🐬 Caso de uso
description: Reporta tu caso de uso y cuales fueron tus resultados
labels: [usecase]
title: '[🐬]'
body:
- type: markdown
attributes:
value: |
Gracias por tomarte el tiempo de detallar este caso de uso, sera de gran utilidad para mantener un software de calidad puedes comenzar
⚡ `npm create bot-whatsapp@dev`
- type: dropdown
id: version
attributes:
label: ¿Cual proveedor usaste?
description: 'Actualmente tenemos varios proveedores que sirven como punto de entrada y salida con Whatsapp'
options:
- whatsapp-web.js
- venom
- bailey
- twilio
- meta
validations:
required: true
- type: dropdown
id: component
attributes:
label: ¿Cual base de datos usaste?
options:
- memory
- mongo
- mysql
- json
validations:
required: true
- type: dropdown
id: result
attributes:
label: Conclusion de la prueba
options:
- muy buena
- buena
- tiene errores
validations:
required: true
- type: textarea
id: description
attributes:
description: 'Trata de ser lo más claro posible, de esa manera podemos entender el contexto del caso de uso'
label: Describe tu caso
placeholder: Yo tengo un caso....
validations:
required: true
- type: textarea
id: logs
attributes:
label: ¿Logs Importantes?
description: Si tienes algunos logs importantes a tener en cuenta o que muetren algun error en concreto.
render: shell
- type: textarea
id: additional_information
attributes:
label: Información Adicional
validations:
required: false
- type: input
id: usernames
attributes:
label: ¿Quieres que te mencionemos?
description: Siempre buscamos fomentar la comunidad por lo cual si quieres que te mencionemos publicamente en nuestras redes sociales puedes dejar tu username
placeholder: twitter o github o instagram o alguna url
validations:
required: false

View File

@@ -1,17 +0,0 @@
# Que tipo de Pull Request es?
- [ ] Mejoras
- [ ] Bug
- [ ] Docs / tests
# Descripción
Por favor agrega una descripción de tu aporte para tener más contexto y poder avanzar más rápido. Si es de ayuda puedes usar plataformar como [https://www.loom.com/](https://www.loom.com/) para grabar un video.
> Forma parte de este proyecto.
- [Discord](https://link.codigoencasa.com/DISCORD)
- [Twitter](https://twitter.com/leifermendez)
- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
- [Telegram](https://t.me/leifermendez)

View File

@@ -1,4 +1,4 @@
name: Build and Test
name: BotWhatsapp Build-Test
on:
pull_request:
@@ -12,7 +12,6 @@ jobs:
runs-on: ubuntu-latest
needs:
- test-unit
- test-e2e
steps:
- name: Checkout
uses: actions/checkout@v3
@@ -30,7 +29,7 @@ jobs:
run: yarn install --immutable --network-timeout 300000
- name: Build Package
run: yarn build:full
run: yarn build
- name: Build Eslint rules
run: yarn lint:fix
@@ -57,27 +56,3 @@ jobs:
- name: Unit Tests
run: yarn test
############ UNIT TEST ############
test-e2e:
name: e2e Tests
runs-on: ubuntu-latest
needs:
- test-unit
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: e2e Tests
run: yarn test.e2e

View File

@@ -1,76 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: 'CodeQL'
on:
push:
branches: [release/next]
pull_request:
# The branches below must be a subset of the branches above
branches: ['main']
schedule:
- cron: '21 16 * * 5'
jobs:
analyze:
if: ${{ !github.event.act }}
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ['javascript']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: '/language:${{matrix.language}}'

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

@@ -0,0 +1,19 @@
name: Add contributors
on:
pull_request:
branches:
- dev
- main
types: [closed]
jobs:
contrib-readme-job:
runs-on: ubuntu-latest
name: A job to automate contrib in readme
steps:
- name: Contribute List
uses: akhilmhdh/contributors-readme-action@v2.3.6
with:
image_size: 50
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,47 +0,0 @@
name: 📄 Desplegando documentacion
on:
pull_request:
branches:
- 'feat/docs-**'
- 'fix/docs-**'
push:
branches:
- 'feat/docs-**'
- 'fix/docs-**'
jobs:
############ DOCUMENTATION BUILD ############
build-documentation:
if: ${{ !github.event.act }}
name: Build Package
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: Add netlify
run: yarn add netlify-cli -D
- name: Create .env build file
run: |
touch packages/docs/.env
echo VITE_GITHUB_TOKEN=${{ secrets.COLLABORATORS_TOKEN }} >> packages/docs/.env
- name: Build and Deploy
run: |
cd packages/docs
netlify deploy --build --site ${{ secrets.NETLIFY_SITE_ID }} --auth ${{ secrets.NETLIFY_AUTH_TOKEN }}

View File

@@ -1,41 +0,0 @@
name: 📄 (PROD) Desplegando documentacion
on:
push:
branches:
- release/next
jobs:
############ DOCUMENTATION BUILD ############
build-documentation-prod:
name: Build Package
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: Add netlify
run: yarn add netlify-cli -D
- name: Create .env build file
run: |
touch packages/docs/.env
echo VITE_GITHUB_TOKEN=${{ secrets.COLLABORATORS_TOKEN }} >> packages/docs/.env
- name: Build and Deploy
run: |
cd packages/docs
netlify deploy --prod --build --site ${{ secrets.NETLIFY_SITE_ID }} --auth ${{ secrets.NETLIFY_AUTH_TOKEN }}

View File

@@ -1,24 +1,22 @@
name: 🚀 (DEV) Liberando versiones
name: BotWhatsapp Releases(DEV)
on:
push:
branches:
- release/next
- next-release
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}}
ref: ${{ github.head_ref }}
persist-credentials: false
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v3
@@ -33,7 +31,7 @@ jobs:
run: yarn install --immutable --network-timeout 300000
- name: Build Package
run: yarn build:full
run: yarn build
- name: Release @bot-whatsapp/bot
run: yarn node ./scripts/release.js --name=bot --version= --token="${{ secrets.NPM_TOKEN }}"
@@ -50,15 +48,9 @@ jobs:
- name: Release @bot-whatsapp/provider
run: yarn node ./scripts/release.js --name=provider --version= --token="${{ secrets.NPM_TOKEN }}"
- 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
- name: Commit Versioning & Push changes
uses: actions-js/push@master
with:
branch: release/next
github_token: ${{ secrets.GITHUB_TOKEN }}
force: true
message: 'ci(version): :zap: automatic - "${date}" updated versions every packages'
branch: 'dev'

View File

@@ -1,18 +1,22 @@
name: 🚀⚡ Liberando versiones
name: BotWhatsapp Releases(Prod)
on:
push:
branches:
- release/production
tags:
- 'v*.*.*'
jobs:
############ RELEASE ############
release-prod:
if: ${{ !github.event.act }}
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}
persist-credentials: false
fetch-depth: 0
- name: Set output
id: vars
@@ -27,49 +31,30 @@ 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:full
run: yarn build
- name: Release @bot-whatsapp/bot
run: yarn node ./scripts/release.js --name=bot --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}"
run: yarn node ./scripts/release.js --name=bot --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}"
- name: Release @bot-whatsapp/cli
run: yarn node ./scripts/release.js --name=cli --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}"
run: yarn node ./scripts/release.js --name=cli --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}"
- name: Release @bot-whatsapp/create-bot-whatsapp
run: yarn node ./scripts/release.js --name=create-bot-whatsapp --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}"
run: yarn node ./scripts/release.js --name=create-bot-whatsapp --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}"
- name: Release @bot-whatsapp/database
run: yarn node ./scripts/release.js --name=database --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}"
run: yarn node ./scripts/release.js --name=database --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}"
- name: Release @bot-whatsapp/provider
run: yarn node ./scripts/release.js --name=provider --version="${{ steps.package-version.outputs.current-version}}" --token="${{ secrets.NPM_TOKEN }}"
run: yarn node ./scripts/release.js --name=provider --version="${{ steps.vars.outputs.tag }}" --token="${{ secrets.NPM_TOKEN }}"
- 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
- name: Commit Versioning & Push changes
uses: actions-js/push@master
with:
branch: release/production
github_token: ${{ secrets.GITHUB_TOKEN }}
force: true
message: 'ci(version): :zap: automatic - "${date}" updated versions every packages'
branch: 'dev'

View File

@@ -1,27 +0,0 @@
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
#
# You can adjust the behavior by modifying this file.
# For more information, see:
# https://github.com/actions/stale
name: Revisar ISSUES abandonadas
on:
schedule:
- cron: '55 22 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: '¿Alguna novedad sobre esta ISSUE?'
stale-pr-message: '¿Alguna novedad sobre esta PULL REQUEST?'
stale-issue-label: 'no-issue-activity'
stale-pr-label: 'no-pr-activity'
exempt-issue-assignees: 'leifermendez'

11
.gitignore vendored
View File

@@ -1,5 +1,4 @@
/node_modules
/packages/repl
/packages/*/starters
/packages/*/node_modules
/packages/*/dist
@@ -15,10 +14,6 @@ mediaSend/*
!mediaSend/nota-de-voz.mp3
.env
.wwebjs_auth
/session
/session/*
/tokens
/tokens/*
packages/cli/config.json
config.json
.yarnrc.yml
@@ -36,12 +31,8 @@ tmp/
.fleet/
example-app*/
base-*/
test-*.json
!starters/apps/base-*/
qr.svg
package-lock.json
yarn-error.log
.npmrc
# Local Netlify folder
.netlify
.secrets
.npmrc

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint:fix && npx --no -- commitlint --edit
npx --no -- commitlint --edit

View File

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

View File

@@ -2,6 +2,5 @@
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": true,
"printWidth": 120
"singleQuote": true
}

View File

@@ -1,24 +0,0 @@
{
"Flow Bot (simple)": {
"scope": "javascript",
"prefix": "bot:flow",
"description": "Crear un flujo simple",
"body": [
"export const flow${1} = addKeyword(['hola', 'buenas'])",
" .addAnswer('Hola! 🚀 Bienvenido a este CHATBOT')",
" .addAnswer('¿Como puedo ayudarte?')"
]
},
"Flow Bot (completo)": {
"scope": "javascript",
"prefix": "bot:flow completo",
"description": "Crear un flujo completo",
"body": [
"export const flow${1} = addKeyword(['categorias'])",
" .addAnswer('⚡ Tenemos las siguientes categorias')",
" .addAnswer(['🚀 Computadoras', '🚀 Celulares', '🚀 Otros'], {",
" delay: 1500, //Milisegundo 1500 = 1.5segundos",
"})"
]
}
}

View File

@@ -1,3 +0,0 @@
{
"recommendations": ["xyc.vscode-mdx-preview", "vivaxy.vscode-conventional-commits", "mhutchie.git-graph"]
}

View File

@@ -7,8 +7,6 @@
"provider",
"adapter",
"ci",
"starters",
"conflict",
"contexts"
"starters"
]
}

View File

@@ -2,323 +2,6 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.1.21](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.20...v0.1.21) (2023-02-12)
### Features
* **provider:** :bug: add_location ([a46a9ef](https://github.com/leifermendez/bot-whatsapp/commit/a46a9efd8dbd921c773395d331154dc9a8aae783))
* **provider:** :rocket: feat Instance provider ([2278149](https://github.com/leifermendez/bot-whatsapp/commit/227814929561cedc11a1f69c8029515a7f47c9ff))
* **provider:** :rocket: provider raw ([2d44a00](https://github.com/leifermendez/bot-whatsapp/commit/2d44a002ff226fb0eb7362ad49936f1e00b84242))
* **provider:** :zap: add location provider ([c7de860](https://github.com/leifermendez/bot-whatsapp/commit/c7de860803fb362f5afe06cc38ad71b2c316d524))
* **provider:** :zap: add location provider ([c0ece6f](https://github.com/leifermendez/bot-whatsapp/commit/c0ece6feb2b0325476880a604c32de341eb60544))
* **provider:** :zap: support location <20> ([a147677](https://github.com/leifermendez/bot-whatsapp/commit/a147677a26b36bba429c3dd3c2c81a44a7a4d2b6))
### Bug Fixes
* arreglando dir y varios mensajes en dialog flow essential ([01c7db8](https://github.com/leifermendez/bot-whatsapp/commit/01c7db8fe7dda2482eb0aa1fd7b3469b6ae8eae1))
### [0.1.20](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.19...v0.1.20) (2023-02-05)
### Features
* **cli:** :fire: add regex expression in addKeyworkd ([e34560c](https://github.com/leifermendez/bot-whatsapp/commit/e34560c77d4852d2e90930f0858e51aa67d4eeab))
* **provider:** :zap: possible get class provider ([76ba717](https://github.com/leifermendez/bot-whatsapp/commit/76ba717927a75b3d6299206aa0b8aee2bc25b726))
### Bug Fixes
* **cli:** :zap: working flowDynamic test ([c0113ca](https://github.com/leifermendez/bot-whatsapp/commit/c0113ca49295aff220d8defcb53f2ba7f2872d75))
* **cli:** :zap: working flowDynamic test ([aef52d2](https://github.com/leifermendez/bot-whatsapp/commit/aef52d2694fa6616d614338643db198b4f7f1fe8))
* **cli:** :zap: working flowDynamic test ([f769320](https://github.com/leifermendez/bot-whatsapp/commit/f76932021ce968d93241b55cfcdb8ae0e0e6c934))
* **cli:** :zap: working flowDynamic test ([23e09ef](https://github.com/leifermendez/bot-whatsapp/commit/23e09efaeccaf51018c55da492edff45b625f0a9))
* **database:** add support emoji in mysql ([9311aa0](https://github.com/leifermendez/bot-whatsapp/commit/9311aa0a65623a1bf40e96207a281625154dae90))
* **database:** fix naming ([cd082f2](https://github.com/leifermendez/bot-whatsapp/commit/cd082f235012cd5f5844c6437f51711beee0c865))
* **database:** fix naming ([1afc3ba](https://github.com/leifermendez/bot-whatsapp/commit/1afc3ba182070713b5bec40eaab0fa1f680830cd))
* **database:** fix naming ([c9831d2](https://github.com/leifermendez/bot-whatsapp/commit/c9831d202ab2c85f15a0247cd2a2426bc435270c))
* **provider:** :zap: baily wa.link ([96c2bff](https://github.com/leifermendez/bot-whatsapp/commit/96c2bffd093269be8e39474a84c156938504a6cb))
### [0.1.19](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.18...v0.1.19) (2023-01-29)
### Features
* :fire: bailey add media ([eab39e4](https://github.com/leifermendez/bot-whatsapp/commit/eab39e4ac06fd46f1a4671f8c15d1456b4400b97))
* :zap: more feature ([e19c3a2](https://github.com/leifermendez/bot-whatsapp/commit/e19c3a25a40259c74b4add9635af4844907eed26))
* **provider:** :rocket: fix issues in providers venom and wwebjs ([cbe438b](https://github.com/leifermendez/bot-whatsapp/commit/cbe438b77854e8df48b9dafaf7a837d21124ac5f))
* **provider:** :rocket: fix provider ([0ad4c58](https://github.com/leifermendez/bot-whatsapp/commit/0ad4c58457b548dc41c0f9e8470d59c48de7b95a))
* **provider:** :rocket: fix provider ([f8c7184](https://github.com/leifermendez/bot-whatsapp/commit/f8c7184487065443ab10f77aaf585e8bd63ca441))
* **provider:** :rocket: fix provider ([b2afa45](https://github.com/leifermendez/bot-whatsapp/commit/b2afa45352a7ab1f5d9775f3c1fde475bd8ca204))
* **provider:** :rocket: fix provider venom and wwebjs ([dcb0566](https://github.com/leifermendez/bot-whatsapp/commit/dcb0566d2bc3da40cd0c71554bb5ea0ec115d9ca))
* **provider:** :rocket: implements all send media to venom provider ([9dd7c02](https://github.com/leifermendez/bot-whatsapp/commit/9dd7c02b6a5474aff063f7d6be0ca8519504b93c))
* **provider:** :rocket: send file wwebjs ([6ff1a3a](https://github.com/leifermendez/bot-whatsapp/commit/6ff1a3a980196c01c66ed04ee07d0e7e57256504))
* **provider:** :zap: bailey add send file video audio ([14d1a61](https://github.com/leifermendez/bot-whatsapp/commit/14d1a61fa259c09135c37c55bd79e97c9c8367e4))
* **provider:** :zap: venom wweb ([fd2847a](https://github.com/leifermendez/bot-whatsapp/commit/fd2847aea0db17a0bdf33b5bca67a4cb8db2da16))
* **provider:** :zap: venom wweb ([f95331d](https://github.com/leifermendez/bot-whatsapp/commit/f95331d3dc70e76a3dfbe4c8d24059f0e7a164ef))
* **provider:** 🚀 implements all send media to venom provider ([bd7d150](https://github.com/leifermendez/bot-whatsapp/commit/bd7d150c047af41fdbb47f0a50a21e82cd79ee85))
### Bug Fixes
* **bot:** :fire: endFlow with ctx ([f6114af](https://github.com/leifermendez/bot-whatsapp/commit/f6114affadfbc324536a86167d1fdfe8da3c8de6))
* **bot:** :fire: endFlow with ctx ([b655ae4](https://github.com/leifermendez/bot-whatsapp/commit/b655ae449e7958ea940d8cc3c678fd66f60b6385))
* **bot:** :zap: endFlow butons ([87a4203](https://github.com/leifermendez/bot-whatsapp/commit/87a4203cd5b88f566387a76d586248e4265d6e4e))
* **bot:** :zap: fix fallback refactor ([e22780d](https://github.com/leifermendez/bot-whatsapp/commit/e22780d3faba94f71a70f1f201a20690608fa5bf))
* **cli:** :zap: endflow ([1c66f17](https://github.com/leifermendez/bot-whatsapp/commit/1c66f178a56d284bb8cb9df5ca17685c7e5d1ddd))
* **cli:** :zap: refactor fallback in child flow ([b33e346](https://github.com/leifermendez/bot-whatsapp/commit/b33e34692d3abcb6874308a9be79f74be4a2c3a8))
* **cli:** :zap: refactor fallback in child flow ([8da4b20](https://github.com/leifermendez/bot-whatsapp/commit/8da4b204b41125b5d0fa0aee4fa87c1f5faf5568))
### [0.1.18](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.17...v0.1.18) (2023-01-24)
### 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)
### Features
* **adapter:** :zap: send messages with dialogflow ([c20e151](https://github.com/leifermendez/bot-whatsapp/commit/c20e151e209d33de9e7425a64f003c85360f1832))
* **baileys:** added more methods ([1b23b83](https://github.com/leifermendez/bot-whatsapp/commit/1b23b837460ce4533ff33f10f1de5e3a344a5623))
* **bot:** :zap: http responses support ([e331c2d](https://github.com/leifermendez/bot-whatsapp/commit/e331c2dcc40eeb82a93f9d29f6a82333b8465927))
* **bot:** :zap: http responses support ([2d2bb08](https://github.com/leifermendez/bot-whatsapp/commit/2d2bb085cd95604a84ca3fe5c4ddc84b3824ac1c))
* **bot:** :zap: rev-03 everything work fine ([3012e02](https://github.com/leifermendez/bot-whatsapp/commit/3012e026b77ab4e99334b992d166a89189f76503))
* **cli:** :sparkles: added bailey ([06acec2](https://github.com/leifermendez/bot-whatsapp/commit/06acec2bf29d72c2b46f4ce81fed115bab97351f))
* **cli:** :sparkles: added bailey ([c868f73](https://github.com/leifermendez/bot-whatsapp/commit/c868f7346245bec94582b25a342febc657926c9d))
* **conflict:** :zap: remove unused variable ([eba9229](https://github.com/leifermendez/bot-whatsapp/commit/eba92299cfd84c971f09697d027043f19eec2b7c))
* **contexts:** :zap: add new dialogflowcx ([4d8cf62](https://github.com/leifermendez/bot-whatsapp/commit/4d8cf623ff86b3d08c8d52293d4e289dfda68e1c))
* **contexts:** :zap: add new dialogflowcx ([9885872](https://github.com/leifermendez/bot-whatsapp/commit/98858729919b2544dace07c49badce7888ddfd82))
* **contexts:** dialogflowcx support ([9179421](https://github.com/leifermendez/bot-whatsapp/commit/917942139f9736f1c0f8ce5f07b4e12e5768b0c7))
* correccion de flujos en app.js de ejemplo ([99f508f](https://github.com/leifermendez/bot-whatsapp/commit/99f508f93889d70240861158bc304c25a3b2daef))
* **docs:** master class updated ([69fd81a](https://github.com/leifermendez/bot-whatsapp/commit/69fd81a565e61b249ac50917585293d2d84e3dd4))
* **docs:** master class updated ([d522b03](https://github.com/leifermendez/bot-whatsapp/commit/d522b03e2e6e6e3f7c467c59e3d2d6f288fe37b2))
* **provider:** :bug: dialogflow ([4ec6f1e](https://github.com/leifermendez/bot-whatsapp/commit/4ec6f1e120879e545fa111615f2d79b792d947a5))
* **provider:** :sparkles: added dialogflow ([2f633c7](https://github.com/leifermendez/bot-whatsapp/commit/2f633c72da24f98d6c318d1e725571b62e04604c))
* **provider:** :sparkles: added dialogflow ([798f1ce](https://github.com/leifermendez/bot-whatsapp/commit/798f1cebdefe43624c1698a219dcb224bb842d38))
* **provider:** :sparkles: endpoint is added to validate the webhook … ([478929d](https://github.com/leifermendez/bot-whatsapp/commit/478929d1340d46d6bf997ae8edabbaae4511172d))
* **provider:** :sparkles: endpoint is added to validate the webhook token ([1ec1564](https://github.com/leifermendez/bot-whatsapp/commit/1ec15647dc462363d5b765f42debddbe6ef6266b))
* **provider:** :zap: add new methods ([0b4e353](https://github.com/leifermendez/bot-whatsapp/commit/0b4e35308dace0ccdf618cb1d04987ed5200d58c))
* **provider:** :zap: add sendfile and sendButtons ([5433610](https://github.com/leifermendez/bot-whatsapp/commit/5433610a84d7a050a387e4daf2ded1daebfc03a4))
* **provider:** :zap: add sendfile and sendButtons ([342cbcc](https://github.com/leifermendez/bot-whatsapp/commit/342cbccff1d09f9aabe5423ad6d686d590a2448f))
* **provider:** :zap: added new venom provider ([01fe9eb](https://github.com/leifermendez/bot-whatsapp/commit/01fe9ebc9a943f2aa086ee415153d1cccdb14ec0))
* **provider:** :zap: added tamplate venom ([337c2e9](https://github.com/leifermendez/bot-whatsapp/commit/337c2e94bccd0ae173958fe2db08b494bdc93c28))
* **provider:** :zap: baileysProvider ([23b2e8e](https://github.com/leifermendez/bot-whatsapp/commit/23b2e8e439ecec24450bd5cf1a3820316e643434))
* **provider:** :zap: solution error buttons ([1b83871](https://github.com/leifermendez/bot-whatsapp/commit/1b83871cca6996c6acae3d4c8b6b42aec05ea146))
* **provider:** :zap: solution error utils venom ([31c83f5](https://github.com/leifermendez/bot-whatsapp/commit/31c83f5d689a01490d3adb96006f54c2a5d3268b))
* **provider:** :zap: update ([b62d21a](https://github.com/leifermendez/bot-whatsapp/commit/b62d21a0bf94466e43c25c6e8c0f5db9ae91c572))
* **provider:** :zap: update ([0c94647](https://github.com/leifermendez/bot-whatsapp/commit/0c94647a27747c3ddf4f02926580370f0d81bdc2))
* **provider:** meta provider is added ([b041f7d](https://github.com/leifermendez/bot-whatsapp/commit/b041f7d0c7cc6f152d3f36785d1d398a4141d57d))
* **provider:** meta provider is added ([438607c](https://github.com/leifermendez/bot-whatsapp/commit/438607c222b91d6f8814201dabe5f7c3e7ba1abb))
* **provider:** new added baileys ([4e0fcbd](https://github.com/leifermendez/bot-whatsapp/commit/4e0fcbd8347f8a430adb43351b5415098a5d10df))
* **provider:** new provider - venon:zap: configuracion inicial provi… ([66f75f8](https://github.com/leifermendez/bot-whatsapp/commit/66f75f872200334bfc9eda744bed92c509dfee56))
* **provider:** new provider - venon:zap: configuracion inicial provider venom ([fee7c2e](https://github.com/leifermendez/bot-whatsapp/commit/fee7c2e967b7fe8835b5acc243c19f7713acfbe7))
* se agregaron los datros del adapter mongo en app.js y package.json ([8160d13](https://github.com/leifermendez/bot-whatsapp/commit/8160d13c866b8ae17b0ec8e68eee1bc0373595b0))
* se agrego informacion al ejemplo en app.js ([954e751](https://github.com/leifermendez/bot-whatsapp/commit/954e751f700c6a39ec70c0bc5168637c0dc7e07c))
* se agrego informacion al ejemplo en app.js ([b2f1339](https://github.com/leifermendez/bot-whatsapp/commit/b2f13396104db9ccef5b3bad1c4e19c6a4bad2d4))
* **starters:** meta memory base template added ([11c784f](https://github.com/leifermendez/bot-whatsapp/commit/11c784f882965d6bd3a2313cf91bed9fb3aa5f26))
* **starters:** meta memory base template added ([e8d6252](https://github.com/leifermendez/bot-whatsapp/commit/e8d625201ed86e162e0b4e82100ede1d08985555))
### Bug Fixes
* :art: update ([7d6708c](https://github.com/leifermendez/bot-whatsapp/commit/7d6708c01bbdc5043a7e6ed56fe15a9618115b91))
* :sparkles: updated starters ([5da4b7a](https://github.com/leifermendez/bot-whatsapp/commit/5da4b7a4d1e5950be94361ac439938741b9d299c))
* actualizar app.js de ejemplo ([1746613](https://github.com/leifermendez/bot-whatsapp/commit/17466138ddcef60a23a0c87911f22045f26d3233))
* actualizar ejemplo app.js ([60fdbf3](https://github.com/leifermendez/bot-whatsapp/commit/60fdbf3d3cd62819e618853a9dc2fd0e23fe8752))
* **adapter:** :fire: clear log ([9ad4874](https://github.com/leifermendez/bot-whatsapp/commit/9ad4874fdafabfbf0e9e20e6b3281f702bb9fbe7))
* **adapter:** :fire: clear log ([4d34d3a](https://github.com/leifermendez/bot-whatsapp/commit/4d34d3ab1daab4e72fb5244216c78cf836d1a164))
* **adapter:** :fire: improvement baileys ([e6fefb4](https://github.com/leifermendez/bot-whatsapp/commit/e6fefb4049847f996f2a169b9acfc27c2428d3e6))
* **adapter:** :fire: improvement baileys ([2d5ac26](https://github.com/leifermendez/bot-whatsapp/commit/2d5ac2664bea09e60ac85ff2612609ae21050945))
* **adapter:** :rocket: venom update - cli - qr iamge ([041bf62](https://github.com/leifermendez/bot-whatsapp/commit/041bf6280e5f6956393716907e0669aa3ca78b4a))
* **adapter:** :rocket: venom update - cli - qr iamge ([e37fd0d](https://github.com/leifermendez/bot-whatsapp/commit/e37fd0da3635aa1041664d490d5f9803d2c441ca))
* **adapter:** :rocket: venom update - cli - qr iamge ([ca6afbb](https://github.com/leifermendez/bot-whatsapp/commit/ca6afbb87fceec12d4a383486ad693905e36881f))
* **adapter:** json db change is made ([386c1bb](https://github.com/leifermendez/bot-whatsapp/commit/386c1bbbac036aa58335fb5f62e3af2493766b6b))
* **adapter:** json db change is made ([3bdc7af](https://github.com/leifermendez/bot-whatsapp/commit/3bdc7afe8062527ff08620650d2c1177dfea83f5))
* agregamos variables para mysql ([dcf65b8](https://github.com/leifermendez/bot-whatsapp/commit/dcf65b87bc7e7e6381e6448e83118077986898e7))
* **bot:** :ambulance: fix callback functions ([d9aa97c](https://github.com/leifermendez/bot-whatsapp/commit/d9aa97c7819aca1446657bc0b75e9732f0f20c6b)), closes [#252](https://github.com/leifermendez/bot-whatsapp/issues/252)
* **bot:** :ambulance: fix callback functions ([964a074](https://github.com/leifermendez/bot-whatsapp/commit/964a074aa41324bd09d0c4e2e7aa663a0602b69c))
* **bot:** :fire: fix rev ([21407c0](https://github.com/leifermendez/bot-whatsapp/commit/21407c0e37f1ab12efecf887e699cedf05e3946a))
* **bot:** :fire: fix rev ([484c8c3](https://github.com/leifermendez/bot-whatsapp/commit/484c8c3bdefbc7824c32a86090bafae0593ecdac))
* **bot:** :zap: working callback Phase 1 ([952ce86](https://github.com/leifermendez/bot-whatsapp/commit/952ce86ffaa48a0d6fbc0a00a08c5d1efa14ee8e))
* **bot:** :zap: working nested new flow ([2cbc962](https://github.com/leifermendez/bot-whatsapp/commit/2cbc96245d795de749d894a3a0d99b6550f08d9e))
* **cli:** :art: starters ([a2be57f](https://github.com/leifermendez/bot-whatsapp/commit/a2be57f0aa42c6b5e13ad19c34abc7d9e81dc135))
* **cli:** :art: starters ([670ecf1](https://github.com/leifermendez/bot-whatsapp/commit/670ecf121babf53e76c2ea106c0710cbe59facde))
* **cli:** :fire: update instructions ([e585e2f](https://github.com/leifermendez/bot-whatsapp/commit/e585e2f5f644ed0188dc9cd2b3c697c9d6050669))
* **cli:** :fire: update instructions ([ed36ce0](https://github.com/leifermendez/bot-whatsapp/commit/ed36ce0a7796320c6a4a452f29c05a3f0f7368db))
* **cli:** :fire: update instructions ([bad1694](https://github.com/leifermendez/bot-whatsapp/commit/bad16943fc2089887d6bf0b6d90075d3bec6f9c7))
* **cli:** :fire: update instructions ([a21633f](https://github.com/leifermendez/bot-whatsapp/commit/a21633fb7cf348cc37f4e4714f51172b49b193b5))
* **cli:** :zap: updated ([a6f4aa8](https://github.com/leifermendez/bot-whatsapp/commit/a6f4aa8d1e809330c06c165aaf9a9f90b8922bb5))
* **conflict:** conflict resolution ([71d43b5](https://github.com/leifermendez/bot-whatsapp/commit/71d43b585a0ce173061c84e9879915e4602db026))
* **contexts:** :fire: added buttons ([eabef7a](https://github.com/leifermendez/bot-whatsapp/commit/eabef7a92d005cd0190196cfe75828c38885aadf))
* **contexts:** :fire: added buttons ([1b878d2](https://github.com/leifermendez/bot-whatsapp/commit/1b878d2ba0daeb3609af74a2ebae7948456e7fb0))
* **contexts:** :fire: added buttons ([78b0a9d](https://github.com/leifermendez/bot-whatsapp/commit/78b0a9dddc2a6e0fceb721ee7794efa2047f25fc))
* **contexts:** :fire: added buttons ([d8309f7](https://github.com/leifermendez/bot-whatsapp/commit/d8309f77e1d9137c0bec977ed9faef633cd90552))
* correccion en app.js para remover addChild en starters ([32db429](https://github.com/leifermendez/bot-whatsapp/commit/32db429f2946f344d949cb169a9595d657c06279))
* fix del db provider mysql ([b59d4fc](https://github.com/leifermendez/bot-whatsapp/commit/b59d4fcdd7462cde3f68ab5746d49960b547a592))
* provider equivocado en app.js de venom ([4e0a109](https://github.com/leifermendez/bot-whatsapp/commit/4e0a1091ee85cedfaa5a9c3d40e5cd50bc36cda3))
* **provider:** :bug: create static site html qr ([c7e56a4](https://github.com/leifermendez/bot-whatsapp/commit/c7e56a4b13c8829f91769eeca7f1f6b3473f68cf))
* **provider:** :bug: fix metea provider ([85f50be](https://github.com/leifermendez/bot-whatsapp/commit/85f50be9dcbf3817107898d8d2980baf05acd678))
* **provider:** :bug: fix metea provider ([a52aaa1](https://github.com/leifermendez/bot-whatsapp/commit/a52aaa11d883bbaf526cf87720d3c3fd9f89a986))
* **provider:** :bug: qr code accurate ([6c4845d](https://github.com/leifermendez/bot-whatsapp/commit/6c4845d733720d9916bb4008f9069ae4fd986a4b))
* **provider:** :bug: qr code accurate ([91bfdc4](https://github.com/leifermendez/bot-whatsapp/commit/91bfdc46301207cbc5274308da6f39c7b4652c63))
* **provider:** :fire: baileys fix ([928365d](https://github.com/leifermendez/bot-whatsapp/commit/928365dcafb3631acf6b1d0c239a906f8e1c4b0d))
* **provider:** :fire: send message togther with media ([78aa23f](https://github.com/leifermendez/bot-whatsapp/commit/78aa23fab094059145f82e6781f9366d5d582b4f))
* **provider:** :fire: send message togther with media ([b6bf43d](https://github.com/leifermendez/bot-whatsapp/commit/b6bf43d70fc28c6a229522b9b0de76cec43ac864))
* **provider:** :zap: baileys fix restart ([ae83774](https://github.com/leifermendez/bot-whatsapp/commit/ae83774365027e2e86127ab7713ae9ee2df31f33))
* **provider:** :zap: edit starter ([ff65832](https://github.com/leifermendez/bot-whatsapp/commit/ff65832012003423cc86d25cf0923452b1f8acb7))
* **provider:** :zap: edit starter ([68dd182](https://github.com/leifermendez/bot-whatsapp/commit/68dd1820f05d04780824b318072d053eaf7db654))
* **provider:** :zap: json space ([3cef741](https://github.com/leifermendez/bot-whatsapp/commit/3cef741c9ee30024eb42770a5f32931fcd372160))
* **provider:** :zap: json space ([9b087e0](https://github.com/leifermendez/bot-whatsapp/commit/9b087e071019a7b6c79195a24dc7ddec498c5716))
* **provider:** :zap: json space ([208fb4e](https://github.com/leifermendez/bot-whatsapp/commit/208fb4e9131dd5d4fd7230ba1aa11181337d9181))
* **provider:** :zap: json space ([54a59c7](https://github.com/leifermendez/bot-whatsapp/commit/54a59c7f0d4dbaab006ce7e3c74412d8d3613ecd))
* **provider:** qr-fix margin ([663641a](https://github.com/leifermendez/bot-whatsapp/commit/663641a1b8bf9234a88b0f3c38381ebc4bfa4bf9))
* se quito addChild de las constantes porque no se usa ([ba2291a](https://github.com/leifermendez/bot-whatsapp/commit/ba2291a3ddac0d4101021e11d03cb222c5a4bb3b))
* **starters:** :fire: updated staters ([4d4f15c](https://github.com/leifermendez/bot-whatsapp/commit/4d4f15ce73486d9335ad474d9e37c3b155670134))
* **starters:** :fire: updated staters ([a30eaac](https://github.com/leifermendez/bot-whatsapp/commit/a30eaac77534d17eb980f6ec126140e9d30aa06e))
* **starters:** :memo: update MIGRATION ([37fe323](https://github.com/leifermendez/bot-whatsapp/commit/37fe32322eb1bd41eecd151e52f17ec0588fb85e))
* **starters:** :memo: update MIGRATION ([9b30e7d](https://github.com/leifermendez/bot-whatsapp/commit/9b30e7dcfc30bc160b56427cc6cdc2dc982bde2a))
* **starters:** base templates are added for meta ([229e017](https://github.com/leifermendez/bot-whatsapp/commit/229e017ae20b84c9d12c7282f97b7034f5f33e6d))
* **starters:** base templates are added for meta ([20f6651](https://github.com/leifermendez/bot-whatsapp/commit/20f665175c9b47226df41ce43e05574bd6ab1930))
### [0.1.2](https://github.com/leifermendez/bot-whatsapp/compare/v0.1.1...v0.1.2) (2022-12-12)

View File

@@ -1,128 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
leifer.contacto@gmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -1,25 +1,7 @@
# CONTRIBUTING
### 📄 Bienvenido/a
Si deseas colaborar con el proyecto existen varias maneras, la primera de ellas es aportando conocimiento y mejorando el repositorio (actualizando documentación, mejorando código, revisando __[issues](https://github.com/codigoencasa/bot-whatsapp/issues)__, etc).
También es bien recibido los aportes económicos que se utilizaran para diferentes fines __[ver más](https://opencollective.com/bot-whatsapp)__
El lenguaje principal que se utilizó para desarrollar este proyecto fue __JavaScript__ con el fin de qué personas que están iniciando en el mundo de la programación puedan entender fácilmente.
### 🤔 Preguntas frecuentes
- ¿Como puedo hacer aportaciones de código en el proyecto?: [Ver Video](https://youtu.be/Lxt8Acob6aU)
- ¿Como ejecutar el entorno de pruebas?: [Ver Video](https://youtu.be/Mf9V-dloBfk)
- ¿Como crear un nuevo proveedor?: [Ver Video](https://youtu.be/cahK9zH3SI8)
- ¿Que son los GithubActions?: [Ver Video](https://youtu.be/nYBEBFKLiqw)
- ¿Canales de comunicación?: [Discord](https://link.codigoencasa.com/DISCORD)
-----
![](https://i.giphy.com/media/ntMt6TvalpstTIx7Ak/giphy.webp)
__Requerimientos:__
- Node v16 o superior __[descargar node](https://nodejs.org/es/download/)__
- __[Yarn](https://classic.yarnpkg.com/lang/en/docs/install/#windows-stable)__ como gestor de paquetes. En el link conseguirás las intrucciones para instalar yarn.
@@ -31,7 +13,7 @@ __Requerimientos:__
__Clonar repo rama dev__
```
git clone --branch dev https://github.com/codigoencasa/bot-whatsapp
git clone --branch dev https://github.com/leifermendez/bot-whatsapp
```
__Instalar dependencias__
```
@@ -52,49 +34,24 @@ Se ejecuta el CLI (Command Line Interface) para ayudarte a crear un app-bot de e
yarn run cli
```
Selecionas (mediante las flechas arriba y abajo) el proveedor que quieras usar y cuando estes sobre el presiona la barra de espacio, igualmente selecciona la base de datos que quieras usar.
Se creó un subdirecorio con el nombre del proveedor y base de datos que seleccionaste, ejemplo: `base-bailey-mysql`
Dentro de ese directorio necesitas editar el archivo package.json y borrar las siguientes lineas:
Abrir carpeta __example-app-base__ y ejecutar
```
"@bot-whatsapp/bot": "latest",
"@bot-whatsapp/cli": "latest",
"@bot-whatsapp/database": "latest",
"@bot-whatsapp/provider": "latest",
```
Cambiate al directorio creado ejemplo: `base-bailey-mysql`
```
cd base-baileys-mysql
```
Ejecuta los comandos:
```
npm install
cd example-app-base
npm i
npm run pre-copy
npm start
```
En el caso de MySql y Mongo es necesario especificar en app.js los datos de la conexión, ejemplo de MySql:
```
const BaileysProvider = require('@bot-whatsapp/provider/baileys')
const MySQLAdapter = require('@bot-whatsapp/database/mysql')
/**
* Declaramos las conexiones de MySQL
*/
const MYSQL_DB_HOST = 'localhost'
const MYSQL_DB_USER = 'usr'
const MYSQL_DB_PASSWORD = 'pass'
const MYSQL_DB_NAME = 'bot'
```
<!-- __Seguir instrucciones__
En la consola encontraras los pasos a seguir -->
### __Commit y Push__
![](https://i.imgur.com/dC6lEwy.png)
El proyecto tiene implementado __[husky](https://typicode.github.io/husky/#/)__, es una herramienta que dispara unas acciones al momento de hacer commit y hacer push.
__commit:__ Los commit son semánticos, esto quiere decir que deben cumplir un standar al momento de escribirlos ejemplo: ` feat(adapter): new adapter myqsl ` puede ver más info sobre esto __[aquí](https://github.com/conventional-changelog/commitlint/#what-is-commitlint)__
__push:__ Cada push ejecutar `yarn run test` el cual realiza los test internos que tienen que cumplir con __95% de cobertura__.
> __NOTA:__ [Eres libre de aportar informacion a este documento o arreglar ortografia 🤣](
https://github.com/codigoencasa/bot-whatsapp/edit/dev/CONTRIBUTING.md)
> Documento en constante actualización....
------
- [Discord](https://link.codigoencasa.com/DISCORD)

View File

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

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2022 Leifer Mendez
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,214 +0,0 @@
# Migración
#### Versión (legacy)
En la ***versión (legacy)*** se implementaban los flujos de esta manera, en dos archivos independientes.
> __`initial.json`__ para establecer las palabras claves y el flujo a responder, por otro lado tambien se necesitaba implementar
> __`response.json`__ donde se escriben los mensajes a responder.
```json
//initial.json
[
{
"keywords": [
"hola",
"ola",
"alo"
],
"key": "hola"
},
{
"keywords": ["productos", "info"],
"key": "productos"
},
{
"keywords": ["adios", "bye"],
"key": "adios"
},
{
"keywords": ["imagen", "foto"],
"key": "catalogo"
}
]
```
```json
//response.json
{
"hola":{
"replyMessage":[
"Gracias a ti! \n"
],
"media":null,
"trigger":null
},
"adios":{
"replyMessage":[
"Que te vaya bien!!"
],
},
"productos":{
"replyMessage":[
"Más productos aquí"
],
"trigger":null,
"actions":{
"title":"¿Que te interesa ver?",
"message":"Abajo unos botons",
"footer":"",
"buttons":[
{"body":"Telefonos"},
{"body":"Computadoras"},
{"body":"Otros"}
]
}
},
"catalogo":{
"replyMessage":[
"Te envio una imagen"
],
"media":"https://media2.giphy.com/media/VQJu0IeULuAmCwf5SL/giphy.gif",
"trigger":null,
},
}
```
#### Versión 2 (0.2.X)
En esta versión es mucho más sencillo, abajo encontraras un ejemplo del mismo flujo anteriormente mostrado.
```js
//app.js
const {
createBot,
createProvider,
createFlow,
addKeyword,
addChild,
} = require('@bot-whatsapp/bot')
const BaileysProvider = require('@bot-whatsapp/provider/baileys')
const MockAdapter = require('@bot-whatsapp/database/mock')
/**
* Declarando flujos principales.
*/
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 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 main = async () => {
const adapterDB = new MockAdapter()
const adapterFlow = createFlow([flowHola, flowAdios, flowProductos, flowCatalogo])
const adapterProvider = createProvider(BaileysProvider)
createBot({
flow: adapterFlow,
provider: adapterProvider,
database: adapterDB,
})
}
```
#### Flujos hijos
A continuación se muestra un ejemplo de flujos hijos, estos nos sirven para crear flujos que solo se disparan cuando el flujo anterior es el especificado, ejemplo:
> Menu Principal (Escoge zapatos o bolsos)
> - SubMenu 1 (Elegiste bolsos, ahora escoge piel o tela)
> - Submenu 1.1 (piel)
> - Submenu 2 (Elegiste zapatos, ahora escoge piel o tela)
> - Submenu 2.1 (piel)
El __submenu 1__ solo se va a disparar cuando el flujo anterior sea el __principal__, e igualmente el __submenu 1.1__, solo cuando el flujo anterior sea el __submenu 1__, ejemplo:
```js
/**
* Aqui declaramos los flujos hijos, los flujos se declaran de atras para adelante, es decir que si tienes un flujo de este tipo:
*
* Menu Principal
* - SubMenu 1
* - Submenu 1.1
* - Submenu 2
* - Submenu 2.1
*
* Primero declaras los submenus 1.1 y 2.1, luego el 1 y 2 y al final el principal.
*/
const flowBolsos2 = addKeyword(['bolsos2', '2'])
.addAnswer('🤯 *MUCHOS* bolsos ...')
.addAnswer('y mas bolsos... bla bla')
const flowZapatos2 = addKeyword(['zapatos2', '2'])
.addAnswer('🤯 repito que tengo *MUCHOS* zapatos.')
.addAnswer('y algunas otras cosas.')
const flowZapatos = addKeyword(['1', 'zapatos', 'ZAPATOS'])
.addAnswer('🤯 Veo que elegiste zapatos')
.addAnswer('Tengo muchos zapatos...bla bla')
.addAnswer(
['Manda:', '*(2) Zapatos2*', 'para mas información'],
{ capture: true },
(ctx) => {
console.log('Aqui puedes ver más info del usuario...')
console.log('Puedes enviar un mail, hook, etc..')
console.log(ctx)
},
[...addChild(flowZapatos2)]
)
const flowBolsos = addKeyword(['2', 'bolsos', 'BOLSOS'])
.addAnswer('🙌 Veo que elegiste bolsos')
.addAnswer('Tengo muchos bolsos...bla bla')
.addAnswer(
['Manda:', '*(2) Bolsos2*', 'para mas información.'],
{ capture: true },
(ctx) => {
console.log('Aqui puedes ver más info del usuario...')
console.log('Puedes enviar un mail, hook, etc..')
console.log(ctx)
},
[...addChild(flowBolsos2)]
)
/**
* Declarando flujo principal
*/
const flowPrincipal = addKeyword(['hola', 'ole', 'alo'])
.addAnswer(['Hola, bienvenido a mi tienda', '¿Como puedo ayudarte?'])
.addAnswer(['Tengo:', 'Zapatos', 'Bolsos', 'etc ...'])
.addAnswer(
['Para continuar escribe:', '*(1) Zapatos*', '*(2) Bolsos*'],
{ capture: true },
(ctx) => {
console.log('Aqui puedes ver más info del usuario...')
console.log('Puedes enviar un mail, hook, etc..')
console.log(ctx)
},
[...addChild(flowBolsos), ...addChild(flowZapatos)]
)
```
> Forma parte de este proyecto.
- [Discord](https://link.codigoencasa.com/DISCORD)
- [Twitter](https://twitter.com/leifermendez)
- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
- [Telegram](https://t.me/leifermendez)

126
README.md
View File

@@ -1,45 +1,16 @@
# Chatbot Library
![](https://img.shields.io/npm/v/@bot-whatsapp/bot?color=%2300c200&label=%40bot-whatsapp)
[![Test / Coverage](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml/badge.svg)](https://github.com/leifermendez/bot-whatsapp/actions/workflows/ci.yml)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
[![](https://img.shields.io/discord/915193197645402142?logo=discord)](https://link.codigoencasa.com/DISCORD)
--------
🦊 Documentación: [https://bot-whatsapp.pages.dev/](https://bot-whatsapp.pages.dev/)
Video como hacer PR: https://youtu.be/Lxt8Acob6aU
<p align="center">
<img width="300" src="https://i.imgur.com/Oauef6t.png">
</p>
🚀 __Roadmap:__ [https://github.com/users/leifermendez/projects/4/views/1](https://github.com/users/leifermendez/projects/4/views/1)
**Con esta librería, puedes construir flujos automatizados de conversación de manera agnóstica al proveedor de WhatsApp,** 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, puedes configurar fácilmente disparadores que te ayudaran a expandir las funcionalidades sin límites. **[Ver más informacion](https://bot-whatsapp.netlify.app/)**
## Comenzar
```
npm create bot-whatsapp@latest
```
Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
- Instalacion
- Base de datos
- Proveedores
## Recursos
- [📄 Documentación](https://bot-whatsapp.netlify.app/)
- [🚀 Roadmap](https://github.com/orgs/codigoencasa/projects/1)
- [💻 Discord](https://link.codigoencasa.com/DISCORD)
- [👌 Twitter](https://twitter.com/leifermendez)
- [🎥 Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
## Comunidad
**Comunidad**
<!-- readme: collaborators,contributors -start -->
<table>
<tr>
<td align="center">
<a href="https://github.com/cheveguerra">
<img src="https://avatars.githubusercontent.com/u/5891114?v=4" width="50;" alt="cheveguerra"/>
<br />
<sub><b>Jose Alberto Guerra Ugalde</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/leifermendez">
<img src="https://avatars.githubusercontent.com/u/15802366?v=4" width="50;" alt="leifermendez"/>
@@ -68,42 +39,6 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
<sub><b>Leifer Mendez</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/danielcasta0398">
<img src="https://avatars.githubusercontent.com/u/98791147?v=4" width="50;" alt="danielcasta0398"/>
<br />
<sub><b>Juan Daniel Castaño</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/marianarolfo">
<img src="https://avatars.githubusercontent.com/u/68322254?v=4" width="50;" alt="marianarolfo"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/HKong31">
<img src="https://avatars.githubusercontent.com/u/113340082?v=4" width="50;" alt="HKong31"/>
<br />
<sub><b>HLKong</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jzvi12">
<img src="https://avatars.githubusercontent.com/u/10729787?v=4" width="50;" alt="jzvi12"/>
<br />
<sub><b>Zvi</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/JosephVTX">
<img src="https://avatars.githubusercontent.com/u/91026290?v=4" width="50;" alt="JosephVTX"/>
<br />
<sub><b>Joseph Vega Callupe</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Gonzalito87">
<img src="https://avatars.githubusercontent.com/u/100331586?v=4" width="50;" alt="Gonzalito87"/>
@@ -112,41 +47,13 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
</a>
</td>
<td align="center">
<a href="https://github.com/devrlbusiness">
<img src="https://avatars.githubusercontent.com/u/66280283?v=4" width="50;" alt="devrlbusiness"/>
<a href="https://github.com/jzvi12">
<img src="https://avatars.githubusercontent.com/u/10729787?v=4" width="50;" alt="jzvi12"/>
<br />
<sub><b>Developer RL Business</b></sub>
<sub><b>Null</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/Gregoriotecnico">
<img src="https://avatars.githubusercontent.com/u/118696506?v=4" width="50;" alt="Gregoriotecnico"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/jlferrete">
<img src="https://avatars.githubusercontent.com/u/36698913?v=4" width="50;" alt="jlferrete"/>
<br />
<sub><b>Jose Luis Ferrete Olarte</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/lisandroprada">
<img src="https://avatars.githubusercontent.com/u/7232326?v=4" width="50;" alt="lisandroprada"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/6rak0">
<img src="https://avatars.githubusercontent.com/u/12260031?v=4" width="50;" alt="6rak0"/>
<br />
<sub><b>Null</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/tonyvazgar">
<img src="https://avatars.githubusercontent.com/u/21047090?v=4" width="50;" alt="tonyvazgar"/>
@@ -154,14 +61,20 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
<sub><b>Luis Antonio Vázquez García</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/ulisesvina">
<img src="https://avatars.githubusercontent.com/u/20508563?v=4" width="50;" alt="ulisesvina"/>
<br />
<sub><b>Ulises Viña</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/rrruuuyyy">
<img src="https://avatars.githubusercontent.com/u/33061671?v=4" width="50;" alt="rrruuuyyy"/>
<br />
<sub><b>Rodrigo Mendoza Cabrera</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/yond1994">
<img src="https://avatars.githubusercontent.com/u/47557263?v=4" width="50;" alt="yond1994"/>
@@ -172,4 +85,9 @@ Entiende más a fondo sus funcionalidades explicadas en nuestra documentación.
</table>
<!-- readme: collaborators,contributors -end -->
> Forma parte de este proyecto.
- [Discord](https://link.codigoencasa.com/DISCORD)
- [Twitter](https://twitter.com/leifermendez)
- [Youtube](https://www.youtube.com/watch?v=5lEMCeWEJ8o&list=PL_WGMLcL4jzWPhdhcUyhbFU6bC0oJd2BR)
- [Telegram](https://t.me/leifermendez)

View File

@@ -1,21 +0,0 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.

View File

@@ -1,50 +0,0 @@
const MOCK_DB = require('../packages/database/src/mock')
const PROVIDER_DB = require('../packages/provider/src/mock')
class MOCK_FLOW {
allCallbacks = { ref: () => 1 }
flowSerialize = []
flowRaw = []
find = (arg) => {
if (arg) {
return [{ answer: 'answer', ref: 'ref' }]
} else {
return null
}
}
findBySerialize = () => ({})
findIndexByRef = () => 0
}
const cleaName = (name) => {
name = name.toLowerCase()
name = name.replaceAll(' ', '-')
name = name.replaceAll(':', '-')
name = name.replaceAll('"', '-')
return name
}
/**
* Preparar env para el test
* @param {*} context
*/
const setup = async (context) => {
const name = cleaName(`${context.__suite__}-${context.__test__}`)
const filename = `test-${name}.json`
context.provider = new PROVIDER_DB()
context.database = new MOCK_DB({ filename })
context.flow = new MOCK_FLOW()
await delay(10)
}
const clear = async (context) => {
context.provider = null
context.database = null
context.flow = null
}
function delay(ms) {
return new Promise((res) => setTimeout(res, ms))
}
module.exports = { setup, clear, delay }

6
__mocks__/mobile.mock.js Normal file
View File

@@ -0,0 +1,6 @@
const MOCK_MOBILE_WS = {
from: 'XXXXXX',
hasMedia: false,
}
module.exports = { MOCK_MOBILE_WS }

View File

@@ -1,51 +0,0 @@
const { suite } = require('uvu')
const assert = require('uvu/assert')
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
const { setup, clear, delay } = require('../__mocks__/env')
const suiteCase = suite('Flujo: Simple')
suiteCase.before.each(setup)
suiteCase.after.each(clear)
suiteCase(`Responder a "hola"`, async ({ database, provider }) => {
const flow = addKeyword('hola').addAnswer('Buenas!').addAnswer('Como vamos!')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await delay(10)
assert.is('Buenas!', database.listHistory[0].answer)
assert.is('Como vamos!', database.listHistory[1].answer)
assert.is(undefined, database.listHistory[2])
})
suiteCase(`NO reponder a "pepe"`, async ({ database, provider }) => {
const flow = addKeyword('hola').addAnswer('Buenas!').addAnswer('Como vamos!')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'pepe',
})
await delay(100)
assert.is(undefined, database.listHistory[0])
assert.is(undefined, database.listHistory[1])
})
suiteCase.run()

View File

@@ -1,31 +0,0 @@
const { suite } = require('uvu')
const assert = require('uvu/assert')
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
const { setup, clear, delay } = require('../__mocks__/env')
const suiteCase = suite('Flujo: Provider envia un location')
suiteCase.before.each(setup)
suiteCase.after.each(clear)
suiteCase(`Responder a "CURRENT_LOCATION"`, async ({ database, provider }) => {
const flow = addKeyword('#CURRENT_LOCATION#').addAnswer('Gracias por tu location')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: '#CURRENT_LOCATION#',
})
await delay(200)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is('Gracias por tu location', getHistory[0])
assert.is(undefined, getHistory[1])
})
suiteCase.run()

View File

@@ -1,51 +0,0 @@
const { suite } = require('uvu')
const assert = require('uvu/assert')
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
const { setup, clear, delay } = require('../__mocks__/env')
const suiteCase = suite('Flujo: sensitive')
suiteCase.before.each(setup)
suiteCase.after.each(clear)
suiteCase(`Responder a "ole" en minuscula`, async ({ database, provider }) => {
const flow = addKeyword(['ola', 'ole'], { sensitive: true }).addAnswer('Bienvenido a la OLA')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'ole',
})
await delay(100)
assert.is('Bienvenido a la OLA', database.listHistory[0].answer)
assert.is(undefined, database.listHistory[1])
})
suiteCase(`NO Responder a "ole" en minuscula`, async ({ database, provider }) => {
const flow = addKeyword(['ola', 'ole'], { sensitive: true }).addAnswer('Bienvenido a la OLA')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'OLE',
})
await delay(100)
assert.is(undefined, database.listHistory[0])
assert.is(undefined, database.listHistory[1])
})
suiteCase.run()

View File

@@ -1,89 +0,0 @@
const { suite } = require('uvu')
const assert = require('uvu/assert')
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
const { setup, clear, delay } = require('../__mocks__/env')
const fakeHTTP = async () => {
await delay(10)
}
const suiteCase = suite('Flujo: hijos con callbacks')
suiteCase.before.each(setup)
suiteCase.after.each(clear)
suiteCase(`Debe continuar el flujo del hijo`, async ({ database, provider }) => {
const flowCash = addKeyword('cash').addAnswer('Traeme los billetes! 😎')
const flowOnline = addKeyword('paypal')
.addAnswer('Voy generar un link de paypal *escribe algo*', { capture: true }, async (_, { flowDynamic }) => {
await fakeHTTP()
await flowDynamic('Esperate.... estoy generando esto toma su tiempo')
})
.addAnswer('Aqui lo tienes 😎😎', null, async (_, { flowDynamic }) => {
await fakeHTTP()
await flowDynamic('http://paypal.com')
})
.addAnswer('Apurate!')
const flujoPrincipal = addKeyword('hola')
.addAnswer('¿Como estas todo bien?')
.addAnswer('Espero que si')
.addAnswer('¿Cual es tu email?', { capture: true }, async (ctx, { fallBack }) => {
if (!ctx.body.includes('@')) {
return fallBack('Veo que no es um mail *bien*')
}
})
.addAnswer('Voy a validar tu email...', null, async (_, { flowDynamic }) => {
await fakeHTTP()
return flowDynamic('Email validado correctamten!')
})
.addAnswer('¿Como vas a pagar *paypal* o *cash*?', { capture: true }, async () => {}, [flowCash, flowOnline])
createBot({
database,
flow: createFlow([flujoPrincipal]),
provider,
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await provider.delaySendMessage(30, 'message', {
from: '000',
body: 'test@test.com',
})
await provider.delaySendMessage(60, 'message', {
from: '000',
body: 'paypal',
})
await provider.delaySendMessage(90, 'message', {
from: '000',
body: 'continue!',
})
await delay(800)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is('¿Como estas todo bien?', getHistory[0])
assert.is('Espero que si', getHistory[1])
assert.is('¿Cual es tu email?', getHistory[2])
assert.is('test@test.com', getHistory[3])
assert.is('Voy a validar tu email...', getHistory[4])
assert.is('Email validado correctamten!', getHistory[5])
assert.is('¿Como vas a pagar *paypal* o *cash*?', getHistory[6])
assert.is('paypal', getHistory[7])
assert.is('Voy generar un link de paypal *escribe algo*', getHistory[8])
assert.is('continue!', getHistory[9])
assert.is('Esperate.... estoy generando esto toma su tiempo', getHistory[10])
assert.is('Aqui lo tienes 😎😎', getHistory[11])
assert.is('http://paypal.com', getHistory[12])
assert.is('Apurate!', getHistory[13])
assert.is(undefined, getHistory[14])
})
suiteCase.run()

View File

@@ -1,59 +0,0 @@
const { suite } = require('uvu')
const assert = require('uvu/assert')
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
const { setup, clear, delay } = require('../__mocks__/env')
const suiteCase = suite('Flujo: regex')
suiteCase.before.each(setup)
suiteCase.after.each(clear)
suiteCase(`Responder a una expresion regular`, async ({ database, provider }) => {
const REGEX_CREDIT_NUMBER = `/(^4[0-9]{12}(?:[0-9]{3})?$)|(^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$)|(3[47][0-9]{13})|(^3(?:0[0-5]|[68][0-9])[0-9]{11}$)|(^6(?:011|5[0-9]{2})[0-9]{12}$)|(^(?:2131|1800|35\d{3})\d{11}$)/gm`
const flow = addKeyword(REGEX_CREDIT_NUMBER, { regex: true })
.addAnswer(`Gracias por proporcionar un numero de tarjeta valido`)
.addAnswer('Fin!')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: '374245455400126',
})
await delay(100)
assert.is('Gracias por proporcionar un numero de tarjeta valido', database.listHistory[0].answer)
assert.is('Fin!', database.listHistory[1].answer)
assert.is(undefined, database.listHistory[2])
})
suiteCase(`NO Responder a una expresion regular`, async ({ database, provider }) => {
const REGEX_CREDIT_NUMBER = `/(^4[0-9]{12}(?:[0-9]{3})?$)|(^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$)|(3[47][0-9]{13})|(^3(?:0[0-5]|[68][0-9])[0-9]{11}$)|(^6(?:011|5[0-9]{2})[0-9]{12}$)|(^(?:2131|1800|35\d{3})\d{11}$)/gm`
const flow = addKeyword(REGEX_CREDIT_NUMBER, { regex: true })
.addAnswer(`Gracias por proporcionar un numero de tarjeta valido`)
.addAnswer('Fin!')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await delay(100)
assert.is(undefined, database.listHistory[0])
assert.is(undefined, database.listHistory[1])
})
suiteCase.run()

View File

@@ -1,40 +0,0 @@
const { suite } = require('uvu')
const assert = require('uvu/assert')
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
const { setup, clear, delay } = require('../__mocks__/env')
const suiteCase = suite('Flujo: capture')
suiteCase.before.each(setup)
suiteCase.after.each(clear)
suiteCase(`Responder a "pregunta"`, async ({ database, provider }) => {
const flow = addKeyword(['hola'])
.addAnswer(['Hola como estas?', '¿Cual es tu edad?'], { capture: true })
.addAnswer('Gracias por tu respuesta')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await provider.delaySendMessage(10, 'message', {
from: '000',
body: '90',
})
await delay(100)
assert.is(['Hola como estas?', '¿Cual es tu edad?'].join('\n'), database.listHistory[0].answer)
assert.is('90', database.listHistory[1].answer)
assert.is('Gracias por tu respuesta', database.listHistory[2].answer)
assert.is(undefined, database.listHistory[3])
})
suiteCase.run()

View File

@@ -1,162 +0,0 @@
const { suite } = require('uvu')
const assert = require('uvu/assert')
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
const { setup, clear, delay } = require('../__mocks__/env')
const fakeHTTP = async (fakeData = []) => {
await delay(50)
const data = fakeData.map((u) => ({ body: `${u}` }))
return Promise.resolve(data)
}
const suiteCase = suite('Flujo: flowDynamic')
suiteCase.before.each(setup)
suiteCase.after.each(clear)
suiteCase(`Responder con mensajes asyncronos`, async ({ database, provider }) => {
const MOCK_VALUES = [
'Bienvenido te envio muchas marcas (5510)',
'Seleccione marca del auto a cotizar, con el *número* correspondiente',
'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:',
'Los precios rondan:',
]
const flow = addKeyword(['hola'])
.addAnswer(MOCK_VALUES[0], null, async (_, { flowDynamic }) => {
const data = await fakeHTTP(['Ford', 'GM', 'BMW'])
return flowDynamic(data)
})
.addAnswer(MOCK_VALUES[1], null, async (_, { flowDynamic }) => {
const data = await fakeHTTP(['Ranger', 'Explorer'])
return flowDynamic(data)
})
.addAnswer(MOCK_VALUES[2], null, async (_, { flowDynamic }) => {
const data = await fakeHTTP(['Usado', 'Nuevos'])
return flowDynamic(data)
})
.addAnswer(MOCK_VALUES[3], null, async (_, { flowDynamic }) => {
const data = await fakeHTTP(['1000', '2000', '3000'])
return flowDynamic(data)
})
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await delay(1500)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is(MOCK_VALUES[0], getHistory[0])
//FlowDynamic
assert.is('Ford', getHistory[1])
assert.is('GM', getHistory[2])
assert.is('BMW', getHistory[3])
assert.is(MOCK_VALUES[1], getHistory[4])
//FlowDynamic
assert.is('Ranger', getHistory[5])
assert.is('Explorer', getHistory[6])
assert.is(MOCK_VALUES[2], getHistory[7])
//FlowDynamic
assert.is('Usado', getHistory[8])
assert.is('Nuevos', getHistory[9])
assert.is(MOCK_VALUES[3], getHistory[10])
//FlowDynamic
assert.is('1000', getHistory[11])
assert.is('2000', getHistory[12])
assert.is('3000', getHistory[13])
assert.is(undefined, getHistory[14])
})
suiteCase(`Responder con un "string"`, async ({ database, provider }) => {
const flow = addKeyword(['hola'])
.addAnswer('Como vas?', null, async (_, { flowDynamic }) => {
return flowDynamic('Todo bien!')
})
.addAnswer('y vos?')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await delay(100)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is('Como vas?', getHistory[0])
assert.is('Todo bien!', getHistory[1])
assert.is('y vos?', getHistory[2])
assert.is(undefined, getHistory[3])
})
suiteCase(`Responder con un "array"`, async ({ database, provider }) => {
const flow = addKeyword(['hola'])
.addAnswer('Como vas?', null, async (_, { flowDynamic }) => {
return flowDynamic(['Todo bien!', 'trabajando'])
})
.addAnswer('y vos?')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await delay(100)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is('Como vas?', getHistory[0])
assert.is('Todo bien!', getHistory[1])
assert.is('trabajando', getHistory[2])
assert.is('y vos?', getHistory[3])
assert.is(undefined, getHistory[4])
})
suiteCase(`Responder con un "object"`, async ({ database, provider }) => {
const flow = addKeyword(['hola'])
.addAnswer('Como vas?', null, async (_, { flowDynamic }) => {
return flowDynamic([{ body: 'Todo bien!' }])
})
.addAnswer('y vos?')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await delay(100)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is('Como vas?', getHistory[0])
assert.is('Todo bien!', getHistory[1])
assert.is('y vos?', getHistory[2])
assert.is(undefined, getHistory[3])
})
suiteCase.run()

View File

@@ -1,167 +0,0 @@
const { suite } = require('uvu')
const assert = require('uvu/assert')
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
const { setup, clear, delay } = require('../__mocks__/env')
const fakeHTTP = async (fakeData = []) => {
await delay(50)
const data = fakeData.map((u) => ({ body: `${u}` }))
return Promise.resolve(data)
}
const suiteCase = suite('Flujo: endFlow')
suiteCase.before.each(setup)
suiteCase.after.each(clear)
suiteCase(`Detener el flujo`, async ({ database, provider }) => {
const MOCK_VALUES = [
'Bienvenido te envio muchas marcas',
'Seleccione marca del auto a cotizar, con el *número* correspondiente',
'Seleccione la sub marca del auto a cotizar, con el *número* correspondiente:',
'Los precios rondan:',
]
const flow = addKeyword(['hola'])
.addAnswer(MOCK_VALUES[0], null, async (_, { flowDynamic }) => {
const data = await fakeHTTP(['Ford', 'GM', 'BMW'])
return flowDynamic(data)
})
.addAnswer(MOCK_VALUES[1], null, async (_, { endFlow }) => {
return endFlow()
})
.addAnswer(MOCK_VALUES[2])
.addAnswer(MOCK_VALUES[3], null, async (_, { flowDynamic }) => {
const data = await fakeHTTP(['1000', '2000', '3000'])
return flowDynamic(data)
})
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await delay(900)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is(MOCK_VALUES[0], getHistory[0])
//FlowDynamic
assert.is('Ford', getHistory[1])
assert.is('GM', getHistory[2])
assert.is('BMW', getHistory[3])
assert.is(MOCK_VALUES[1], getHistory[4])
//FlowDynamic
assert.is(undefined, getHistory[5])
assert.is(undefined, getHistory[6])
})
suiteCase(`Detener el flujo flowDynamic`, async ({ database, provider }) => {
const flow = addKeyword(['hola'])
.addAnswer('Buenas!', null, async (_, { endFlow, flowDynamic }) => {
await flowDynamic('Continuamos...')
return endFlow()
})
.addAnswer('Como estas!')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await delay(100)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is('Buenas!', getHistory[0])
assert.is('Continuamos...', getHistory[1])
assert.is(undefined, getHistory[2])
})
suiteCase(`flowDynamic con capture`, async ({ database, provider }) => {
const MOCK_VALUES = ['¿CUal es tu email?', 'Continuamos....', '¿Cual es tu edad?']
const flow = addKeyword(['hola'])
.addAnswer(
MOCK_VALUES[0],
{
capture: true,
},
async (ctx, { flowDynamic, fallBack }) => {
const validation = ctx.body.includes('@')
if (validation) {
const getDataFromApi = await fakeHTTP(['Gracias por tu email se ha validado de manera correcta'])
return flowDynamic(getDataFromApi)
}
return fallBack()
}
)
.addAnswer(MOCK_VALUES[1])
.addAnswer(MOCK_VALUES[2], { capture: true }, async (ctx, { flowDynamic, fallBack }) => {
if (ctx.body !== '18') {
await delay(20)
return fallBack('Ups creo que no eres mayor de edad')
}
return flowDynamic('Bien tu edad es correcta!')
})
.addAnswer('Puedes pasar')
createBot({
database,
provider,
flow: createFlow([flow]),
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await provider.delaySendMessage(10, 'message', {
from: '000',
body: 'this is not email value',
})
await provider.delaySendMessage(20, 'message', {
from: '000',
body: 'test@test.com',
})
await provider.delaySendMessage(90, 'message', {
from: '000',
body: '20',
})
await provider.delaySendMessage(200, 'message', {
from: '000',
body: '18',
})
await delay(900)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is(MOCK_VALUES[0], getHistory[0])
assert.is('this is not email value', getHistory[1])
assert.is(MOCK_VALUES[0], getHistory[2])
assert.is('test@test.com', getHistory[3])
assert.is('Gracias por tu email se ha validado de manera correcta', getHistory[4])
assert.is(MOCK_VALUES[1], getHistory[5])
assert.is(MOCK_VALUES[2], getHistory[6])
assert.is('20', getHistory[7])
assert.is('Ups creo que no eres mayor de edad', getHistory[8])
assert.is('18', getHistory[9])
assert.is('Bien tu edad es correcta!', getHistory[10])
assert.is('Puedes pasar', getHistory[11])
})
suiteCase.run()

View File

@@ -1,97 +0,0 @@
const { suite } = require('uvu')
const assert = require('uvu/assert')
const { addKeyword, createBot, createFlow } = require('../packages/bot/index')
const { setup, clear, delay } = require('../__mocks__/env')
const suiteCase = suite('Flujo: manejo de estado')
suiteCase.before.each(setup)
suiteCase.after.each(clear)
suiteCase(`Debe retornar un mensaje resumen`, async ({ database, provider }) => {
let STATE_APP = {}
const MOCK_VALUES = ['¿Cual es tu nombre?', '¿Cual es tu edad?', 'Tu datos son:']
const flujoPrincipal = addKeyword(['hola'])
.addAnswer(
MOCK_VALUES[0],
{
capture: true,
},
async (ctx, { flowDynamic }) => {
STATE_APP[ctx.from] = { ...STATE_APP[ctx.from], name: ctx.body }
flowDynamic('Gracias por tu nombre!')
}
)
.addAnswer(
MOCK_VALUES[1],
{
capture: true,
},
async (ctx, { flowDynamic }) => {
STATE_APP[ctx.from] = { ...STATE_APP[ctx.from], age: ctx.body }
await flowDynamic(`Gracias por tu edad! ${STATE_APP[ctx.from].name}`)
}
)
.addAnswer(MOCK_VALUES[2], null, async (ctx, { flowDynamic }) => {
flowDynamic(`Nombre: ${STATE_APP[ctx.from].name} Edad: ${STATE_APP[ctx.from].age}`)
})
.addAnswer('🤖🤖 Gracias por tu participacion')
createBot({
database,
flow: createFlow([flujoPrincipal]),
provider,
})
await provider.delaySendMessage(0, 'message', {
from: '000',
body: 'hola',
})
await provider.delaySendMessage(5, 'message', {
from: '001',
body: 'hola',
})
await provider.delaySendMessage(10, 'message', {
from: '000',
body: 'Leifer',
})
await provider.delaySendMessage(15, 'message', {
from: '000',
body: '90',
})
await provider.delaySendMessage(20, 'message', {
from: '001',
body: 'Maria',
})
await provider.delaySendMessage(25, 'message', {
from: '001',
body: '100',
})
await delay(1000)
const getHistory = database.listHistory.map((i) => i.answer)
assert.is(MOCK_VALUES[0], getHistory[0])
assert.is('¿Cual es tu nombre?', getHistory[1])
assert.is('Leifer', getHistory[2])
assert.is('Gracias por tu nombre!', getHistory[3])
assert.is('¿Cual es tu edad?', getHistory[4])
assert.is('90', getHistory[5])
assert.is('Gracias por tu edad! Leifer', getHistory[6])
assert.is('Tu datos son:', getHistory[7])
assert.is('Nombre: Leifer Edad: 90', getHistory[8])
assert.is('🤖🤖 Gracias por tu participacion', getHistory[9])
assert.is('Maria', getHistory[10])
assert.is('Gracias por tu nombre!', getHistory[11])
assert.is('100', getHistory[12])
assert.is(undefined, getHistory[13])
})
suiteCase.run()

View File

@@ -1,10 +1,28 @@
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: {
@@ -38,7 +56,8 @@ 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',
},
@@ -48,7 +67,8 @@ module.exports = {
value: 'release',
},
style: {
description: 'Markup, white-space, formatting, missing semi-colons...',
description:
'Markup, white-space, formatting, missing semi-colons...',
emoji: '(💄)',
value: 'style',
},
@@ -60,7 +80,8 @@ 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:',

View File

@@ -1,6 +1,6 @@
{
"name": "@bot-whatsapp/root",
"version": "0.1.21",
"version": "0.1.2",
"description": "Bot de wahtsapp open source para MVP o pequeños negocios",
"main": "app.js",
"private": true,
@@ -10,21 +10,16 @@
"create-bot:rollup": "rollup --config ./packages/create-bot-whatsapp/rollup-create.config.js ",
"bot:rollup": "rollup --config ./packages/bot/rollup-bot.config.js",
"provider:rollup": "rollup --config ./packages/provider/rollup-provider.config.js ",
"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: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",
"build": "yarn run cli:rollup && yarn run bot:rollup && yarn run provider:rollup && yarn run database:rollup && yarn run create-bot-whatsapp:rollup",
"copy.lib": "node ./scripts/move.js",
"test.unit": "node ./node_modules/uvu/bin.js packages test",
"test.e2e": "node ./node_modules/uvu/bin.js __test__",
"test.coverage": "node ./node_modules/c8/bin/c8.js npm run test.unit",
"test": "npm run test.coverage",
"cli": "node ./packages/cli/bin/cli.js",
@@ -34,7 +29,7 @@
"prepare": "npx husky install",
"preinstall": "npx only-allow yarn",
"postinstall": "npx prettier --write .",
"release": "standard-version -- --prerelease --global"
"release": "standard-version -- --prerelease"
},
"workspaces": [
"packages/create-bot-whatsapp",
@@ -42,8 +37,6 @@
"packages/cli",
"packages/database",
"packages/provider",
"packages/contexts",
"packages/portal",
"packages/docs"
],
"keywords": [
@@ -68,7 +61,6 @@
"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",
@@ -81,7 +73,6 @@
"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",

View File

@@ -8,9 +8,6 @@ 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
@@ -21,12 +18,10 @@ class CoreClass {
flowClass
databaseClass
providerClass
generalArgs = { blackList: [] }
constructor(_flow, _database, _provider, _args) {
constructor(_flow, _database, _provider) {
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)
@@ -43,7 +38,8 @@ 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',
@@ -51,7 +47,8 @@ class CoreClass {
},
{
event: 'auth_failure',
func: ({ instructions }) => printer(instructions, '⚡⚡ ERROR AUTH ⚡⚡'),
func: ({ instructions }) =>
printer(instructions, '⚡⚡ ERROR AUTH ⚡⚡'),
},
{
@@ -61,22 +58,22 @@ class CoreClass {
]
/**
* GLOSSARY.md
* @param {*} messageCtxInComming
*
* @param {*} messageInComming
* @returns
*/
handleMsg = async (messageCtxInComming) => {
logger.log(`[handleMsg]: `, messageCtxInComming)
const { body, from } = messageCtxInComming
handleMsg = async (messageInComming) => {
logger.log(`[handleMsg]: `, messageInComming)
const { body, from } = messageInComming
let msgToSend = []
let endFlowFlag = false
let fallBackFlag = false
if (this.generalArgs.blackList.includes(from)) return
if (!body) return
if (!body.length) return
let prevMsg = await this.databaseClass.getPrevByNumber(from)
const refToContinue = this.flowClass.findBySerialize(prevMsg?.refSerialize)
const prevMsg = await this.databaseClass.getPrevByNumber(from)
const refToContinue = this.flowClass.findBySerialize(
prevMsg?.refSerialize
)
if (prevMsg?.ref) {
const ctxByNumber = toCtx({
@@ -84,172 +81,51 @@ class CoreClass {
from,
prevRef: prevMsg.refSerialize,
})
await this.databaseClass.save(ctxByNumber)
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
// 📄 [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
}
return toCtx({
body,
from,
keyword: null,
index,
options: { media, buttons, capture },
// 📄 [options: callback]: Si se tiene un callback se ejecuta
if (!fallBackFlag && refToContinue && prevMsg?.options?.callback) {
const indexFlow = this.flowClass.findIndexByRef(refToContinue?.ref)
this.flowClass.allCallbacks[indexFlow].callback(messageInComming, {
fallBack,
})
}
// 📄 Limpiar cola de procesos
const clearQueue = () => {
QueuePrincipal.pendingPromise = false
QueuePrincipal.queue = []
}
// 📄 Finalizar flujo
const endFlow =
(flag) =>
async (message = null) => {
flag.endFlow = true
endFlowFlag = true
if (message) this.sendProviderAndSave(from, createCtxMessage(message))
clearQueue()
sendFlow([])
return
}
// 📄 Esta funcion se encarga de enviar un array de mensajes dentro de este ctx
const sendFlow = async (messageToSend, numberOrId, 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)
await QueuePrincipal.enqueue(() =>
this.sendProviderAndSave(numberOrId, ctxMessage).then(() => resolveCbEveryCtx(ctxMessage))
)
}
return Promise.all(queue)
}
const continueFlow = async () => {
const currentPrev = await this.databaseClass.getPrevByNumber(from)
const nextFlow = (await this.flowClass.find(refToContinue?.ref, true)) ?? []
const filterNextFlow = nextFlow.filter((msg) => msg.refSerialize !== currentPrev?.refSerialize)
const isContinueFlow = filterNextFlow.map((i) => i.keyword).includes(currentPrev?.ref)
if (!isContinueFlow) {
const refToContinueChild = this.flowClass.getRefToContinueChild(currentPrev?.keyword)
const flowStandaloneChild = this.flowClass.getFlowsChild()
const nextChildMessages =
(await this.flowClass.find(refToContinueChild?.ref, true, flowStandaloneChild)) || []
if (nextChildMessages?.length) return await sendFlow(nextChildMessages, from, { prev: undefined })
}
if (!isContinueFlow) {
await sendFlow(filterNextFlow, from, { prev: undefined })
return
}
}
// 📄 [options: fallBack]: esta funcion se encarga de repetir el ultimo mensaje
const fallBack =
(flag) =>
async (message = null) => {
QueuePrincipal.queue = []
flag.fallBack = true
await this.sendProviderAndSave(from, {
...prevMsg,
answer: typeof message === 'string' ? message : message?.body ?? prevMsg.answer,
options: {
...prevMsg.options,
buttons: prevMsg.options?.buttons,
},
})
return
}
// 📄 [options: flowDynamic]: esta funcion se encarga de responder un array de respuesta esta limitado a 5 mensajes
// para evitar bloque de whatsapp
const flowDynamic =
(flag) =>
async (listMsg = []) => {
flag.flowDynamic = true
if (!Array.isArray(listMsg)) listMsg = [listMsg]
const parseListMsg = listMsg.map((opt, index) => createCtxMessage(opt, index))
if (endFlowFlag) return
for (const msg of parseListMsg) {
await this.sendProviderAndSave(from, msg)
}
await continueFlow()
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 = async (inRef) => {
let flags = {
endFlow: false,
fallBack: false,
flowDynamic: false,
wait: true,
}
const provider = this.providerClass
if (!this.flowClass.allCallbacks[inRef]) return Promise.resolve()
const argsCb = {
provider,
fallBack: fallBack(flags),
flowDynamic: flowDynamic(flags),
endFlow: endFlow(flags),
}
await this.flowClass.allCallbacks[inRef](messageCtxInComming, argsCb)
const wait = !(!flags.endFlow && !flags.fallBack && !flags.flowDynamic)
if (!wait) await continueFlow()
return
}
// 📄🤘(tiene return) [options: nested(array)]: Si se tiene flujos hijos los implementa
if (!endFlowFlag && prevMsg?.options?.nested?.length) {
if (!fallBackFlag && prevMsg?.options?.nested?.length) {
const nestedRef = prevMsg.options.nested
const flowStandalone = nestedRef.map((f) => ({
...nestedRef.find((r) => r.refSerialize === f.refSerialize),
}))
msgToSend = this.flowClass.find(body, false, flowStandalone) || []
await sendFlow(msgToSend, from)
this.sendFlow(msgToSend, from)
return
}
// 📄🤘(tiene return) Si el mensaje previo implementa capture
if (!endFlowFlag && !prevMsg?.options?.nested?.length) {
// 📄🤘(tiene return) [options: capture (boolean)]: Si se tiene option boolean
if (!fallBackFlag && !prevMsg?.options?.nested?.length) {
const typeCapture = typeof prevMsg?.options?.capture
const valueCapture = prevMsg?.options?.capture
if (typeCapture === 'boolean' && fallBackFlag) {
if (['string', 'boolean'].includes(typeCapture) && valueCapture) {
msgToSend = this.flowClass.find(refToContinue?.ref, true) || []
await sendFlow(msgToSend, from)
this.sendFlow(msgToSend, from)
return
}
}
msgToSend = this.flowClass.find(body) || []
sendFlow(msgToSend, from)
this.sendFlow(msgToSend, from)
}
/**
@@ -258,15 +134,27 @@ class CoreClass {
* @param {*} ctxMessage ver más en GLOSSARY.md
* @returns
*/
sendProviderAndSave = async (numberOrId, ctxMessage) => {
sendProviderAndSave = (numberOrId, ctxMessage) => {
const { answer } = ctxMessage
await this.providerClass.sendMessage(numberOrId, answer, ctxMessage)
await this.databaseClass.save({ ...ctxMessage, from: numberOrId })
return
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)
}
/**
* @deprecated
* @private
* @param {*} message
* @param {*} ref
@@ -279,22 +167,5 @@ 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

View File

@@ -8,7 +8,8 @@ const { addKeyword, addAnswer, addChild, toSerialize } = require('./io/methods')
* @param {*} args
* @returns
*/
const createBot = async ({ flow, database, provider }, args = {}) => new CoreClass(flow, database, provider, args)
const createBot = async ({ flow, database, provider }) =>
new CoreClass(flow, database, provider)
/**
* Crear instancia de clase Io (Flow)
@@ -28,7 +29,8 @@ 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
}

View File

@@ -1,5 +1,4 @@
const { toSerialize } = require('./methods/toSerialize')
const { flatObject } = require('../utils/flattener')
class FlowClass {
allCallbacks = []
@@ -9,7 +8,10 @@ class FlowClass {
if (!Array.isArray(_flow)) throw new Error('Esto debe ser un ARRAY')
this.flowRaw = _flow
this.allCallbacks = flatObject(_flow)
this.allCallbacks = _flow
.map((cbIn) => cbIn.ctx.callbacks)
.flat(2)
.map((c, i) => ({ callback: c?.callback, index: i }))
const mergeToJsonSerialize = Object.keys(_flow)
.map((indexObjectFlow) => _flow[indexObjectFlow].toJson())
@@ -25,9 +27,9 @@ class FlowClass {
let refSymbol = null
overFlow = overFlow ?? this.flowSerialize
const mapSensitive = (str, mapOptions = { sensitive: false, regex: false }) => {
if (mapOptions.regex) return new RegExp(str)
const regexSensitive = mapOptions.sensitive ? 'g' : 'i'
/** Retornar expresion regular para buscar coincidencia */
const mapSensitive = (str, flag = false) => {
const regexSensitive = flag ? 'g' : 'i'
if (Array.isArray(str)) {
return new RegExp(str.join('|'), regexSensitive)
}
@@ -35,7 +37,9 @@ class FlowClass {
}
const findIn = (keyOrWord, symbol = false, flow = overFlow) => {
const sensitive = refSymbol?.options?.sensitive || false
capture = refSymbol?.options?.capture || false
if (capture) return messages
if (symbol) {
@@ -44,9 +48,7 @@ class FlowClass {
if (refSymbol?.ref) findIn(refSymbol.ref, true)
} else {
refSymbol = flow.find((c) => {
const sensitive = c?.options?.sensitive || false
const regex = c?.options?.regex || false
return mapSensitive(c.keyword, { sensitive, regex }).test(keyOrWord)
return mapSensitive(c.keyword, sensitive).test(keyOrWord)
})
if (refSymbol?.ref) findIn(refSymbol.ref, true)
return messages
@@ -56,40 +58,10 @@ class FlowClass {
return messages
}
findBySerialize = (refSerialize) => this.flowSerialize.find((r) => r.refSerialize === refSerialize)
findBySerialize = (refSerialize) =>
this.flowSerialize.find((r) => r.refSerialize === refSerialize)
findIndexByRef = (ref) => this.flowSerialize.findIndex((r) => r.ref === ref)
getRefToContinueChild = (keyword) => {
try {
const flowChilds = this.flowSerialize
.reduce((acc, cur) => {
const merge = [...acc, cur?.options?.nested].flat(2)
return merge
}, [])
.filter((i) => !!i && i?.refSerialize === keyword)
.shift()
return flowChilds
} catch (e) {
return undefined
}
}
getFlowsChild = () => {
try {
const flowChilds = this.flowSerialize
.reduce((acc, cur) => {
const merge = [...acc, cur?.options?.nested].flat(2)
return merge
}, [])
.filter((i) => !!i)
return flowChilds
} catch (e) {
return []
}
}
}
module.exports = FlowClass

View File

@@ -1,6 +1,4 @@
const { flatObject } = require('../../utils/flattener')
const { generateRef } = require('../../utils/hash')
const { addChild } = require('./addChild')
const { toJson } = require('./toJson')
/**
*
@@ -17,36 +15,26 @@ 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,
})
const getNested = () => {
let flatNested = []
if (Array.isArray(nested)) {
for (const iterator of nested) {
flatNested = [...flatNested, ...addChild(iterator)]
}
const getNested = () => ({
nested: Array.isArray(nested) ? nested : [],
})
return {
nested: flatNested,
}
}
return {
nested: addChild(nested),
}
}
/**
* Esta funcion aplana y busca los callback anidados de los hijos
* @returns
*/
const getCbFromNested = () => flatObject(Array.isArray(nested) ? nested : [nested])
const callback = typeof cb === 'function' ? cb : () => null
const callback =
typeof cb === 'function'
? cb
: () => console.log('Callback no definida')
const lastCtx = inCtx.hasOwnProperty('ctx') ? inCtx.ctx : inCtx
@@ -74,12 +62,12 @@ const addAnswer =
},
])
getCbFromNested()
const callbacks = {
...inCtx.callbacks,
...getCbFromNested(),
[ref]: callback,
}
const callbacks = [].concat(inCtx.callbacks).concat([
{
ref: lastCtx.ref,
callback,
},
])
return {
...lastCtx,

View File

@@ -8,14 +8,12 @@ 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,
regex: typeof options?.regex === 'boolean' ? options?.regex : false,
sensitive:
typeof options?.sensitive === 'boolean'
? options?.sensitive
: false,
}
return defaultProperties

View File

@@ -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, options = {}, index }) => {
const toCtx = ({ body, from, prevRef, index }) => {
return {
ref: generateRef(),
keyword: prevRef,
answer: body,
options: options ?? {},
options: {},
from,
refSerialize: generateRefSerialize({ index, answer: body }),
}

View File

@@ -1,6 +1,6 @@
{
"name": "@bot-whatsapp/bot",
"version": "0.0.100-alpha.0",
"version": "0.0.21-alpha.0",
"description": "",
"main": "./lib/bundle.bot.cjs",
"scripts": {
@@ -28,9 +28,5 @@
},
"dependencies": {
"dotenv": "^16.0.3"
},
"repository": {
"type": "git",
"url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/bot"
}
}

View File

@@ -20,11 +20,10 @@ class ProviderClass extends EventEmitter {
*/
sendMessage = async (userId, message) => {
if (NODE_ENV !== 'production') console.log('[sendMessage]', { userId, message })
if (NODE_ENV !== 'production')
console.log('[sendMessage]', { userId, message })
return message
}
getInstance = () => this.vendor
}
module.exports = ProviderClass

View File

@@ -3,24 +3,14 @@ const commonjs = require('@rollup/plugin-commonjs')
const { nodeResolve } = require('@rollup/plugin-node-resolve')
const { join } = require('path')
module.exports = [
{
input: join(__dirname, 'index.js'),
output: {
banner: banner['banner.output'].join(''),
file: join(__dirname, 'lib', 'bundle.bot.cjs'),
format: 'cjs',
sourcemap: true,
},
plugins: [commonjs(), nodeResolve()],
const PATH = join(__dirname, 'lib', 'bundle.bot.cjs')
module.exports = {
input: join(__dirname, 'index.js'),
output: {
banner: banner['banner.output'].join(''),
file: PATH,
format: 'cjs',
},
{
input: join(__dirname, 'index.js'),
output: {
banner: banner['banner.output'].join(''),
file: join(__dirname, 'lib', 'bundle.bot.cjs'),
format: 'cjs',
},
plugins: [commonjs(), nodeResolve()],
},
]
plugins: [commonjs(), nodeResolve()],
}

View File

@@ -2,10 +2,16 @@ 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 }
allCallbacks = [{ callback: () => console.log('') }]
flowSerialize = []
flowRaw = []
find = (arg) => {
@@ -17,8 +23,6 @@ class MockFlow {
}
findBySerialize = () => ({})
findIndexByRef = () => 0
getRefToContinueChild = () => ({})
getFlowsChild = () => []
}
class MockDBA {
@@ -96,13 +100,20 @@ 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)
@@ -110,12 +121,21 @@ 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 () => {
@@ -146,13 +166,20 @@ 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)
@@ -160,12 +187,21 @@ 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 () => {
@@ -198,13 +234,20 @@ 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)
@@ -212,12 +255,21 @@ 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()

View File

@@ -1,28 +0,0 @@
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()

View File

@@ -35,7 +35,10 @@ 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
@@ -68,7 +71,9 @@ 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)
})
@@ -102,7 +107,10 @@ 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_/)

View File

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

View File

@@ -1,23 +0,0 @@
const flatObject = (listArray = []) => {
const cbNestedList = Array.isArray(listArray) ? listArray : []
if (!listArray.length) return {}
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) => ({
[icb]: Object.values(current)[i],
}))
return [...acc, ...parse]
}, [])
const flatObj = {}
for (const iteration of queueCb) {
const [keyCb] = Object.keys(iteration)
flatObj[keyCb] = iteration[keyCb]
}
return flatObj
}
module.exports = { flatObject }

View File

@@ -16,6 +16,9 @@ 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 }

View File

@@ -4,7 +4,9 @@ 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(``)
}
}

View File

@@ -1,8 +1,8 @@
class Queue {
queue = []
pendingPromise = false
static queue = []
static pendingPromise = false
enqueue(promise) {
static enqueue(promise) {
return new Promise((resolve, reject) => {
this.queue.push({
promise,
@@ -13,7 +13,7 @@ class Queue {
})
}
dequeue() {
static dequeue() {
if (this.workingOnPromise) {
return false
}

View File

@@ -1,59 +1,38 @@
const { red, yellow, green, bgCyan } = require('kleur')
const { exec } = require('node:child_process')
const checkNodeVersion = () => {
return new Promise((resolve, reject) => {
console.log(bgCyan('🚀 Revisando tu Node.js'))
const version = process.version
const majorVersion = parseInt(version.replace('v', '').split('.').shift())
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()
})
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(``)
}
const checkOs = () => {
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(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(green(`OS: compatible ✅`))
console.log(``)
resolve()
})
console.log(yellow(messages.join(' \n')))
}
console.log(``)
}
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 }
module.exports = { checkNodeVersion, checkOs }

View File

@@ -2,7 +2,10 @@ 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 = []

View File

@@ -23,7 +23,11 @@ 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 }

View File

@@ -20,9 +20,13 @@ 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)

View File

@@ -1,9 +1,9 @@
const prompts = require('prompts')
const { join } = require('path')
const { yellow, red, cyan, bgMagenta, bgRed } = require('kleur')
const { existsSync } = require('fs')
const { yellow, red, cyan, bgMagenta } = require('kleur')
const { copyBaseApp } = require('../create-app')
const { checkNodeVersion, checkOs, checkGit } = require('../check')
const { join } = require('path')
const { existsSync } = require('fs')
const { checkNodeVersion, checkOs } = require('../check')
const bannerDone = () => {
console.log(``)
@@ -12,7 +12,7 @@ const bannerDone = () => {
[
`[Agradecimientos]: Este es un proyecto OpenSource, si tienes intenciones de colaborar puedes hacerlo:`,
`[😉] Comprando un cafe https://www.buymeacoffee.com/leifermendez`,
`[⭐] Dar estrella https://github.com/codigoencasa/bot-whatsapp`,
`[⭐] Dar estrella https://github.com/leifermendez/bot-whatsapp`,
`[🚀] Realizando mejoras en el codigo`,
].join('\n')
)
@@ -21,20 +21,6 @@ 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',
@@ -46,11 +32,11 @@ const nextSteps = async () => {
name: 'providerWs',
message: '¿Cuál proveedor de whatsapp quieres utilizar?',
choices: [
{ title: 'Baileys (gratis)', value: 'baileys' },
{ title: 'Venom (gratis)', value: 'venom' },
{ title: 'whatsapp-web.js (gratis)', value: 'wweb' },
{ title: 'Twilio', value: 'twilio' },
{ title: 'Meta', value: 'meta' },
{ title: 'Venom (gratis)', value: 'venom' },
{ title: 'Baileys (gratis)', value: 'bailey' },
{ title: 'API Oficial (Meta)', value: 'meta', disabled: true },
],
max: 1,
hint: 'Espacio para seleccionar',
@@ -62,9 +48,9 @@ const nextSteps = async () => {
message: '¿Cuál base de datos quieres utilizar?',
choices: [
{ title: 'Memory', value: 'memory' },
{ title: 'Json', value: 'json' },
{ title: 'Mongo', value: 'mongo' },
{ title: 'MySQL', value: 'mysql' },
{ title: 'Json', value: 'json', disabled: true },
],
max: 1,
hint: 'Espacio para seleccionar',
@@ -72,6 +58,9 @@ const nextSteps = async () => {
},
]
console.clear()
checkNodeVersion()
checkOs()
const onCancel = () => {
console.log('¡Proceso cancelado!')
return true
@@ -80,7 +69,8 @@ const nextSteps = async () => {
const { outDir = '', providerDb = [], providerWs = [] } = response
const createApp = async (templateName = null) => {
if (!templateName) throw new Error('TEMPLATE_NAME_INVALID: ', templateName)
if (!templateName)
throw new Error('TEMPLATE_NAME_INVALID: ', templateName)
const possiblesPath = [
join(__dirname, '..', '..', 'starters', 'apps', templateName),
@@ -95,7 +85,7 @@ const nextSteps = async () => {
const indexOfPath = possiblesPath.find((a) => existsSync(a))
await copyBaseApp(indexOfPath, join(process.cwd(), templateName))
console.log(``)
console.log(bgMagenta(`⚡⚡⚡ INSTRUCCIONES ⚡⚡⚡`))
console.log(bgMagenta(`⚡⚡⚡INSTRUCCIONES⚡⚡⚡`))
console.log(yellow(`cd ${templateName}`))
console.log(yellow(`npm install`))
console.log(yellow(`npm start`))
@@ -112,7 +102,11 @@ const nextSteps = async () => {
const vendorProvider = async () => {
const [answer] = providerWs
if (!providerWs.length) {
console.log(red(`Debes seleccionar un proveedor de whatsapp. Tecla [Space] para seleccionar`))
console.log(
red(
`Debes seleccionar un proveedor de whatsapp. Tecla [Space] para seleccionar`
)
)
process.exit(1)
}
return answer
@@ -125,7 +119,11 @@ const nextSteps = async () => {
const dbProvider = async () => {
const [answer] = providerDb
if (!providerDb.length) {
console.log(red(`Debes seleccionar un proveedor de base de datos. Tecla [Space] para seleccionar`))
console.log(
red(
`Debes seleccionar un proveedor de base de datos. Tecla [Space] para seleccionar`
)
)
process.exit(1)
}
return answer

View File

@@ -1,6 +1,6 @@
{
"name": "@bot-whatsapp/cli",
"version": "0.0.72-alpha.0",
"version": "0.0.28-alpha.0",
"description": "",
"main": "index.js",
"devDependencies": {
@@ -15,9 +15,5 @@
],
"bin": {
"bot": "./bin/cli.js"
},
"repository": {
"type": "git",
"url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/cli"
}
}

View File

@@ -1,21 +0,0 @@
{
"name": "@bot-whatsapp/contexts",
"version": "0.0.16-alpha.0",
"description": "",
"main": "./lib/bundle.contexts.cjs",
"files": [
"./lib/"
],
"exports": {
"./mock": "./lib/mock/index.cjs",
"./dialogflow": "./lib/dialogflow/index.cjs",
"./dialogflowcx": "./lib/dialogflow-cx/index.cjs"
},
"dependencies": {
"@bot-whatsapp/bot": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/contexts"
}
}

View File

@@ -1,33 +0,0 @@
const banner = require('../../config/banner.rollup.json')
const commonjs = require('@rollup/plugin-commonjs')
const { join } = require('path')
module.exports = [
{
input: join(__dirname, 'src', 'mock', 'index.js'),
output: {
banner: banner['banner.output'].join(''),
file: join(__dirname, 'lib', 'mock', 'index.cjs'),
format: 'cjs',
},
plugins: [commonjs()],
},
{
input: join(__dirname, 'src', 'dialogflow', 'index.js'),
output: {
banner: banner['banner.output'].join(''),
file: join(__dirname, 'lib', 'dialogflow', 'index.cjs'),
format: 'cjs',
},
plugins: [commonjs()],
},
{
input: join(__dirname, 'src', 'dialogflow-cx', 'index.js'),
output: {
banner: banner['banner.output'].join(''),
file: join(__dirname, 'lib', 'dialogflow-cx', 'index.cjs'),
format: 'cjs',
},
plugins: [commonjs()],
},
]

View File

@@ -1,114 +0,0 @@
const { CoreClass } = require('@bot-whatsapp/bot')
const { SessionsClient } = require('@google-cloud/dialogflow-cx').v3beta1
const { existsSync, readFileSync } = require('fs')
const { join } = require('path')
/**
* Necesita extender de core.class
* handleMsg(messageInComming) // const { body, from } = messageInComming
*/
const GOOGLE_ACCOUNT_PATH = join(process.cwd(), 'google-key.json')
class DialogFlowCXContext extends CoreClass {
// Opciones del usuario
optionsDX = {
language: 'es',
location: '',
agentId: '',
}
projectId = null
configuration = null
sessionClient = null
constructor(_database, _provider, _optionsDX = {}) {
super(null, _database, _provider)
this.optionsDX = { ...this.optionsDX, ..._optionsDX }
this.init()
}
/**
* Verificar conexión con servicio de DialogFlow
*/
init = () => {
if (!existsSync(GOOGLE_ACCOUNT_PATH)) {
console.log(`[ERROR]: No se encontro ${GOOGLE_ACCOUNT_PATH}`)
/**
* Emitir evento de error para que se mueste por consola dicinedo que no tiene el json
* */
}
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)
this.projectId = project_id
this.sessionClient = new SessionsClient({
credentials: { private_key, client_email },
apiEndpoint: `${this.optionsDX.location}-dialogflow.googleapis.com`,
})
}
/**
* GLOSSARY.md
* @param {*} messageCtxInComming
* @returns
*/
handleMsg = async (messageCtxInComming) => {
const languageCode = this.optionsDX.language
const { from, body } = messageCtxInComming
/**
* 📄 Creamos session de contexto basado en el numero de la persona
* para evitar este problema.
* https://github.com/codigoencasa/bot-whatsapp/pull/140
*/
const session = this.sessionClient.projectLocationAgentSessionPath(
this.projectId,
this.optionsDX.location,
this.optionsDX.agentId,
from
)
const reqDialog = {
session,
queryInput: {
text: {
text: body,
},
languageCode,
},
}
const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [null]
const listMessages = single.queryResult.responseMessages.map((res) => {
if (res.message == 'text') {
return { answer: res.text.text[0] }
}
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 }
})
return {
answer: answer?.stringValue,
options: {
media: media?.stringValue,
buttons: buttonsArray,
},
}
}
})
this.sendFlowSimple(listMessages, from)
}
}
module.exports = DialogFlowCXContext

View File

@@ -1,13 +0,0 @@
const DialogCXFlowClass = require('./dialogflow-cx.class')
/**
* Crear instancia de clase Bot
* @param {*} args
* @returns
*/
const createBotDialog = async ({ database, provider }, _options) => new DialogCXFlowClass(database, provider, _options)
module.exports = {
createBotDialog,
DialogCXFlowClass,
}

View File

@@ -1,123 +0,0 @@
const { CoreClass } = require('@bot-whatsapp/bot')
const dialogflow = require('@google-cloud/dialogflow')
const { existsSync, readFileSync } = require('fs')
const { join } = require('path')
/**
* Necesita extender de core.class
* handleMsg(messageInComming) // const { body, from } = messageInComming
*/
const GOOGLE_ACCOUNT_PATH = join(process.cwd(), 'google-key.json')
class DialogFlowContext extends CoreClass {
projectId = null
configuration = null
sessionClient = null
optionsDX = {
language: 'es',
}
constructor(_database, _provider, _optionsDX = {}) {
super(null, _database, _provider)
this.optionsDX = { ...this.optionsDX, ..._optionsDX }
this.init()
}
/**
* Verificar conexión con servicio de DialogFlow
*/
init = () => {
if (!existsSync(GOOGLE_ACCOUNT_PATH)) {
console.log(`[ERROR]: No se encontro ${GOOGLE_ACCOUNT_PATH}`)
/**
* Emitir evento de error para que se mueste por consola dicinedo que no tiene el json
* */
}
const rawJson = readFileSync(GOOGLE_ACCOUNT_PATH, 'utf-8')
const { project_id, private_key, client_email } = JSON.parse(rawJson)
this.projectId = project_id
this.configuration = {
credentials: {
private_key,
client_email,
},
}
this.sessionClient = new dialogflow.SessionsClient(this.configuration)
}
/**
* GLOSSARY.md
* @param {*} messageCtxInComming
* @returns
*/
handleMsg = async (messageCtxInComming) => {
const languageCode = this.optionsDX.language
const { from, body } = messageCtxInComming
let customPayload = {}
/**
* 📄 Creamos session de contexto basado en el numero de la persona
* para evitar este problema.
* https://github.com/codigoencasa/bot-whatsapp/pull/140
*/
const session = this.sessionClient.projectAgentSessionPath(this.projectId, from)
const reqDialog = {
session,
queryInput: {
text: {
text: body,
languageCode,
},
},
}
const [single] = (await this.sessionClient.detectIntent(reqDialog)) || [null]
const { queryResult } = single
const msgPayload = queryResult?.fulfillmentMessages?.find((a) => a.message === 'payload')
// Revisamos si el dialogFlow tiene multimedia
if (msgPayload && msgPayload?.payload) {
const { fields } = msgPayload.payload
const mapButtons = fields?.buttons?.listValue?.values.map((m) => {
return { body: m?.structValue?.fields?.body?.stringValue }
})
customPayload = {
options: {
media: fields?.media?.stringValue,
buttons: mapButtons,
},
}
const ctxFromDX = {
...customPayload,
answer: fields?.answer?.stringValue,
}
this.sendFlowSimple([ctxFromDX], from)
return
}
/* const ctxFromDX = {
answer: queryResult?.fulfillmentText,
} */
const messagesFromCX = queryResult['fulfillmentMessages']
.map((a) => {
if (a.message === 'text') {
return { answer: a.text.text[0] }
}
})
.filter((e) => e)
this.sendFlowSimple(messagesFromCX, from)
}
}
module.exports = DialogFlowContext

View File

@@ -1,13 +0,0 @@
const DialogFlowClass = require('./dialogflow.class')
/**
* Crear instancia de clase Bot
* @param {*} args
* @returns
*/
const createBotDialog = async ({ database, provider }) => new DialogFlowClass(database, provider)
module.exports = {
createBotDialog,
DialogFlowClass,
}

View File

@@ -1,13 +0,0 @@
const MockClass = require('./mock.class')
/**
* Crear instancia de clase Bot
* @param {*} args
* @returns
*/
const createBotMock = async ({ database, provider }) => new MockClass(database, provider)
module.exports = {
createBotMock,
MockClass,
}

View File

@@ -1,24 +0,0 @@
const { CoreClass } = require('@bot-whatsapp/bot')
/**
* Necesita extender de core.class
* handleMsg(messageInComming) // const { body, from } = messageInComming
*/
class MockContext extends CoreClass {
constructor(_database, _provider) {
super(null, _database, _provider)
}
init = () => {}
/**
* GLOSSARY.md
* @param {*} messageCtxInComming
* @returns
*/
handleMsg = async () => {
console.log('DEBUG:')
}
}
module.exports = MockContext

View File

@@ -1,6 +1,6 @@
{
"name": "create-bot-whatsapp",
"version": "0.0.93-alpha.0",
"version": "0.0.39-alpha.0",
"description": "",
"main": "./lib/bundle.create-bot-whatsapp.cjs",
"files": [
@@ -11,9 +11,5 @@
"bin": "./bin/create.js",
"dependencies": {
"@bot-whatsapp/cli": "*"
},
"repository": {
"type": "git",
"url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/create-bot-whatsapp"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@bot-whatsapp/database",
"version": "0.0.64-alpha.0",
"version": "0.0.20-alpha.0",
"description": "Esto es el conector a mysql, pg, mongo",
"main": "./lib/mock/index.cjs",
"keywords": [],
@@ -12,16 +12,13 @@
"dependencies": {
"dotenv": "^16.0.3",
"mongodb": "^4.11.0",
"mysql2": "^2.3.3"
"mysql2": "^2.3.3",
"stormdb": "^0.6.0"
},
"exports": {
"./mock": "./lib/mock/index.cjs",
"./mongo": "./lib/mongo/index.cjs",
"./json": "./lib/json/index.cjs",
"./json-file": "./lib/json-file/index.cjs",
"./mysql": "./lib/mysql/index.cjs"
},
"repository": {
"type": "git",
"url": "https://github.com/codigoencasa/bot-whatsapp/tree/main/packages/database"
}
}

View File

@@ -31,11 +31,10 @@ module.exports = [
plugins: [commonjs()],
},
{
input: join(__dirname, 'src', 'json', 'index.js'),
input: join(__dirname, 'src', 'json-file', 'index.js'),
output: {
banner: banner['banner.output'].join(''),
file: join(__dirname, 'lib', 'json', 'index.cjs'),
format: 'cjs',
file: join(__dirname, 'lib', 'json-file', 'index.cjs'),
},
plugins: [commonjs()],
},

View File

@@ -0,0 +1,48 @@
const path = require('path')
const StormDB = require('stormdb')
const engine = new StormDB.localFileEngine(
path.join(process.cwd(), './db.stormdb')
)
class JsonFileAdapter {
db
listHistory = []
constructor() {
this.init().then()
}
init() {
return new Promise((resolve) => {
this.db = new StormDB(engine)
this.db.default({ history: [] })
resolve(this.db)
})
}
getPrevByNumber = async (from) => {
const response = await this.db.get('history')
const { history } = response.state
if (!history.length) {
return null
}
const result = history.filter((res) => res.from === from).pop()
return {
...result,
}
}
save = async (ctx) => {
await this.db
.get('history')
.push({ ...ctx })
.save()
console.log('Guardado en DB...', ctx)
this.listHistory.push(ctx)
}
}
module.exports = JsonFileAdapter

View File

@@ -1,80 +0,0 @@
const { join } = require('path')
const { existsSync } = require('fs')
const { writeFile, readFile } = require('fs').promises
class JsonFileAdapter {
db
pathFile
listHistory = []
options = { filename: 'db.json' }
constructor(options = {}) {
this.options = { ...this.options, ...options }
this.pathFile = join(process.cwd(), this.options.filename)
this.init().then()
}
/**
* Revisamos si existe o no el json file
* @returns
*/
init = async () => {
if (existsSync(this.pathFile)) {
return Promise.resolve()
}
try {
const parseData = JSON.stringify([], null, 2)
return writeFile(this.pathFile, parseData, 'utf-8')
} catch (e) {
return Promise.reject(e.message)
}
}
validateJson = (raw) => {
try {
return JSON.parse(raw)
} catch (e) {
return {}
}
}
/**
* Leer archivo y parsear
* @returns
*/
readFileAndParse = async () => {
const data = await readFile(this.pathFile, 'utf-8')
const parseData = this.validateJson(data)
return parseData
}
/**
* Buscamos el ultimo mensaje por numero
* @param {*} from
* @returns
*/
getPrevByNumber = async (from) => {
const history = await this.readFileAndParse()
if (!history.length) {
return []
}
const result = history
.slice()
.reverse()
.filter((i) => !!i.keyword)
return result.find((a) => a.from === from)
}
/**
* Guardar dato
* @param {*} ctx
*/
save = async (ctx) => {
this.listHistory.push(ctx)
const parseData = JSON.stringify(this.listHistory, null, 2)
await writeFile(this.pathFile, parseData, 'utf-8')
}
}
module.exports = JsonFileAdapter

View File

@@ -10,10 +10,7 @@ class MockDatabase {
constructor() {}
getPrevByNumber = (from) => {
const history = this.listHistory
.slice()
.reverse()
.filter((i) => !!i.keyword)
const history = this.listHistory.slice().reverse()
return history.find((a) => a.from === from)
}

View File

@@ -1,20 +1,23 @@
require('dotenv').config()
const { MongoClient } = require('mongodb')
const DB_URI = process.env.DB_URI || 'mongodb://0.0.0.0:27017'
const DB_NAME = process.env.DB_NAME || 'db_bot'
class MongoAdapter {
db
listHistory = []
credentials = { dbUri: null, dbName: null }
constructor(_credentials) {
this.credentials = _credentials
constructor() {
this.init().then()
}
init = async () => {
try {
const client = new MongoClient(this.credentials.dbUri, {})
const client = new MongoClient(DB_URI, {})
await client.connect()
console.log('🆗 Conexión Correcta DB')
const db = client.db(this.credentials.dbName)
const db = client.db(DB_NAME)
this.db = db
return true
} catch (e) {
@@ -24,12 +27,18 @@ class MongoAdapter {
}
getPrevByNumber = async (from) => {
const result = await this.db.collection('history').find({ from }).sort({ _id: -1 }).limit(1).toArray()
const result = await this.db
.collection('history')
.find({ from })
.sort({ _id: -1 })
.limit(1)
.toArray()
return result[0]
}
save = async (ctx) => {
await this.db.collection('history').insert(ctx)
console.log('Guardando DB...', ctx)
this.listHistory.push(ctx)
}
}

View File

@@ -3,7 +3,7 @@ const mysql = require('mysql2')
class MyslAdapter {
db
listHistory = []
credentials = { host: null, user: null, database: null, password: null }
credentials = { host: null, user: null, database: null }
constructor(_credentials) {
this.credentials = _credentials
@@ -46,8 +46,18 @@ 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
@@ -61,14 +71,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)
CHARACTER SET utf8mb4 COLLATE utf8mb4_General_ci`
(id INT AUTO_INCREMENT PRIMARY KEY,
ref varchar(255) NOT NULL,
keyword varchar(255) NOT NULL,
answer longtext NOT NULL,
refSerialize varchar(255) NOT NULL,
phone varchar(255) NOT NULL,
options longtext NOT NULL
)`
this.db.query(sql, (err) => {
if (err) throw err

View File

@@ -41,6 +41,3 @@ package-lock.json
# Cloudflare
functions/**/*.js
# Netlify
.netlify

View File

@@ -238,119 +238,3 @@ By default, the Cloudflare pages adaptor _does not_ include a `public/_routes.js
In the above example, it's saying _all_ pages should be SSR'd. However, the root static files such as `/favicon.ico` and any static assets in `/build/*` should be excluded from the Functions, and instead treated as a static file.
In most cases the generated `dist/_routes.json` file is ideal. However, if you need more granular control over each path, you can instead provide you're own `public/_routes.json` file. When the project provides its own `public/_routes.json` file, then the Cloudflare adaptor will not auto-generate the routes config and instead use the committed one within the `public` directory.
## Cloudflare Pages
Cloudflare's [wrangler](https://github.com/cloudflare/wrangler) CLI can be used to preview a production build locally. To start a local server, run:
```
npm run serve
```
Then visit [http://localhost:8787/](http://localhost:8787/)
### Deployments
[Cloudflare Pages](https://pages.cloudflare.com/) are deployable through their [Git provider integrations](https://developers.cloudflare.com/pages/platform/git-integration/).
If you don't already have an account, then [create a Cloudflare account here](https://dash.cloudflare.com/sign-up/pages). Next go to your dashboard and follow the [Cloudflare Pages deployment guide](https://developers.cloudflare.com/pages/framework-guides/deploy-anything/).
Within the projects "Settings" for "Build and deployments", the "Build command" should be `npm run build`, and the "Build output directory" should be set to `dist`.
### Function Invocation Routes
Cloudflare Page's [function-invocation-routes config](https://developers.cloudflare.com/pages/platform/functions/function-invocation-routes/) can be used to include, or exclude, certain paths to be used by the worker functions. Having a `_routes.json` file gives developers more granular control over when your Function is invoked.
This is useful to determine if a page response should be Server-Side Rendered (SSR) or if the response should use a static-site generated (SSG) `index.html` file.
By default, the Cloudflare pages adaptor _does not_ include a `public/_routes.json` config, but rather it is auto-generated from the build by the Cloudflare adaptor. An example of an auto-generate `dist/_routes.json` would be:
```
{
"include": [
"/*"
],
"exclude": [
"/_headers",
"/_redirects",
"/build/*",
"/favicon.ico",
"/manifest.json",
"/service-worker.js",
"/about"
],
"version": 1
}
```
In the above example, it's saying _all_ pages should be SSR'd. However, the root static files such as `/favicon.ico` and any static assets in `/build/*` should be excluded from the Functions, and instead treated as a static file.
In most cases the generated `dist/_routes.json` file is ideal. However, if you need more granular control over each path, you can instead provide you're own `public/_routes.json` file. When the project provides its own `public/_routes.json` file, then the Cloudflare adaptor will not auto-generate the routes config and instead use the committed one within the `public` directory.
## Express Server
This app has a minimal [Express server](https://expressjs.com/) implementation. After running a full build, you can preview the build using the command:
```
npm run serve
```
Then visit [http://localhost:8080/](http://localhost:8080/)
## Netlify
This starter site is configured to deploy to [Netlify Edge Functions](https://docs.netlify.com/edge-functions/overview/), which means it will be rendered at an edge location near to your users.
### Local development
The [Netlify CLI](https://docs.netlify.com/cli/get-started/) can be used to preview a production build locally. To do so: First build your site, then to start a local server, run:
1. Install Netlify CLI globally `npm i -g netlify-cli`.
2. Build your site with both ssr and static `npm run build`.
3. Start a local server with `npm run serve`.
In this project, `npm run serve` uses the `netlify dev` command to spin up a server that can handle Netlify's Edge Functions locally.
4. Visit [http://localhost:8888/](http://localhost:8888/) to check out your site.
### Edge Functions Declarations
[Netlify Edge Functions declarations](https://docs.netlify.com/edge-functions/declarations/)
can be configured to run on specific URL patterns. Each edge function declaration associates
one site path pattern with one function to execute on requests that match the path. A single request can execute a chain of edge functions from a series of declarations. A single edge function can be associated with multiple paths across various declarations.
This is useful to determine if a page response should be Server-Side Rendered (SSR) or
if the response should use a static-site generated (SSG) `index.html` file instead.
By default, the Netlify Edge adaptor will generate a `.netlify/edge-middleware/manifest.json` file, which is used by the Netlify deployment to determine which paths should, and should not, use edge functions.
To override the generated manifest, you can [add a declaration](https://docs.netlify.com/edge-functions/declarations/#add-a-declaration) to the `netlify.toml` using the `[[edge_functions]]` config. For example:
```toml
[[edge_functions]]
path = "/admin"
function = "auth"
```
### Deployments
You can [deploy your site to Netlify](https://docs.netlify.com/site-deploys/create-deploys/) either via a Git provider integration or through the Netlify CLI. This starter site includes a `netlify.toml` file to configure your build for deployment.
#### Deploying via Git
Once your site has been pushed to your Git provider, you can either link it [in the Netlify UI](https://app.netlify.com/start) or use the CLI. To link your site to a Git provider from the Netlify CLI, run the command:
```shell
netlify link
```
This sets up [continuous deployment](https://docs.netlify.com/site-deploys/create-deploys/#deploy-with-git) for your site's repo. Whenever you push new commits to your repo, Netlify starts the build process..
#### Deploying manually via the CLI
If you wish to deploy from the CLI rather than using Git, you can use the command:
```shell
netlify deploy --build
```
You must use the `--build` flag whenever you deploy. This ensures that the Edge Functions that this starter site relies on are generated and available when you deploy your site.
Add `--prod` flag to deploy to production.

View File

@@ -1,4 +1,4 @@
import { netifyEdgeAdaptor } from '@builder.io/qwik-city/adaptors/netlify-edge/vite'
import { cloudflarePagesAdaptor } from '@builder.io/qwik-city/adaptors/cloudflare-pages/vite'
import { extendConfig } from '@builder.io/qwik-city/vite'
import baseConfig from '../../vite.config'
@@ -7,12 +7,11 @@ export default extendConfig(baseConfig, () => {
build: {
ssr: true,
rollupOptions: {
input: ['src/entry.netlify-edge.tsx', '@qwik-city-plan'],
input: ['src/entry.cloudflare-pages.tsx', '@qwik-city-plan'],
},
outDir: '.netlify/edge-functions/entry.netlify-edge',
},
plugins: [
netifyEdgeAdaptor({
cloudflarePagesAdaptor({
staticGenerate: true,
}),
],

View File

@@ -1,7 +0,0 @@
[build]
publish = "dist"
command = "npm run build"
[[edge_functions]]
path = "/*"
function = "entry.netlify-edge"

View File

@@ -2,14 +2,15 @@
"name": "qwind",
"description": "A template to make your website using Qwik + Tailwind CSS.",
"version": "0.1.1",
"private": true,
"scripts": {
"build": "qwik build && npm run subfont",
"build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx",
"build.server": "vite build -c adaptors/netlify-edge/vite.config.ts",
"build.server": "vite build -c adaptors/cloudflare-pages/vite.config.ts",
"build.types": "tsc --incremental --noEmit",
"deploy": "netlify deploy --prod",
"dev": "vite --host --mode ssr",
"deploy": "wrangler pages dev ./dist",
"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 .",
@@ -20,37 +21,29 @@
"qwik": "qwik"
},
"devDependencies": {
"@builder.io/qwik": "0.16.1",
"@builder.io/qwik": "0.15.0",
"@builder.io/qwik-city": "0.0.128",
"@fontsource/inter": "^4.5.14",
"@iconify-json/tabler": "^1.1.49",
"@tailwindcss/aspect-ratio": "^0.4.0",
"@tailwindcss/forms": "^0.5.3",
"@tailwindcss/line-clamp": "^0.3.1",
"@tailwindcss/typography": "^0.5.0",
"@types/compression": "^1.7.2",
"@types/eslint": "8.4.10",
"@types/node": "latest",
"@typescript-eslint/eslint-plugin": "5.45.0",
"@typescript-eslint/parser": "5.45.0",
"autoprefixer": "10.4.13",
"compression": "^1.7.4",
"eslint": "8.29.0",
"eslint-plugin-qwik": "0.15.0",
"imagetools-core": "^3.2.3",
"netlify-cli": "^12.0.11",
"node-fetch": "^3.3.0",
"node-fetch": "3.3.0",
"postcss": "^8.4.19",
"prettier": "2.8.0",
"rehype-autolink-headings": "^6.1.1",
"subfont": "^6.12.2",
"tailwindcss": "^3.1.8",
"typescript": "4.8.4",
"vite": "3.2.4",
"vite-imagetools": "^4.0.11",
"vite-tsconfig-paths": "3.6.0"
"vite-tsconfig-paths": "3.6.0",
"wrangler": "latest"
},
"engines": {
"node": ">=17.0.0"
"node": ">=15.0.0"
}
}

View File

@@ -1,2 +1,4 @@
# https://developers.cloudflare.com/pages/platform/headers/
/build/*
Cache-Control: public, max-age=31536000, s-maxage=31536000, immutable

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 947 B

After

Width:  |  Height:  |  Size: 947 B

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