Files
n8n/Calendario Voz IA/BLUEPRINT.md
T

519 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Voz → Calendario Nextcloud — Blueprint Completo
**Versión:** 1.0
**Fecha:** 2026-07-03
**Autor:** Álvaro
**n8n:** https://n8n.crewinghunters.com
---
## Visión General
Grabas un audio en el teléfono → se lo mandas al bot de Telegram → la IA local transcribe y entiende el evento → aparece en tu calendario de Nextcloud → te recuerda 30 minutos antes por Telegram.
```
📱 Audio (teléfono)
▼ Telegram
┌─────────────────────────────────────────────────┐
│ n8n (Raspberry Pi 4 — Colombia) │
│ │
│ 1. Recibe audio de Telegram │
│ 2. Descarga el archivo .ogg/.mp3 │
│ 3. Llama a Whisper API (tu PC via Tailscale) │
│ 4. Llama a Ollama (tu PC via Tailscale) │
│ 5. Crea evento en Nextcloud Calendar (CalDAV) │
│ 6. Confirma por Telegram │
│ 7. Programa recordatorio 30min antes │
└─────────────────────────────────────────────────┘
▼ Tailscale VPN (túnel privado gratis)
┌─────────────────────────────────────────────────┐
│ Tu PC (Windows) │
│ ├── Ollama → AsistentePersonal / qwen2.5:14b │
│ └── Whisper API → faster-whisper (Python) │
└─────────────────────────────────────────────────┘
```
---
## Stack Tecnológico
| Componente | Herramienta | Dónde corre |
|-----------|------------|------------|
| Automatización | n8n | RPi4 Colombia |
| Bot mensajería | Telegram Bot | Cloud (Telegram) |
| Transcripción voz | faster-whisper | Tu PC (Windows) |
| LLM extracción evento | Ollama / qwen2.5:14b | Tu PC (Windows) |
| Calendario | Nextcloud CalDAV | RPi4 Colombia |
| Túnel privado | Tailscale | PC + RPi4 |
| API transcripción | FastAPI (Python) | Tu PC (Windows) |
---
## PASO 1 — Tailscale (conectar PC con RPi4)
Tailscale crea una red privada entre tus dispositivos. Una vez instalado, tu PC tendrá una IP tipo `100.x.x.x` accesible desde el RPi4 sin abrir puertos ni router.
### En tu PC (Windows)
1. Descarga e instala Tailscale: https://tailscale.com/download/windows
2. Inicia sesión con Google o email
3. Anota tu IP Tailscale: aparece en el icono de la bandeja del sistema
- Ejemplo: `100.64.1.10`
### En el RPi4 (via SSH desde Colombia)
```bash
# Conectar al RPi4
ssh usuario@n8n.crewinghunters.com
# Instalar Tailscale en RPi4
curl -fsSL https://tailscale.com/install.sh | sh
# Iniciar y autenticar (con la MISMA cuenta que en el PC)
sudo tailscale up
# Verificar que ve tu PC
tailscale ping 100.64.1.10 # ← IP de tu PC Tailscale
```
✅ Si el ping responde, están conectados. El RPi4 puede hablar con tu PC.
---
## PASO 2 — Whisper API en tu PC
Instalamos faster-whisper y levantamos una API HTTP que n8n puede llamar.
### Instalar faster-whisper
```bash
# En tu PC (PowerShell)
pip install faster-whisper fastapi uvicorn python-multipart
```
### Crear el servidor API
Guarda este archivo en: `C:\whisper-api\server.py`
```python
from fastapi import FastAPI, UploadFile, File
from faster_whisper import WhisperModel
import tempfile, os
app = FastAPI()
# Carga el modelo una vez al iniciar (medium es bueno para español)
# Opciones: "tiny", "base", "small", "medium", "large-v3"
model = WhisperModel("medium", device="cpu", compute_type="int8")
@app.post("/transcribe")
async def transcribe(file: UploadFile = File(...)):
# Guardar audio temporalmente
suffix = os.path.splitext(file.filename)[1] or ".ogg"
with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp:
tmp.write(await file.read())
tmp_path = tmp.name
# Transcribir
segments, info = model.transcribe(tmp_path, language="es")
text = " ".join([seg.text for seg in segments]).strip()
os.unlink(tmp_path)
return {"text": text, "language": info.language}
@app.get("/health")
def health():
return {"status": "ok"}
```
### Script para iniciar la API (guarda como `C:\whisper-api\start.bat`)
```bat
@echo off
cd C:\whisper-api
uvicorn server:app --host 0.0.0.0 --port 8765
```
### Iniciar automáticamente con Windows
1. Presiona `Win + R` → escribe `shell:startup`
2. Crea un acceso directo a `start.bat` en esa carpeta
3. La API estará disponible en: `http://100.64.1.10:8765` (via Tailscale)
### Verificar que funciona (desde RPi4)
```bash
# Desde el RPi4 via SSH
curl http://100.64.1.10:8765/health
# Debe responder: {"status":"ok"}
```
---
## PASO 3 — Exponer Ollama via Tailscale
Por defecto Ollama solo escucha en localhost. Hay que permitir que escuche en todas las interfaces.
### En tu PC (PowerShell como Administrador)
```powershell
# Agregar variable de entorno para Ollama
[System.Environment]::SetEnvironmentVariable("OLLAMA_HOST", "0.0.0.0:11434", "Machine")
# Reiniciar Ollama (busca en la bandeja del sistema → clic derecho → Quit, luego reabre)
```
### Verificar desde RPi4
```bash
curl http://100.64.1.10:11434/api/tags
# Debe listar tus modelos
```
---
## PASO 4 — Bot de Telegram
### Crear el bot
1. Abre Telegram → busca `@BotFather`
2. Escribe `/newbot`
3. Nombre del bot: `Mi Calendario Personal` (o el que quieras)
4. Username: `mi_calendario_alvaro_bot` (debe terminar en `_bot`)
5. BotFather te da un **token** — guárdalo: `7123456789:AAF...`
### Obtener tu Chat ID
1. Escribe cualquier mensaje a tu nuevo bot
2. Abre en el navegador (reemplaza TOKEN):
```
https://api.telegram.org/botTOKEN/getUpdates
```
3. Busca `"id"` dentro de `"chat"` — ese número es tu **Chat ID**
- Ejemplo: `123456789`
---
## PASO 5 — Nextcloud Calendar (CalDAV)
n8n se conecta al calendario de Nextcloud via CalDAV.
### Obtener la URL CalDAV
En Nextcloud → Calendario → ⚙️ (engranaje junto al calendario) → "Copiar enlace privado"
La URL tiene esta forma:
```
https://tu-nextcloud.com/remote.php/dav/calendars/USUARIO/personal/
```
### Credenciales
- Usuario: tu usuario de Nextcloud
- Contraseña: genera una **contraseña de aplicación** en Nextcloud:
Configuración → Seguridad → Dispositivos y sesiones → "Crear nueva contraseña de aplicación"
- Nombre: `n8n-calendario`
---
## PASO 6 — Organizar Carpetas en n8n
Antes de crear el workflow, organiza las carpetas:
1. Abre https://n8n.crewinghunters.com
2. En el panel izquierdo → "Workflows" → "Add folder"
3. Crea estas carpetas:
```
📁 Prisa Yachts ← mueve el bot de WhatsApp aquí
📁 Personal - Productividad
📁 AIS Navigator
📁 Pruebas / Sandbox
```
4. El nuevo workflow irá en `📁 Personal - Productividad`
---
## PASO 7 — Workflow n8n Completo
Nombre del workflow: `🎙️ Voz → Calendario Nextcloud`
Carpeta: `Personal - Productividad`
### Nodos del workflow (en orden):
```
[Telegram Trigger]
[IF: ¿es audio?]
│ sí
[HTTP: Descargar audio de Telegram]
[HTTP: Whisper API → transcripción]
[HTTP: Ollama → extraer evento]
[Code: parsear JSON respuesta Ollama]
[HTTP: Crear evento CalDAV en Nextcloud]
[Telegram: Confirmar al usuario]
[Wait: esperar hasta 30min antes del evento]
[Telegram: Enviar recordatorio]
```
---
### Nodo 1 — Telegram Trigger
```
Tipo: Telegram Trigger
Credential: (crear nueva → Token del bot)
Updates: message
```
### Nodo 2 — IF: ¿Es audio?
```
Tipo: IF
Condición: {{ $json.message.voice }} existe
O {{ $json.message.audio }} existe
```
### Nodo 3 — HTTP: Descargar audio de Telegram
```
Tipo: HTTP Request
Método: GET
URL: https://api.telegram.org/bot{{ $env.TELEGRAM_TOKEN }}/getFile
Parámetros:
file_id: {{ $json.message.voice.file_id }}
```
Luego un segundo HTTP Request para descargar el archivo:
```
URL: https://api.telegram.org/file/bot{{ $env.TELEGRAM_TOKEN }}/{{ $json.result.file_path }}
Respuesta: archivo binario
```
### Nodo 4 — HTTP: Whisper API (transcripción)
```
Tipo: HTTP Request
Método: POST
URL: http://100.64.1.10:8765/transcribe ← IP Tailscale de tu PC
Body: form-data
file: [archivo de audio del nodo anterior]
```
Resultado esperado:
```json
{ "text": "Reunión con Juan el viernes a las diez de la mañana en el puerto" }
```
### Nodo 5 — HTTP: Ollama (extraer evento)
```
Tipo: HTTP Request
Método: POST
URL: http://100.64.1.10:11434/api/generate
Body JSON:
{
"model": "qwen2.5:14b",
"stream": false,
"prompt": "Extrae el evento de esta frase y devuelve SOLO JSON válido sin explicaciones:\n\nFrase: '{{ $json.text }}'\n\nFecha actual: {{ $now.format('YYYY-MM-DD') }}, {{ $now.format('dddd') }}\n\nDevuelve exactamente este formato:\n{\"titulo\": \"...\", \"fecha\": \"YYYY-MM-DD\", \"hora_inicio\": \"HH:MM\", \"hora_fin\": \"HH:MM\", \"lugar\": \"...\", \"descripcion\": \"...\"}\n\nSi no hay hora fin, suma 1 hora a la hora inicio. Si no hay lugar, pon vacío."
}
```
### Nodo 6 — Code: Parsear respuesta Ollama
```javascript
// Extraer el JSON de la respuesta de Ollama
const responseText = $input.item.json.response;
// Buscar el JSON dentro del texto (por si Ollama añade texto extra)
const jsonMatch = responseText.match(/\{[\s\S]*\}/);
if (!jsonMatch) throw new Error("Ollama no devolvió JSON válido: " + responseText);
const evento = JSON.parse(jsonMatch[0]);
// Construir fechas iCal (formato: 20260704T100000)
const fechaInicio = evento.fecha.replace(/-/g, '') + 'T' + evento.hora_inicio.replace(':', '') + '00';
const fechaFin = evento.fecha.replace(/-/g, '') + 'T' + evento.hora_fin.replace(':', '') + '00';
// UID único para el evento
const uid = 'n8n-' + Date.now() + '@crewinghunters.com';
// Formato iCalendar (CalDAV)
const ical = `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//n8n//Calendario Voz//ES
BEGIN:VEVENT
UID:${uid}
DTSTART:${fechaInicio}
DTEND:${fechaFin}
SUMMARY:${evento.titulo}
LOCATION:${evento.lugar || ''}
DESCRIPTION:${evento.descripcion || 'Creado por voz via n8n'}
END:VEVENT
END:VCALENDAR`;
return {
json: {
...evento,
ical,
uid,
fechaInicio,
fechaFin,
// Para el recordatorio: fecha ISO completa
fechaRecordatorio: new Date(evento.fecha + 'T' + evento.hora_inicio + ':00').toISOString()
}
};
```
### Nodo 7 — HTTP: Crear evento en Nextcloud (CalDAV)
```
Tipo: HTTP Request
Método: PUT
URL: https://tu-nextcloud.com/remote.php/dav/calendars/USUARIO/personal/{{ $json.uid }}.ics
Autenticación: Basic Auth
Usuario: tu_usuario_nextcloud
Password: contraseña_de_aplicacion
Headers:
Content-Type: text/calendar; charset=utf-8
Body: {{ $json.ical }}
```
### Nodo 8 — Telegram: Confirmar
```
Tipo: Telegram
Operación: Send Message
Chat ID: {{ $env.TELEGRAM_CHAT_ID }}
Texto:
✅ *Evento creado en tu calendario*
📅 *{{ $json.titulo }}*
🗓 {{ $json.fecha }}
🕐 {{ $json.hora_inicio }} {{ $json.hora_fin }}
📍 {{ $json.lugar || 'Sin ubicación' }}
_Te recordaré 30 minutos antes_ ⏰
```
### Nodo 9 — Wait (hasta 30min antes del evento)
```
Tipo: Wait
Modo: Until specified time
Fecha/Hora: {{ $json.fechaRecordatorio }}
Offset: -30 minutos
```
### Nodo 10 — Telegram: Recordatorio
```
Tipo: Telegram
Operación: Send Message
Chat ID: {{ $env.TELEGRAM_CHAT_ID }}
Texto:
⏰ *Recordatorio — en 30 minutos:*
📅 *{{ $json.titulo }}*
🕐 {{ $json.hora_inicio }}
📍 {{ $json.lugar || '' }}
```
---
## PASO 8 — Variables de Entorno en n8n
En n8n → Settings → Variables → Agregar:
| Variable | Valor |
|---------|-------|
| `TELEGRAM_TOKEN` | `7123456789:AAF...` (token del bot) |
| `TELEGRAM_CHAT_ID` | `123456789` (tu chat ID) |
| `WHISPER_URL` | `http://100.64.1.10:8765` |
| `OLLAMA_URL` | `http://100.64.1.10:11434` |
| `NEXTCLOUD_URL` | `https://tu-nextcloud.com` |
| `NEXTCLOUD_USER` | `tu_usuario` |
| `NEXTCLOUD_PASS` | `contraseña_de_aplicacion` |
---
## PASO 9 — Prueba Completa
1. Graba un audio en el teléfono diciendo:
> *"Reunión con Federico el próximo martes a las tres de la tarde en el puerto de Miami"*
2. Envía el audio al bot de Telegram
3. En ~10-15 segundos deberías recibir:
```
✅ Evento creado en tu calendario
📅 Reunión con Federico
🗓 2026-07-07
🕐 15:00 16:00
📍 Puerto de Miami
```
4. Verifica en Nextcloud Calendar que el evento aparece
5. Espera el recordatorio 30 min antes ✅
---
## Checklist de Instalación
### En tu PC (Windows)
- [ ] Instalar Tailscale → anotar IP (`100.x.x.x`)
- [ ] `pip install faster-whisper fastapi uvicorn python-multipart`
- [ ] Crear `C:\whisper-api\server.py` (código arriba)
- [ ] Crear `C:\whisper-api\start.bat`
- [ ] Agregar `start.bat` al inicio de Windows
- [ ] Iniciar la API y verificar: `curl http://localhost:8765/health`
- [ ] Configurar Ollama para escuchar en `0.0.0.0`: variable `OLLAMA_HOST`
### En el RPi4 (via SSH)
- [ ] `curl -fsSL https://tailscale.com/install.sh | sh`
- [ ] `sudo tailscale up` → autenticar con la misma cuenta
- [ ] Verificar ping a PC: `tailscale ping 100.x.x.x`
- [ ] Verificar Whisper: `curl http://100.x.x.x:8765/health`
- [ ] Verificar Ollama: `curl http://100.x.x.x:11434/api/tags`
### En Telegram
- [ ] Crear bot con @BotFather → guardar token
- [ ] Obtener tu Chat ID
- [ ] Escribir un mensaje al bot para activarlo
### En Nextcloud
- [ ] Copiar URL CalDAV del calendario
- [ ] Crear contraseña de aplicación para n8n
### En n8n
- [ ] Crear carpetas: Prisa Yachts / Personal / AIS Navigator / Sandbox
- [ ] Crear variables de entorno (8 variables)
- [ ] Crear workflow `🎙️ Voz → Calendario Nextcloud`
- [ ] Agregar los 10 nodos en orden
- [ ] Activar el workflow
- [ ] Prueba con audio real
---
## Solución de Problemas
| Problema | Causa probable | Solución |
|---------|---------------|---------|
| n8n no llega a Whisper | Tailscale no conectado | `sudo tailscale up` en RPi4 |
| Whisper tarda mucho | Modelo "medium" pesado en CPU | Cambiar a "small" en server.py |
| Ollama no responde | OLLAMA_HOST no configurado | Reiniciar Ollama tras la variable |
| Evento en fecha incorrecta | Ollama interpretó mal el día | Ajustar el prompt con más contexto |
| CalDAV 401 Unauthorized | Contraseña de app incorrecta | Crear nueva contraseña en Nextcloud |
| Audio no se procesa | Formato no soportado | Telegram envía .ogg — faster-whisper lo soporta |
---
## Mejoras Futuras (Fase 2)
- [ ] **Cancelar eventos por voz**: "Cancela la reunión del martes" → n8n borra el evento
- [ ] **Consultar agenda**: "¿Qué tengo mañana?" → n8n lee el CalDAV y responde
- [ ] **Recordatorio personalizable**: "Recuérdame 1 hora antes"
- [ ] **Múltiples calendarios**: personal, trabajo, AIS Navigator
- [ ] **Texto además de voz**: si escribes (no mandas audio), también funciona
---
*Blueprint generado: 2026-07-03 | AR Electronics / Álvaro*