{"id":"484125","finished":false,"mode":"webhook","retryOf":null,"retrySuccessId":null,"status":"error","createdAt":"2026-07-30T19:50:01.778Z","startedAt":"2026-07-30T19:50:01.790Z","stoppedAt":"2026-07-30T19:52:15.288Z","deletedAt":null,"workflowId":"uvnMZfrW0Giut5lM","waitTill":null,"storedAt":"db","data":{"version":1,"startData":{},"resultData":{"error":{"level":"warning","tags":{},"timestamp":1785441135287,"context":{"itemIndex":0,"request":{"body":{"chat_id":"7769073261","text":"⏳ Gerando matéria...\n\n🔗 https://cidadeverde.com/ultimas/460238/piaui-pode-ter-umidade-abaixo-de-15-nos-proximos-dias-inmet-mantem-alerta","parse_mode":"HTML"},"headers":{"accept":"application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7"},"method":"POST","uri":"https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage","gzip":true,"rejectUnauthorized":true,"followRedirect":true,"resolveWithFullResponse":true,"sendCredentialsOnCrossOriginRedirect":true,"followAllRedirects":true,"timeout":300000,"encoding":null,"json":false,"useStream":true}},"functionality":"regular","name":"NodeApiError","node":{"parameters":{"curlImport":"","method":"POST","url":"https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage","authentication":"none","provideSslCertificates":false,"sendQuery":false,"sendHeaders":false,"sendBody":true,"contentType":"json","specifyBody":"json","jsonBody":"={{ JSON.stringify({ chat_id: \"7769073261\", text: \"⏳ Gerando matéria...\\n\\n🔗 \" + $json.url, parse_mode: \"HTML\" }) }}","options":{},"infoMessage":""},"id":"node-processing","name":"Telegram - Processando","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[624,96]},"messages":["connect ETIMEDOUT 2001:67c:4e8:f004::9:443","connect ETIMEDOUT 2001:67c:4e8:f004::9:443"],"httpCode":"ETIMEDOUT","message":"The connection timed out, consider setting the 'Retry on Fail' option in the node settings","stack":"NodeApiError: The connection timed out, consider setting the 'Retry on Fail' option in the node settings\n    at ExecuteContext.execute (/usr/lib/node_modules/n8n/node_modules/n8n-nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts:816:16)\n    at processTicksAndRejections (node:internal/process/task_queues:104:5)\n    at WorkflowExecute.executeNode (/usr/lib/node_modules/n8n/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:1045:8)\n    at WorkflowExecute.runNode (/usr/lib/node_modules/n8n/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:1224:11)\n    at /usr/lib/node_modules/n8n/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:1670:27\n    at /usr/lib/node_modules/n8n/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:2315:11"},"runData":{"Webhook Dispatcher":[{"startTime":1785441001815,"executionIndex":0,"source":[],"hints":[],"executionTime":1,"executionStatus":"success","data":{"main":[[{"json":{"headers":{"accept":"application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7","content-type":"application/json","user-agent":"axios/1.13.5","content-length":"605","accept-encoding":"gzip, compress, deflate, br","host":"localhost:5678","connection":"close"},"params":{},"query":{},"body":{"result":[{"update_id":183722542,"message":{"message_id":4326,"from":{"id":7769073261,"is_bot":false,"first_name":"Ultimo","last_name":"Campos","language_code":"pt-br"},"chat":{"id":7769073261,"first_name":"Ultimo","last_name":"Campos","type":"private"},"date":1785440938,"text":"Publicar https://cidadeverde.com/ultimas/460238/piaui-pode-ter-umidade-abaixo-de-15-nos-proximos-dias-inmet-mantem-alerta","entities":[{"offset":9,"length":112,"type":"url"}],"link_preview_options":{"url":"https://cidadeverde.com/ultimas/460238/piaui-pode-ter-umidade-abaixo-de-15-nos-proximos-dias-inmet-mantem-alerta"}}}]},"webhookUrl":"http://localhost:5678/webhook/dispatch-publicar","executionMode":"production"},"pairedItem":{"item":0}}]]}}],"Extrair URL (publicar)":[{"startTime":1785441001817,"executionIndex":1,"source":[{"previousNode":"Webhook Dispatcher","previousNodeOutput":0,"previousNodeRun":0}],"hints":[],"executionTime":19,"executionStatus":"success","data":{"main":[[{"json":{"url":"https://cidadeverde.com/ultimas/460238/piaui-pode-ter-umidade-abaixo-de-15-nos-proximos-dias-inmet-mantem-alerta","messageId":4326,"chatId":"7769073261","updateId":183722542},"pairedItem":{"item":0}}]]}}],"Telegram - Processando":[{"startTime":1785441001836,"executionIndex":2,"source":[{"previousNode":"Extrair URL (publicar)","previousNodeOutput":0,"previousNodeRun":0}],"hints":[],"executionTime":133451,"executionStatus":"error","error":{"level":"warning","tags":{},"timestamp":1785441135287,"context":{"itemIndex":0,"request":{"body":{"chat_id":"7769073261","text":"⏳ Gerando matéria...\n\n🔗 https://cidadeverde.com/ultimas/460238/piaui-pode-ter-umidade-abaixo-de-15-nos-proximos-dias-inmet-mantem-alerta","parse_mode":"HTML"},"headers":{"accept":"application/json,text/html,application/xhtml+xml,application/xml,text/*;q=0.9, image/*;q=0.8, */*;q=0.7"},"method":"POST","uri":"https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage","gzip":true,"rejectUnauthorized":true,"followRedirect":true,"resolveWithFullResponse":true,"sendCredentialsOnCrossOriginRedirect":true,"followAllRedirects":true,"timeout":300000,"encoding":null,"json":false,"useStream":true}},"functionality":"regular","name":"NodeApiError","node":{"parameters":{"curlImport":"","method":"POST","url":"https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage","authentication":"none","provideSslCertificates":false,"sendQuery":false,"sendHeaders":false,"sendBody":true,"contentType":"json","specifyBody":"json","jsonBody":"={{ JSON.stringify({ chat_id: \"7769073261\", text: \"⏳ Gerando matéria...\\n\\n🔗 \" + $json.url, parse_mode: \"HTML\" }) }}","options":{},"infoMessage":""},"id":"node-processing","name":"Telegram - Processando","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[624,96]},"messages":["connect ETIMEDOUT 2001:67c:4e8:f004::9:443","connect ETIMEDOUT 2001:67c:4e8:f004::9:443"],"httpCode":"ETIMEDOUT","message":"The connection timed out, consider setting the 'Retry on Fail' option in the node settings","stack":"NodeApiError: The connection timed out, consider setting the 'Retry on Fail' option in the node settings\n    at ExecuteContext.execute (/usr/lib/node_modules/n8n/node_modules/n8n-nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts:816:16)\n    at processTicksAndRejections (node:internal/process/task_queues:104:5)\n    at WorkflowExecute.executeNode (/usr/lib/node_modules/n8n/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:1045:8)\n    at WorkflowExecute.runNode (/usr/lib/node_modules/n8n/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:1224:11)\n    at /usr/lib/node_modules/n8n/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:1670:27\n    at /usr/lib/node_modules/n8n/node_modules/n8n-core/src/execution-engine/workflow-execute.ts:2315:11"}}]},"lastNodeExecuted":"Telegram - Processando"},"executionData":{"contextData":{},"nodeExecutionStack":[{"node":{"parameters":{"curlImport":"","method":"POST","url":"https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage","authentication":"none","provideSslCertificates":false,"sendQuery":false,"sendHeaders":false,"sendBody":true,"contentType":"json","specifyBody":"json","jsonBody":"={{ JSON.stringify({ chat_id: \"7769073261\", text: \"⏳ Gerando matéria...\\n\\n🔗 \" + $json.url, parse_mode: \"HTML\" }) }}","options":{},"infoMessage":""},"id":"node-processing","name":"Telegram - Processando","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[624,96]},"data":{"main":[[{"json":{"url":"https://cidadeverde.com/ultimas/460238/piaui-pode-ter-umidade-abaixo-de-15-nos-proximos-dias-inmet-mantem-alerta","messageId":4326,"chatId":"7769073261","updateId":183722542},"pairedItem":{"item":0}}]]},"source":{"main":[{"previousNode":"Extrair URL (publicar)","previousNodeOutput":0,"previousNodeRun":0}]}}],"metadata":{},"waitingExecution":{},"waitingExecutionSource":{},"runtimeData":{"version":1,"establishedAt":1785441001813,"source":"webhook","redaction":{"version":1,"policy":"none"},"triggerNode":{"name":"Webhook Dispatcher","type":"n8n-nodes-base.webhook"}}},"resumeToken":"88d03f5864996ae1766e735a0f2e6d9a44ae30fa5a794ba7f09ce6b4010c5eac"},"workflowData":{"id":"uvnMZfrW0Giut5lM","name":"OOeirense - Telegram → Publicar Matéria no Portal","active":true,"activeVersionId":"0e5c6a42-ed2a-4c53-84af-143b692d4a10","isArchived":false,"createdAt":"2026-04-17T02:40:26.739Z","updatedAt":"2026-07-06T21:20:23.054Z","nodes":[{"parameters":{"mode":"runOnceForAllItems","language":"javaScript","jsCode":"const response = $input.first().json.body;\nconst results = response.result || [];\nif (!results.length) { return []; }\n\nconst msg = results[0];\n// SEMPRE avancar offset local, mesmo quando nao for para este workflow\n// (para nao bloquear o Instagram workflow que le o mesmo bot)\nconst staticData = $getWorkflowStaticData('global');\nstaticData.publishLastUpdateId = msg.update_id + 1;\n\nif (msg.message?.from?.is_bot) { return []; }\nconst chatId = String(msg.message?.chat?.id || '');\nif (chatId !== '7769073261') { return []; }\n\nconst text = (msg.message?.text || '').trim();\nconst match = text.match(/^(?:publicar|publica[cç][aã]o)\\s+(https?:\\/\\/\\S+)/i);\nif (!match) { return []; }\n\nconst url = match[1];\n\n// ── Trava de deduplicação por URL ──\n// Mantém lista das ultimas URLs processadas. Se URL repetida em <24h, bloqueia.\n// Persiste como array top-level (subchaves nao persistem no task runner v2.15).\nconst now = Date.now();\nconst TTL_MS = 24 * 60 * 60 * 1000; // 24h\nconst prev = Array.isArray(staticData.processedUrls) ? staticData.processedUrls : [];\nconst fresh = prev.filter(e => e && e.ts && (now - e.ts) < TTL_MS);\n\nif (fresh.some(e => e.url === url)) {\n  // ja processada — ignora silenciosamente (apenas avanca offset do Telegram)\n  staticData.processedUrls = fresh;\n  return [];\n}\n\n// marca otimisticamente ANTES de prosseguir (bloqueia duplicatas concorrentes)\nfresh.push({ url, ts: now });\nstaticData.processedUrls = fresh.slice(-200);\n\nreturn [{ json: {\n  url,\n  messageId: msg.message.message_id,\n  chatId,\n  updateId: msg.update_id\n} }];\n","notice":""},"id":"node-extract","name":"Extrair URL (publicar)","type":"n8n-nodes-base.code","typeVersion":2,"position":[432,304]},{"parameters":{"curlImport":"","method":"POST","url":"https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage","authentication":"none","provideSslCertificates":false,"sendQuery":false,"sendHeaders":false,"sendBody":true,"contentType":"json","specifyBody":"json","jsonBody":"={{ JSON.stringify({ chat_id: \"7769073261\", text: \"⏳ Gerando matéria...\\n\\n🔗 \" + $json.url, parse_mode: \"HTML\" }) }}","options":{},"infoMessage":""},"id":"node-processing","name":"Telegram - Processando","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[624,96]},{"parameters":{"curlImport":"","method":"GET","url":"={{ 'https://r.jina.ai/' + $('Extrair URL (publicar)').first().json.url }}","authentication":"none","provideSslCertificates":false,"sendQuery":false,"sendHeaders":true,"specifyHeaders":"keypair","headerParameters":{"parameters":[{"name":"Accept","value":"application/json"},{"name":"User-Agent","value":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"},{"name":"X-Timeout","value":"45"}]},"sendBody":false,"options":{"timeout":60000},"infoMessage":""},"id":"node-jina","name":"Jina Reader","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[640,352],"onError":"continueRegularOutput"},{"parameters":{"mode":"runOnceForAllItems","language":"javaScript","jsCode":"// Jina Reader retorna: { data: { title, description, url, content, metadata } }\nconst response = $('Jina Reader').first().json;\nconst jinaData = response.data || response;\nconst meta = jinaData.metadata || {};\nconst url = $('Extrair URL (publicar)').first().json.url;\n\n// Titulo + descricao\nlet title = (meta['og:title'] || jinaData.title || '').replace(/\\s*[-|].*$/, '').trim();\nlet description = (meta['og:description'] || meta.description || jinaData.description || '').trim();\n\n// Imagem — priorizar og:image do metadata\nlet imageUrl = (meta['og:image'] || meta['twitter:image'] || '').trim();\nlet content = jinaData.content || '';\n\n// Detectar bloqueio do Jina (403 / conteudo vazio / pagina de erro)\nconst jinaBlocked =\n  /403 forbidden|access denied|don't have permission/i.test(title) ||\n  /403 forbidden|don't have permission/i.test(content) ||\n  content.trim().length < 300 ||\n  !imageUrl;\n\nif (jinaBlocked) {\n  try {\n    const html = await this.helpers.httpRequest({\n      method: 'GET',\n      url: url,\n      headers: {\n        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',\n        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n        'Accept-Language': 'pt-BR,pt;q=0.9,en;q=0.8',\n      },\n      returnFullResponse: false,\n    });\n    const extractMeta = (html, key) => {\n      let rx = new RegExp('<meta[^>]+(?:property|name)\\\\s*=\\\\s*[\"\\'](' + key + ')[\"\\'][^>]*content\\\\s*=\\\\s*[\"\\']([^\"\\']+)[\"\\']', 'i');\n      let m = html.match(rx);\n      if (m) return m[2];\n      rx = new RegExp('<meta[^>]+content\\\\s*=\\\\s*[\"\\']([^\"\\']+)[\"\\'][^>]*(?:property|name)\\\\s*=\\\\s*[\"\\'](' + key + ')[\"\\']', 'i');\n      m = html.match(rx);\n      return m ? m[1] : '';\n    };\n    const newTitle = (extractMeta(html, 'og:title') || extractMeta(html, 'twitter:title') || '').replace(/\\s*[-|].*$/, '').trim();\n    const newDesc = extractMeta(html, 'og:description') || extractMeta(html, 'twitter:description') || extractMeta(html, 'description') || '';\n    const newImg = extractMeta(html, 'og:image') || extractMeta(html, 'twitter:image') || '';\n    if (newTitle) title = newTitle;\n    if (newDesc) description = newDesc;\n    if (newImg) imageUrl = newImg;\n    const paras = [];\n    const pRx = /<p[^>]*>([\\s\\S]*?)<\\/p>/gi;\n    let mm;\n    while ((mm = pRx.exec(html)) !== null) {\n      const txt = mm[1].replace(/<[^>]+>/g, '').replace(/&nbsp;/g,' ').replace(/&amp;/g,'&').replace(/&quot;/g,'\"').replace(/&#39;/g,\"'\").trim();\n      if (txt.length > 40) paras.push(txt);\n    }\n    const fallbackContent = paras.join('\\n\\n');\n    if (fallbackContent.length > content.length) content = fallbackContent;\n  } catch (e) {\n    // se fallback falhar, deixa campos como estao\n  }\n}\n\n// Decodificar entidades HTML na URL (ex: &amp; -> &)\nimageUrl = imageUrl.replace(/&amp;/g, '&');\n\n// Se og:image usa CDN Cloudflare (cdn-cgi/image/...), extrair URL original\nif (imageUrl.includes('cdn-cgi/image/')) {\n  const originalMatch = imageUrl.match(/\\/https?:\\/\\/.+$/);\n  if (originalMatch) imageUrl = originalMatch[0].substring(1);\n}\n\n// ── Remover marca d'agua dos portais que servem og:image watermarked ──\n// gp1.com.br: /media/watermarks/ → /media/image_bank/\n// lupa1.com.br: /storage/XXX/conversions/*-social.jpg → /storage/XXX/*.{jpeg,jpg,png}\nconst isGp1Watermark = /gp1\\.com\\.br\\/media\\/watermarks\\//i.test(imageUrl);\nconst isLupa1Watermark = /lupa1\\.com\\.br\\/storage\\/\\d+\\/conversions\\//i.test(imageUrl);\n\nif (isGp1Watermark || isLupa1Watermark) {\n  try {\n    const html = await this.helpers.httpRequest({\n      method: 'GET',\n      url: url,\n      headers: {\n        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',\n        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n        'Accept-Language': 'pt-BR,pt;q=0.9,en;q=0.8',\n      },\n      returnFullResponse: false,\n    });\n\n    if (isGp1Watermark) {\n      // Pega imagens em /media/image_bank/, exclui thumbnails (.90x58, .60x60 etc)\n      const rx = /https:\\/\\/[^\"'\\s)]*gp1\\.com\\.br\\/media\\/image_bank\\/[^\"'\\s)]+\\.(?:jpg|jpeg|png|webp)[^\"'\\s)]*/gi;\n      const candidates = (html.match(rx) || []).filter(u => !/\\.\\d{2,3}x\\d{2,3}_/.test(u));\n      const large = candidates.find(u => /1200x|800x|upscale/i.test(u));\n      if (large || candidates[0]) imageUrl = large || candidates[0];\n    } else if (isLupa1Watermark) {\n      const m = imageUrl.match(/\\/storage\\/(\\d+)\\/conversions\\//);\n      if (m) {\n        const storageId = m[1];\n        const rx = new RegExp('https://[^\"\\'\\\\s)]*lupa1\\\\.com\\\\.br/storage/' + storageId + '/[^\"\\'\\\\s)]+\\\\.(?:jpg|jpeg|png|webp)[^\"\\'\\\\s)]*', 'gi');\n        const candidates = (html.match(rx) || []).filter(u => !/\\/conversions\\//.test(u));\n        if (candidates[0]) imageUrl = candidates[0];\n      }\n    }\n  } catch (e) {\n    // Se fetch falhar, mantem og:image original (com marca)\n  }\n}\n\n// Fallback: primeira imagem real no markdown (pular logos, icones, social)\nif (!imageUrl) {\n  const skipPatterns = /logo|social-40|icon|favicon|brand|avatar|banner_site|sprite|sem-foto/i;\n  const imgRegex = /!\\[.*?\\]\\((https?:\\/\\/[^\\s)]+\\.(jpg|jpeg|png|webp)[^\\s)]*)\\)/gi;\n  let match;\n  while ((match = imgRegex.exec(content)) !== null) {\n    if (!skipPatterns.test(match[1])) { imageUrl = match[1]; break; }\n  }\n}\n\n// ABORTAR se nao tiver imagem\nif (!imageUrl) {\n  throw new Error('SEM_IMAGEM: nao foi possivel extrair imagem da materia. URL: ' + url);\n}\n\n// Corpo\nconst body = content\n  .replace(/!\\[.*?\\]\\(.*?\\)/g, '')\n  .replace(/\\[([^\\]]+)\\]\\([^\\)]+\\)/g, '$1')\n  .replace(/#{1,6}\\s/g, '')\n  .replace(/[*_]{1,3}/g, '')\n  .replace(/\\n{3,}/g, '\\n\\n')\n  .trim()\n  .substring(0, 3000) || description;\n\nreturn [{ json: { url, title, description, ogImage: imageUrl, body } }];\n","notice":""},"id":"node-extract-data","name":"Extrair Dados","type":"n8n-nodes-base.code","typeVersion":2,"position":[880,576],"onError":"continueErrorOutput"},{"parameters":{"curlImport":"","method":"POST","url":"https://api.openai.com/v1/chat/completions","authentication":"none","provideSslCertificates":false,"sendQuery":false,"sendHeaders":true,"specifyHeaders":"keypair","headerParameters":{"parameters":[{"name":"Authorization","value":"Bearer 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"},{"name":"Content-Type","value":"application/json"}]},"sendBody":true,"contentType":"json","specifyBody":"json","jsonBody":"={{ ({\n  \"model\": \"gpt-4o\",\n  \"max_tokens\": 4500,\n  \"temperature\": 0.2,\n  \"messages\": [{\n    \"role\": \"user\",\n    \"content\": `Você é jornalista do portal O Oeirense (Oeiras, Piauí). REESCREVA a matéria-fonte abaixo com palavras próprias, FIEL AOS FATOS, sem inventar nada. Produza JSON (sem markdown, sem \\`\\`\\`).\n\nESTRUTURA:\n1. CHAPÉU: 1-2 palavras MAIÚSCULAS que rotulem o tema específico (ex: SEMANA SANTA, ELEIÇÕES 2026, SAÚDE PÚBLICA, PROTEÇÃO ANIMAL)\n2. TÍTULO: até 100 caracteres, direto, sem ponto final\n3. SUBTÍTULO: 2 frases, 200-280 caracteres, com dados concretos\n4. CORPO (bodyHtml): parágrafos em HTML usando <p> (e <em> só para citação que exista LITERALMENTE na fonte). Use a QUANTIDADE de parágrafos que a fonte sustentar — normalmente 3 a 6. NÃO force tamanho: se a fonte é curta, a matéria é curta.\n   - Comece pelo lide (quem, o quê, quando, onde) e desenvolva apenas contexto e dados QUE ESTEJAM NA FONTE.\n   - NÃO crie parágrafo de \"reações\", \"repercussão\" ou \"declarações\" se a fonte não relatar isso de fato.\n   - Impacto local: só escreva sobre efeito em Oeiras ou na região SE a fonte tratar disso. Se o fato não tem ligação com Oeiras, relate-o como ele é, sem inventar conexão local.\n5. CATEGORIA (categorySlug) — ESCOLHA UMA baseado no TEMA CENTRAL da matéria:\n   • \"cidades\" — notícias urbanas/municipais gerais, trânsito, comércio local, cotidiano, ONGs, proteção animal, acidentes que não são crime\n   • \"politica\" — Câmara, prefeitura, eleições, vereadores, deputados, governo, projetos de lei, ALEPI\n   • \"seguranca\" — Polícia Civil/Militar, prisões, apreensões, investigações criminais, violência urbana\n   • \"saude\" — SUS, UBS, hospitais, campanhas, secretaria de saúde, medicina (exceto graduação)\n   • \"educacao\" — escolas, universidades, IFPI/UFPI, alfabetização, cursos, ensino\n   • \"cultura-religiao\" — Semana Santa, procissões, festas religiosas, patrimônio histórico, arte, shows\n   • \"esportes\" — campeonatos, atletas, futebol, competições\n   • \"economia-agro\" — agricultura, pecuária, piscicultura, comércio, produtores, ranking econômico, desenvolvimento regional\n   • \"infraestrutura\" — obras, pavimentação, saneamento (Águas do Piauí), esgoto, energia, iluminação, estradas\n   • \"opiniao\" — artigos assinados, análises, reflexões, editorial, crônicas\n   REGRA: escolha pelo TEMA DOMINANTE. Se for sobre Câmara aprovando projeto de agricultura familiar → politica (ato legislativo dominante). Se for sobre agricultor recebendo alevinos → economia-agro (produtor dominante).\n6. TAGS: 4-6 palavras-chave lowercase, separadas por relevância ao tema\n\nREGRAS INVIOLÁVEIS:\n- FIDELIDADE À FONTE: a matéria deve conter APENAS fatos presentes na fonte, reescritos com palavras próprias. Não invente conteúdo novo.\n- PROIBIDO inventar falas, citações, declarações, entrevistas, reações, opiniões, números, datas, nomes, cargos ou locais.\n- Só use fala entre aspas ou <em> se a frase aparecer LITERALMENTE na fonte. Nunca transforme um fato em declaração textual de alguém.\n- Não alongue o texto com contexto, histórico ou desdobramentos ausentes na fonte. Fidelidade vale mais que tamanho.\n- Linguagem clara, objetiva, sem sensacionalismo.\n\nRESPONDA APENAS JSON:\n{\n  \"chapeu\": \"PALAVRA\",\n  \"title\": \"...\",\n  \"subtitle\": \"...\",\n  \"bodyHtml\": \"<p>...</p><p>...</p>...\",\n  \"categorySlug\": \"uma-das-10-slugs-acima\",\n  \"tags\": [\"tag1\",\"tag2\",\"tag3\",\"tag4\"]\n}\n\nFONTE:\nTítulo: ${$json.title}\nDescrição: ${$json.description}\nURL: ${$json.url}\nCorpo:\n${$json.body}`\n  }]\n}) }}","options":{},"infoMessage":""},"id":"node-openai","name":"OpenAI - Gerar Matéria","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1088,352]},{"parameters":{"mode":"runOnceForAllItems","language":"javaScript","jsCode":"const raw = $input.first().json?.choices?.[0]?.message?.content || '';\nconst ogImage = $('Extrair Dados').first().json.ogImage || '';\nconst sourceUrl = $('Extrair URL (publicar)').first().json.url;\n\nlet parsed;\ntry {\n  const jsonMatch = raw.match(/\\{[\\s\\S]*\\}/);\n  parsed = JSON.parse(jsonMatch ? jsonMatch[0] : raw);\n} catch(e) {\n  throw new Error('Falha ao parsear JSON do OpenAI: ' + raw.slice(0, 200));\n}\n\nconst categoryNames = {\n  'cidades': 'Cidades', 'politica': 'Política', 'seguranca': 'Segurança',\n  'saude': 'Saúde', 'educacao': 'Educação', 'cultura-religiao': 'Cultura & Religião',\n  'esportes': 'Esportes', 'economia-agro': 'Economia & Agro',\n  'infraestrutura': 'Infraestrutura', 'opiniao': 'Opinião'\n};\n\nconst categorySlug = parsed.categorySlug || 'cidades';\nconst categoryName = categoryNames[categorySlug] || 'Cidades';\n\nreturn [{ json: {\n  title: parsed.title,\n  subtitle: parsed.subtitle,\n  chapeu: parsed.chapeu,\n  bodyHtml: parsed.bodyHtml,\n  categorySlug,\n  categoryName,\n  tags: parsed.tags || [],\n  coverUrl: ogImage,\n  coverAlt: parsed.title,\n  sourceUrl,\n  authorName: 'Redação O Oeirense',\n  instagramDispatch: true,\n  whatsappDispatch: true\n}}];","notice":""},"id":"node-parse","name":"Parsear Matéria","type":"n8n-nodes-base.code","typeVersion":2,"position":[1312,352]},{"parameters":{"curlImport":"","method":"POST","url":"https://ooeirense.com.br/api/articles/create","authentication":"none","provideSslCertificates":false,"sendQuery":false,"sendHeaders":true,"specifyHeaders":"keypair","headerParameters":{"parameters":[{"name":"x-api-key","value":"ooeirense-n8n-2026-secret"},{"name":"Content-Type","value":"application/json"}]},"sendBody":true,"contentType":"json","specifyBody":"json","jsonBody":"={{ JSON.stringify($json) }}","options":{},"infoMessage":""},"id":"node-create","name":"Criar Artigo no Portal","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1520,352],"onError":"continueRegularOutput"},{"parameters":{"curlImport":"","method":"POST","url":"https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage","authentication":"none","provideSslCertificates":false,"sendQuery":false,"sendHeaders":false,"sendBody":true,"contentType":"json","specifyBody":"json","jsonBody":"={{ JSON.stringify((()=>{\n  const j = $json;\n  const title = ($(\"Parsear Matéria\").first().json.title) || \"\";\n  let url = j.url || \"\";\n  let duplicate = false;\n  if (j.error && typeof j.error.message === \"string\" && j.error.message.indexOf(\"409\") !== -1) {\n    duplicate = true;\n    const m = j.error.message.match(/\"url\":\"([^\"]+)\"/);\n    if (m) url = m[1];\n  }\n  const text = duplicate\n    ? \"⚠️ Esta matéria já estava publicada\\n\\n🔗 \" + url\n    : \"✅ Matéria publicada!\\n\\n📰 <b>\" + title + \"</b>\\n\\n🔗 \" + url + \"\";\n  return { chat_id: \"7769073261\", text, parse_mode: \"HTML\" };\n})()) }}","options":{},"infoMessage":""},"id":"node-confirm","name":"Telegram - Confirmação","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1744,304]},{"parameters":{"curlImport":"","method":"POST","url":"https://api.telegram.org/bot8684225036:AAHPUgR6b1y7oN7y5q1FMQ7vcoi3Io9nysE/sendMessage","authentication":"none","provideSslCertificates":false,"sendQuery":false,"sendHeaders":false,"sendBody":true,"contentType":"json","specifyBody":"json","jsonBody":"={{ JSON.stringify({ chat_id: \"7769073261\", text: \"❌ <b>Não foi possível publicar</b>\\n\\n\" + ($json.error && $json.error.message ? $json.error.message : \"Erro desconhecido\") + \"\\n\\n<i>A matéria não foi criada porque a imagem não pôde ser extraída.</i>\", parse_mode: \"HTML\" }) }}","options":{},"infoMessage":""},"id":"node-error","name":"Telegram - Erro","type":"n8n-nodes-base.httpRequest","typeVersion":4.2,"position":[1744,464]},{"parameters":{"multipleMethods":false,"httpMethod":"POST","path":"dispatch-publicar","authentication":"none","responseMode":"onReceived","contentTypeNotice":"","options":{}},"id":"wh-dispatch-publicar","name":"Webhook Dispatcher","type":"n8n-nodes-base.webhook","typeVersion":2,"position":[0,304],"webhookId":"wh-dispatch-publicar"}],"connections":{"Extrair URL (publicar)":{"main":[[{"node":"Telegram - Processando","type":"main","index":0}]]},"Jina Reader":{"main":[[{"node":"Extrair Dados","type":"main","index":0}]]},"Extrair Dados":{"main":[[{"node":"OpenAI - Gerar Matéria","type":"main","index":0}],[{"node":"Telegram - Erro","type":"main","index":0}]]},"OpenAI - Gerar Matéria":{"main":[[{"node":"Parsear Matéria","type":"main","index":0}]]},"Parsear Matéria":{"main":[[{"node":"Criar Artigo no Portal","type":"main","index":0}]]},"Criar Artigo no Portal":{"main":[[{"node":"Telegram - Confirmação","type":"main","index":0}]]},"Telegram - Processando":{"main":[[{"node":"Jina Reader","type":"main","index":0}]]},"Telegram - Confirmação":{"main":[[]]},"Webhook Dispatcher":{"main":[[{"node":"Extrair URL (publicar)","type":"main","index":0}]]}},"settings":{"executionOrder":"v1","callerPolicy":"workflowsFromSameOwner","availableInMCP":false,"binaryMode":"separate"},"staticData":{"node:Poll Telegram (Matérias)":{"recurrenceRules":[]},"global":{"publishLastUpdateId":183722539,"processedUrls":[{"url":"https://www.muraldavila.com.br/noticias/filho-de-oeiras-nelson-silva-rodrigues-junior-se-forma-em-medicina-e-amplia-trajetoria-marcada-pelo-estudo-e-pelo-cuidado-com-as-pessoas-88355.html","ts":1785337921715}]}},"pinData":{}},"workflowVersionId":"0e5c6a42-ed2a-4c53-84af-143b692d4a10","customData":{}}