Tools: Complete Guide to Crea un asistente de correo con IA usando n8n y Telegram

Tools: Complete Guide to Crea un asistente de correo con IA usando n8n y Telegram

¿Qué necesitas?

1. Obtener credenciales

DeepSeek API

Bot de Telegram

Obtener el Chat ID

2. Construir el flujo en n8n

Paso a paso:

3. Probar el flujo

4. Ajustes para producción

Filtrar por remitente

Rate limiting

Historial de conversación

Manejo de adjuntos

Conclusión ¿Tu bandeja de entrada está llena de correos repetitivos? Consultas de soporte, preguntas frecuentes, solicitudes de información... todo requiere tiempo y respuestas manuales. En este artículo vas a construir un asistente automatizado de correo electrónico que: Diagrama realizado con https://savnet.co Regístrate en platform.deepseek.com, ve a la sección de API Keys y crea una nueva. Vas a necesitar: Crear un bot en Telegram es rápido y no requiere registro de empresa ni tarjetas. Para crear el bot abre Telegram, busca @BotFather, envía /newbot y sigue los pasos. Al finalizar te dará un token como 4839574812:AAFD39kkdpWt3ywyRZergyOLMaJhac60qc. Para más detalle sobre la creación del bot: Telegram Bot Creation Handbook Necesitas saber el Chat ID (tu ID de usuario en Telegram) para que el bot sepa a quién enviarle los mensajes. Guarda ambos valores (Bot Token y Chat ID) para usarlos en n8n. Si eres nuevo en n8n lo primero es crear un flujo. En cada paso te diré el nombre del nodo a agregar para que lo puedas buscar así: Nodo 1 - Trigger IMAP Agrega el nodo Email Trigger (IMAP), el cual será el activador de nuestro flujo y revisará los correos sin leer en nuestro buzón. Por defecto, cuando publiquemos el workflow el nodo hará polling cada cierto tiempo (por defecto cada 60 segundos) para detectar correos nuevos. Nodo 2 - Code (Base de conocimiento) El "secreto" del asistente está aquí. En lugar de dejar que DeepSeek improvise, le defines un contexto claro con las respuestas que esperas. Agrega un nodo Code (Code in Javascript), cambia el modo a "Run Once for All Items" (arriba a la derecha del editor) y conéctalo al Nodo 1 (IMAP). El código debe preservar los datos del correo que nos interesan y agregar el campo knowledgeBase. Puedes hacer esto tan detallado como quieras. Mientras más contexto, mejor responderá. Al usar ...item.json los datos del correo original se conservan y el Nodo 3 (extraer contenido) los recibe sin problema. Este nodo se llamará, por ejemplo, CodeKnow. Nodo 3 - Code (extraer contenido) Usa otro nodo Code (Code in Javascript) para extraer la información necesaria de cada correo y generar el chatInput con el prompt que enviaremos a nuestra IA. Cambia el modo a "Run for Each Item" : Nodo 4 - DeepSeek Chat Model (configuración) Agrega un nodo DeepSeek Chat Model. Es probable que esto te genere 3 nodos automáticamente: When chatmessage received, Basic LLM Chain y DeepSeek Chat Model. Quédate solo con el LLM y DeepSeek, el otro sobra. Conecta el bloque de código con el de Basic LLM, así: En los bloques que nos quedan debemos: Lo que hemos hecho es enviarle un prompt con nuestra intención y la data del correo que definimos en el bloque CodeProcessEmail junto con un mensaje de sistema con nuestra base de conocimiento, información que definimos en el bloque CodeKnow. ¿Y el costo? DeepSeek v4 cobra ~$0.14 por millón de tokens de entrada y ~$0.28 por salida. Un correo típico (~1,000 tokens) cuesta ~$0.0002. Con 1,000 correos al mes gastarías ~$0.20. En ajustes para producción te muestro cómo filtrar para evitar llamadas innecesarias. El nodo toma el chatInput del Nodo 3 (base de conocimiento + correo) y devuelve la respuesta generada por DeepSeek. Nodo 5 - Code (mapear respuesta del asistente) El bloque de DeepSeek solo nos retorna la respuesta de la IA con lo cual perdemos la información completa del correo que necesitamos más adelante. Por eso agregamos este nodo Code en modo "Run Once for All Items" que combina la respuesta de la IA con los datos del correo original y determina si puede responder o no: Este bloque es clave porque: Nodo 6 - IF (¿Puede responder?) Teniendo a la mano la respuesta de la IA y la información del correo, agregamos un bloque if. Este bloque evalúa el campo {{ $json.canReply }} con la condición: Nodo 7.1 - Email (SMTP) - Responder Configura el nodo Send Email para enviar la respuesta automática: Nodo 7.2 - Telegram - Enviar notificación Para los correos que la IA no puede responder, no enviaremos una respuesta automática. En su lugar, recibirás una alerta por Telegram. Sin embargo, antes debemos verificar que no vamos a enviar mensajes de más de 4.000 caracteres —el límite de Telegram—. Para ello: Listo, con esto hemos terminado nuestro flujo :D ⚠️ Antes de lanzarte a producción, haz pruebas controladas. No conectes este flujo directamente a tu buzón personal o corporativo. Usa una cuenta de correo de pruebas (IMAP + SMTP) que no contenga información sensible ni confidencial. Si algo sale mal — una mala interpretación de la IA, un dominio mal configurado — podrías terminar respondiendo automáticamente a clientes reales con información incorrecta. Mejor prevenir. Con el flujo listo, haz lo siguiente: Cuando todo funcione como esperas, solo da clic en Publish para activar el polling y dejarlo en producción. Agrega un nodo IF después del trigger para procesar solo correos de dominios o direcciones específicas: DeepSeek tiene límites de peticiones por minuto. Si esperas muchos correos, agrega un nodo Wait de 1-2 segundos entre procesamiento y procesamiento, o usa una cola. Si quieres que DeepSeek recuerde conversaciones anteriores, puedes almacenar el historial en una base de datos (PostgreSQL, Redis) y pasarlo como contexto adicional en cada iteración. DeepSeek no procesa archivos directamente. Si recibes correos con adjuntos, puedes usar un nodo Code para extraer el texto de PDFs o imágenes (con OCR) y pasarlo como contexto. Tienes un asistente de correo automatizado que: Esto no reemplaza un equipo de soporte, pero sí absorbe el volumen de preguntas repetitivas y libera tiempo para lo que realmente importa. Si quieres documentar visualmente este flujo o diagramas de infraestructura más complejos, Savnet te ayuda a diseñarlos. Y si necesitas validar este tipo de automatizaciones de forma continua, SavFalconEye te permite mantener la confianza en cada cambio. ¿Te animas a probarlo? Cuéntame en los comentarios cómo te fue. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

