{
  "id": "qVwL4UVRZaWLVZtQ",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "ESPOLBot CALENDARIO|FAQS",
  "tags": [],
  "nodes": [
    {
      "id": "6d06b9e5-4027-4626-8315-2b02a3087727",
      "name": "Construir_Prompt_Gemini1",
      "type": "n8n-nodes-base.code",
      "position": [
        -1008,
        2448
      ],
      "parameters": {
        "jsCode": "const preguntaUsuario = $json.pregunta_usuario;\nconst faqsRelevantes = $json.contexto_faqs || [];\n\nlet contextoFAQs = '';\nif (faqsRelevantes.length > 0) {\n  contextoFAQs = 'Aqui hay informacion relevante de la base de datos:\\n\\n';\n  contextoFAQs += faqsRelevantes.map((faq, index) => {\n    return `${index + 1}. P: ${faq.pregunta}\\n   R: ${faq.respuesta}${faq.enlaces ? `\\n   Link: ${faq.enlaces}` : ''}`;\n  }).join('\\n\\n');\n} else {\n  contextoFAQs = 'No encontre informacion especifica en la base de datos para esta pregunta.';\n}\n\nconst prompt = `Eres un asistente de ESPOL. Un estudiante te pregunto:\n\n\"${preguntaUsuario}\"\n\n${contextoFAQs}\n\nIMPORTANTE:\n- La pregunta del usuario ES VALIDA y CLARA\n- Si hay informacion arriba, usala para responder\n- Si NO hay informacion especifica, di algo como: \"No tengo informacion especifica sobre [tema], pero puedes contactar a ESPOL\"\n- NUNCA digas \"no has hecho ninguna pregunta\"\n\nContactos generales:\n- Email: user@example.com\n- Telefono: (04) 2269-269\n- Web: https://www.espol.edu.ec\n\nResponde de forma COMPLETA, clara y util. Incluye TODOS los detalles necesarios:`;\n\nreturn [{\n  json: {\n    prompt: prompt\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "95fcc718-5c12-423d-b062-b9935d3f8506",
      "name": "Buscar_FAQs_Relevantes",
      "type": "n8n-nodes-base.code",
      "position": [
        -1264,
        2448
      ],
      "parameters": {
        "jsCode": "function limpiarTexto(texto) {\n  if (!texto) return '';\n  return texto\n    .toLowerCase()\n    .normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n    .replace(/[\u00bf?\u00a1!.,;:]/g, '')\n    .replace(/\\s+/g, ' ')\n    .trim();\n}\n\n// Leer la pregunta del Telegram Trigger\n// En runOnceForAllItems, usar .all() para acceder al primer (y normalmente \u00fanico) mensaje\nconst telegramInput = $('Telegram Trigger - Inicio').all()[0]?.json || {};\nconst preguntaUsuario = telegramInput.message?.text || '';\nconst chatId = telegramInput.message?.chat?.id || '';\n\n// Las FAQs vienen del input (MongoDB)\nconst todasLasFAQs = $input.all();\n\nconst preguntaLimpia = limpiarTexto(preguntaUsuario);\nconst palabrasUsuario = preguntaLimpia.split(' ').filter(p => p.length > 2);\n\nconst faqsRelevantes = [];\n\nconsole.log(`YOUR_AWS_SECRET_KEY_HERE`);\nconsole.log(`Pregunta usuario: \"${preguntaUsuario}\"`);\nconsole.log(`Chat ID: ${chatId}`);\nconsole.log(`Palabras clave: ${palabrasUsuario.join(', ')}`);\nconsole.log(`Total FAQs en MongoDB: ${todasLasFAQs.length}`);\n\nfor (const faq of todasLasFAQs) {\n  const preguntaFAQ = faq.json.PREGUNTA || faq.json.pregunta || '';\n  if (!preguntaFAQ) continue;\n  \n  const preguntaFAQLimpia = limpiarTexto(preguntaFAQ);\n  \n  let coincidencias = 0;\n  for (const palabra of palabrasUsuario) {\n    if (preguntaFAQLimpia.includes(palabra)) {\n      coincidencias++;\n    }\n  }\n  \n  if (coincidencias >= 2) {\n    const score = coincidencias / palabrasUsuario.length;\n    \n    faqsRelevantes.push({\n      pregunta: preguntaFAQ,\n      respuesta: faq.json.RESPUESTA || faq.json.respuesta || '',\n      enlaces: faq.json.ENLACES || faq.json.enlaces || null,\n      similitud: Math.round(score * 100) / 100,\n      coincidencias: coincidencias\n    });\n  }\n}\n\nfaqsRelevantes.sort((a, b) => b.coincidencias - a.coincidencias);\nconst top10 = faqsRelevantes.slice(0, 10);\n\nconsole.log(`FAQs encontradas: ${top10.length}`);\nif (top10.length > 0) {\n  console.log(`Top 5 candidatas:`);\n  top10.slice(0, 5).forEach((faq, i) => {\n    console.log(`  ${i+1}. \"${faq.pregunta.substring(0, 70)}...\" (${faq.coincidencias} palabras)`);\n  });\n} else {\n  console.log(`\u26a0\ufe0f NO SE ENCONTRARON FAQs`);\n}\nconsole.log(`YOUR_AWS_SECRET_KEY_HERE`);\n\nreturn [{\n  json: {\n    pregunta_usuario: preguntaUsuario,\n    contexto_faqs: top10,\n    total_encontradas: top10.length,\n    chat_id: chatId\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "d5e736d4-9d75-44d5-b1c8-dfe94b289e68",
      "name": "Respuesta Usuario1",
      "type": "n8n-nodes-base.switch",
      "position": [
        -1616,
        1184
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "3874c471-84a4-4058-8a35-0cc833b8617d",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.callback_query.data }}",
                    "rightValue": "feedback_yes"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "a53cc39a-2efc-4196-9425-1afba6fc8d7d",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.callback_query.data }}",
                    "rightValue": "feedback_no"
                  }
                ]
              }
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.3
    },
    {
      "id": "d9139b9c-e970-4c60-88b8-47bd105ab0aa",
      "name": "S\u00ed - Agradecimiento1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -1264,
        1008
      ],
      "parameters": {
        "text": "\ud83c\udf89 \u00a1Genial! Me alegra mucho saber que pude ayudarte \ud83d\udcaa Si necesitas algo m\u00e1s, estoy aqu\u00ed para ti \ud83e\udd16",
        "chatId": "={{ $json.result.id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9c41f1ef-12bf-4620-9719-821461334db6",
      "name": "No - Mensaje Feedback1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -1264,
        1152
      ],
      "parameters": {
        "text": "=\ud83d\ude14 Lamento que la informaci\u00f3n no te haya sido \u00fatil.\n\u00bfPodr\u00edas contarme qu\u00e9 necesitabas exactamente para mejorar mi ayuda? \ud83e\udd16",
        "chatId": "={{ $json.callback_query.message.chat.id }}",
        "forceReply": {
          "force_reply": true
        },
        "replyMarkup": "forceReply",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "cc0258e3-303c-44e5-8c96-80287ba1ab88",
      "name": "Comandos1",
      "type": "n8n-nodes-base.switch",
      "position": [
        -1248,
        1664
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "/help",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "62d19c71-6b1b-4d67-9a45-8aff710df07c",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json[\"message\"] && $json[\"message\"][\"text\"] === \"/help\" }}",
                    "rightValue": "/start"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "/faqs",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "4a392957-3709-4c6e-a7ac-6f6004836860",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json[\"message\"] && $json[\"message\"][\"text\"] === \"/faqs\" }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "/contact",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "32b9b535-3e0a-4d3f-8691-fdc1e955a9ce",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json[\"message\"] && $json[\"message\"][\"text\"] === \"/contact\" }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "/events",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "9393b68b-7c44-48cf-b2d0-d23289673ff6",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json[\"message\"] && $json[\"message\"][\"text\"] === \"/events\" }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "/feedback",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "df2037b1-36f0-4428-bdd4-b5118f687d8f",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json[\"message\"] && $json[\"message\"][\"text\"] === \"/feedback\" }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "/start",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "58d0352b-c98a-4ac7-bf60-f6a6e6919744",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json[\"message\"] && $json[\"message\"][\"text\"] === \"/start\" }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {},
        "looseTypeValidation": true
      },
      "typeVersion": 3.3
    },
    {
      "id": "79a97506-c1e2-4049-a62a-e5820b681a0d",
      "name": "Inicio - Feedback",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -880,
        1312
      ],
      "parameters": {
        "text": "=\ud83d\udcac *Enviar Feedback*\n\n\u00bfTienes alguna sugerencia o comentario sobre el bot? \n\n\ud83d\udcdd Escribe tu mensaje a continuaci\u00f3n y lo revisaremos.\n\n\ud83e\udd16 *\u00a1Tu opini\u00f3n es importante!* \n\n\ud83d\udca1 *Tip:* Los botones \ud83d\udc4d \ud83d\udc4e aparecen despu\u00e9s de cada respuesta para que califiques la informaci\u00f3n.",
        "chatId": "={{ $json.message.chat.id }}",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "\ud83d\udc4d S\u00ed, me ayud\u00f3",
                    "additionalFields": {
                      "callback_data": "feedback_yes"
                    }
                  },
                  {
                    "text": "\ud83d\udc4e No, no me ayud\u00f3",
                    "additionalFields": {
                      "callback_data": "feedback_no"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "accfeb10-fcb6-4b1e-a1bb-a40ed82ebcdd",
      "name": "FAQs",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -880,
        784
      ],
      "parameters": {
        "text": "\ud83d\udcac Secci\u00f3n de Consultas \u2013 ChatBot ESPOL\n\nEst\u00e1s en la secci\u00f3n de preguntas frecuentes.\nAqu\u00ed puedes escribir cualquier duda o consulta sobre la universidad \u2014por ejemplo, temas de matr\u00edcula, eventos, horarios o servicios estudiantiles\u2014 y te responder\u00e9 enseguida.\n\n\u270d\ufe0f Escribe tu pregunta para continuar.\nPor ejemplo: \u201cNecesito informaci\u00f3n sobre los eventos de esta semana\u201d o \u201c\u00bfD\u00f3nde puedo comunicarme con admisiones?\u201d",
        "chatId": "={{ $('Telegram Trigger - Inicio').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "ccd1f1ed-5aa2-402c-a40e-5650ee6066d7",
      "name": "Leer FAQs de MongoDB",
      "type": "n8n-nodes-base.mongoDb",
      "position": [
        -1520,
        2448
      ],
      "parameters": {
        "options": {},
        "collection": "espol_faqs"
      },
      "credentials": {
        "mongoDb": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6529ea5d-0af3-4acc-b81b-087730a484b6",
      "name": "Telegram Trigger - Inicio",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -1872,
        1744
      ],
      "parameters": {
        "updates": [
          "message",
          "callback_query",
          "*"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5bfddfd3-92ac-432f-aa64-3f19ab58f30a",
      "name": "Respuesta Contact",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -880,
        960
      ],
      "parameters": {
        "text": "=\ud83d\udcde *Informaci\u00f3n de Contacto ESPOL*\n\n\ud83c\udf10 Web: https://www.espol.edu.ec\n\ud83d\udce7 Email: admision@espol.edu.ec\n\ud83d\udcf1 Tel\u00e9fono: (04) 2269-269\n\n\ud83d\udccd Campus Gustavo Galindo Velasco\nKm. 30.5 V\u00eda Perimetral\nGuayaquil, Ecuador\n\n\ud83d\udce7 Otros contactos:\nComunicaci\u00f3n: comunicacion@espol.edu.ec\nRelaciones Externas y Vinculaci\u00f3n Corporativa:  relex@espol.edu.ec\nPostgrados: postgrad@espol.edu.ec\nTransparencia: transparencia@espol.edu.ec",
        "chatId": "={{ $('Telegram Trigger - Inicio').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "8aa3525d-815a-486a-92cb-9d9e5da991e5",
      "name": "Respuesta Events",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -880,
        1136
      ],
      "parameters": {
        "text": "=\ud83d\udcc5 *Pr\u00f3ximos Eventos Acad\u00e9micos*\n\nPara ver el calendario completo de eventos, visita:\nhttps://www.espol.edu.ec/es/calendario-academico\n\nO preg\u00fantame sobre fechas espec\u00edficas, por ejemplo:\n\"\u00bfCu\u00e1ndo son las vacaciones?\"\n\"\u00bfCu\u00e1ndo es el examen de admisi\u00f3n?\"",
        "chatId": "={{ $json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "767c3c89-dfb0-4505-8c87-a0208fe31aba",
      "name": "Limpiar texto Gemini",
      "type": "n8n-nodes-base.code",
      "position": [
        -480,
        2448
      ],
      "parameters": {
        "jsCode": "const geminiResponse = $input.first().json;\nconst chatId = $('Buscar_FAQs_Relevantes').first().json.chat_id;\n\nlet texto = '';\n\ntry {\n  if (geminiResponse.candidates && geminiResponse.candidates[0] && geminiResponse.candidates[0].content && geminiResponse.candidates[0].content.parts) {\n    texto = geminiResponse.candidates[0].content.parts[0].text;\n  } else if (geminiResponse.content && geminiResponse.content.parts) {\n    texto = geminiResponse.content.parts[0].text;\n  } else if (geminiResponse.text) {\n    texto = geminiResponse.text;\n  } else {\n    texto = \"Lo siento, hubo un problema al procesar la respuesta.\";\n  }\n} catch (error) {\n  texto = \"Lo siento, ocurrio un error.\";\n}\n\nif (texto && texto.length > 0) {\n  texto = texto.replace(/\\*\\*/g, '').replace(/`/g, '').trim();\n}\n\nreturn [{\n  json: {\n    texto_limpio: texto,\n    chat_id: chatId\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7cabbbc4-e002-4c04-a4d9-b5c3633e98af",
      "name": "Get a chat",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -1488,
        1056
      ],
      "parameters": {
        "chatId": "={{ $json.callback_query.message.chat.id }}",
        "resource": "chat"
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b5cdf1bf-6f19-4e62-a812-022e5fe6073c",
      "name": "Switch_Que_BD_Leer",
      "type": "n8n-nodes-base.switch",
      "position": [
        -1856,
        2304
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "=Consultar Calendario",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "9404fad9-6063-443e-9a4c-80c86a20a6a9",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.es_consulta_calendario }}",
                    "rightValue": ""
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3.3
    },
    {
      "id": "09ddecdf-9223-4371-bd53-9d836315b7a3",
      "name": "Detector_Calendario_Pre",
      "type": "n8n-nodes-base.code",
      "position": [
        -2064,
        2304
      ],
      "parameters": {
        "jsCode": "// ===== DETECTOR PRE-LECTURA DE CALENDARIO =====\n\nfunction limpiarTexto(texto) {\n  if (!texto) return '';\n  return texto\n    .toLowerCase()\n    .normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n    .replace(/[\u00bf?\u00a1!.,;:]/g, '')\n    .replace(/\\s+/g, ' ')\n    .trim();\n}\n\n// Procesar todos los items entrantes del Trigger\nconst inputItems = $('Telegram Trigger - Inicio').all();\nconst resultados = [];\n\nfor (const item of inputItems) {\n  const preguntaUsuario = item.json.message?.text || '';\n  const chatId = item.json.message?.chat?.id || '';\n  const preguntaLimpia = limpiarTexto(preguntaUsuario);\n\n  // ===== PALABRAS CLAVE DE CALENDARIO =====\n  const palabrasClaveCalendario = {\n    eventos: [\n      'matricula', 'matriculas', 'matriculacion',\n      'inicio de clases', 'fin de clases', 'clases',\n      'primer termino', 'segundo termino', 'tercer termino',\n      'examenes', 'examen', 'prueba', 'pruebas',\n      'evaluaciones', 'evaluacion',\n      'suspension', 'feriado', 'feriados',\n      'vacaciones', 'receso',\n      'cambio de carrera', 'cambio de materia',\n      'retiro', 'retiros',\n      'solicitudes', 'solicitud',\n      'inscripciones', 'inscripcion',\n      'graduacion', 'grado',\n      'titulacion',\n      'becas', 'beca',\n      'nivelacion'\n    ],\n    \n    tiempo: [\n      'cuando', 'fecha', 'fechas', 'dia', 'dias',\n      'plazo', 'plazos', 'periodo', 'periodos',\n      'termino', 'terminos', 'semestre', 'a\u00f1o',\n      'inicio', 'fin', 'cierre', 'apertura',\n      'hasta cuando', 'desde cuando',\n      'calendario', 'cronograma',\n      'horario', 'horarios',\n      'proxima', 'proximo', 'siguiente'\n    ],\n    \n    terminos: [\n      '2024-2025', '2025-2026',\n      'PAO', 'PAOI', 'PAOII', 'IPAO', 'IIPAO', 'PAO1', 'PAO2',\n      'primer', 'segundo', 'tercer',\n      'trimestre', 'bimestre', 'semestral'\n    ]\n  };\n\n  // Combinar todas las palabras clave\n  const todasLasPalabrasClave = [\n    ...palabrasClaveCalendario.eventos,\n    ...palabrasClaveCalendario.tiempo,\n    ...palabrasClaveCalendario.terminos\n  ];\n\n  // ===== DETECCI\u00d3N =====\n  let palabrasEncontradas = [];\n  let puntuacion = 0;\n\n  for (const palabra of todasLasPalabrasClave) {\n    if (preguntaLimpia.includes(palabra)) {\n      palabrasEncontradas.push(palabra);\n      if (palabrasClaveCalendario.eventos.includes(palabra)) {\n        puntuacion += 2;\n      } else {\n        puntuacion += 1;\n      }\n    }\n  }\n\n  // Decisi\u00f3n: \u00bfEs consulta de calendario?\n  const esConsultaCalendario = puntuacion >= 2 || palabrasEncontradas.length >= 2;\n\n  // LOGS\n  console.log(`YOUR_AWS_SECRET_KEY_HERE`);\n  console.log(`\ud83d\udd0d DETECTOR PRE-LECTURA DE CALENDARIO`);\n  console.log(`Pregunta: \"${preguntaUsuario}\"`);\n  console.log(`Palabras encontradas: ${palabrasEncontradas.join(', ') || 'ninguna'}`);\n  console.log(`Puntuaci\u00f3n: ${puntuacion}`);\n  console.log(`\u00bfEs calendario? ${esConsultaCalendario ? '\u2705 S\u00cd' : '\u274c NO'}`);\n  console.log(`${esConsultaCalendario ? '\u2192 Ir\u00e1 a BD_Calendario' : '\u2192 Ir\u00e1 a FAQs (flujo normal)'}`);\n  console.log(`YOUR_AWS_SECRET_KEY_HERE`);\n\n  resultados.push({\n    json: {\n      pregunta_usuario: preguntaUsuario,\n      chat_id: chatId,\n      es_consulta_calendario: esConsultaCalendario,\n      palabras_encontradas: palabrasEncontradas,\n      puntuacion: puntuacion\n    }\n  });\n}\n\nreturn resultados;"
      },
      "typeVersion": 2
    },
    {
      "id": "81ea9f6c-121c-4bc5-99e4-e947dae8bf7a",
      "name": "Mensaje de Gemini",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        -800,
        2448
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-2.5-flash-lite-preview-06-17",
          "cachedResultName": "models/gemini-2.5-flash-lite-preview-06-17"
        },
        "options": {
          "temperature": 0.3,
          "maxOutputTokens": 2000
        },
        "messages": {
          "values": [
            {
              "content": "={{ $json.prompt }}"
            }
          ]
        },
        "simplify": false
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1,
      "alwaysOutputData": false
    },
    {
      "id": "8116c6c4-e4ca-4450-89ab-17df67b4b886",
      "name": "Enviar Respuesta sobre faqs",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -224,
        2448
      ],
      "parameters": {
        "text": "={{ $json.texto_limpio }}\n\n\ud83d\udcac \u00bfDeseas revisar otra consulta o hacer una nueva pregunta?",
        "chatId": "={{ $json.chat_id }}",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "\ud83d\udc4d S\u00ed",
                    "additionalFields": {
                      "callback_data": "=feedback_yes"
                    }
                  },
                  {
                    "text": "\ud83d\udc4e No",
                    "additionalFields": {
                      "callback_data": "=feedback_no"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5f4eed47-a777-4d27-a80a-bcc145808f42",
      "name": "Guardado en cvs Feedback",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1264,
        1296
      ],
      "parameters": {
        "columns": {
          "value": {
            "id": "={{ $json.message.chat.id }}",
            "Nombre": "={{ $json.message.reply_to_message.chat.first_name }}",
            "Comentario": "={{ $json.message.text }}"
          },
          "schema": [
            {
              "id": "id",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "id",
              "defaultMatch": true,
              "canBeUsedToMatch": true
            },
            {
              "id": "Nombre",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Nombre",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "Comentario",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "Comentario",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "id"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1dcEsIPMMnjBtQ1YXy5Zc50OWwBVOcWXpxkJUJWZmE4E/edit#gid=0",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1dcEsIPMMnjBtQ1YXy5Zc50OWwBVOcWXpxkJUJWZmE4E",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1dcEsIPMMnjBtQ1YXy5Zc50OWwBVOcWXpxkJUJWZmE4E/edit?usp=drivesdk",
          "cachedResultName": "Feedback"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "cdf6ad08-7f31-467e-9feb-9d3a8e1c9ba6",
      "name": "Construir_Prompt_Calendario1",
      "type": "n8n-nodes-base.code",
      "position": [
        -1008,
        2160
      ],
      "parameters": {
        "jsCode": "const preguntaUsuario = $json.consulta || $json.pregunta_usuario || $json.pregunta || '';\nconst chatId = $json.chat_id;\n\n// Preferir el nuevo esquema del buscador\nlet eventos = Array.isArray($json.resultados) ? $json.resultados : [];\n\n// Compatibilidad con el arreglo antiguo { eventos_calendario: [...] }\nif ((!eventos || eventos.length === 0) && Array.isArray($json.eventos_calendario)) {\n  eventos = $json.eventos_calendario.map((e) => ({\n    PERIODO_FECHAS: e.periodo || e.PERIODO_FECHAS || '',\n    ACTIVIDADES_GRADO: e.actividades_grado || e.ACTIVIDADES_GRADO || '',\n    PROCESOS_GRADO: e.procesos_grado || e.PROCESOS_GRADO || '',\n    ACTIVIDADES_FORMACION: e.actividades_formacion || e.ACTIVIDADES_FORMACION || '',\n    fecha_inicio_ts: e.fecha_inicio_ts ?? null,\n    score: e.score ?? null,\n  }));\n}\n\n// Se\u00f1al de \u201cdemasiadas coincidencias\u201d y facetas sugeridas (si vienen del buscador)\nconst demasiadas = Boolean($json.demasiadas_coincidencias);\nconst filtros = $json.filtros_sugeridos || null;\n\n// === Funciones auxiliares ===\nfunction safe(v) { return (v == null ? '' : String(v)); }\n\nfunction fmtFecha(ts) {\n  if (ts == null) return '';\n  try {\n    const d = new Date(Number(ts));\n    if (isNaN(d.getTime())) return '';\n    const dd = String(d.getDate()).padStart(2, '0');\n    const mm = String(d.getMonth() + 1).padStart(2, '0');\n    const yyyy = d.getFullYear();\n    return `${yyyy}-${mm}-${dd}`;\n  } catch { return ''; }\n}\n\n// === Determinar PAO actual seg\u00fan la fecha del sistema ===\nconst ahora = new Date();\nconst mesActual = ahora.getMonth() + 1;\nconst anioActual = ahora.getFullYear();\n\nlet paoActual = '';\nif (mesActual >= 5 && mesActual <= 9) paoActual = 'PAO I';\nelse if (mesActual >= 9 || mesActual <= 2) paoActual = 'PAO II';\nelse paoActual = 'Vacaciones o PAE';\n\nconst contextoTiempo = `\ud83d\udcc6 Fecha actual: ${anioActual}-${String(mesActual).padStart(2, '0')} (${paoActual} en curso)`;\n\n// === Ordenar eventos por relevancia y fecha ===\nconst eventosOrdenados = [...eventos].sort((a, b) => {\n  const sa = (a.score == null) ? -Infinity : Number(a.score);\n  const sb = (b.score == null) ? -Infinity : Number(b.score);\n  if (sb !== sa) return sb - sa;\n\n  const ta = (a.fecha_inicio_ts == null) ? Number.POSITIVE_INFINITY : Number(a.fecha_inicio_ts);\n  const tb = (b.fecha_inicio_ts == null) ? Number.POSITIVE_INFINITY : Number(b.fecha_inicio_ts);\n  return ta - tb;\n});\n\nlet contextoCalendario = '';\n\nif (eventosOrdenados.length > 0) {\n  contextoCalendario = '\ud83d\udcc5 He encontrado estas coincidencias (ordenadas por relevancia):\\n\\n';\n\n  // Mostrar solo el top 10\n  const toShow = eventosOrdenados.slice(0, 10);\n\n  contextoCalendario += toShow.map((ev, idx) => {\n    const periodo = safe(ev.PERIODO_FECHAS || ev.periodo);\n    const ag = safe(ev.ACTIVIDADES_GRADO || ev.actividades_grado);\n    const pg = safe(ev.PROCESOS_GRADO || ev.procesos_grado);\n    const af = safe(ev.ACTIVIDADES_FORMACION || ev.actividades_formacion);\n    const fechaISO = fmtFecha(ev.fecha_inicio_ts);\n    const score = (ev.score != null) ? Number(ev.score).toFixed(3) : '';\n\n    let info = `${idx + 1}. \ud83d\udccc ${periodo || '(sin fecha)'}`;\n    if (fechaISO) info += `\\n   \ud83d\uddd3\ufe0f Inicio (estimado): ${fechaISO}`;\n    if (ag.trim()) info += `\\n   \ud83c\udf93 Actividades de Grado: ${ag}`;\n    if (pg.trim()) info += `\\n   \ud83d\udccb Procesos: ${pg}`;\n    if (af.trim()) info += `\\n   \ud83d\udd27 Formaci\u00f3n T\u00e9cnica: ${af}`;\n    if (score) info += `\\n   \ud83d\udd0e score:${score}`;\n    return info;\n  }).join('\\n\\n');\n\n  // Sugerencias si hay demasiadas coincidencias\n  if (demasiadas) {\n    const tips = [];\n    if (filtros?.posibles_anios?.length) tips.push(`\u2022 especifica a\u00f1o: ${filtros.posibles_anios.slice(-3).join(' / ')}`);\n    if (filtros?.posibles_meses?.length) tips.push(`\u2022 a\u00f1ade mes: ${filtros.posibles_meses.slice(0, 4).join(' / ')}`);\n    if (filtros?.posibles_tipos_evaluacion?.length) tips.push(`\u2022 tipo de evaluaci\u00f3n: ${filtros.posibles_tipos_evaluacion.join(' / ')}`);\n    if (filtros?.posibles_ciclos?.length) tips.push(`\u2022 ciclo: ${filtros.posibles_ciclos.join(' / ')}`);\n\n    if (tips.length) {\n      contextoCalendario += '\\n\\n\u26a0\ufe0f Hay muchas coincidencias. Sugerencias para refinar:\\n' + tips.join('\\n');\n    }\n  }\n\n} else {\n  contextoCalendario = 'No encontr\u00e9 informaci\u00f3n espec\u00edfica en el calendario acad\u00e9mico para tu consulta.';\n}\n\n// === Reglas de interpretaci\u00f3n PAO/PAE ===\nconst reglasPAO = `\nREGLAS DE INTERPRETACI\u00d3N (PAO/PAE y feriados):\n\u2022 Si un evento dice \u201cvacaciones\u201d y la fecha cae ENTRE finales de febrero y los primeros d\u00edas de mayo \u21d2 corresponde a VACACIONES del PAO II.\n\u2022 Desde inicios de mayo hasta inicios/mediados de septiembre \u21d2 corresponde a PAO I.\n\u2022 Si un evento cae entre finales de septiembre y la 1.\u00aa o 2.\u00aa semana de febrero \u21d2 corresponde a PAO II (per\u00edodo lectivo).\n\u2022 En PAO II, si aparecen \u201cvacaciones estudiantiles\u201d del 28 de diciembre al 1 de enero \u21d2 son feriado (no vacaciones entre PAOs).\n\u2022 PAE (Programa de Acompa\u00f1amiento/Evaluaci\u00f3n) ocurre normalmente desde inicios de marzo hasta finales de abril o inicios de mayo.\n\u2022 Si dentro de un PAO aparece \u201cvacaciones\u201d, interpr\u00e9talas como feriados espec\u00edficos, no como cambio de PAO.\n\u2022 Si el evento no indica expl\u00edcitamente PAO I o II, infi\u00e9relo con estas reglas. Si hay ambig\u00fcedad o la fecha cae fuera de rango, indica que no hay informaci\u00f3n espec\u00edfica.\n`.trim();\n\n// === Construcci\u00f3n final del prompt ===\nconst prompt = `Eres un asistente de ESPOL especializado en calendario acad\u00e9mico.\n\n${contextoTiempo}\n\nPregunta del estudiante:\n\"${preguntaUsuario}\"\n\n${contextoCalendario}\n\n${reglasPAO}\n\nINSTRUCCIONES IMPORTANTES (ESTRICTAS):\n1) Si existe la lista anterior, elige SOLO 1 evento que sea la MEJOR COINCIDENCIA para la pregunta.\n   \u2022 Considera intenci\u00f3n (evaluaci\u00f3n/vacaciones/inicio/fin), ordinal (primera/segunda/tercera),\n     y a\u00f1o/mes impl\u00edcitos o expl\u00edcitos.\n   \u2022 Si ning\u00fan evento es claramente pertinente, responde que NO hay informaci\u00f3n espec\u00edfica.\n2) No inventes datos. No cites eventos que no est\u00e9n arriba.\n3) Si das un resultado, pres\u00e9ntalo claro y breve con emojis, destacando FECHA y ACTIVIDAD.\n4) Si NO hay informaci\u00f3n espec\u00edfica, sugiere contactar a:\n   \ud83d\udce7 Email: user@example.com\n   \ud83d\udcde Tel\u00e9fono: (04) 2269-269\n   \ud83c\udf10 Web: https://www.espol.edu.ec\n5) Si identificas el PAO (I o II) por las fechas, ind\u00edcalo entre par\u00e9ntesis o nota breve (\u201cseg\u00fan fechas corresponde al PAO I/II\u201d).\n6) Responde de forma clara, organizada y \u00fatil.`;\n\n// === Salida para el siguiente nodo (Gemini) ===\nreturn [{\n  json: {\n    prompt,\n    chat_id: chatId\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "728f012c-ceed-400d-a643-8f1353d9f8f1",
      "name": "Message a model1",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        -800,
        2160
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-2.5-flash-preview-05-20",
          "cachedResultName": "models/gemini-2.5-flash-preview-05-20"
        },
        "options": {
          "temperature": 0.3,
          "maxOutputTokens": 200000000
        },
        "messages": {
          "values": [
            {
              "content": "={{ $json.prompt }}"
            }
          ]
        },
        "simplify": false
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4af01328-9345-4714-a79d-ea81b2a16a38",
      "name": "Limpiar_Texto_Gemini1",
      "type": "n8n-nodes-base.code",
      "position": [
        -480,
        2160
      ],
      "parameters": {
        "jsCode": "// ---- 1) Extracci\u00f3n robusta del texto de Gemini ----\nfunction extraerTextoGemini(payload) {\n  // Formato m\u00e1s com\u00fan: candidates[0].content.parts[*].text\n  try {\n    const parts = payload?.candidates?.[0]?.content?.parts;\n    if (Array.isArray(parts) && parts.length > 0) {\n      // Concatenar todos los parts .text\n      const textos = parts\n        .map(p => (typeof p?.text === 'string' ? p.text : ''))\n        .filter(Boolean);\n      if (textos.length) return textos.join('\\n').trim();\n    }\n  } catch {}\n\n  // Alternativos:\n  if (typeof payload?.text === 'string' && payload.text.trim()) {\n    return payload.text.trim();\n  }\n\n  if (Array.isArray(payload?.content?.parts) && payload.content.parts.length > 0) {\n    const textos2 = payload.content.parts\n      .map(p => (typeof p?.text === 'string' ? p.text : ''))\n      .filter(Boolean);\n    if (textos2.length) return textos2.join('\\n').trim();\n  }\n\n  // \u00daltimo recurso: stringify recortado\n  try {\n    const s = JSON.stringify(payload);\n    if (s && s.length) return s.substring(0, 8000);\n  } catch {}\n\n  return 'Lo siento, no pude procesar tu consulta.';\n}\n\nconst respuestaGemini = extraerTextoGemini($json);\n\n// ---- 2) Recuperar chat_id desde varios nodos/ubicaciones ----\nlet chatId = $json.chat_id;\n\nif (!chatId) {\n  // Tu prompt actual\n  try { chatId = $('Construir_Prompt_Calendario1').first()?.json?.chat_id; } catch {}\n}\nif (!chatId) {\n  // Nombre anterior\n  try { chatId = $('Construir_Prompt_Calendario').first()?.json?.chat_id; } catch {}\n}\nif (!chatId) {\n  try { chatId = $('Buscar_Eventos_Calendario').first()?.json?.chat_id; } catch {}\n}\nif (!chatId) {\n  try { chatId = $('Detector_Calendario_Pre').first()?.json?.chat_id; } catch {}\n}\nif (!chatId) {\n  // Telegram Trigger\n  try { chatId = $('Telegram Trigger - Inicio').first()?.json?.message?.chat?.id; } catch {}\n}\n\n// ---- 3) Limpieza / saneamiento para enviar por Telegram con parse_mode HTML ----\nfunction escapeHTML(s) {\n  // Evita que Telegram interprete etiquetas si usas parse_mode: \"HTML\"\n  return s.replace(/&/g, '&amp;')\n          .replace(/</g, '&lt;')\n          .replace(/>/g, '&gt;');\n}\n\nfunction limpiarRespuesta(texto) {\n  if (!texto) return 'Lo siento, no obtuve una respuesta v\u00e1lida.';\n\n  // Eliminar bloques de c\u00f3digo ```...``` (incluye variantes con lenguaje)\n  texto = texto.replace(/```[\\s\\S]*?```/g, '');\n\n  // Eliminar comillas invertidas inline `\n  texto = texto.replace(/`+/g, '');\n\n  // Convertir enlaces markdown [texto](url) \u2192 \"texto (url)\"\n  texto = texto.replace(/\\[([^\\]]+)\\]\\((https?:\\/\\/[^\\s)]+)\\)/g, '$1 ($2)');\n\n  // Quitar markdown simple de estilo: **negritas**, *it\u00e1licas*, __subrayado__ _\n  texto = texto.replace(/\\*\\*(.*?)\\*\\*/g, '$1')\n               .replace(/\\*(.*?)\\*/g, '$1')\n               .replace(/__(.*?)__/g, '$1')\n               .replace(/_(.*?)_/g, '$1');\n\n  // Quitar encabezados markdown (##, ###, etc.)\n  texto = texto.replace(/^\\s{0,3}#{1,6}\\s+/gm, '');\n\n  // Normalizar saltos y espacios\n  texto = texto.replace(/\\r/g, '')\n               .replace(/\\n{3,}/g, '\\n\\n')\n               .replace(/[ \\t]+\\n/g, '\\n')\n               .trim();\n\n  // Escapar HTML para Telegram (parse_mode: \"HTML\")\n  texto = escapeHTML(texto);\n\n  // L\u00edmite prudente para Telegram (4096 es el duro). Dejamos margen.\n  const LIMITE = 3500;\n  if (texto.length > LIMITE) {\n    texto = texto.substring(0, LIMITE - 20).trimEnd() + '\u2026 (mensaje acortado)';\n  }\n\n  return texto;\n}\n\nconst textoLimpio = limpiarRespuesta(respuestaGemini);\n\n// ---- 4) Logs \u00fatiles ----\nconsole.log(`YOUR_AWS_SECRET_KEY_HERE`);\nconsole.log(`\ud83e\uddf9 LIMPIEZA DE TEXTO`);\nconsole.log(`Chat ID encontrado: ${chatId}`);\nconsole.log(`Texto original (100): ${String(respuestaGemini).substring(0, 100)}...`);\nconsole.log(`Texto limpio   (100): ${textoLimpio.substring(0, 100)}...`);\nconsole.log(`YOUR_AWS_SECRET_KEY_HERE`);\n\n// ---- 5) Salida ----\nreturn [{\n  json: {\n    texto_limpio: textoLimpio,\n    chat_id: chatId,\n    respuesta_original: respuestaGemini\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "84c7ffb2-d72d-432a-ac54-9892d31ac5b9",
      "name": "Enviar Respuesta sobre calendario1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -240,
        2160
      ],
      "parameters": {
        "text": "={{ $json.texto_limpio }}\n\n\ud83d\udcac \u00bfDeseas revisar otra consulta o hacer una nueva pregunta?",
        "chatId": "={{ $json.chat_id }}",
        "replyMarkup": "inlineKeyboard",
        "inlineKeyboard": {
          "rows": [
            {
              "row": {
                "buttons": [
                  {
                    "text": "\ud83d\udc4d S\u00ed",
                    "additionalFields": {
                      "callback_data": "=feedback_yes"
                    }
                  },
                  {
                    "text": "\ud83d\udc4e No",
                    "additionalFields": {
                      "callback_data": "=feedback_no"
                    }
                  }
                ]
              }
            }
          ]
        },
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "144e290f-5f0a-400b-b82b-269d90d5f32a",
      "name": "Calendario Acad\u00e9mico",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -1520,
        2160
      ],
      "parameters": {
        "options": {
          "outputFormatting": {
            "values": {
              "date": "FORMATTED_STRING",
              "general": "UNFORMATTED_VALUE"
            }
          },
          "returnFirstMatch": false
        },
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1817620056,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit#gid=1817620056",
          "cachedResultName": "CALENDAR"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit?usp=drivesdk",
          "cachedResultName": "BASE-DATOS-FAQ_ESPOL"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "21267be1-e42b-4362-aefc-42bf96705258",
      "name": "Buscar_Eventos_Calendario1",
      "type": "n8n-nodes-base.code",
      "position": [
        -1264,
        2160
      ],
      "parameters": {
        "jsCode": "const detectorItems = $('Detector_Calendario_Pre').all();\nconst detector =\n  Array.isArray(detectorItems) && detectorItems.length > 0\n    ? detectorItems[0].json\n    : {};\nconst preguntaUsuario = detector.pregunta_usuario || '';\nconst chatId = detector.chat_id ?? null;\nconst filas = $input.all().map(it => it.json || {});\n\nconst HOY = new Date();\nconst ANIO_ACTUAL = HOY.getFullYear();\nconst ANIO_SIG = ANIO_ACTUAL + 1;\n\n// ---------- helpers ----------\nfunction limpiarTexto(t) {\n  if (!t) return '';\n  return String(t)\n    .toLowerCase()\n    .normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')\n    .replace(/[^a-z0-9\u00f1\\s]/gi, ' ')\n    .replace(/\\s+/g, ' ')\n    .trim();\n}\n\nconst STOP = new Set([\n  \"de\",\"la\",\"que\",\"el\",\"en\",\"y\",\"a\",\"los\",\"del\",\"se\",\"las\",\"por\",\"un\",\"para\",\"con\",\"no\",\"una\",\n  \"su\",\"al\",\"lo\",\"como\",\"mas\",\"m\u00e1s\",\"pero\",\"sus\",\"le\",\"ya\",\"o\",\"fue\",\"ha\",\"si\",\"s\u00ed\",\"porque\",\n  \"muy\",\"sin\",\"sobre\",\"tambien\",\"tambi\u00e9n\",\"entre\",\"cuando\",\"todo\",\"esta\",\"est\u00e1\",\"ser\",\"son\",\n  \"dos\",\"han\",\"hay\",\"donde\",\"quien\",\"qui\u00e9n\",\"desde\",\"cada\",\"cual\",\"cu\u00e1l\",\"cuales\",\"cu\u00e1les\"\n]);\nfunction tokenizar(t) { return limpiarTexto(t).split(' ').filter(x => x && x.length > 2 && !STOP.has(x)); }\n\nfunction baseEs(w) {\n  w = limpiarTexto(w);\n  if (!w) return '';\n  w = w.replace(/\\b1(ra|era)\\b/g, 'primera')\n       .replace(/\\b2da\\b/g, 'segunda')\n       .replace(/\\b3ra\\b/g, 'tercera');\n  if (w.endsWith('es')) w = w.slice(0, -2);\n  else if (w.endsWith('s')) w = w.slice(0, -1);\n  w = w.replace(/evaluacione?$/,'evaluacion');\n  return w;\n}\nfunction matchToken(userTok, rowTok) {\n  const u = baseEs(userTok), r = baseEs(rowTok);\n  if (!u || !r) return false;\n  if (u === r) return true;\n  if (u.length >= 5 && (r.includes(u) || u.includes(r))) return true;\n  return false;\n}\n\nfunction detectarIntencion(pregunta) {\n  const p = limpiarTexto(pregunta);\n  if (/(vacaciones|receso|feriado|descanso)/.test(p)) return \"vacaciones\";\n  if (/(evaluacion|evaluaci\u00f3n|examen|parcial|prueba|evaluar|evaluaciones)/.test(p)) return \"evaluacion\";\n  if (/(inicio|empieza|matriculacion|matriculaci\u00f3n|induccion|inducci\u00f3n|apertura|arranque)/.test(p)) return \"inicio\";\n  if (/(final|fin|cierre|termina|culmina|conclusion|conclusi\u00f3n)/.test(p)) return \"fin\";\n  if (/(eleccion|elecci\u00f3n|votacion|votaci\u00f3n)/.test(p)) return \"elecciones\";\n  return \"otro\";\n}\nfunction detectarOrdinal(p) {\n  const t = limpiarTexto(p);\n  if (/\\b(primera|1ra|1era)\\b/.test(t) || /\\bevaluacion\\s*i\\b/.test(t)) return 1;\n  if (/\\b(segunda|2da)\\b/.test(t)     || /\\bevaluacion\\s*ii\\b/.test(t)) return 2;\n  if (/\\b(tercera|3ra)\\b/.test(t)     || /\\bevaluacion\\s*iii\\b/.test(t)) return 3;\n  return 0;\n}\n\nconst MESES = { ene:0, enero:0, feb:1, febrero:1, mar:2, marzo:2, abr:3, abril:3, may:4, mayo:4, jun:5, junio:5, jul:6, julio:6, ago:7, agosto:7, sep:8, sept:8, septiembre:8, oct:9, octubre:9, nov:10, noviembre:10, dic:11, diciembre:11 };\n\nfunction normalizarPeriodo(p) {\n  if (!p) return '';\n  return String(p).replace(/([0-9])([a-zA-Z])/g, '$1 $2').replace(/\\s*-\\s*/g, ' - ').replace(/\\s+/g, ' ').trim();\n}\nfunction parseFechaInicio(periodo) {\n  const txt = normalizarPeriodo(periodo).toLowerCase();\n\n  // \"13 - 17 julio 2026\"\n  let m = /(\\d{1,2})\\s*-\\s*(\\d{1,2})\\s+([a-z\u00e1\u00e9\u00ed\u00f3\u00fa\u00f1]+)\\s+(\\d{4})/.exec(txt);\n  if (m) {\n    const d1 = parseInt(m[1], 10);\n    const mes = MESES[m[3].normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')];\n    const year = parseInt(m[4], 10);\n    if (mes != null) return new Date(year, mes, d1, 0, 0, 0, 0).getTime();\n  }\n\n  // \"30 marzo - 03 abril 2026\"\n  m = /(\\d{1,2})\\s+([a-z\u00e1\u00e9\u00ed\u00f3\u00fa\u00f1]+)\\s*-\\s*(\\d{1,2})\\s+([a-z\u00e1\u00e9\u00ed\u00f3\u00fa\u00f1]+)\\s+(\\d{4})/.exec(txt);\n  if (m) {\n    const d1 = parseInt(m[1], 10);\n    const mes1 = MESES[m[2].normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')];\n    const year = parseInt(m[5], 10);\n    if (mes1 != null) return new Date(year, mes1, d1, 0, 0, 0, 0).getTime();\n  }\n\n  // sin a\u00f1o \u2192 asume actual\n  m = /(\\d{1,2})\\s*-\\s*(\\d{1,2})\\s+([a-z\u00e1\u00e9\u00ed\u00f3\u00fa\u00f1]+)/.exec(txt);\n  if (m) {\n    const d1 = parseInt(m[1], 10);\n    const mes1 = MESES[m[3].normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')];\n    if (mes1 != null) return new Date(ANIO_ACTUAL, mes1, d1, 0, 0, 0, 0).getTime();\n  }\n  m = /(\\d{1,2})\\s+([a-z\u00e1\u00e9\u00ed\u00f3\u00fa\u00f1]+)\\s*-\\s*(\\d{1,2})\\s+([a-z\u00e1\u00e9\u00ed\u00f3\u00fa\u00f1]+)/.exec(txt);\n  if (m) {\n    const d1 = parseInt(m[1], 10);\n    const mes1 = MESES[m[2].normalize('NFD').replace(/[\\u0300-\\u036f]/g, '')];\n    if (mes1 != null) return new Date(ANIO_ACTUAL, mes1, d1, 0, 0, 0, 0).getTime();\n  }\n  return null;\n}\nfunction extraerAniosTexto(texto) {\n  const t = String(texto || '');\n  const m = t.match(/\\b(20\\d{2})\\b/g);\n  if (!m) return new Set();\n  return new Set(m.map(x => parseInt(x, 10)));\n}\nfunction extraerAniosPregunta(p) { return extraerAniosTexto(p); }\nfunction extraerMesesPregunta(p) {\n  const t = limpiarTexto(p);\n  const meses = new Set();\n  Object.keys(MESES).forEach(k => { if (new RegExp(`\\\\b${k}\\\\b`).test(t)) meses.add(MESES[k]); });\n  return meses;\n}\nfunction yearFromTs(ts) { if (ts == null) return null; try { return new Date(ts).getFullYear(); } catch { return null; } }\n\nconst intencion = detectarIntencion(preguntaUsuario);\nconst ordinalBuscado = detectarOrdinal(preguntaUsuario);\nconst tokensUsuario = tokenizar(preguntaUsuario);\nconst setTokens = new Set(tokensUsuario);\nconst aniosPregunta = extraerAniosPregunta(preguntaUsuario);\nconst mesesPregunta = extraerMesesPregunta(preguntaUsuario);\nconst HARD_FILTER_BY_YEAR = aniosPregunta.size > 0;\n\n// ---------- scoring ----------\nconst PESOS = { PERIODO_FECHAS: 0.8, ACTIVIDADES_GRADO: 1.0, PROCESOS_GRADO: 0.6, ACTIVIDADES_FORMACION: 0.9 };\nconst BONUS_INTENCION = 2.2;\nconst BONUS_FRASE_EXACTA = 1.0;\nconst BONUS_TIENE_EVALUACION = 0.7;\nconst BONUS_ORDINAL_MATCH = 1.5;\nconst PENALIZA_ORD_DISTINTO = 0.5;\nconst PENALIZA_SIN_EVAL_CON_ORD = 0.35;\n\nconst BONUS_ANIO_ACTUAL_STRONG = 1.1;\nconst PENALIZA_ANIO_NO_ACTUAL   = 0.6;\nconst BONUS_MATCH_ANIO_PREGUNTA = 1.2;\nconst BONUS_MATCH_MES_ACTUAL    = 0.6;\nconst BONUS_ANIO_EXPLICITO      = 0.3;\n\nconst UMBRAL_SCORE_MIN = 0;\nconst UMBRAL_DEMASIADAS = 25; // \u2190 si hay m\u00e1s que esto, sugerimos refinar\n\nif (!preguntaUsuario || !limpiarTexto(preguntaUsuario)) {\n  return [{ json: { ok:false, motivo:\"pregunta_vacia\", mensaje:\"No se recibi\u00f3 una pregunta (pregunta_usuario).\", chat_id:chatId } }];\n}\nif (!Array.isArray(filas) || filas.length === 0) {\n  return [{ json: { ok:false, motivo:\"sin_datos\", mensaje:\"No llegaron filas desde Google Sheets.\", chat_id:chatId } }];\n}\n\nfunction filaTokens(row) {\n  const F = (k) => { const v = row[k]; return (v == null) ? '' : String(v); };\n  const actTecKey = 'ACTIVIDADES DE FORMACI\u00d3N T\u00c9CNICA Y TECNOL\u00d3GICA';\n  const campos = {\n    row_number: row.row_number ?? null,\n    PERIODO_FECHAS: F('PERIODO_FECHAS'),\n    ACTIVIDADES_GRADO: F('ACTIVIDADES_GRADO'),\n    PROCESOS_GRADO: F('PROCESOS_GRADO'),\n    ACTIVIDADES_FORMACION: F(actTecKey) || F('ACTIVIDADES_FORMACION') || ''\n  };\n  const textoTotal = [campos.PERIODO_FECHAS, campos.ACTIVIDADES_GRADO, campos.PROCESOS_GRADO, campos.ACTIVIDADES_FORMACION].filter(Boolean).join(' | ');\n  const normTotal = limpiarTexto(textoTotal);\n  const toks = {\n    PERIODO_FECHAS: tokenizar(campos.PERIODO_FECHAS),\n    ACTIVIDADES_GRADO: tokenizar(campos.ACTIVIDADES_GRADO),\n    PROCESOS_GRADO: tokenizar(campos.PROCESOS_GRADO),\n    ACTIVIDADES_FORMACION: tokenizar(campos.ACTIVIDADES_FORMACION),\n    TOTAL: tokenizar(textoTotal)\n  };\n  const aniosTexto = extraerAniosTexto(textoTotal);\n  const tsInicio = parseFechaInicio(campos.PERIODO_FECHAS);\n  const anioTs = yearFromTs(tsInicio);\n  const aniosFila = new Set(aniosTexto);\n  if (anioTs != null) aniosFila.add(anioTs);\n  let mesInicio = null;\n  if (tsInicio != null) mesInicio = new Date(tsInicio).getMonth();\n\n  return { campos, textoTotal, normTotal, toks, aniosFila, tsInicio, mesInicio };\n}\nfunction filaTieneOrdinalEvaluacion(norm) {\n  const hasEval = /\\bevaluacion\\b/.test(norm);\n  const primera = hasEval && (/\\bprimera\\b/.test(norm) || /\\bevaluacion\\s*i\\b/.test(norm) || /\\bi\\s*evaluacion\\b/.test(norm));\n  const segunda = hasEval && (/\\bsegunda\\b/.test(norm) || /\\bevaluacion\\s*ii\\b/.test(norm) || /\\bii\\s*evaluacion\\b/.test(norm));\n  const tercera = hasEval && (/\\btercera\\b/.test(norm) || /\\bevaluacion\\s*iii\\b/.test(norm) || /\\biii\\s*evaluacion\\b/.test(norm));\n  return { hasEval, primera, segunda, tercera };\n}\nfunction intersect(setA, setB) { for (const x of setA) if (setB.has(x)) return true; return false; }\n\nfunction puntuar(row) {\n  const { campos, textoTotal, normTotal, toks, aniosFila, tsInicio, mesInicio } = filaTokens(row);\n  if (!normTotal) return null;\n\n  if (HARD_FILTER_BY_YEAR) {\n    if (aniosFila.size > 0 && !intersect(aniosFila, aniosPregunta)) return null;\n    // si no hay a\u00f1o en la fila, la dejamos pasar con score bajo (puedes excluirla retornando null)\n  }\n\n  let score = 0, coincidencias = 0;\n  const matchPorCampo = { PERIODO_FECHAS: [], ACTIVIDADES_GRADO: [], PROCESOS_GRADO: [], ACTIVIDADES_FORMACION: [] };\n  const matchTokens = [];\n\n  for (const t of setTokens) {\n    let hit = false;\n    for (const campo of Object.keys(PESOS)) {\n      const arr = toks[campo];\n      if (arr && arr.some(rt => matchToken(t, rt))) {\n        score += PESOS[campo];\n        matchPorCampo[campo].push(t);\n        hit = true;\n      }\n    }\n    if (hit) { coincidencias++; matchTokens.push(t); }\n  }\n\n  const INTENT_TERMS = {\n    vacaciones: [\"vacaciones\",\"receso\",\"feriado\",\"descanso\",\"estudiantiles\"],\n    evaluacion: [\"evaluacion\",\"evaluaciones\",\"examen\",\"parcial\",\"prueba\",\"ciclo\",\"semana de evaluacion\"],\n    inicio: [\"inicio\",\"empieza\",\"apertura\",\"induccion\",\"matriculacion\",\"novatos\"],\n    fin: [\"final\",\"fin\",\"cierre\",\"termina\",\"culmina\",\"conclusion\",\"proceso final\"],\n    elecciones: [\"eleccion\",\"votacion\"]\n  };\n  if (intencion !== 'otro') {\n    const terms = INTENT_TERMS[intencion] || [];\n    if (terms.some(term => normTotal.includes(limpiarTexto(term)))) score += BONUS_INTENCION;\n  }\n\n  const ord = filaTieneOrdinalEvaluacion(normTotal);\n  if (intencion === 'evaluacion' && ord.hasEval) score += BONUS_TIENE_EVALUACION;\n  if (ordinalBuscado) {\n    const ok = (ordinalBuscado === 1 && ord.primera) || (ordinalBuscado === 2 && ord.segunda) || (ordinalBuscado === 3 && ord.tercera);\n    if (ok) score += BONUS_ORDINAL_MATCH;\n    else {\n      if (ord.primera || ord.segunda || ord.tercera) score -= PENALIZA_ORD_DISTINTO;\n      if (!ord.hasEval) score -= PENALIZA_SIN_EVAL_CON_ORD;\n    }\n  }\n\n  if (HARD_FILTER_BY_YEAR) {\n    if (aniosFila.size > 0 && intersect(aniosFila, aniosPregunta)) score += BONUS_MATCH_ANIO_PREGUNTA;\n  } else {\n    if (aniosFila.size > 0) {\n      if (aniosFila.has(ANIO_ACTUAL)) score += BONUS_ANIO_ACTUAL_STRONG;\n      else score -= PENALIZA_ANIO_NO_ACTUAL;\n    }\n  }\n\n  if (mesesPregunta.size > 0 && mesInicio != null) {\n    if (mesesPregunta.has(mesInicio)) {\n      if (!HARD_FILTER_BY_YEAR && (aniosFila.size === 0 || aniosFila.has(ANIO_ACTUAL))) score += BONUS_MATCH_MES_ACTUAL;\n      else score += BONUS_MATCH_MES_ACTUAL * 0.6;\n    }\n  }\n\n  if (/\\b20\\d{2}\\b/.test(normTotal)) score += BONUS_ANIO_EXPLICITO;\n\n  const frase = limpiarTexto(preguntaUsuario);\n  if (frase && normTotal.includes(frase)) score += BONUS_FRASE_EXACTA;\n\n  const len = normTotal.split(' ').length || 1;\n  score = score / Math.pow(len, 0.03);\n\n  return {\n    row_number: campos.row_number,\n    PERIODO_FECHAS: campos.PERIODO_FECHAS,\n    ACTIVIDADES_GRADO: campos.ACTIVIDADES_GRADO,\n    PROCESOS_GRADO: campos.PROCESOS_GRADO,\n    ACTIVIDADES_FORMACION: campos.ACTIVIDADES_FORMACION,\n    preview: textoTotal,\n    score: Number(score.toFixed(4)),\n    coincidencias,\n    match_tokens: matchTokens,\n    match_por_campo: matchPorCampo,\n    fecha_inicio_ts: tsInicio,\n    mes_inicio: mesInicio,\n    anios_fila: [...aniosFila]\n  };\n}\n\n// ---------- ejecutar y ordenar ----------\nconst evaluadas = [];\nfor (const row of filas) {\n  const r = puntuar(row);\n  if (!r) continue;\n  if (r.score > UMBRAL_SCORE_MIN || r.coincidencias > 0) evaluadas.push(r);\n}\n\nevaluadas.sort((a, b) => {\n  if (b.score !== a.score) return b.score - a.score;\n  if (b.coincidencias !== a.coincidencias) return b.coincidencias - a.coincidencias;\n  const ta = a.fecha_inicio_ts ?? Number.POSITIVE_INFINITY;\n  const tb = b.fecha_inicio_ts ?? Number.POSITIVE_INFINITY;\n  return ta - tb;\n});\n\n// chat_ids v\u00e1lidos (si existen en la data)\nconst chatIds = $input.all()\n  .map(it => it.json?.CHAT_ID)\n  .filter(id => id !== undefined && id !== null && String(id).trim() !== '')\n  .map(id => String(id).trim());\nconst chat_ids_validos = [...new Set(chatIds)];\n\n// ---------- sin resultados ----------\nif (evaluadas.length === 0) {\n  const sugerencias = { email: \"user@example.com\", telefono: \"(04) 2269-269\", web: \"https://www.espol.edu.ec\" };\n  const yearInfo = aniosPregunta.size > 0 ? ` para el/los a\u00f1o(s): ${[...aniosPregunta].join(', ')}` : ` para el a\u00f1o ${ANIO_ACTUAL}`;\n  const mensaje = [\n    `\ud83d\ude15 No encontr\u00e9 informaci\u00f3n espec\u00edfica${yearInfo}.`,\n    \"Puedes comunicarte por:\",\n    `\ud83d\udce7 ${sugerencias.email}`,\n    `\ud83d\udcde ${sugerencias.telefono}`,\n    `\ud83c\udf10 ${sugerencias.web}`\n  ].join('\\n');\n\n  return [{ json: {\n    ok: false,\n    consulta: preguntaUsuario,\n    intencion_detectada: intencion,\n    mensaje,\n    sugerencias_contacto: sugerencias,\n    chat_id: chatId,\n    chat_ids_validos\n  }}];\n}\n\n// ---------- construir sugerencias si hay demasiadas coincidencias ----------\nfunction nombreMes(idx) {\n  const nombres = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre'];\n  return (idx >=0 && idx <=11) ? nombres[idx] : null;\n}\nfunction recolectarFacetas(rows) {\n  const anios = new Set();\n  const meses = new Set();\n  const tiposEval = new Set(); // primera/segunda/tercera\n  const ciclos = new Set();    // Ciclo 1/2/3\n\n  for (const r of rows) {\n    (r.anios_fila || []).forEach(y => anios.add(y));\n    if (typeof r.mes_inicio === 'number') meses.add(r.mes_inicio);\n\n    const norm = limpiarTexto(`${r.PERIODO_FECHAS} ${r.ACTIVIDADES_GRADO} ${r.PROCESOS_GRADO} ${r.ACTIVIDADES_FORMACION}`);\n    if (/\\bprimera\\b/.test(norm) || /\\bevaluacion\\s*i\\b/.test(norm)) tiposEval.add('primera');\n    if (/\\bsegunda\\b/.test(norm) || /\\bevaluacion\\s*ii\\b/.test(norm)) tiposEval.add('segunda');\n    if (/\\btercera\\b/.test(norm) || /\\bevaluacion\\s*iii\\b/.test(norm)) tiposEval.add('tercera');\n\n    const m = norm.match(/\\bciclo\\s*(1|2|3)\\b/);\n    if (m) ciclos.add(`Ciclo ${m[1]}`);\n  }\n\n  return {\n    anios: [...anios].sort((a,b)=>a-b),\n    meses: [...meses].sort((a,b)=>a-b).map(nombreMes).filter(Boolean),\n    tiposEval: [...tiposEval],\n    ciclos: [...ciclos]\n  };\n}\n\nlet sugerencia_reformulacion = null;\nlet filtros_sugeridos = null;\nconst DEMASIADAS = evaluadas.length > UMBRAL_DEMASIADAS;\n\nif (DEMASIADAS) {\n  const facetas = recolectarFacetas(evaluadas);\n  filtros_sugeridos = {\n    posibles_anios: facetas.anios,\n    posibles_meses: facetas.meses,\n    posibles_tipos_evaluacion: facetas.tiposEval,\n    posibles_ciclos: facetas.ciclos\n  };\n\n  const tips = [];\n  if (facetas.anios.length > 0) tips.push(`\u2022 especifica a\u00f1o: **${facetas.anios.slice(-3).join(' / ')}**`);\n  if (facetas.meses.length > 0) tips.push(`\u2022 a\u00f1ade mes: **${facetas.meses.slice(0,4).join(' / ')}**`);\n  if (facetas.tiposEval.length > 0) tips.push(`\u2022 tipo de evaluaci\u00f3n: **${facetas.tiposEval.join(' / ')}**`);\n  if (facetas.ciclos.length > 0) tips.push(`\u2022 ciclo: **${facetas.ciclos.join(' / ')}**`);\n\n  // ejemplo guiado\n  const ejemplo = [];\n  if (intencion === 'evaluacion') {\n    const ejA = facetas.anios[0] ?? ANIO_ACTUAL;\n    const ejMes = facetas.meses[0] ?? 'julio';\n    ejemplo.push(`Ej.: \"\u00bfCu\u00e1ndo es la **primera evaluaci\u00f3n** de **${ejMes} ${ejA}** en PAE?\"`);\n  } else if (facetas.meses.length > 0 || facetas.anios.length > 0) {\n    const ejA = facetas.anios[0] ?? ANIO_ACTUAL;\n    const ejMes = facetas.meses[0] ?? 'abril';\n    ejemplo.push(`Ej.: \"Actividades formativas de **${ejMes} ${ejA}**\"`);\n  }\n\n  sugerencia_reformulacion =\n    `\ud83d\udd0e He encontrado **${evaluadas.length}** coincidencias. ` +\n    `Para llegar m\u00e1s r\u00e1pido a lo que necesitas, prueba a refinar tu pregunta:\\n` +\n    (tips.length ? tips.join('\\n') + '\\n' : '') +\n    (ejemplo.length ? ejemplo.join('\\n') : '');\n}\n\n// ---------- mensaje formateado (todas las coincidencias) ----------\nconst encabezado = `\ud83d\udcc5 Encontr\u00e9 ${evaluadas.length} coincidencia(s) para:\\n\u201c${preguntaUsuario}\u201d`;\nconst cuerpo = evaluadas.map((e, i) => {\n  const partes = [];\n  partes.push(`${i+1}. \ud83d\udccc ${e.PERIODO_FECHAS || '(sin fecha)'}`);\n  if (e.ACTIVIDADES_GRADO?.trim()) partes.push(`   \ud83c\udf93 Actividades de Grado: ${e.ACTIVIDADES_GRADO}`);\n  if (e.PROCESOS_GRADO?.trim())     partes.push(`   \ud83d\udccb Procesos: ${e.PROCESOS_GRADO}`);\n  if (e.ACTIVIDADES_FORMACION?.trim()) partes.push(`   \ud83d\udd27 Formaci\u00f3n T\u00e9cnica: ${e.ACTIVIDADES_FORMACION}`);\n  return partes.join('\\n');\n}).join('\\n\\n');\n\nconst mensaje_formateado = sugerencia_reformulacion\n  ? `${encabezado}\\n\\n${cuerpo}\\n\\n${sugerencia_reformulacion}`\n  : `${encabezado}\\n\\n${cuerpo}`;\n\nreturn [{\n  json: {\n    ok: true,\n    consulta: preguntaUsuario,\n    intencion_detectada: intencion,\n    ordinal_detectado: detectarOrdinal(preguntaUsuario) || null,\n    anios_detectados_en_pregunta: [...aniosPregunta],\n    meses_detectados_en_pregunta: [...mesesPregunta],\n    total_evaluadas: filas.length,\n    total_encontrados: evaluadas.length,\n    demasiadas_coincidencias: DEMASIADAS,\n    umbral_demasiadas: UMBRAL_DEMASIADAS,\n    filtros_sugeridos,                 // \u2190 facetas para construir botones/quick-replies si quieres\n    resultados: evaluadas,             // \u2190 TODAS las coincidencias ordenadas\n    mensaje_formateado,\n    chat_id: chatId,\n    chat_ids_validos: chat_ids_validos\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "2f04c332-95f8-4251-a7d8-c3183c3bf341",
      "name": "Mensaje Help",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -880,
        608
      ],
      "parameters": {
        "text": "=\ud83e\udd16 Bienvenido al ChatBot ESPOL\n\n\ud83d\udc4b \u00a1Hola! Soy ChatBot ESPOL, tu asistente virtual de la Escuela Superior Polit\u00e9cnica del Litoral.\nEstoy aqu\u00ed para responder tus preguntas sobre la universidad: matr\u00edculas, eventos, horarios, docentes, servicios estudiantiles y mucho m\u00e1s.\n\n\ud83d\udcac Solo escribe tu duda y te responder\u00e9 al instante.\nPor ejemplo:\n\n\u201c\u00bfCu\u00e1ndo empiezan las matr\u00edculas?\u201d\n\u201c\u00bfQu\u00e9 eventos hay esta semana?\u201d\n\u201c\u00bfD\u00f3nde puedo contactar a Bienestar Estudiantil?\u201d\n\nTambi\u00e9n puedes usar estos comandos:\n\n/help \u2013 Ver los comandos disponibles\n\n/faqs \u2013 Acceder a la secci\u00f3n de consultas y preguntas frecuentes\n\n/contact \u2013 Mostrar contactos y canales oficiales\n\n/events \u2013 Ver pr\u00f3ximos eventos\n\n/feedback \u2013 Enviar sugerencias o comentarios sobre el bot o los servicios\n\n\ud83c\udf93 Tu informaci\u00f3n, siempre a un mensaje de distancia.\n\u00bfSobre qu\u00e9 te gustar\u00eda consultar hoy?",
        "chatId": "={{ $json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "Markdown",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "58882226-f879-4ae5-b653-025ae55e833f",
      "name": "BIENVENIDA",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -880,
        1504
      ],
      "parameters": {
        "text": "=\ud83d\udc4b\ud83c\udffd \u00a1Qu\u00e9 m\u00e1s, polit\u00e9cnico o futuro polit\u00e9cnico! \n\nBienvenido al bot oficial de ayuda para toda la comunidad de la ESPOL \ud83d\udc22\ud83d\udc9b\ud83d\udc99.  \n\nAqu\u00ed puedes preguntarme lo que necesites sobre tu vida polit\u00e9cnica o lo que se viene en la U:  \n\ud83d\udcc5 Fechas importantes del calendario acad\u00e9mico. \n\ud83c\udf93 Preguntas frecuentes de todas las facultades \u2014 tanto pregrado como postgrado. \n\ud83c\udf9f\ufe0f Info sobre becas, clubes, cursos, eventos, transporte, servicios y m\u00e1s.  \n\nY pilas \ud83d\udc40, porque cada semana te voy a mandar un recordatorio con lo m\u00e1s relevante que se viene para que no se te pase nada \u2014 ni matr\u00edcula, ni retiro, ni feriado \ud83d\ude0e.  As\u00ed que tranqui, compa, pregunta nom\u00e1s.\n\n\u00a1Vamos con todo, polit\u00e9cnico! \ud83d\ude80\ud83d\udcaa",
        "chatId": "={{ $json.message.chat.id }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "5f1bf72a-e7f4-405f-a1fc-549008b73747",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -672,
        1680
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-2.5-flash-lite"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bef601ae-58f3-4785-a49e-4a7225c34ed9",
      "name": "BD_ESPOL",
      "type": "n8n-nodes-base.googleSheetsTool",
      "position": [
        -528,
        1712
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "id",
          "value": "={{ /*n8n-auto-generated-fromAI-override*/ $fromAI('Sheet', ``, 'string') }}"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit?usp=drivesdk",
          "cachedResultName": "BASE-DATOS-FAQ_ESPOL"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "c925a437-914c-4e86-8619-2a670ee53c40",
      "name": "PREGUNTAS AL AZAR DE GUIA",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -672,
        1504
      ],
      "parameters": {
        "text": "=El usuario acaba de iniciar el bot con /start.\nNo le muestres un mensaje de bienvenida.\nS\u00f3lo sugi\u00e9rele 5 preguntas al azar (diferentes cada vez) que podr\u00eda hacerle al bot, relacionadas con la informaci\u00f3n que est\u00e1 guardada en BD_ESPOL (preguntas frecuentes de facultades, calendario acad\u00e9mico, becas, clubes, cursos, eventos, etc.).\n\nEjemplo de formato esperado:\n\n\ud83d\udc4b\ud83c\udffd \u00a1Qu\u00e9 m\u00e1s, futuro polit\u00e9cnico! Bienvenido al bot de ayuda de la ESPOL \ud83d\udc22\ud83d\udc9b\ud83d\udc99\nAqu\u00ed puedes preguntar lo que necesites sobre tu vida universitaria o lo que se viene en el semestre.\n\nPuedes empezar con algo como:\n\u2022 \u00bfCu\u00e1ndo abren las matr\u00edculas?\n\u2022 \u00bfQu\u00e9 clubes hay en la ESPOL?\n\u2022 \u00bfD\u00f3nde puedo ver las fechas de titulaci\u00f3n?\n\u2022 \u00bfQuien es el coordinador de la FIMCP?\n\u2022 \u00bfCuando empiezan las vacaciones del IIPAO 2025?\n\u00a1Dale nom\u00e1s, pregunta lo que necesites que el bot te ayuda al instante! \ud83d\ude0e",
        "options": {
          "systemMessage": "=Eres un asistente polit\u00e9cnico amigable y juvenil, que habla con tono guayaco, relajado y positivo. Est\u00e1s conectado a una base de datos llamada BD_ESPOL, que contiene informaci\u00f3n sobre preguntas frecuentes, calendario acad\u00e9mico, becas, clubes, cursos, eventos, servicios, vida universitaria, transporte, titulaci\u00f3n y reglamentos.\n\nTu objetivo es ayudar a los estudiantes y futuros polit\u00e9cnicos a orientarse en la ESPOL.\n\nCada vez que un usuario nuevo inicia conversaci\u00f3n, no debes volver a saludar, ya que el bot principal ya lo hizo. Solo debes explicarle brevemente c\u00f3mo puede usar el bot y mostrarle 5 preguntas aleatorias distintas cada vez que podr\u00eda intentar para descubrir las funciones del bot.\n\nEl mensaje debe sonar natural, como si fueras un compa\u00f1ero universitario de Guayaquil, usando expresiones relajadas pero respetuosas.\nEvita usar asteriscos, puntos o cualquier formato Markdown. Usa guiones (-) para enumerar las preguntas.\n\nLas preguntas deben cubrir temas variados: calendario acad\u00e9mico, becas, facultades, servicios, transporte, vida universitaria, titulaci\u00f3n y eventos.\nCada vez que respondas, cambia el orden o tipo de preguntas para que no sean las mismas.\n\nEjemplo de estilo y estructura:\nAqu\u00ed puedes preguntar sobre fechas, becas, clubes o eventos\nMira, puedes probar con algo como\n\n- \u00bfCu\u00e1ndo empiezan las clases?\n\n- \u00bfQu\u00e9 becas hay disponibles?\n\n- \u00bfD\u00f3nde veo los cursos extracurriculares?\n\n- \u00bfQu\u00e9 eventos hay esta semana?\n\n- \u00bfC\u00f3mo puedo unirme a un club estudiantil?\n\nTermina siempre con una frase motivadora tipo\n\n\u00a1Dale, pregunta nom\u00e1s!\n\n\u00a1Pru\u00e9balo y ve lo bac\u00e1n que est\u00e1 esto!"
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "b74244ac-ed9a-4979-95ac-d15a620c7df4",
      "name": "Wait",
      "type": "n8n-nodes-base.wait",
      "position": [
        -368,
        1504
      ],
      "parameters": {},
      "typeVersion": 1.1
    },
    {
      "id": "3104667b-6c32-4a83-9627-23609865000e",
      "name": "GUIA DE COMO PREGUNTAR",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -208,
        1504
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "chatId": "={{ $('Telegram Trigger - Inicio').item.json.message.chat.id }}",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "dff25489-2b4d-49da-9398-9efa73bbe8f5",
      "name": "Google Gemini Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        -3168,
        1840
      ],
      "parameters": {
        "options": {}
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7e30adff-e90b-495b-af22-d82ab8a464a3",
      "name": "BD_CALENDARIO1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -3872,
        1664
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1817620056,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit#gid=1817620056",
          "cachedResultName": "CALENDAR"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit?usp=drivesdk",
          "cachedResultName": "BASE-DATOS-FAQ_ESPOL"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "6ee54764-d999-48bc-a498-d4ded389b2f9",
      "name": "GENERADOR DE ANUNCIOS1",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -3168,
        1664
      ],
      "parameters": {
        "text": "=Convierte el siguiente texto en un mensaje casual ecuatoriano:\n\nPeriodo: {{ $json[\"RESULTADOS[0].PERIODO_FECHAS\"] }}\nActividades: {{ $json[\"RESULTADOS[0].ACTIVIDADES_GRADO\"] }}\nProcesos: {{ $json[\"RESULTADOS[0].PROCESOS_GRADO\"] }}\nFormacion tecnica: {{ $json['RESULTADOS[0][\\'ACTIVIDADES DE FORMACI\u00d3N T\u00c9CNICA Y TECNOL\u00d3GICA\\']'] }}\n",
        "options": {
          "systemMessage": "=Eres un redactor ecuatoriano experto en comunicaci\u00f3n universitaria. Tu tarea es convertir los datos de tres semanas (anterior, actual y siguiente) en un solo mensaje casual, fresco y cercano, como si lo dijera un estudiante polit\u00e9cnico.  \n\nSigue estas reglas:\n\n1. **Prioriza la semana del medio (la actual)**. Es la principal. Describe sus actividades con entusiasmo, usando un tono motivador y natural.\n2. Luego menciona brevemente lo ocurrido **la semana pasada (primer item)**, con frases tipo:  \n   \u201cSi te lo perdiste la semana pasada\u2026\u201d o \u201cPor si no te enteraste\u2026\u201d.\n3. Finalmente, habla de **la semana siguiente (\u00faltimo item)**, pero nunca digas \u201cla pr\u00f3xima semana\u201d a secas.  \n   Siempre menciona expl\u00edcitamente **la fecha del periodo**, por ejemplo:  \n   \u201cY para la semana del 17 al 21 de noviembre\u2026\u201d  \n   Usa frases como: \u201cprep\u00e1rate para\u201d, \u201cse viene\u201d, \u201catentos que en la semana del\u2026\u201d.\n4. Usa expresiones naturales de Ecuador como *pilas, \u00f1a\u00f1o, pana, de una, qu\u00e9 bestia, bac\u00e1n, full, a romperla*.\n5. Separa las secciones con saltos de l\u00ednea y mant\u00e9n un estilo cercano y fluido, tipo mensaje de Telegram o redes.\n6. No inventes informaci\u00f3n fuera del JSON, solo reformula y organiza lo recibido.\n7. El resultado final debe ser un **solo texto en espa\u00f1ol ecuatoriano**, sin etiquetas, ni formato JSON, ni comillas.\n\nEjemplo de estructura esperada:\n\n\u00a1Qu\u00e9 m\u00e1s, mi gente polit\u00e9cnica! Esta semana del 10 al 14 de noviembre se viene con todo \ud83d\udcaa.  \nSeguimos a full con las actividades formativas y los panas de Formaci\u00f3n T\u00e9cnica tambi\u00e9n est\u00e1n d\u00e1ndole duro. \u00a1Qu\u00e9 bestia la energ\u00eda que se siente en el campus!  \n\nSi te lo perdiste la semana pasada, hubo evaluaciones y descansos para recargar pilas. \u00a1Espero que hayas aprovechado! \ud83d\ude0e  \n\nY atentos para la semana del 17 al 21 de noviembre, que llega la PRIMERA EVALUACI\u00d3N del grado. \u00a1A romperla con todo el \u00f1eque, panas! \ud83d\udcda\ud83d\udd25\n"
        },
        "promptType": "define"
      },
      "typeVersion": 3
    },
    {
      "id": "91ec2456-dae0-4a1c-9e19-883df9771bd8",
      "name": "Sticky Note20",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4208,
        1376
      ],
      "parameters": {
        "color": 5,
        "width": 2224,
        "height": 656,
        "content": "# \ud83d\udce2 **SISTEMA GENERADOR DE ANUNCIOS** \ud83e\udde0\n"
      },
      "typeVersion": 1
    },
    {
      "id": "e145604d-3454-4370-847b-c88a46e7973f",
      "name": "Sticky Note21",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4176,
        1488
      ],
      "parameters": {
        "color": 5,
        "width": 256,
        "height": 368,
        "content": "## RECORDATORIO SEMANAL\nCADA DOMINGO A LAS 07:00PM SE NOTIFICAR\u00c1 AL USUARIO SOBRE LOS ANUNCIOS IMPORTANTES DE LA  SEMANA ENTRANTE"
      },
      "typeVersion": 1
    },
    {
      "id": "bb5ecbad-5350-4010-a658-bd868d7bdcad",
      "name": "Sticky Note22",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3920,
        1488
      ],
      "parameters": {
        "color": 5,
        "width": 208,
        "height": 368,
        "content": "## CALENDARIO\nEL SISTEMA TENDR\u00c1 A SU DISPOSICI\u00d3N A TIEMPO REAL CALENDARIO ACAD\u00c9MICO DE ESPOL"
      },
      "typeVersion": 1
    },
    {
      "id": "1b0a4908-baec-4ce6-b482-ce5ac6707fbe",
      "name": "Sticky Note23",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3712,
        1488
      ],
      "parameters": {
        "color": 5,
        "width": 496,
        "height": 368,
        "content": "## AUTOMATIZACI\u00d3N\nEL SISTEMA COMPARAR\u00c1 LA FECHA ACTUAL Y ENTREGAR\u00c1 LAS ACTIVIDADES DE LA SEMANA ENTRANTE"
      },
      "typeVersion": 1
    },
    {
      "id": "ffcd4a54-58f6-4718-be90-678e020d12b8",
      "name": "Sticky Note24",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3216,
        1488
      ],
      "parameters": {
        "color": 5,
        "width": 368,
        "height": 496,
        "content": "## ANUNCIO AUTOM\u00c1TICO\nUN AGENTE DE IA, TOMAR\u00c1 LA INFORMACI\u00d3N DE LAS ACTIVIDADES DE LA SEMANA Y GENERAR\u00c1 UN MENSAJE PERSONALIZADO AL USUARIO PARA MANTENERLO SIEMPE AL D\u00cdA"
      },
      "typeVersion": 1
    },
    {
      "id": "65cf8025-fbd1-4c53-892c-3c39af419f6a",
      "name": "Sticky Note25",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2848,
        1456
      ],
      "parameters": {
        "color": 5,
        "width": 336,
        "height": 320,
        "content": "## GENERADOR DE AUDIO\nPARA MEJORAR LA EXPERIENCIA DEL USUARIO, EL TEXTO SE TRANSFORMA A VOZ "
      },
      "typeVersion": 1
    },
    {
      "id": "2010027d-9238-4bab-8025-0abcc28dd958",
      "name": "Sticky Note26",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2512,
        1408
      ],
      "parameters": {
        "color": 5,
        "width": 352,
        "height": 304,
        "content": "## ENVIO DE VOZ\nEL MENSAJE DE VOZ PERSONALIZADO SE ENV\u00cdA AUTOMATICAMENTE CADA DOMINGO A LAS 07:00PM AL USUARIO V\u00cdA TELEGRAM"
      },
      "typeVersion": 1
    },
    {
      "id": "8c041a63-8828-4f90-b670-85e018f73157",
      "name": "ENVIO DE MENSAJE DE VOZ1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2400,
        1536
      ],
      "parameters": {
        "file": "={{ $('GENERADOR DE MENSAJE DE VOZ').item.json.URL }}",
        "chatId": "={{ $json.chatId }}",
        "operation": "sendAudio",
        "additionalFields": {
          "caption": "={{ \n(() => {\n  const today = new Date();\n  const dayOfWeek = today.getDay(); // 0=domingo\n  const diffMonday = (dayOfWeek === 0 ? -6 : 1 - dayOfWeek) + 7; // +7 => pr\u00f3xima semana\n  const diffFriday = diffMonday + 4;\n\n  const monday = new Date(today);\n  monday.setDate(today.getDate() + diffMonday);\n\n  const friday = new Date(today);\n  friday.setDate(today.getDate() + diffFriday);\n\n  const fmt = d => `${d.getDate().toString().padStart(2, '0')}/${(d.getMonth() + 1).toString().padStart(2, '0')}`;\n\n  return `Anuncio Semanal - Semana del ${fmt(monday)} al ${fmt(friday)}`;\n})()\n}}\n"
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9681b6de-a5db-443e-89a5-e6a927b92b89",
      "name": "ENVIO DE MENSAJE DE TEXTO1",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -2384,
        1824
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "chatId": "={{ $json.chatId }}",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "245f9062-c877-46f0-9df3-d722308dca44",
      "name": "Sticky Note27",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2512,
        1728
      ],
      "parameters": {
        "color": 5,
        "width": 352,
        "height": 272,
        "content": "## ENVIO DE TEXTO\nSE ENV\u00cdA AUTOMATICAMENTE EL MENSAJE"
      },
      "typeVersion": 1
    },
    {
      "id": "67e74e9f-13b3-4f5f-80f4-e35e75558704",
      "name": "OPTIMIZADOR",
      "type": "n8n-nodes-base.code",
      "position": [
        -2800,
        1808
      ],
      "parameters": {
        "jsCode": "// Tu array de chat IDs\nconst chatIds = $('ACTIVIDADES DE LA SEMANA1').first().json.CHAT_IDS_VALIDOS; // reemplaza con tus IDs\n\n// Mensaje a enviar\nconst mensaje = $input.first().json.output;\n\n// Creamos un item por cada chat ID\nreturn chatIds.map(id => ({\n    json: {\n        chatId: id,\n        text: mensaje\n    }\n}));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "23d73abe-5b2b-42c5-b50f-fea043f4cd1a",
      "name": "OPTIMIZADOR1",
      "type": "n8n-nodes-base.code",
      "position": [
        -2640,
        1584
      ],
      "parameters": {
        "jsCode": "// Tu array de chat IDs\nconst chatIds = $('ACTIVIDADES DE LA SEMANA1').first().json.CHAT_IDS_VALIDOS; // reemplaza con tus IDs\n\n// Mensaje a enviar\nconst mensaje = $input.first().json.output;\n\n// Creamos un item por cada chat ID\nreturn chatIds.map(id => ({\n    json: {\n        chatId: id,\n        text: mensaje\n    }\n}));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "5dfc42b7-8684-4e92-95ca-7e1154345402",
      "name": "GENERADOR DE MENSAJE DE VOZ",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -2800,
        1584
      ],
      "parameters": {
        "url": "https://ttsmp3.com/makemp3_new.php",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "msg",
              "value": "={{ $json.output }}"
            },
            {
              "name": "lang",
              "value": "Mia"
            },
            {
              "name": "source",
              "value": "ttsmp3"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7008a1aa-c9fb-4777-aac2-63f51cb88d49",
      "name": "OBTENER_ID1",
      "type": "n8n-nodes-base.set",
      "position": [
        -880,
        1712
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "fc9b6408-8e37-4395-935f-33985719dae3",
              "name": "chat_id",
              "type": "number",
              "value": "={{ $json.message.chat.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0310e05e-bc18-48de-952d-2c7e5163ab1f",
      "name": "AGREGA ID_UNICAS1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -624,
        1856
      ],
      "parameters": {
        "columns": {
          "value": {
            "CHAT_ID": "={{ $json.chat_id }}"
          },
          "schema": [
            {
              "id": "PERIODO_FECHAS",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "PERIODO_FECHAS",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ACTIVIDADES_GRADO",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "ACTIVIDADES_GRADO",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PROCESOS_GRADO",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "PROCESOS_GRADO",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ACTIVIDADES DE FORMACI\u00d3N T\u00c9CNICA Y TECNOL\u00d3GICA",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "ACTIVIDADES DE FORMACI\u00d3N T\u00c9CNICA Y TECNOL\u00d3GICA",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "CHAT_ID",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "CHAT_ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "CHAT_ID"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1817620056,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit#gid=1817620056",
          "cachedResultName": "CALENDAR"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit?usp=drivesdk",
          "cachedResultName": "BASE-DATOS-FAQ_ESPOL"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "c797bd9d-e22d-40af-b685-0ecff2c2e838",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        320
      ],
      "parameters": {
        "color": 5,
        "width": 1040,
        "height": 1712,
        "content": "# \ud83c\udff7\ufe0f **SISTEMA DE TAGS - REFERENCIA R\u00c1PIDA** \u2728\n\n## ESTE **SISTEMA DE TAGS** PERMITE ORGANIZAR Y CLASIFICAR LA INFORMACI\u00d3N. CADA TAG FACILITA FILTRAR, IDENTIFICAR Y ACCEDER R\u00c1PIDAMENTE A LOS NODOS, MEJORANDO LA CLARIDAD Y EFICIENCIA."
      },
      "typeVersion": 1
    },
    {
      "id": "52455d30-745d-4373-a56e-770a643192b7",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        1680
      ],
      "parameters": {
        "color": 5,
        "width": 432,
        "height": 304,
        "content": "## \ud83d\udc4b **BIENVENIDA AL USUARIO** \ud83c\udf89\nAL INGRESAR UN NUEVO USUARIO SE ENVIA UN MENSAJE DE BIENVENIDA Y GU\u00cdA DE USO DEL BOT.\n\n\n## \ud83c\udd94 **OBTENCI\u00d3N DE LAS IDs** \ud83d\udd0d\nGUARDA EN UNA BASE DE DATOS LA ID DE CADA USUARIO NUEVO QUE INGRESE AL BOT "
      },
      "typeVersion": 1
    },
    {
      "id": "97535bd5-a38d-4d93-8eb8-11ea593ec658",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3328,
        416
      ],
      "parameters": {
        "color": 5,
        "width": 608,
        "height": 288,
        "content": "## MANIPULACI\u00d3N Y  ORGANIZACI\u00d3N DE DATOS\nUSO DE LOS NODOS HTML, SPLIT-OUT Y CODE PARA OBTENER LOS DATOS  REQUERIDOS Y MANTENER UN ORDEN DE SALIDA"
      },
      "typeVersion": 1
    },
    {
      "id": "6f2891fd-2874-4273-aeeb-5092191df6ec",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -3056,
        560
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "PERIODOS-FECHAS, ACTIVIDADES_GRADO, PROCESOS_GRADO, ACTIVIDADES_TECNICAS-TECNOL\u00d3GICAS"
      },
      "typeVersion": 1
    },
    {
      "id": "525e2001-f72e-4f12-b0b8-1773498e9298",
      "name": "HTML",
      "type": "n8n-nodes-base.html",
      "position": [
        -3248,
        560
      ],
      "parameters": {
        "options": {},
        "operation": "extractHtmlContent",
        "extractionValues": {
          "values": [
            {
              "key": "PERIODOS-FECHAS",
              "cssSelector": "table tbody tr td:first-child",
              "returnArray": true
            },
            {
              "key": "ACTIVIDADES_GRADO",
              "cssSelector": "table tbody tr td:nth-child(2)",
              "returnArray": true
            },
            {
              "key": "PROCESOS_GRADO",
              "cssSelector": "table tbody tr td:nth-child(3)",
              "returnArray": true
            },
            {
              "key": "ACTIVIDADES_TECNICAS-TECNOL\u00d3GICAS",
              "cssSelector": "table tbody tr td:nth-child(4)",
              "returnArray": true
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "bc233dd1-fa9b-4d75-ab77-4542eb14b535",
      "name": "Sticky Note11",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2720,
        416
      ],
      "parameters": {
        "color": 5,
        "width": 256,
        "height": 288,
        "content": "## BASE DE DATOS\nAGREGA Y ACTUALIZA LA BASE DE  DATOS CON LAS FECHAS Y ACTIVIDADES DEL CALENDARIO"
      },
      "typeVersion": 1
    },
    {
      "id": "f3e79e09-fe05-4b71-a2cd-5810a88e8252",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3552,
        416
      ],
      "parameters": {
        "color": 5,
        "width": 224,
        "height": 288,
        "content": "## SCRAPPING\nPARA LA OBTENCI\u00d3N DE FECHAS  Y ACTIVIDADES DE TODOS LOS PERIODOS LECTIVOS"
      },
      "typeVersion": 1
    },
    {
      "id": "6a1fe95d-ab71-42a9-8906-dca393412815",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3824,
        416
      ],
      "parameters": {
        "color": 5,
        "width": 272,
        "height": 288,
        "content": "## TEMPORIZADOR DE 7 D\u00cdAS\nCADA SEMANA SE ACTUALIZAR\u00c1N LAS BASES DE DATOS DEL CALENDARIO ACAD\u00c9MICO"
      },
      "typeVersion": 1
    },
    {
      "id": "4fc9dc13-c0a9-424e-8fab-873ae62f7c09",
      "name": "BD-CALENDARIO",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        -2656,
        560
      ],
      "parameters": {
        "columns": {
          "value": {
            "PERIODO_FECHAS": "={{ $json[\"PERIODOS-FECHAS\"] }}",
            "PROCESOS_GRADO": "={{ $json.PROCESOS_GRADO }}",
            "ACTIVIDADES_GRADO": "={{ $json.ACTIVIDADES_GRADO }}",
            "ACTIVIDADES DE FORMACI\u00d3N T\u00c9CNICA Y TECNOL\u00d3GICA": "={{ $json[\"ACTIVIDADES_TECNICAS-TECNOL\u00d3GICAS\"] }}"
          },
          "schema": [
            {
              "id": "PERIODO_FECHAS",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "PERIODO_FECHAS",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ACTIVIDADES_GRADO",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ACTIVIDADES_GRADO",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "PROCESOS_GRADO",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "PROCESOS_GRADO",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "ACTIVIDADES DE FORMACI\u00d3N T\u00c9CNICA Y TECNOL\u00d3GICA",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "ACTIVIDADES DE FORMACI\u00d3N T\u00c9CNICA Y TECNOL\u00d3GICA",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "CHAT_ID",
              "type": "string",
              "display": true,
              "removed": true,
              "required": false,
              "displayName": "CHAT_ID",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "PERIODO_FECHAS"
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "appendOrUpdate",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1817620056,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit#gid=1817620056",
          "cachedResultName": "CALENDAR"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit?usp=drivesdk",
          "cachedResultName": "BASE-DATOS-FAQ_ESPOL"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "3c9d1e36-949b-41c0-82d3-14fe677e407e",
      "name": "Sticky Note12",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4208,
        320
      ],
      "parameters": {
        "color": 5,
        "width": 2224,
        "height": 1056,
        "content": "# \ud83d\udcc5 **CALENDARIO (OBTENCI\u00d3N - V\u00cdA SCRAPING WEB)** \ud83d\udd0d\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "db4dfee5-023e-4736-8b12-4f62879a3907",
      "name": "SCRRAPPING",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3488,
        560
      ],
      "parameters": {
        "url": "https://www.espol.edu.ec/es/vida-politecnica/calendario-grado",
        "options": {
          "timeout": 60000
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d23cb762-8460-41b9-b291-179baa9985d2",
      "name": "CORRECTOR DE FECHAS",
      "type": "n8n-nodes-base.code",
      "position": [
        -2880,
        560
      ],
      "parameters": {
        "jsCode": "// Recibir items\nconst itemsIn = $input.all();\nlet currentYear = '';\nconst results = [];\n\nfunction detectMonth(text) {\n  // Detecta el mes en el texto (min\u00fasculas para evitar errores)\n  const months = {\n    enero: 1, febrero: 2, marzo: 3, abril: 4, mayo: 5, junio: 6,\n    julio: 7, agosto: 8, septiembre: 9, octubre: 10, noviembre: 11, diciembre: 12\n  };\n  const lower = text.toLowerCase();\n  for (const [month, num] of Object.entries(months)) {\n    if (lower.includes(month)) return num;\n  }\n  return null;\n}\n\nfor (const item of itemsIn) {\n  const data = item.json;\n  let period = data[\"PERIODOS-FECHAS\"] ? String(data[\"PERIODOS-FECHAS\"]).trim() : \"\";\n\n  // Detectar si hay un a\u00f1o expl\u00edcito\n  const yearMatch = period.match(/\\b(20\\d{2})\\b/);\n  if (yearMatch) {\n    currentYear = parseInt(yearMatch[1]);\n  }\n\n  // Si es encabezado, mantener igual\n  if (/^(VACACIONES|PAE|PAO)/i.test(period)) {\n    results.push({ json: { ...data } });\n    continue;\n  }\n\n  // Detectar meses del per\u00edodo\n  const firstMonth = detectMonth(period.split('-')[0] || '');\n  const secondMonth = detectMonth(period.split('-')[1] || '');\n\n  // Si tenemos dos meses y el segundo es menor que el primero \u2192 cambio de a\u00f1o\n  if (firstMonth && secondMonth && secondMonth < firstMonth) {\n    currentYear++;\n  }\n\n  // Si no hay a\u00f1o en el texto, agregar el actual\n  if (!/\\b20\\d{2}\\b/.test(period) && currentYear) {\n    period = `${period} ${currentYear}`;\n  }\n\n  results.push({\n    json: {\n      ...data,\n      \"PERIODOS-FECHAS\": period\n    }\n  });\n}\n\nreturn results;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "d3138f0c-9007-4d94-ab5f-308ff5fca401",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4144,
        720
      ],
      "parameters": {
        "color": 5,
        "width": 1680,
        "height": 624,
        "content": "# \ud83d\udcbe **BASES DE DATOS** \ud83d\uddc2\ufe0f \n\n## - **FIMCP** (FACULTAD DE INGENIER\u00cdA EN MEC\u00c1NICA Y CIENCIAS DE LA PRODUCCI\u00d3N) \u2699\ufe0f\n\n## - **FIEC** (FACULTAD DE INGENIER\u00cdA EN ELECTRICIDAD Y COMPUTACI\u00d3N) \ud83d\udcbb\n\n## - **FICT** (FACULTAD DE INGENIER\u00cdA EN CIENCIAS DE LA TIERRA) \ud83c\udf0e\n\n## - **FCNM** (FACULTAD DE CIENCIAS NATURALES Y MATEM\u00c1TICAS) \ud83d\udd2c\n\n## - **FCSH** (FACULTAD DE CIENCIAS SOCIALES Y HUMAN\u00cdSTICAS) \ud83d\udcda\n\n## - **FADCOM** (FACULTAD DE ARTE, DISE\u00d1O Y COMUNICACI\u00d3N AUDIOVISUAL) \ud83c\udfa8\n\n## - **FIMCM** (FACULTAD DE INGENIER\u00cdA MAR\u00cdTIMA Y CIENCIAS DEL MAR) \u2693\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "aae3673d-ab7d-421c-8955-2321e55717b1",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1968,
        320
      ],
      "parameters": {
        "color": 3,
        "width": 976,
        "height": 1136,
        "content": "# \ud83d\udcac FLUJO: RESPUESTAS DEL USUARIO & FEEDBACK\n\n## \ud83c\udfaf PROP\u00d3SITO\n\n## GESTIONAR LA **RESPUESTA DEL USUARIO** TRAS UNA INTERACCI\u00d3N CON EL BOT Y REGISTRAR SU **RETROALIMENTACI\u00d3N** EN UNA HOJA DE C\u00c1LCULO.\n\n## \ud83d\udd17 FLUJO RESUMIDO\n\n## 1. **RESPUESTA USUARIO1 (SWITCH):**\n   DETECTA SI EL USUARIO ELIGE CONTINUAR O ENVIAR FEEDBACK (POR EJEMPLO: \u201cS\u00cd\u201d O \u201cNO\u201d).\n\n## 2. **GET A CHAT:**\n   OBTIENE LOS DATOS DEL CHAT ACTUAL PARA MANTENER LA CONVERSACI\u00d3N ACTIVA.\n\n## 3. **NO \u2013 MENSAJE FEEDBACK1:**\n   ENV\u00cdA UN MENSAJE SOLICITANDO AL USUARIO QUE DESCRIBA SU EXPERIENCIA O MOTIVO DE LA RESPUESTA NEGATIVA.\n\n## 4. **GUARDADO FEEDBACK1:**\n   REGISTRA EL MENSAJE DE RETROALIMENTACI\u00d3N DIRECTAMENTE EN UNA **GOOGLE SHEET** PARA SU AN\u00c1LISIS POSTERIOR.\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4bccb5c4-73cf-413b-a8e9-494e8af81b71",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        592
      ],
      "parameters": {
        "color": 5,
        "width": 688,
        "height": 128,
        "content": "## \ud83c\udd98 **AYUDA (/HELP)** \ud83d\udca1\nMUESTRA UNA LISTA DE COMANDOS DISPONIBLES Y EXPLICA BREVEMENTE PARA QU\u00c9 SIRVE CADA UNO, AYUDANDO AL USUARIO A ENTENDER C\u00d3MO USAR EL BOT.\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "33fba8d9-8d5a-4328-9f3f-9a847cd1eafc",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        768
      ],
      "parameters": {
        "color": 5,
        "width": 688,
        "height": 128,
        "content": "## \u2753 **CONSULTAS (/FAQS)** \ud83d\udcac\nMANDA A LA SECCI\u00d3N DE PREGUNTAS FRECUENTES Y CONSULTAS. AQU\u00cd PUEDES CONSULTAR TEMAS DE MATR\u00cdCULA, EVENTOS, HORARIOS O SERVICIOS ESTUDIANTILES Y RECIBIR RESPUESTAS INMEDIATAS.\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "ffb51e14-36fb-4cbf-98e1-7f507786aa3f",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        944
      ],
      "parameters": {
        "color": 5,
        "width": 688,
        "height": 128,
        "content": "## \ud83d\udcc7 **CONTACTOS (/CONTACT)** \ud83d\udcde\nMUESTRA CONTACTOS Y CANALES OFICIALES DE LA ESPOL.\n(WEBS - CORREOS - ENLACES - N\u00daMEROS - DIRECCI\u00d3N)\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "fced05c4-2d46-4083-98e7-d5e852cea1cd",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        1120
      ],
      "parameters": {
        "color": 5,
        "width": 688,
        "height": 128,
        "content": "## \ud83d\udcc5 **EVENTOS (/EVENTS)** \ud83c\udf89\nMUESTRA PR\u00d3XIMOS EVENTOS ACAD\u00c9MICOS DE ESPOL\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "85c34e8b-d150-493b-9e3c-6bdad3cdb2d9",
      "name": "Sticky Note13",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -672,
        1296
      ],
      "parameters": {
        "color": 5,
        "width": 688,
        "height": 128,
        "content": "## \ud83d\udcdd **FEEDBACK (/FEEDBACK)** \u2b50\nPERMITE ENVIAR OPINIONES, SUGERENCIAS O COMENTARIOS SOBRE EL FUNCIONAMIENTO DEL BOT, FACILITANDO LA RETROALIMENTACI\u00d3N DE LOS USUARIOS CON EL FIN DE MEJORAR EL SERVICIO Y OPTIMIZAR LA EXPERIENCIA GENERAL DEL USUARIO.\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f58f8a5d-646c-4ebd-9c43-dfe65a0c6514",
      "name": "Sticky Note14",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1968,
        1456
      ],
      "parameters": {
        "color": 3,
        "width": 976,
        "height": 576,
        "content": "# \ud83d\ude80 **INICIO DE AUTOMATIZACI\u00d3N** \u2699\ufe0f\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "dea1510f-eae5-4f48-9b9d-cf61ba080faa",
      "name": "Sticky Note15",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1936,
        1600
      ],
      "parameters": {
        "color": 3,
        "width": 224,
        "height": 304,
        "content": "## TRIGGER TELEGRAM\nSE ACTIVA EN CUALQUIER INTERACCI\u00d3N DEL USUARIO CON EL BOT\n"
      },
      "typeVersion": 1
    },
    {
      "id": "506b453a-061b-4644-b318-237fc1b660b9",
      "name": "Sticky Note16",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1712,
        1536
      ],
      "parameters": {
        "color": 3,
        "width": 384,
        "height": 400,
        "content": "## CLASIFICADOR DE MENSAJES\n### - 0: FEEDBACK \n### - 1: COMENTARIO\n### - 2: TAGS\n### - Fallback: MENSAJE \n"
      },
      "typeVersion": 1
    },
    {
      "id": "797b1458-c900-40af-89fb-866cf15e0ee8",
      "name": "Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        -1712,
        1712
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "0e1c1bb4-3315-4632-a9dc-5f917baf41da",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.callback_query !== undefined }}",
                    "rightValue": "0"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "23c2e3a0-e7c8-46de-b504-2cabd445ed54",
                    "operator": {
                      "type": "string",
                      "operation": "contains"
                    },
                    "leftValue": "={{ ($json.message?.reply_to_message?.text || '') }}",
                    "rightValue": "Lamento que la informaci\u00f3n no te haya sido \u00fatil"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": false,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "318d7097-ff17-4636-b7bf-958de3da6788",
                    "operator": {
                      "type": "boolean",
                      "operation": "true",
                      "singleValue": true
                    },
                    "leftValue": "={{ $json.message?.text?.startsWith('/') }}",
                    "rightValue": ""
                  }
                ]
              }
            }
          ]
        },
        "options": {
          "ignoreCase": true,
          "fallbackOutput": "extra"
        },
        "looseTypeValidation": true
      },
      "typeVersion": 3.3
    },
    {
      "id": "d69dcc76-b210-4b80-893a-1649643e602d",
      "name": "Sticky Note17",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1328,
        1600
      ],
      "parameters": {
        "color": 3,
        "width": 320,
        "height": 304,
        "content": "\n##  CLASIFICADOR DE TAGS\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "76454d3f-9393-486c-a229-696c60ce324e",
      "name": "Sticky Note18",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3120,
        720
      ],
      "parameters": {
        "color": 5,
        "width": 528,
        "height": 624,
        "content": "## - **ADMISI\u00d3N** \ud83d\udcdd\n\n## - **NIVELACI\u00d3N** \ud83c\udfaf\n\n## - **BECAS** \ud83c\udf93\n\n## - **MATR\u00cdCULAS** \ud83e\uddfe\n\n## - **CERTIFICADOS Y TITULACI\u00d3N** \ud83d\udcc4\n\n## - **VIDA UNIVERSITARIA** \ud83c\udfeb\n\n## - **SERVICIOS DE SALUD Y BIENESTAR** \u2764\ufe0f\n\n## - **CULTURA Y DEPORTES** \ud83c\udfc6\n\n## - **TRANSPORTE** \ud83d\ude8c\n\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "f75dd036-9cc5-4c9d-92ae-34bb89bbf4cd",
      "name": "Sticky Note19",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2592,
        720
      ],
      "parameters": {
        "color": 5,
        "width": 560,
        "height": 624,
        "content": "## - **HISTORIA DE LA ESPOL** \ud83c\udfdb\ufe0f\n\n## - **SOSTENIBILIDAD** \ud83c\udf31\n\n## - **CENTROS DE INVESTIGACI\u00d3N** \ud83d\udd2c\n\n## - **VINCULACI\u00d3N CON LA SOCIEDAD** \ud83e\udd1d\n\n## - **REGLAMENTOS** \ud83d\udcd8\n\n## - **PROGRAMAS INTERNACIONALES** \ud83c\udf0d\n\n## - **VOLUNTARIADO** \ud83d\ude4c\n\n## - SERVICIOS: **CELEX**, **CONDUESPOL** Y MAS \ud83d\udcac\n\n## - **ALUMNI** Y **CIB** \ud83e\udde0"
      },
      "typeVersion": 1
    },
    {
      "id": "6a8a52db-c44d-4174-a92f-cd113b5a0d2a",
      "name": "Sticky Note28",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4208,
        -192
      ],
      "parameters": {
        "width": 2224,
        "height": 496,
        "content": "# \ud83e\udd16 **GRUPO #10 \u2013 CHATBOT POLIT\u00c9CNICO DE PREGUNTAS FRECUENTES Y CALENDARIO ACAD\u00c9MICO EN TELEGRAM** \ud83d\udcc5\ud83d\udcac\n\n# \ud83e\udde9 **WORKFLOW CREADO POR:**\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n# \ud83e\uddd1\u200d\ud83c\udfeb **MENTOR: JAREN** \ud83c\udf1f\n\n\n\n\n\n\n\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "4602e289-d4d7-4dd0-9777-e148b1175a20",
      "name": "Sticky Note29",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3952,
        112
      ],
      "parameters": {
        "width": 1760,
        "height": 80,
        "content": "# \u2728 **PROYECTO DE AUTOMATIZACI\u00d3N INTELIGENTE CON ENFOQUE EN ASISTENCIA UNIVERSITARIA \u2728**"
      },
      "typeVersion": 1
    },
    {
      "id": "0cde6cc8-dd35-4fb3-afc9-1ec73921b970",
      "name": "Sticky Note30",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2592,
        -16
      ],
      "parameters": {
        "width": 448,
        "height": 96,
        "content": "# \ud83d\udc69\u200d\ud83d\udcbb **NICOLE GUEVARA** \ud83d\ude80\n"
      },
      "typeVersion": 1
    },
    {
      "id": "08dc6029-171d-44d5-92b1-eaeb6cc57bc0",
      "name": "Sticky Note31",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3328,
        -16
      ],
      "parameters": {
        "width": 496,
        "height": 96,
        "content": "# \ud83d\udc69\u200d\ud83d\udcbb **DOM\u00c9NICA AMORES** \ud83d\udca1"
      },
      "typeVersion": 1
    },
    {
      "id": "a35f356d-1afd-4aa9-bfbf-d43ac88909b7",
      "name": "Sticky Note32",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4048,
        -16
      ],
      "parameters": {
        "width": 464,
        "height": 96,
        "content": "# \ud83d\udc68\u200d\ud83d\udcbb **ADRI\u00c1N VILLAMAR** \u2699\ufe0f"
      },
      "typeVersion": 1
    },
    {
      "id": "e4d2da34-4840-4228-8d25-54cb5325a6f2",
      "name": "Sticky Note33",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1936,
        1024
      ],
      "parameters": {
        "color": 2,
        "width": 272,
        "height": 384,
        "content": "## \ud83d\udca1 *ESTE FLUJO PERMITE RECOPILAR COMENTARIOS DE LOS USUARIOS Y ALMACENARLOS DE FORMA AUTOM\u00c1TICA, MEJORANDO EL SEGUIMIENTO DE LA SATISFACCI\u00d3N Y LAS MEJORAS DEL BOT.*"
      },
      "typeVersion": 1
    },
    {
      "id": "107e8fe1-081d-4541-a73e-d7aac4e11c11",
      "name": "Sticky Note34",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4208,
        2048
      ],
      "parameters": {
        "width": 4272,
        "height": 672,
        "content": ""
      },
      "typeVersion": 1
    },
    {
      "id": "ae2d015b-3691-459b-b4c8-2ad948c176ba",
      "name": "Sticky Note35",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2192,
        2064
      ],
      "parameters": {
        "width": 2224,
        "height": 608,
        "content": "#      \ud83e\udd16 **SISTEMA DE GENERACI\u00d3N DE MENSAJES AUTOM\u00c1TICOS Y PERSONALIZADOS** \ud83d\udcac\n"
      },
      "typeVersion": 1
    },
    {
      "id": "090a8f45-775a-45be-afe5-ada7e40f426c",
      "name": "Sticky Note36",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4160,
        2064
      ],
      "parameters": {
        "width": 1904,
        "height": 608,
        "content": "# \ud83e\udde0 **CEREBRO DE LA GENERACI\u00d3N DE MENSAJES AUTOM\u00c1TICOS Y PERSONALIZADOS**\n\n## \ud83e\udd16 ESTE FLUJO EN N8N FUNCIONA COMO UN ASISTENTE INTELIGENTE QUE ANALIZA LOS MENSAJES DEL USUARIO Y ENV\u00cdA RESPUESTAS AUTOM\u00c1TICAS Y PERSONALIZADAS \ud83d\udcac\u26a1.\n\n# \ud83d\udcc5 1.- CONSULTA DE CALENDARIO ACAD\u00c9MICO:\n\n## - CUANDO EL USUARIO PREGUNTA SOBRE FECHAS O EVENTOS DEL CALENDARIO, EL SISTEMA BUSCA LA INFORMACI\u00d3N EN UNA HOJA DE C\u00c1LCULO \ud83d\udcd6, CREA UN PROMPT \ud83e\udde9 Y GENERA UNA RESPUESTA CLARA QUE LUEGO SE ENV\u00cdA POR TELEGRAM \ud83d\udcf2.\n\n# \ud83d\udca1 2.- CONSULTA DE PREGUNTAS FRECUENTES (FAQ):\n\n## - SI EL MENSAJE DEL USUARIO ES UNA DUDA GENERAL, EL SISTEMA CONECTA CON LA BASE DE DATOS MONGODB \ud83c\udf43, IDENTIFICA LAS RESPUESTAS RELEVANTES \ud83d\udd0d Y ENV\u00cdA UN MENSAJE AUTOM\u00c1TICO CON LA INFORMACI\u00d3N SOLICITADA \ud83d\udcac\u2728.\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "876d72d2-2dbc-44a8-a3f3-98b6fdece7a4",
      "name": "Sticky Note37",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1968,
        -192
      ],
      "parameters": {
        "width": 2032,
        "height": 496,
        "content": "# \ud83d\udd17 ENLACES DEL BOT Y BASE DE DATOS\n\n# \ud83e\udd16 BOT DE TELEGRAM: \n\n# https://web.telegram.org/k/#@ESPOLInfoBot\n\n\n\n# \ud83d\udcc2 BASE DE DATOS EN SHEETS DE FAQ Y CALENDARIO: \n\n# https://docs.google.com/spreadsheets/d/1WWE-eLP0g5M9Q2CeGpQZat56OhJrQyeYZJShjtETCgo/edit?gid=0#gid=0"
      },
      "typeVersion": 1
    },
    {
      "id": "207caeb2-0dad-4a90-8cb7-f943663ecfc6",
      "name": "Code in JavaScript1",
      "type": "n8n-nodes-base.code",
      "position": [
        -3360,
        1664
      ],
      "parameters": {
        "jsCode": "const semanas = $input.all();\n\n// Inicializamos un objeto vac\u00edo\nconst combinado = {};\n\n// Recorremos todas las semanas y sus claves\nfor (const semana of semanas) {\n  const datos = semana.json;\n  for (const [clave, valor] of Object.entries(datos)) {\n    if (!combinado[clave]) combinado[clave] = [];\n    if (valor !== undefined && valor !== null && valor !== \"\")\n      combinado[clave].push(String(valor).trim());\n  }\n}\n\n// Unimos los valores repetidos en un solo string, separados por comas\nfor (const clave of Object.keys(combinado)) {\n  combinado[clave] = [...new Set(combinado[clave])].join(\", \");\n}\n\n// Devolvemos un solo item con todo unido\nreturn [{ json: combinado }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2760029c-b039-4aab-95bd-005caeead0b6",
      "name": "Split Out2",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        -3504,
        1664
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "RESULTADOS[0].PERIODO_FECHAS, RESULTADOS[0].ACTIVIDADES_GRADO, RESULTADOS[0].PROCESOS_GRADO, RESULTADOS[0]['ACTIVIDADES DE FORMACI\u00d3N T\u00c9CNICA Y TECNOL\u00d3GICA']"
      },
      "typeVersion": 1
    },
    {
      "id": "9dff1b64-2d74-4de5-8dee-cfcebfcd3977",
      "name": "ACTIVIDADES DE LA SEMANA1",
      "type": "n8n-nodes-base.code",
      "position": [
        -3664,
        1664
      ],
      "parameters": {
        "jsCode": "// \ud83d\uddd3\ufe0f FECHA ACTUAL Y A\u00d1O\nconst hoy = new Date();\nconst anioActual = hoy.getFullYear();\n\n// \u2705 FUNCI\u00d3N PARA CALCULAR EL LUNES DE UNA SEMANA RELATIVA\nfunction obtenerLunes(base, desplazamientoSemanas = 0) {\n  const dia = base.getDay(); // 0=domingo, 1=lunes...\n  const lunes = new Date(base);\n  const diff = (1 - dia + 7) % 7; // hasta lunes\n  lunes.setDate(base.getDate() + diff + desplazamientoSemanas * 7);\n  return lunes;\n}\n\n// \ud83d\udcc6 CALCULAR LAS TRES SEMANAS CLAVE\nconst lunesSemanaAnterior = obtenerLunes(hoy, -1);\nconst lunesProximaSemana = obtenerLunes(hoy, 0);\nconst lunesSemanaPosterior = obtenerLunes(hoy, 1);\n\n// \ud83e\udde0 FUNCI\u00d3N PARA INTERPRETAR RANGOS TIPO \u201c09 - 13 NOVIEMBRE\u201d O \u201c9-13nov\u201d\nfunction parsearRango(rango) {\n  if (!rango || typeof rango !== \"string\") return null;\n\n  rango = rango\n    .replace(/([0-9])([a-zA-Z\u00f1\u00d1])/g, \"$1 $2\")\n    .replace(/\\s+/g, \" \")\n    .trim()\n    .toLowerCase();\n\n  const match = rango.match(/(\\d{1,2})\\s*-\\s*(\\d{1,2})\\s*([a-z\u00f1]+)/);\n  if (!match) return null;\n\n  const [, inicioStr, finStr, mesStr] = match;\n  const inicio = parseInt(inicioStr);\n  const fin = parseInt(finStr);\n\n  const meses = {\n    ene: 0, enero: 0,\n    feb: 1, febrero: 1,\n    mar: 2, marzo: 2,\n    abr: 3, abril: 3,\n    may: 4, mayo: 4,\n    jun: 5, junio: 5,\n    jul: 6, julio: 6,\n    ago: 7, agosto: 7,\n    sep: 8, sept: 8, septiembre: 8,\n    oct: 9, octubre: 9,\n    nov: 10, noviembre: 10,\n    dic: 11, diciembre: 11\n  };\n\n  const mesNum = meses[mesStr];\n  if (mesNum === undefined) return null;\n\n  let anioRango = anioActual;\n  if (mesNum === 0 && hoy.getMonth() === 11) {\n    anioRango = anioActual + 1;\n  }\n\n  const inicioFecha = new Date(anioRango, mesNum, inicio);\n  const finFecha = new Date(anioRango, mesNum, fin);\n  return { inicio: inicioFecha, fin: finFecha };\n}\n\n// \ud83d\udd0d FUNCI\u00d3N PARA BUSCAR COINCIDENCIA DE SEMANA\nfunction filtrarPorSemana(items, lunesReferencia) {\n  const resultados = [];\n  for (const item of items) {\n    const rango = parsearRango(item.json.PERIODO_FECHAS);\n    if (!rango) continue;\n\n    const mismoInicio =\n      rango.inicio.getFullYear() === lunesReferencia.getFullYear() &&\n      rango.inicio.getDate() === lunesReferencia.getDate() &&\n      rango.inicio.getMonth() === lunesReferencia.getMonth();\n\n    if (mismoInicio) resultados.push(item);\n  }\n  return resultados;\n}\n\n// \ud83d\udcca OBTENER RESULTADOS PARA CADA SEMANA\nconst resultadosAnterior = filtrarPorSemana(items, lunesSemanaAnterior);\nconst resultadosActual = filtrarPorSemana(items, lunesProximaSemana);\nconst resultadosPosterior = filtrarPorSemana(items, lunesSemanaPosterior);\n\n// \ud83e\uddf9 CHAT_IDS V\u00c1LIDOS (\u00daNICOS)\nconst chatIds = items\n  .map(item => item.json.CHAT_ID)\n  .filter(id => id !== undefined && id !== null && String(id).trim() !== \"\")\n  .map(id => String(id).trim());\n\nconst uniqueChatIds = [...new Set(chatIds)];\n\n// \ud83c\udfc1 SALIDA FINAL\nreturn [\n  {\n    json: {\n      SEMANA: \"ANTERIOR\",\n      LUNES_SEMANA: lunesSemanaAnterior.toISOString().split(\"T\")[0],\n      RESULTADOS: resultadosAnterior.map(i => i.json),\n      CHAT_IDS_VALIDOS: uniqueChatIds\n    }\n  },\n  {\n    json: {\n      SEMANA: \"ACTUAL / PR\u00d3XIMA\",\n      LUNES_SEMANA: lunesProximaSemana.toISOString().split(\"T\")[0],\n      RESULTADOS: resultadosActual.map(i => i.json),\n      CHAT_IDS_VALIDOS: uniqueChatIds\n    }\n  },\n  {\n    json: {\n      SEMANA: \"POSTERIOR\",\n      LUNES_SEMANA: lunesSemanaPosterior.toISOString().split(\"T\")[0],\n      RESULTADOS: resultadosPosterior.map(i => i.json),\n      CHAT_IDS_VALIDOS: uniqueChatIds\n    }\n  }\n];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "03a100d6-1492-4834-a638-653ff0a235ca",
      "name": "ANUNCIO SEMANAL",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -4096,
        1680
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtHour": 19
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "af582ce7-df08-42dc-b4e6-e04bbc086c21",
      "name": "ACTUALIZAR CALENDARIO",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -3728,
        560
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "daysInterval": 7
            }
          ]
        }
      },
      "typeVersion": 1.2
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "1f231775-d723-4123-a5cf-9178e15e246d",
  "connections": {
    "HTML": {
      "main": [
        [
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait": {
      "main": [
        [
          {
            "node": "GUIA DE COMO PREGUNTAR",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "Respuesta Usuario1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Guardado en cvs Feedback",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Comandos1",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Detector_Calendario_Pre",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BD_ESPOL": {
      "ai_tool": [
        [
          {
            "node": "PREGUNTAS AL AZAR DE GUIA",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Comandos1": {
      "main": [
        [
          {
            "node": "Mensaje Help",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "FAQs",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respuesta Contact",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respuesta Events",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Inicio - Feedback",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "BIENVENIDA",
            "type": "main",
            "index": 0
          },
          {
            "node": "OBTENER_ID1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "CORRECTOR DE FECHAS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BIENVENIDA": {
      "main": [
        [
          {
            "node": "PREGUNTAS AL AZAR DE GUIA",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get a chat": {
      "main": [
        [
          {
            "node": "S\u00ed - Agradecimiento1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SCRRAPPING": {
      "main": [
        [
          {
            "node": "HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out2": {
      "main": [
        [
          {
            "node": "Code in JavaScript1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OBTENER_ID1": {
      "main": [
        [
          {
            "node": "AGREGA ID_UNICAS1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OPTIMIZADOR": {
      "main": [
        [
          {
            "node": "ENVIO DE MENSAJE DE TEXTO1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OPTIMIZADOR1": {
      "main": [
        [
          {
            "node": "ENVIO DE MENSAJE DE VOZ1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "BD_CALENDARIO1": {
      "main": [
        [
          {
            "node": "ACTIVIDADES DE LA SEMANA1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ANUNCIO SEMANAL": {
      "main": [
        [
          {
            "node": "BD_CALENDARIO1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Message a model1": {
      "main": [
        [
          {
            "node": "Limpiar_Texto_Gemini1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Inicio - Feedback": {
      "main": [
        []
      ]
    },
    "Mensaje de Gemini": {
      "main": [
        [
          {
            "node": "Limpiar texto Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respuesta Usuario1": {
      "main": [
        [
          {
            "node": "Get a chat",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "No - Mensaje Feedback1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch_Que_BD_Leer": {
      "main": [
        [
          {
            "node": "Calendario Acad\u00e9mico",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Leer FAQs de MongoDB",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "CORRECTOR DE FECHAS": {
      "main": [
        [
          {
            "node": "BD-CALENDARIO",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript1": {
      "main": [
        [
          {
            "node": "GENERADOR DE ANUNCIOS1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Leer FAQs de MongoDB": {
      "main": [
        [
          {
            "node": "Buscar_FAQs_Relevantes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limpiar texto Gemini": {
      "main": [
        [
          {
            "node": "Enviar Respuesta sobre faqs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ACTUALIZAR CALENDARIO": {
      "main": [
        [
          {
            "node": "SCRRAPPING",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calendario Acad\u00e9mico": {
      "main": [
        [
          {
            "node": "Buscar_Eventos_Calendario1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Limpiar_Texto_Gemini1": {
      "main": [
        [
          {
            "node": "Enviar Respuesta sobre calendario1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar_FAQs_Relevantes": {
      "main": [
        [
          {
            "node": "Construir_Prompt_Gemini1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GENERADOR DE ANUNCIOS1": {
      "main": [
        [
          {
            "node": "OPTIMIZADOR",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "No - Mensaje Feedback1": {
      "main": [
        []
      ]
    },
    "Detector_Calendario_Pre": {
      "main": [
        [
          {
            "node": "Switch_Que_BD_Leer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Construir_Prompt_Gemini1": {
      "main": [
        [
          {
            "node": "Mensaje de Gemini",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "PREGUNTAS AL AZAR DE GUIA",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Guardado en cvs Feedback": {
      "main": [
        []
      ]
    },
    "ACTIVIDADES DE LA SEMANA1": {
      "main": [
        [
          {
            "node": "Split Out2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "GENERADOR DE ANUNCIOS1",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "PREGUNTAS AL AZAR DE GUIA": {
      "main": [
        [
          {
            "node": "Wait",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger - Inicio": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar_Eventos_Calendario1": {
      "main": [
        [
          {
            "node": "Construir_Prompt_Calendario1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enviar Respuesta sobre faqs": {
      "main": [
        []
      ]
    },
    "GENERADOR DE MENSAJE DE VOZ": {
      "main": [
        [
          {
            "node": "OPTIMIZADOR1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Construir_Prompt_Calendario1": {
      "main": [
        [
          {
            "node": "Message a model1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}