Subir archivos a "/"

This commit is contained in:
2026-06-12 13:37:03 +00:00
parent e1fd0fa3fb
commit 883a5aab56
+413
View File
@@ -0,0 +1,413 @@
# 🎂 Workflow: `hbdwsp` — Bot de Cumpleaños GLM (WhatsApp)
**ID del Workflow:** `ZzH2eMPrsqlY7WPz`
**Autor:** Eidan Then (`ethen@gomezleemarketing.com`)
**Última actualización:** 12 de junio de 2026
**Versión activa:** `2d27b5f7-f5c4-41f9-b008-d8f3a50557a5` (v251)
**Estado:** ✅ Activo
**Zona horaria:** America/Santo_Domingo (UTC-04:00)
---
## Descripción General
Este workflow automatiza el envío de tarjetas de cumpleaños personalizadas al grupo de WhatsApp corporativo de GLM. Cada día a las 10:00 AM, el sistema revisa los empleados registrados en BambooHR, filtra los que cumplen años ese día, genera una tarjeta visual personalizada con su foto y la envía al grupo correspondiente.
El flujo excluye automáticamente a empleados de países no hispanos (Jamaica, Trinidad & Tobago, Barbados, etc.) y maneja casos donde el empleado no tiene fecha de cumpleaños o foto registrada.
---
## Arquitectura del Flujo
```
Schedule Trigger (10am)
└── BambooHR (Reporte CSV #1509)
└── Google Drive (Guardar CSV)
└── Google Sheets (Leer datos)
└── Pais? (Filtrar hispanos)
├── [TRUE] Obtener empleado (BambooHR)
│ └── Merge (unir datos Sheets + Bamboo)
│ └── Loop Over Items
│ └── Tiene fecha?
│ ├── [FALSE] → Wait3 → Loop (siguiente)
│ └── [TRUE] → Coincide con fecha de hoy?
│ ├── [FALSE] → Wait3 → Loop
│ └── [TRUE] → Tiene foto?
│ ├── [TRUE] → Foto Empleado URL
│ └── [FALSE] → URL si no tiene foto (bizcocho)
│ └── HTML (generar tarjeta)
│ └── HTTP Request (screenshot browserless)
│ └── Extract from File
│ └── Enviar imagen WhatsApp
│ └── Wait (3s)
│ └── Loop (siguiente)
└── [FALSE] Ignorado (país no hispano)
```
---
## Nodos del Workflow
### 1. Schedule Trigger
- **Tipo:** `scheduleTrigger`
- **Configuración:** Se ejecuta todos los días a las **10:00 AM** (hora Santo Domingo, UTC-4).
- **Notas:** El trigger está pinned con fecha `2026-05-29` para pruebas manuales.
---
### 2. BambooHR (Reporte CSV)
- **Tipo:** `bambooHr``companyReport`
- **Reporte ID:** `1509`
- **Formato:** CSV
- **Credencial:** `Eidan - BambooHR account`
- **Función:** Descarga el listado completo de empleados activos con sus datos (incluyendo Birthday, Location, EEID, Work Email).
---
### 3. Google Drive (Guardar CSV)
- **Tipo:** `googleDrive``update`
- **Archivo destino:** `HBD.csv` (`1Jw9yOyIKB1QLcr9m8ObGP9PK0tC9z3n6`)
- **Credencial:** `Eidan - Google Drive account`
- **Función:** Actualiza el archivo CSV en Google Drive con los datos más recientes de BambooHR. Sirve como respaldo y fuente de verdad para Google Sheets.
---
### 4. Google Sheets (Leer datos)
- **Tipo:** `googleSheets` — read
- **Spreadsheet:** `HBD` (`1p0bCvHAIHjobiPhDfmTshZWvRP6PWAmmTtSoX77lBmM`)
- **Hoja:** `HBD` (gid: `759771456`)
- **Credencial:** `Eidan - Google Sheets account`
- **Función:** Lee los datos procesados del spreadsheet. Aquí se encuentran campos adicionales como `Location` que se usa para el filtro de país.
---
### 5. Pais? (Filtro de país)
- **Tipo:** `if`
- **Lógica:** `AND` de condiciones `notEquals`
- **Países excluidos (FALSE):**
- Jamaica
- Trinidad and Tobago / Trinidad & Tobago / Tobago
- Barbados
- Saint Thomas
- St. Croix
- **TRUE:** El empleado es de un país hispano → el flujo continúa.
- **FALSE:** El empleado es de un país no hispano → se ignora silenciosamente.
---
### 6. Obtener empleado
- **Tipo:** `bambooHr``get`
- **Employee ID:** `{{ $json.EEID }}`
- **Credencial:** `Eidan - BambooHR account`
- **Configuración:** `retryOnFail: true`
- **Función:** Obtiene el perfil completo del empleado desde BambooHR, incluyendo `photoUrl` y `photoUploaded`.
---
### 7. Merge
- **Tipo:** `merge` — combine (enrich)
- **Join por:** `workEmail``Work Email`
- **Modo:** `enrichInput1` (enriquece los datos de Bamboo con los de Google Sheets)
- **Función:** Combina en un solo objeto los datos de BambooHR (foto, cargo, etc.) con los de Google Sheets (Location, Birthday, etc.).
---
### 8. Loop Over Items
- **Tipo:** `splitInBatches`
- **Función:** Procesa cada empleado de forma individual y secuencial. Al terminar con uno, vuelve al siguiente. Cuando no quedan más ítems, el flujo termina.
---
### 9. Tiene fecha?
- **Tipo:** `if`
- **Condición:** `!$json.Birthday` es `true`
- **TRUE (sin fecha):** → `Wait3` → vuelve al loop (siguiente empleado).
- **FALSE (tiene fecha):** → continúa hacia `Coincide con fecha de hoy?`.
> ⚠️ **Nota:** La nota sticky original decía que el sistema avisaría a RRHH si falta la fecha, pero actualmente el `TRUE` solo hace un `Wait3` y continúa. Considerar agregar una notificación a RRHH.
---
### 10. Coincide con fecha de hoy?
- **Tipo:** `if`
- **Condición:** `$json.Birthday` equals `$now.format('MMMM dd')` (con espacios)
- **Validación:** `loose` (tipos flexibles)
- **`onError`:** `continueRegularOutput`
- **TRUE:** La fecha coincide → continúa el flujo.
- **FALSE:** No es el cumpleaños hoy → `Wait3` → siguiente empleado.
> ⚠️ **Importante:** El `rightValue` tiene un espacio al final: `{{ $now.format('MMMM dd') }} `. Asegurarse de que los datos de Birthday en el CSV también tengan ese formato (`MMMM dd`) para evitar falsos negativos.
---
### 11. Tiene foto?
- **Tipo:** `if`
- **Condición:** `$json.photoUploaded == true` es `true`
- **TRUE:** → `Foto Empleado URL` (usa la foto real del empleado)
- **FALSE:** → `URL si no tiene foto` (usa imagen de bizcocho)
---
### 12. Foto Empleado URL / URL si no tiene foto
- **Tipo:** `set`
- **Foto real:** `{{ $json.photoUrl }}` (URL de BambooHR)
- **Foto fallback:** `https://gomezleemarketing.com/wp-content/uploads/2026/03/hbdcake.png`
- **Output:** Campo `photoUrl` que se pasa al nodo HTML.
---
### 13. HTML (Generación de tarjeta)
- **Tipo:** `html`
- **Dimensiones:** 1080 × 1920 px (formato Instagram Stories / WhatsApp)
- **Background:** `https://gomezleemarketing.com/wp-content/uploads/2026/03/Comunicacion-de-Cumpleanos-Background.jpg`
- **Contenido dinámico:**
- Foto del empleado (circular, 530px)
- Nombre del empleado (`firstName`)
- Fecha de cumpleaños formateada en español (ej: "12 de junio")
- **Nota:** Los divs `.hello`, `.message`, `.position` y `.team` están vacíos en la versión actual. Pueden ser rellenados con texto personalizado.
---
### 14. HTTP Request (Screenshot)
- **Tipo:** `httpRequest` POST
- **URL:** `https://pruebadetemplate-browserless.k11ytw.easypanel.host/chromium/screenshot`
- **Token:** `8d66338953f117a7c29c55364c8c9e6d`
- **Viewport:** `1080 × 1920`
- **Response format:** `file` (binario)
- **Función:** Convierte el HTML generado en una imagen PNG usando Browserless (Chromium headless) alojado en Easypanel.
---
### 15. Extract from File
- **Tipo:** `extractFromFile``binaryToPropery`
- **Función:** Convierte el binario de la imagen en base64 (`data`) para enviarlo a la API de WhatsApp.
---
### 16. Enviar mensaje al whatsapp imagen
- **Tipo:** `httpRequest` POST
- **URL:** `https://wsp.gomezleemarketing.com/message/sendMedia/botsoporte`
- **API Key:** `C267126ABB45-4C12-B626-6BAB1833F5D7`
- **Número destino:** `120363404268176806@g.us` (Grupo oficial Motorola)
- **Tipo de media:** `image`
- **Media:** `{{ $json.data }}` (base64)
- **Delay:** 2000ms
---
### 17. Wait (entre empleados)
- **Tipo:** `wait`
- **Duración:** 3 segundos
- **Función:** Pausa entre el envío y la vuelta al loop para no saturar la API de WhatsApp.
### Wait3 (skip al siguiente)
- **Tipo:** `wait`
- **Duración:** 1 segundo
- **Función:** Pequeña pausa antes de saltar al siguiente empleado cuando no cumple años o no tiene fecha.
---
## Subworkflow: Respaldo en Gitea
Este subworkflow es independiente (trigger manual) y guarda/actualiza el JSON del workflow en Gitea como respaldo de versión.
### Nodos:
| Nodo | Función |
|------|---------|
| `Valores` | Define `repo_name: wsp-cumpleanos` y `file_name: hbd-panama-motorola` |
| `Obtener archivo` | GET al repo Gitea para obtener el SHA actual del archivo |
| `Get a workflow1/2` | Obtiene el JSON del propio workflow (`ZzH2eMPrsqlY7WPz`) vía API de n8n |
| `Code in JavaScript` | Prepara payload con SHA para **actualizar** (PUT) |
| `Code in JavaScript1` | Prepara payload sin SHA para **crear** (POST) |
| `Actualizar archivo` | PUT al endpoint de Gitea (actualiza si ya existe) |
| `Subir archivo` | POST al endpoint de Gitea (crea si no existe) |
**Repositorio:** `https://git.digitalcompass.agency/EidanThen/wsp-cumpleanos`
---
## Requisitos de Instalación
### Servicios externos requeridos
| Servicio | Uso | Credencial |
|----------|-----|------------|
| **BambooHR** | Fuente de datos de empleados | `Eidan - BambooHR account` |
| **Google Drive** | Almacenamiento del CSV actualizado | `Eidan - Google Drive account` |
| **Google Sheets** | Lectura de datos procesados | `Eidan - Google Sheets account` |
| **Browserless** (Easypanel) | Renderizado HTML → PNG | Token en URL |
| **EvolutionAPI** (WhatsApp) | Envío de imágenes al grupo | API Key en header |
| **Gitea** (opcional) | Respaldo del workflow JSON | `Eidan - gitea` |
| **n8n API** (opcional) | Leer JSON del workflow para backup | `Eidan - n8n account` |
---
### Paso 1: Configurar credenciales en n8n
1. Ve a **Settings → Credentials** en n8n.
2. Crea las siguientes credenciales:
#### BambooHR
- **Tipo:** BambooHR API
- **API Key:** (tu API key de BambooHR)
- **Subdomain:** `gomezleemarketing`
#### Google Drive / Sheets
- **Tipo:** Google OAuth2
- Autoriza con la cuenta `ethen@gomezleemarketing.com`
- Aplica a ambos nodos (Drive y Sheets)
#### Gitea (Header Auth)
- **Tipo:** Header Auth
- **Name:** `Authorization`
- **Value:** `token TU_TOKEN_GITEA`
---
### Paso 2: Configurar Google Sheets
El spreadsheet `HBD` debe tener al menos las columnas:
| Columna | Descripción |
|---------|-------------|
| `EEID` | ID del empleado en BambooHR |
| `Work Email` | Email corporativo (clave de join con Bamboo) |
| `Birthday` | Fecha en formato `MMMM dd` (ej: `June 12`) |
| `Location` | País del empleado (ej: `Dominican Republic`) |
| `firstName` | Nombre del empleado |
---
### Paso 3: Configurar Browserless
El workflow usa una instancia de Browserless alojada en Easypanel:
```
URL: https://pruebadetemplate-browserless.k11ytw.easypanel.host
Token: 8d66338953f117a7c29c55364c8c9e6d
```
Si se despliega una nueva instancia, actualizar la URL y el token en el nodo **HTTP Request** (screenshot).
**Para desplegar Browserless en Docker:**
```yaml
services:
browserless:
image: ghcr.io/browserless/chromium:latest
ports:
- "3000:3000"
environment:
- TOKEN=tu_token_aqui
```
---
### Paso 4: Configurar EvolutionAPI (WhatsApp)
El workflow usa EvolutionAPI como intermediario para WhatsApp:
- **Base URL:** `https://wsp.gomezleemarketing.com`
- **Instancia:** `botsoporte`
- **API Key:** configurada en el header `apikey`
Asegúrate de que la instancia `botsoporte` esté conectada y activa antes de ejecutar el workflow.
---
### Paso 5: Importar el workflow en n8n
1. En n8n, ve a **Workflows → Import from file/URL**.
2. Pega el JSON del workflow o importa desde Gitea.
3. Asigna las credenciales correctas a cada nodo.
4. Activa el workflow con el toggle.
---
## Configuración del Grupo de WhatsApp
| Entorno | Número del grupo |
|---------|-----------------|
| **Producción (Motorola)** | `120363404268176806@g.us` |
| **Pruebas** | `120363426353723539@g.us` |
Para cambiar el grupo destino, editar el campo `number` en el nodo **Enviar mensaje al whatsapp imagen**.
---
## Personalización de la Tarjeta HTML
La tarjeta visual se genera en el nodo `HTML`. Los siguientes divs están disponibles para personalización:
```html
<div class="hello">¡Feliz Cumpleaños!</div>
<!-- Saludo principal. Actualmente vacío. -->
<div class="message">Tu mensaje aquí...</div>
<!-- Mensaje de felicitación. Actualmente vacío. -->
<div class="position">{{ cargo del empleado }}</div>
<!-- Cargo/posición. Actualmente vacío. -->
<div class="team">Equipo GLM</div>
<!-- Firma del equipo. Actualmente vacío. -->
```
Para agregar el cargo dinámico, incluir en el nodo `Obtener empleado` el campo `jobTitle` y referenciarlo con `{{ $json.jobTitle }}`.
---
## Solución de Problemas
### El workflow no envía tarjetas aunque es el cumpleaños del empleado
**Causa probable:** El formato de la fecha en Google Sheets no coincide con `$now.format('MMMM dd')`.
**Solución:** Asegúrate de que el campo `Birthday` en el CSV/Sheets tenga el formato `June 12` (no `06/12` ni `12-06-1990`). El nodo `Coincide con fecha de hoy?` también tiene un espacio trailing en el `rightValue`; revisar que no cause falsos negativos.
---
### La imagen no se genera (error en Browserless)
**Causa probable:** La instancia de Browserless está caída o el token expiró.
**Solución:**
1. Verificar que el contenedor de Browserless esté corriendo en Easypanel.
2. Probar manualmente con curl:
```bash
curl -X POST "https://pruebadetemplate-browserless.k11ytw.easypanel.host/chromium/screenshot?token=8d66338953f117a7c29c55364c8c9e6d" \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Test</h1>", "viewport": {"width": 1080, "height": 1920}}'
```
---
### El mensaje no llega al grupo de WhatsApp
**Causa probable:** La instancia `botsoporte` de EvolutionAPI no está conectada.
**Solución:**
1. Ir al panel de EvolutionAPI en `https://wsp.gomezleemarketing.com`.
2. Verificar que la instancia `botsoporte` muestra estado `open`.
3. Si está desconectada, escanear el QR de WhatsApp nuevamente.
---
### Empleados de países no hispanos reciben tarjetas
**Causa probable:** El campo `Location` en Google Sheets tiene un valor diferente al esperado (ej: `Trinidad` en vez de `Trinidad and Tobago`).
**Solución:** Revisar el nodo `Pais?` y agregar las variantes del nombre del país en las condiciones `notEquals`.
---
## Flujo de Error
El workflow tiene configurado el workflow de error `lX5ITf9s4TSQglsM`. Ante un error crítico no manejado, se disparará ese workflow de manejo de errores.
---
## Notas de Mantenimiento
- El reporte de BambooHR (#1509) debe incluir los campos `Birthday`, `Location`, `EEID`, y `Work Email`.
- La imagen de fondo y el fallback de bizcocho están alojadas en el servidor de WordPress de GLM. Si cambian de dominio, actualizar las URLs en el nodo `HTML` y en el nodo `URL si no tiene foto`.
- El workflow respalda su propio JSON en Gitea bajo `EidanThen/wsp-cumpleanos/hbd-panama-motorola.json`. Ejecutar el subworkflow manual periódicamente o tras cambios importantes.