Id: 1234567890 <<< Este es tu Chat ID First: TuNombre Lanf: es .... Id: 1234567890 <<< Este es tu Chat ID First: TuNombre Lanf: es .... Id: 1234567890 <<< Este es tu Chat ID First: TuNombre Lanf: es .... const knowledgeBase = ` Eres un asistente de soporte técnico de [Tu Empresa]. Responde solo si encuentras una coincidencia clara en las políticas siguientes. Si no estás seguro, responde exactamente: NO_PUEDO_RESPONDER POLÍTICAS: 1. Horario de atención: Lunes a viernes de 9:00 a 18:00 (GMT-5). 2. Tiempo de respuesta estimado: 24 horas hábiles. 3. Contraseñas: Nunca solicites ni compartas contraseñas por correo. 4. Reembolsos: Se procesan dentro de los primeros 30 días. El usuario debe enviar un comprobante. 5. Problemas técnicos comunes: - Error 403: El usuario debe limpiar caché y cookies. - Error 500: Reportar al equipo interno, pedir al usuario que espere 1 hora. - Olvido de contraseña: Enviar enlace de restablecimiento a su correo registrado. 6. Facturación: Para cambios de plan o facturación, responder que el usuario ingrese al panel de pago. INSTRUCCIONES: - Responde de forma amable y profesional. - Usa el mismo idioma del correo recibido. - Si el correo contiene más de una pregunta, responde cada punto. - Si el correo es grosero o agresivo, responde: NO_PUEDO_RESPONDER `; const items = $input.all(); return items.map(item => ({ json: { ...item.json, knowledgeBase } })); const knowledgeBase = ` Eres un asistente de soporte técnico de [Tu Empresa]. Responde solo si encuentras una coincidencia clara en las políticas siguientes. Si no estás seguro, responde exactamente: NO_PUEDO_RESPONDER POLÍTICAS: 1. Horario de atención: Lunes a viernes de 9:00 a 18:00 (GMT-5). 2. Tiempo de respuesta estimado: 24 horas hábiles. 3. Contraseñas: Nunca solicites ni compartas contraseñas por correo. 4. Reembolsos: Se procesan dentro de los primeros 30 días. El usuario debe enviar un comprobante. 5. Problemas técnicos comunes: - Error 403: El usuario debe limpiar caché y cookies. - Error 500: Reportar al equipo interno, pedir al usuario que espere 1 hora. - Olvido de contraseña: Enviar enlace de restablecimiento a su correo registrado. 6. Facturación: Para cambios de plan o facturación, responder que el usuario ingrese al panel de pago. INSTRUCCIONES: - Responde de forma amable y profesional. - Usa el mismo idioma del correo recibido. - Si el correo contiene más de una pregunta, responde cada punto. - Si el correo es grosero o agresivo, responde: NO_PUEDO_RESPONDER `; const items = $input.all(); return items.map(item => ({ json: { ...item.json, knowledgeBase } })); const knowledgeBase = ` Eres un asistente de soporte técnico de [Tu Empresa]. Responde solo si encuentras una coincidencia clara en las políticas siguientes. Si no estás seguro, responde exactamente: NO_PUEDO_RESPONDER POLÍTICAS: 1. Horario de atención: Lunes a viernes de 9:00 a 18:00 (GMT-5). 2. Tiempo de respuesta estimado: 24 horas hábiles. 3. Contraseñas: Nunca solicites ni compartas contraseñas por correo. 4. Reembolsos: Se procesan dentro de los primeros 30 días. El usuario debe enviar un comprobante. 5. Problemas técnicos comunes: - Error 403: El usuario debe limpiar caché y cookies. - Error 500: Reportar al equipo interno, pedir al usuario que espere 1 hora. - Olvido de contraseña: Enviar enlace de restablecimiento a su correo registrado. 6. Facturación: Para cambios de plan o facturación, responder que el usuario ingrese al panel de pago. INSTRUCCIONES: - Responde de forma amable y profesional. - Usa el mismo idioma del correo recibido. - Si el correo contiene más de una pregunta, responde cada punto. - Si el correo es grosero o agresivo, responde: NO_PUEDO_RESPONDER `; const items = $input.all(); return items.map(item => ({ json: { ...item.json, knowledgeBase } })); const email = $input.item.json; const subject = email.subject || ''; const body = (email.textPlain && email.textPlain.trim()) || (email.textHtml && email.textHtml.trim()) || ''; return { subject, body: body.substring(0, 5000), from: email.from, to: email.to, uid: email.uid || email.id, chatInput: `From: ${email.from}\nAsunto: ${subject}\nCuerpo: ${body.substring(0, 5000)}` }; const email = $input.item.json; const subject = email.subject || ''; const body = (email.textPlain && email.textPlain.trim()) || (email.textHtml && email.textHtml.trim()) || ''; return { subject, body: body.substring(0, 5000), from: email.from, to: email.to, uid: email.uid || email.id, chatInput: `From: ${email.from}\nAsunto: ${subject}\nCuerpo: ${body.substring(0, 5000)}` }; const email = $input.item.json; const subject = email.subject || ''; const body = (email.textPlain && email.textPlain.trim()) || (email.textHtml && email.textHtml.trim()) || ''; return { subject, body: body.substring(0, 5000), from: email.from, to: email.to, uid: email.uid || email.id, chatInput: `From: ${email.from}\nAsunto: ${subject}\nCuerpo: ${body.substring(0, 5000)}` }; const aiItems = $input.all(); const emailItems = $('CodeProcessEmail').all(); const output = aiItems.map((item, index) => { const originalEmail = emailItems[index]?.json || {}; const aiText = item.json.text || ''; return { json: { ...originalEmail, aiResponse: aiText.trim(), canReply: !aiText.includes('NO_PUEDO_RESPONDER') } }; }); return output; const aiItems = $input.all(); const emailItems = $('CodeProcessEmail').all(); const output = aiItems.map((item, index) => { const originalEmail = emailItems[index]?.json || {}; const aiText = item.json.text || ''; return { json: { ...originalEmail, aiResponse: aiText.trim(), canReply: !aiText.includes('NO_PUEDO_RESPONDER') } }; }); return output; const aiItems = $input.all(); const emailItems = $('CodeProcessEmail').all(); const output = aiItems.map((item, index) => { const originalEmail = emailItems[index]?.json || {}; const aiText = item.json.text || ''; return { json: { ...originalEmail, aiResponse: aiText.trim(), canReply: !aiText.includes('NO_PUEDO_RESPONDER') } }; }); return output; const escapeHtml = (value) => { return String(value || '') .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;'); }; const items = $input.all(); let text = ''; items.forEach((item, index) => { const subject = escapeHtml(item.json.subject || 'Sin asunto'); const aiResponse = escapeHtml(item.json.aiResponse || ''); text += `${index + 1}) ${subject}: ${aiResponse}\n━━━━━━━━━━━━━━━━━━━━\n`; }); // ---- dividir en partes ---- const chunkSize = 3900; const header = `━━━━━━━━━━━━━━━━━━━━\n<b>Recibiste los siguientes correos:</b>\n\n`; const fullText = header + text; const totalParts = Math.ceil(fullText.length / chunkSize) || 1; const output = []; for (let i = 0; i < fullText.length; i += chunkSize) { const part = Math.floor(i / chunkSize) + 1; output.push({ json: { text: `[${part}/${totalParts}]\n${fullText.slice(i, i + chunkSize)}` } }); } return output; const escapeHtml = (value) => { return String(value || '') .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;'); }; const items = $input.all(); let text = ''; items.forEach((item, index) => { const subject = escapeHtml(item.json.subject || 'Sin asunto'); const aiResponse = escapeHtml(item.json.aiResponse || ''); text += `${index + 1}) ${subject}: ${aiResponse}\n━━━━━━━━━━━━━━━━━━━━\n`; }); // ---- dividir en partes ---- const chunkSize = 3900; const header = `━━━━━━━━━━━━━━━━━━━━\n<b>Recibiste los siguientes correos:</b>\n\n`; const fullText = header + text; const totalParts = Math.ceil(fullText.length / chunkSize) || 1; const output = []; for (let i = 0; i < fullText.length; i += chunkSize) { const part = Math.floor(i / chunkSize) + 1; output.push({ json: { text: `[${part}/${totalParts}]\n${fullText.slice(i, i + chunkSize)}` } }); } return output; const escapeHtml = (value) => { return String(value || '') .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;'); }; const items = $input.all(); let text = ''; items.forEach((item, index) => { const subject = escapeHtml(item.json.subject || 'Sin asunto'); const aiResponse = escapeHtml(item.json.aiResponse || ''); text += `${index + 1}) ${subject}: ${aiResponse}\n━━━━━━━━━━━━━━━━━━━━\n`; }); // ---- dividir en partes ---- const chunkSize = 3900; const header = `━━━━━━━━━━━━━━━━━━━━\n<b>Recibiste los siguientes correos:</b>\n\n`; const fullText = header + text; const totalParts = Math.ceil(fullText.length / chunkSize) || 1; const output = []; for (let i = 0; i < fullText.length; i += chunkSize) { const part = Math.floor(i / chunkSize) + 1; output.push({ json: { text: `[${part}/${totalParts}]\n${fullText.slice(i, i + chunkSize)}` } }); } return output; const from = $input.item.json.from; const allowedDomains = ['tudominio.com', 'cliente.com']; const domain = from.split('@')[1]; return allowedDomains.includes(domain); const from = $input.item.json.from; const allowedDomains = ['tudominio.com', 'cliente.com']; const domain = from.split('@')[1]; return allowedDomains.includes(domain); const from = $input.item.json.from; const allowedDomains = ['tudominio.com', 'cliente.com']; const domain = from.split('@')[1]; return allowedDomains.includes(domain); - Lee los correos entrantes vía IMAP - Los analiza con DeepSeek usando una base de conocimiento personalizada - Responde automáticamente si el modelo tiene suficiente contexto - Te notifica por Telegram si necesita revisión manual - Un servidor con n8n funcionando (si no lo tienes, sigue esta guía de instalación) - Una cuenta de correo con acceso IMAP (ZohoMail, Gmail, Outlook, o cualquier proveedor que lo soporte) - Una clave API de DeepSeek (platform.deepseek.com) - Un bot de Telegram creado con https://t.me/BotFather > Nota: Telegram no tiene costos asociados por uso de bots, ideal para automatizaciones sin límites de mensajes. - DEEPSEEK_API_KEY - URL del endpoint: https://api.deepseek.com/v1/chat/completions - Modelo recomendado: deepseek-chat - Abre Telegram y busca @userinfobot - Inicia la conversación con Start - El bot te responde automáticamente con tu información. Busca la línea que dice Id:, ese número es tu Chat ID - En el campo Credential debes configurar (los valores y la forma de activación varían según el proveedor de correo): - User: tu correo - Password: tu contraseña o contraseña de aplicación - Host: tu servidor IMAP (ej: imap.gmail.com) - Secure: true - En el resto de formulario ingresa: - Mailbox Name: nombre del buzón donde se deben extraer los correos, por defecto INBOX (Bandeja de entrada) - Action: Mark as Read, con esto cada mensaje que procesemos será marcado como leído. - En DeepSeek Chat Model, solo especificar las credenciales: - Credential: selecciona o crea una credencial DeepSeek account API Key: tu clave de DeepSeek - API Key: tu clave de DeepSeek - Model: deepseek-v4-flash - API Key: tu clave de DeepSeek - En Basic LLM Chain (procesador): - Source for Prompt: Define below - Prompt (User Message): Analyze this email and provide a short, clear response to send via Telegram. {{ $json.chatInput }} - Chat Messages: Type: System → Message: {{ $json.knowledgeBase }} - Type: System → Message: {{ $json.knowledgeBase }} - Require Specific Output Format: desactivado - Type: System → Message: {{ $json.knowledgeBase }} - Toma los datos originales del correo (subject, from, body, etc.) - Agrega aiResponse con la respuesta limpia de DeepSeek - Agrega canReply (true/false) para decidir si se responde automáticamente - Sí (true) → va a envío SMTP - No (false) → va a notificación Telegram - Credential: te abrirá una ventana para registrar tus credenciales SMTP: User Password Host Port SSL - From Email: le diremos que responda desde el mismo correo al que escribieron: {{ $json.to}} - To: la persona que escribió {{ $json.from }} - Subject: Re: {{ $('CodeProcessEmail').item.json.subject }} - Email Format: HTML - Text: {{ $json.aiResponse }} - Agregamos un nodo Code (JavaScript) en modo Run Once for All Items con el código: - Agregamos un nodo de tipo HTTP Request para enviar cada uno de los mensajes que definimos - Method: POST - URL: https://api.telegram.org/bot{TuTokenTelegram,formato:0000:xxxxxx}/sendMessage - Authentication: none - SendBody: true - Body Content Type: JSON - Specify Body: Using Fields Below chat_id: tu ID de conversación en Telegram text: {{ $json.text }} parse_mode: HTML disable_web_page_preview: TRUE - chat_id: tu ID de conversación en Telegram - text: {{ $json.text }} - parse_mode: HTML - disable_web_page_preview: TRUE - chat_id: tu ID de conversación en Telegram - text: {{ $json.text }} - parse_mode: HTML - disable_web_page_preview: TRUE - Ejecuta manualmente haciendo clic en el botón Execute Workflow (arriba a la derecha). Esto dispara el flujo de inmediato sin esperar el polling automático. - Envíate un correo de prueba con una pregunta que esté en tu base de conocimiento. - Revisa que recibas la respuesta automática en la bandeja del remitente de prueba. - Envíate otro correo con algo fuera de tu base de conocimiento. - Deberías recibir la notificación en Telegram con el contenido del correo. - Lee correos vía IMAP sin perder ninguno - Los analiza con DeepSeek contra tu propia base de conocimiento - Responde automáticamente cuando puede - Te notifica por Telegram cuando necesita tu intervención