+})
diff --git a/packages/docs/src/root.tsx b/packages/docs/src/root.tsx
index 7d6db36..2cddda0 100644
--- a/packages/docs/src/root.tsx
+++ b/packages/docs/src/root.tsx
@@ -52,16 +52,22 @@ export default component$(() => {
title: 'Avanzado',
list: [
{ name: 'Migración', link: '/docs/migration' },
- { name: 'Extender funcionalidades', link: '/docs/custom' },
+ { name: 'MasterClass', link: '/docs/masterclass' },
+ ],
+ },
+ {
+ title: 'Despliegue',
+ list: [
+ { name: 'Local', link: '/docs/deploy/local' },
+ { name: 'Docker', link: '/docs/deploy/docker' },
+ { name: 'Cloud', link: '/docs/deploy/cloud' },
],
},
{
title: 'Comunidad',
list: [
- { name: 'MasterClass', link: '/docs/masterclass' },
{ name: 'Colabores', link: '/docs/contributing' },
{ name: 'Unirme al proyecto', link: '/docs/join' },
- { name: 'Sponsors', link: '/docs/sponsors' },
],
},
])
diff --git a/packages/docs/src/routes/docs/deploy/cloud/index.mdx b/packages/docs/src/routes/docs/deploy/cloud/index.mdx
new file mode 100644
index 0000000..2e38ff3
--- /dev/null
+++ b/packages/docs/src/routes/docs/deploy/cloud/index.mdx
@@ -0,0 +1,108 @@
+import Alert from '../../../../components/widgets/Alert'
+import Navigation from '../../../../components/widgets/Navigation'
+
+# Entorno Cloud
+
+Si deseas tener tu chatbot en ejecución en un servidor en la nueba esta, guía te ayudará.
+El servidor deberá cumplir con los requisitos mínimos, puedes ver en [este enlace.](/docs/requirements)
+
+---
+
+Dependiendo de tu proveedor de **servicio Cloud** debes crear una instancia (máquina virtual), este ejemplo iremos orientando en un entorno de AWS.
+En nuestro ejemplo creamos una maquina virtual con **Ubuntu 20.04**
+
+
+
+---
+
+Posterior al proceso de crear la máquina esperamos unos minutos hasta que ya está operativo y tomamos nota del usuario y la IP pública para proceder a conectarnos vía SSH
+
+## 
+
+## Conectarse via SSH
+
+Luego de obtener los datos necesarios para conectarnos a nuestra máquina, procedemos a hacerlo
+
+```shell
+ssh -i llaveBot.pem ubutnu@34.228.208.104
+```
+
+---
+
+Luego puede aparecer un mensaje como el siguiente donde solo debes de responder **yes**
+
+
+
+---
+
+Una vez conectado ya estás dentro de la máquina virtual, te aconsejamos la primera vez hacer una actualización de dependencias de Ubuntu con los siguientes comandos
+
+```shell
+sudo apt-get update
+```
+
+```shell
+sudo apt-get upgrade
+```
+
+---
+
+## Recuerda instalar Node 16 o superior
+
+Puedes ver más a detalle los pasos de la instalacion en este [blog](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04-es)
+
+```shell
+curl -sL https://deb.nodesource.com/setup_18.x -o nodesource_setup.sh
+```
+
+```shell
+sudo bash nodesource_setup.sh
+```
+
+```shell
+sudo apt-get install -y nodejs
+```
+
+---
+
+## Implementar el bot
+
+Si tienes el código de tu chatbot en un repositorio, solo falta que clones el repo en el servidor y ejecutes `npm start`
+
+Para escanear el **QR** puedes hacerlo vía WEB accediendo a la URL `http://[TU_IP_PUBLICA]:3000` en este ejemplo seria `http://34.228.208.10:3000`
+
+
+
+---
+
+## Firewall
+
+Si no te abre la pagina web asegurate de tener el puerto abierto en tu firewall.
+Ejemplo permitir el puerto **3000**
+
+
+
+---
+
+## Escanear QR
+
+
+
+---
+
+## Ejecutar en Producción
+
+Debes ubicarte en el directorio donde tienes el codigo fuente de tu chatbot.
+
+Independientemente de tu sistema operativo deberás ejecutar el chatbot con el comando atrevés de un sistema que mantenga el proceso en ejecución.
+Recomendamos [Pm2](https://pm2.keymetrics.io/)
+
+```shell
+pm2 start app.js --name=bot1
+```
+
+
+
+La consola de devolver un mensaje con una lista de procesos, en el ejemplo puedes observar, que tenemos un proceso llamado `bot1`
+
+De esta manera ya puedes cerrar la terminal y tu bot seguirá en ejecución sin problema
diff --git a/packages/docs/src/routes/docs/deploy/docker/index.mdx b/packages/docs/src/routes/docs/deploy/docker/index.mdx
new file mode 100644
index 0000000..9db9e2e
--- /dev/null
+++ b/packages/docs/src/routes/docs/deploy/docker/index.mdx
@@ -0,0 +1,32 @@
+import Alert from '../../../../components/widgets/Alert'
+import Navigation from '../../../../components/widgets/Navigation'
+
+# Entorno Docker
+
+Previamente, necesitas tener instalado Docker en tu servidor dependiendo del sistema operativo, los procesos cambian,
+puedes encontrar toda la información oficial de docker en [este enlace.](https://docs.docker.com/get-docker/)
+
+---
+
+Dependiendo del proveedor que has elegido necesitaras una implementación de Docker específica, pero no te preocupes, ya que viene implementada automáticamente en un archivo llamado **Dockerfile**, también puedes ver los otros Dockerfile en el apartado de [plantillas.](https://github.com/codigoencasa/bot-whatsapp/tree/main/starters/apps)
+
+
+
+---
+
+## Contruir imagen
+
+Solo es necesario construir la imagen del docker lo puedes hacer con el siguiente comando
+
+```shell
+docker build . -t botwhatsapp:latest
+```
+
+## Iniciar contenedor
+
+Para iniciar el contenedor con la imagen previamente construida puedes realizarlo ejecutando el siguiente comando.
+Se utiliza el puerto **3001** solo com un ejemplo puedes usar el puerto que tu quieras
+
+```shell
+docker run -e PORT=3001 -p 3001:3001 botwhatsapp:latest
+```
diff --git a/packages/docs/src/routes/docs/deploy/local/index.mdx b/packages/docs/src/routes/docs/deploy/local/index.mdx
new file mode 100644
index 0000000..394af82
--- /dev/null
+++ b/packages/docs/src/routes/docs/deploy/local/index.mdx
@@ -0,0 +1,26 @@
+import Alert from '../../../../components/widgets/Alert'
+import Navigation from '../../../../components/widgets/Navigation'
+
+# Entorno Local
+
+Si deseas tener tu chatbot en ejecución en un servidor local (computadora personal, etc.) esta, guía te ayudará.
+El servidor local deberá cumplir con los requisitos mínimos, puedes ver en [este enlace.](/docs/requirements)
+
+---
+
+Si deseas instalar pm2 puedes ejecutar `npm install pm2 --global`
+
+Debes ubicarte en el directorio donde tienes el codigo fuente de tu chatbot.
+
+Independientemente de tu sistema operativo deberás ejecutar el chatbot con el comando atrevés de un sistema que mantenga el proceso en ejecución.
+Recomendamos [Pm2](https://pm2.keymetrics.io/)
+
+```shell
+pm2 start app.js --name=bot1
+```
+
+
+
+La consola de devolver un mensaje con una lista de procesos, en el ejemplo puedes observar, que tenemos un proceso llamado `bot1`
+
+De esta manera ya puedes cerrar la terminal y tu bot seguirá en ejecución sin problema
diff --git a/packages/docs/src/routes/docs/flows/index.mdx b/packages/docs/src/routes/docs/flows/index.mdx
index 7a2171c..94b1f59 100644
--- a/packages/docs/src/routes/docs/flows/index.mdx
+++ b/packages/docs/src/routes/docs/flows/index.mdx
@@ -23,6 +23,26 @@ const flowPrincipal = addKeyword(['hola', 'alo'])
---
+## blackList
+
+Éste argumento se utiliza para **evitar que el bot se active** cuando los números de la lista activen el bot.
+Es importante que el número **vaya acompañado de su prefijo**, en el caso de España "34".
+
+```js
+createBot(
+ {
+ flow: adapterFlow,
+ provider: adapterProvider,
+ database: adapterDB,
+ },
+ {
+ blackList: ['34XXXXXXXXX', '34XXXXXXXXX', '34XXXXXXXXX', '34XXXXXXXXX'],
+ }
+)
+```
+
+---
+
## addKeyword()
Esta funcion se utliza para iniciar un flujo de conversion. Recibe un `string` o un `array`
@@ -55,7 +75,7 @@ Esta funcion se utliza para responder un mensaje despues del `addKeyword()`
- delay: 0 (milisegundos)
- media: url de imagen
- buttons: array `[{body:'Boton1'}, {body:'Boton2'}, {body:'Boton3'}]`
-- capture: false (para esperar respuesta)
+- capture: true (para esperar respuesta)
- child: Objecto tipo flujo o arra de flujos hijos
```js
@@ -159,6 +179,74 @@ const flowString = addKeyword('hola')
---
+## endFlow()
+
+Esta funcion se utliza para finalizar un flujo con dos o más addAnswer. Un ejemplo de uso sería registrar 3 datos de un usuario en 3 preguntas distinas y
+que el usuario pueda finalizar por él mismo el flujo.
+Como podrás comprobar en el ejemplo siguiente, se puede vincular flowDynamic y todas sus funciones; como por ejemplo botones.
+
+```js
+const flowFormulario = addKeyword(['Hola'])
+ .addAnswer(
+ ['Hola!', 'Escriba su *Nombre* para generar su solicitud'],
+ { capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] },
+ async (ctx, { flowDynamic, endFlow }) => {
+ if (ctx.body == '❌ Cancelar solicitud') {
+ await flowDynamic([
+ {
+ body: '❌ *Su solicitud de cita ha sido cancelada* ❌',
+ buttons: [{ body: '⬅️ Volver al Inicio' }],
+ },
+ ])
+ return endFlow()
+ }
+ }
+ )
+ .addAnswer(
+ ['También necesito tus dos apellidos'],
+ { capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] },
+ async (ctx, { flowDynamic, endFlow }) => {
+ if (ctx.body == '❌ Cancelar solicitud') {
+ await flowDynamic([
+ {
+ body: '❌ *Su solicitud de cita ha sido cancelada* ❌',
+ buttons: [{ body: '⬅️ Volver al Inicio' }],
+ },
+ ])
+ return endFlow()
+ }
+ }
+ )
+ .addAnswer(
+ ['Dejeme su número de teléfono y le llamaré lo antes posible.'],
+ { capture: true, buttons: [{ body: '❌ Cancelar solicitud' }] },
+ async (ctx, { flowDynamic, endFlow }) => {
+ if (ctx.body == '❌ Cancelar solicitud') {
+ await flowDynamic([
+ {
+ body: '❌ *Su solicitud de cita ha sido cancelada* ❌',
+ buttons: [{ body: '⬅️ Volver al Inicio' }],
+ },
+ ])
+ return endFlow()
+ }
+ }
+ )
+```
+
+---
+
+# QRPortalWeb
+
+Argumento para asignar nombre y puerto al BOT
+
+```js
+const BOTNAME = 'bot'
+QRPortalWeb({ name: BOTNAME, port: 3005 })
+```
+
+---
+
{
return (
<>
+ {/* */}
diff --git a/packages/docs/src/routes/docs/masterclass/index.md b/packages/docs/src/routes/docs/masterclass/index.md
deleted file mode 100644
index 9f94b4e..0000000
--- a/packages/docs/src/routes/docs/masterclass/index.md
+++ /dev/null
@@ -1,24 +0,0 @@
-[](https://hackmd.io/79xQyVSgRD6RsTpqtMPPdw)
-### Preguntas Frecuentes para Master Class BOT v2
-> Anota aqui las preguntas o dudas que tengas
-> Pronto estare publicando fecha y hora para la masterclass
-
-1.- Si necesito correr dos bots al mismo tiempo ¿donde puedo cambiar el puerto?
-
-2.- Si necesito agregar o modificar funciones del bot, ¿como puedo hacerlo?
-
-3.- Si quiero mi bot con otra base de datos diferente a MySQL ¿como lo puedo hacer?
-
-4.- Quiero conectarme a tal o cual API con JSON/XML/etc, ¿se puede hacer?
-
-5.- ¿Como integrar listas?
-
-6.- Preguntas y respuestas con el Bot
-
-7.- Guardar conversaciones en Excel.
-
-8.- ¿Puedo usar 2 o mas sesiones (códigos QR) al mismo tiempo?
-
-9.- ¿Puede ser que al usar el provider bailey, al leer el qr.png, que sea desde una url en el navegador, y no desde visual studio? Gracias
-
-10.- ¿Cómo tomo los datos que me envían en un mensaje para utilizarlo internamente en la búsqueda de datos propios y devolver la respuesta?
diff --git a/packages/docs/src/routes/docs/masterclass/index.mdx b/packages/docs/src/routes/docs/masterclass/index.mdx
new file mode 100644
index 0000000..97612b4
--- /dev/null
+++ b/packages/docs/src/routes/docs/masterclass/index.mdx
@@ -0,0 +1,26 @@
+# MasterClass
+
+
+
+---
+
+### Preguntas de la masterclass
+
+- Si necesito correr dos bots al mismo tiempo ¿donde puedo cambiar el puerto?
+- Si necesito agregar o modificar funciones del bot, ¿como puedo hacerlo?
+- Si quiero mi bot con otra base de datos diferente a MySQL ¿como lo puedo hacer?
+- Quiero conectarme a tal o cual API con JSON/XML/etc, ¿se puede hacer?
+- ¿Como integrar listas?
+- Preguntas y respuestas con el Bot
+- Guardar conversaciones en Excel.
+- ¿Puedo usar 2 o mas sesiones (códigos QR) al mismo tiempo?
+- ¿Puede ser que al usar el provider bailey, al leer el qr.png, que sea desde una url en el navegador, y no desde visual studio? Gracias
+- ¿Cómo tomo los datos que me envían en un mensaje para utilizarlo internamente en la búsqueda de datos propios y devolver la respuesta?
diff --git a/packages/docs/src/routes/docs/requirements/index.mdx b/packages/docs/src/routes/docs/requirements/index.mdx
index d51a65b..8f1ef96 100644
--- a/packages/docs/src/routes/docs/requirements/index.mdx
+++ b/packages/docs/src/routes/docs/requirements/index.mdx
@@ -6,7 +6,9 @@ A continuación se describen los puntos técnicos que debes de tener en cuenta a
- Node v16 o superior - **[descargar node](https://nodejs.org/es/download/)**
- Git - **[descargar Git](https://git-scm.com/download/win)**
+
---
+
## ¿Como saber que tengo el Node?
Solo debes ejecutar el siguiente comando y esperar que la versión que te arroja sea superior a v16
@@ -15,12 +17,16 @@ Solo debes ejecutar el siguiente comando y esperar que la versión que te arroja
$ node -v
v18.12.1
```
+
---
+
## ¿Como instalar Node?
- **Windows**: [Ver video](https://youtu.be/xRXHQlqA3Ak?t=376). Si necesitas ayuda para instalar Node en Windows. A continuación te comparto un video en el minuto exacto donde explico como instalar.
- **Ubuntu**: Te comparto un recurso de **[Digital Ocean](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-20-04-es)** donde explica como instalar node en Ubuntu.
-- ---
+
+---
+
## ¿Como saber que tengo Git?
Solo debes ejecutar el siguiente comando y esperar que te mande la versión que tienes instalada, si te manda un error de comando no reconocido es que no lo tienes instalado.
@@ -29,7 +35,9 @@ Solo debes ejecutar el siguiente comando y esperar que te mande la versión que
$ git -v
git
```
+
---
+
## ¿Como instalar Git?
- Solo es necesario instalar Git si estás usando **Windows**, ya que Mac y Linux lo traen preinstalado.
@@ -37,6 +45,7 @@ git
- Descarga la versión necesaria para tu sistema operativo (32-bit o 64-bit).
- Una vez terminada la descarga, ejecuta el archivo descargado y dale "Siguiente" en todas las pantallas.
- Haz clic en el botón de "Finalizar".
+
---
{
const CHECK_GITHUB_TOKEN =
(platform as any)?.['GITHUB_TOKEN'] ?? GITHUB_TOKEN
- console.log(`[🚩 platform]: `, GITHUB_TOKEN)
- const data = await fetchGithub(CHECK_GITHUB_TOKEN)
- return data
+ const dataGithub = await fetchGithub(CHECK_GITHUB_TOKEN)
+ const dataOpenCollective = await fetchOpenCollective()
+ return {
+ dataGithub,
+ dataOpenCollective,
+ }
}
export default component$(() => {
@@ -27,9 +33,16 @@ export default component$(() => {
}
+ onResolved={(data: any) => {
+ return (
+ <>
+
+
+
+ >
+ )
+ }}
>
-
>
)
})
diff --git a/packages/docs/src/services/github.ts b/packages/docs/src/services/github.ts
index 05d2b8f..e60d250 100644
--- a/packages/docs/src/services/github.ts
+++ b/packages/docs/src/services/github.ts
@@ -14,6 +14,9 @@ export const fetchGithub = async (token: string) => {
},
}
)
- const listUsers = data.json()
- return listUsers
+ const listUsers = await data.json()
+ return listUsers.map((u: any) => ({
+ ...u,
+ avatar_url: `${u.avatar_url}&s=80`,
+ }))
}
diff --git a/packages/docs/src/services/opencollective.ts b/packages/docs/src/services/opencollective.ts
new file mode 100644
index 0000000..9a89f25
--- /dev/null
+++ b/packages/docs/src/services/opencollective.ts
@@ -0,0 +1,19 @@
+/**
+ * GET API from OpenCollective
+ * @returns
+ */
+export const fetchOpenCollective = async () => {
+ const data = await fetch(
+ `https://opencollective.com/bot-whatsapp/members/users.json?limit=22&offset=0`,
+ {
+ method: 'GET',
+ }
+ )
+ const listUsers = await data.json()
+ return listUsers.map((u: any) => ({
+ html_url: u.profile,
+ avatar_url: u.image ?? 'https://i.imgur.com/HhiYKwN.png',
+ login: u.name,
+ id: u.MemberId,
+ }))
+}
diff --git a/packages/portal/.vscode/extensions.json b/packages/portal/.vscode/extensions.json
new file mode 100644
index 0000000..1396082
--- /dev/null
+++ b/packages/portal/.vscode/extensions.json
@@ -0,0 +1,4 @@
+{
+ "recommendations": ["dbaeumer.vscode-eslint", "unifiedjs.vscode-mdx"],
+ "unwantedRecommendations": []
+}
diff --git a/packages/portal/.vscode/qwik-city.code-snippets b/packages/portal/.vscode/qwik-city.code-snippets
new file mode 100644
index 0000000..b6c1c17
--- /dev/null
+++ b/packages/portal/.vscode/qwik-city.code-snippets
@@ -0,0 +1,32 @@
+{
+ "onGet": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:onGet",
+ "description": "onGet function for a route index",
+ "body": [
+ "export const onGet: RequestHandler = (request) => {",
+ " $0",
+ "};"
+ ]
+ },
+ "onGet (typed)": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:onGet typed",
+ "description": "onGet function for a route index",
+ "body": [
+ "export interface ${1:PageData} {",
+ " $2",
+ "};",
+ "",
+ "export const onGet: RequestHandler<$1> = (request) => {",
+ " $4",
+ "};"
+ ]
+ },
+ "useEndpoint": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:useEndpoint",
+ "description": "useEndpoint declaration",
+ "body": "const $1 = useEndpoint();"
+ }
+}
diff --git a/packages/portal/.vscode/qwik.code-snippets b/packages/portal/.vscode/qwik.code-snippets
new file mode 100644
index 0000000..4b89dbf
--- /dev/null
+++ b/packages/portal/.vscode/qwik.code-snippets
@@ -0,0 +1,84 @@
+{
+ "Qwik component (simple)": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:component",
+ "description": "Simple Qwik component",
+ "body": [
+ "export const ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}} = component$(() => {",
+ " return <${2:div}>$4$2>",
+ "});"
+ ]
+ },
+ "Qwik component (props)": {
+ "scope": "typescriptreact",
+ "prefix": "q:component w/props",
+ "description": "Qwik component w/ props",
+ "body": [
+ "export interface ${1:${TM_FILENAME_BASE/(.*)/${1:/pascalcase}/}}Props {",
+ " $2",
+ "}",
+ "",
+ "export const $1 = component$<$1Props>((props) => {",
+ " const ${2:count} = useSignal(0);",
+ " return (",
+ " <${3:div} on${4:Click}$={(ev) => {$5}}>",
+ " $6",
+ " ${3}>",
+ " );",
+ "});"
+ ]
+ },
+ "Qwik signal": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:useSignal",
+ "description": "useSignal() declaration",
+ "body": ["const ${1:foo} = useSignal($2);", "$0"]
+ },
+ "Qwik store": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:useStore",
+ "description": "useStore() declaration",
+ "body": ["const ${1:state} = useStore({", " $2", "});", "$0"]
+ },
+ "$ hook": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:$",
+ "description": "$() function hook",
+ "body": ["$(() => {", " $0", "});", ""]
+ },
+ "useClientEffect": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:useClientEffect",
+ "description": "useClientEffect$() function hook",
+ "body": ["useClientEffect$(({ track }) => {", " $0", "});", ""]
+ },
+ "useTask": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:useTask",
+ "description": "useTask$() function hook",
+ "body": [
+ "useTask$(({ track }) => {",
+ " track(() => $1);",
+ " $0",
+ "});",
+ ""
+ ]
+ },
+ "useResource": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:useResource",
+ "description": "useResource$() declaration",
+ "body": [
+ "const $1 = useResource$(({ track, cleanup }) => {",
+ " $0",
+ "});",
+ ""
+ ]
+ },
+ "useServerMount": {
+ "scope": "javascriptreact,typescriptreact",
+ "prefix": "q:useServerMount",
+ "description": "useServerMount$() function hook",
+ "body": ["useServerMount$(() => {", " $0", "});", ""]
+ }
+}
diff --git a/packages/portal/package.json b/packages/portal/package.json
index 80ad83e..c26ab32 100644
--- a/packages/portal/package.json
+++ b/packages/portal/package.json
@@ -1,6 +1,6 @@
{
"name": "@bot-whatsapp/portal",
- "version": "0.0.20-alpha.0",
+ "version": "0.0.22-alpha.0",
"description": "Portal WEB para escanear QR",
"main": "./lib/portal.http.cjs",
"scripts": {
diff --git a/packages/portal/server/@qwik-city-not-found-paths.js b/packages/portal/server/@qwik-city-not-found-paths.js
new file mode 100644
index 0000000..473076c
--- /dev/null
+++ b/packages/portal/server/@qwik-city-not-found-paths.js
@@ -0,0 +1,15 @@
+const notFounds = [
+ [
+ '/',
+ '\n\n \n \n \n 404 Resource Not Found\n \n \n \n \n