{
  "id": "4ZIEGck9n4l5qaDt",
  "name": "📅 Pompeo — Calendar Agent [Schedule]",
  "nodes": [
    {
      "id": "5162caf6661f4aa8",
      "name": "⏰ Schedule",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        0,
        300
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "*/30 * * * *"
            }
          ]
        }
      }
    },
    {
      "id": "63a2b1a9ba7a4cf2",
      "name": "📅 Imposta Range",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        260,
        300
      ],
      "parameters": {
        "jsCode": "const now = new Date();\nconst start = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0).toISOString();\nconst end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 7, 23, 59, 59).toISOString();\nreturn [{json: {start, end}}];"
      }
    },
    {
      "id": "89ee9f58cd2f4d4d",
      "name": "🔑 Ottieni Token Copilot",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        520,
        300
      ],
      "parameters": {
        "url": "https://api.github.com/copilot_internal/v2/token",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "options": {}
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "vBwUxlzKrX3oDHyN",
          "name": "GitHub Copilot OAuth Token"
        }
      }
    },
    {
      "id": "ffb3d86cf2a54659",
      "name": "📋 Prepara Calendari",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        780,
        300
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const start = $('📅 Imposta Range').first().json.start;\nconst end   = $('📅 Imposta Range').first().json.end;\nconst HA    = 'http://10.30.20.100:8123';\n\n// Note: calendar.films and calendar.serie_tv removed — handled by Media Agent (direct Radarr/Sonarr API)\nconst calendars = [\n  {entity_id:'calendar.calendar',                          name:'Lavoro',          category:'work',    user_id:'martin'},\n  {entity_id:'calendar.famiglia',                          name:'Famiglia',        category:'personal',user_id:'martin'},\n  {entity_id:'calendar.spazzatura',                        name:'Spazzatura',      category:'chores',  user_id:'martin'},\n  {entity_id:'calendar.pulizie',                           name:'Pulizie',         category:'chores',  user_id:'martin'},\n  {entity_id:'calendar.formula_1',                         name:'Formula 1',       category:'leisure', user_id:'martin'},\n  {entity_id:'calendar.lm_wec_fia_world_endurance_championship', name:'WEC',       category:'leisure', user_id:'martin'},\n  {entity_id:'calendar.inter_calendar',                    name:'Inter',           category:'leisure', user_id:'martin'},\n  {entity_id:'calendar.birthdays',                         name:'Compleanni',      category:'social',  user_id:'martin'},\n  {entity_id:'calendar.varie',                             name:'Varie',           category:'misc',    user_id:'martin'},\n  {entity_id:'calendar.festivita_in_italia',               name:'Festività Italia',category:'holiday', user_id:'shared'}\n];\n\nreturn calendars.map(cal => ({json: {...cal, start, end, ha_base_url: HA}}));"
      }
    },
    {
      "id": "7985f2d26aa64e69",
      "name": "📡 HA - Scarica Calendario",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1040,
        300
      ],
      "parameters": {
        "url": "={{ $json.ha_base_url + '/api/calendars/' + $json.entity_id + '?start=' + $json.start + '&end=' + $json.end }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "httpHeaderAuth",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        }
      },
      "credentials": {
        "httpHeaderAuth": {
          "id": "u0JCseXGnDG5hS9F",
          "name": "Home Assistant API"
        }
      },
      "onError": "continueRegularOutput"
    },
    {
      "id": "9ed15cad33234465",
      "name": "🏷️ Estrai ed Etichetta",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1300,
        300
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const results = [];\nconst allCals = $('📋 Prepara Calendari').all();\n\nfor (let i = 0; i < $input.all().length; i++) {\n  const httpItem = $input.all()[i];\n  const pairedIdx = (typeof httpItem.pairedItem?.item === 'number') ? httpItem.pairedItem.item : i;\n  const cal = allCals[pairedIdx]?.json ?? {};\n\n  const events = Array.isArray(httpItem.json) ? httpItem.json : [];\n\n  for (const ev of events) {\n    const startVal = ev.start?.dateTime || ev.start?.date || null;\n    const endVal   = ev.end?.dateTime   || ev.end?.date   || null;\n    const uid = ev.uid ||\n      `${cal.entity_id}_${(ev.summary||'').replace(/\\s+/g,'_')}_${startVal}`;\n\n    results.push({json: {\n      uid,\n      summary:          ev.summary || '',\n      description:      ev.description || null,\n      location:         ev.location || null,\n      start_dt:         startVal,\n      end_dt:           endVal,\n      all_day:          !ev.start?.dateTime,\n      calendar_name:    cal.name    || 'Unknown',\n      calendar_entity:  cal.entity_id || '',\n      calendar_category:cal.category || 'misc',\n      user_id:          cal.user_id  || 'martin'\n    }});\n  }\n}\n\nreturn results.length > 0 ? results : [{json:{skip:true}}];"
      }
    },
    {
      "id": "9a2b47a3ce774ca1",
      "name": "📝 Prepara Prompt",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1560,
        300
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const allItems = $input.all().map(i => i.json).filter(e => !e.skip);\nif (!allItems.length) return [{json:{skip:true, events:[], prompt:null, event_count:0}}];\n\n// Dedup by uid\nconst seen = new Set();\nconst events = allItems.filter(e => {\n  if (!e.uid || seen.has(e.uid)) return false;\n  seen.add(e.uid); return true;\n});\n\nconst fmt = dt => dt ? dt.substring(0,16).replace('T',' ') : '';\n\nconst eventList = events.map((e, idx) =>\n  `${idx+1}. [${e.calendar_name}] ${e.summary}` +\n  (e.all_day ? ' | Tutto il giorno' : ` | ${fmt(e.start_dt)} → ${fmt(e.end_dt)}`) +\n  (e.location ? ` | 📍 ${e.location}` : '') +\n  (e.description ? ` | ${e.description.substring(0,80)}` : '') +\n  ` | uid: ${e.uid}`\n).join('\\n');\n\nconst prompt =\n`Sei Pompeo, l'assistente AI di Martin Tahiraj. Analizza questi eventi calendario (prossimi 7 giorni) e classificali.\n\nEVENTI:\n${eventList}\n\nRispondi SOLO con JSON valido nel formato:\n{\n  \"events\": [\n    {\n      \"uid\": \"<uid originale>\",\n      \"category\": \"work|personal|chores|leisure|social|holiday|misc\",\n      \"action_required\": false,\n      \"action_text\": null,\n      \"do_not_disturb\": false,\n      \"priority\": \"high|medium|low\",\n      \"behavioral_context\": \"es: work_meeting|sport_event|home_chores|birthday\",\n      \"home_presence_expected\": true,\n      \"pompeo_note\": \"Nota breve in italiano per il briefing\"\n    }\n  ]\n}`;\n\nreturn [{json:{events, prompt, event_count: events.length}}];"
      }
    },
    {
      "id": "5589a6ce6f1a4f81",
      "name": "🤖 GPT-4.1 - Analizza Calendari",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4,
      "position": [
        1820,
        300
      ],
      "parameters": {
        "method": "POST",
        "url": "https://api.githubcopilot.com/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $('🔑 Ottieni Token Copilot').first().json.token }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            },
            {
              "name": "Copilot-Integration-Id",
              "value": "vscode-chat"
            },
            {
              "name": "Editor-Version",
              "value": "vscode/1.85.0"
            }
          ]
        },
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ JSON.stringify({\"model\":\"gpt-4.1\",\"messages\":[{\"role\":\"system\",\"content\":\"Sei Pompeo, l'assistente AI di Martin. Rispondi SOLO con JSON valido.\"},{\"role\":\"user\",\"content\":$json.prompt}],\"response_format\":{\"type\":\"json_object\"},\"max_tokens\":2048}) }}",
        "options": {}
      }
    },
    {
      "id": "89505d194e014900",
      "name": "📋 Parse Risposta GPT",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2080,
        300
      ],
      "parameters": {
        "jsCode": "const raw = $input.first().json;\nconst content = (raw.choices || [])[0]?.message?.content || '{}';\nlet parsed;\ntry { parsed = JSON.parse(content); }\ncatch(e) { throw new Error('GPT non JSON: ' + content.substring(0,300)); }\n\nconst gptEvents = parsed.events || [];\nconst originalEvents = $('📝 Prepara Prompt').first().json.events || [];\n\nif (!originalEvents.length) return [{json:{skip:true}}];\n\nreturn originalEvents.map((orig, i) => {\n  const enriched = gptEvents.find(e => e.uid === orig.uid) || gptEvents[i] || {};\n  return {json:{\n    ...orig,\n    category:               enriched.category            || orig.calendar_category || 'misc',\n    action_required:        enriched.action_required     || false,\n    action_text:            enriched.action_text         || null,\n    do_not_disturb:         enriched.do_not_disturb      || false,\n    priority:               enriched.priority            || 'low',\n    behavioral_context:     enriched.behavioral_context  || null,\n    home_presence_expected: enriched.home_presence_expected ?? null,\n    pompeo_note:            enriched.pompeo_note         || orig.summary\n  }};\n});"
      }
    },
    {
      "id": "5bb69072497546d3",
      "name": "💾 Postgres - Salva Evento",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        2860,
        300
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO memory_facts (user_id, source, source_ref, category, subject, detail, action_required, action_text, expires_at)\nVALUES (\n  '{{ $json.user_id }}',\n  'calendar',\n  '{{ $json.uid }}',\n  '{{ $json.category }}',\n  '{{ $json.summary }}',\n  '{{ JSON.stringify({start:$json.start_dt,end:$json.end_dt,all_day:$json.all_day,location:$json.location,calendar:$json.calendar_name}) }}'::jsonb,\n  {{ $json.action_required ? 'true' : 'false' }},\n  '{{ $json.action_text || \"\" }}',\n  '{{ $json.expires_at }}'::timestamptz\n)\nON CONFLICT ON CONSTRAINT memory_facts_dedup_idx DO UPDATE SET\n  subject = EXCLUDED.subject,\n  detail = EXCLUDED.detail,\n  action_required = EXCLUDED.action_required,\n  action_text = EXCLUDED.action_text,\n  expires_at = EXCLUDED.expires_at",
        "options": {}
      },
      "credentials": {
        "postgres": {
          "id": "mRqzxhSboGscolqI",
          "name": "Pompeo — PostgreSQL"
        }
      }
    },
    {
      "id": "53f04d2895cf40b2",
      "name": "📦 Aggrega Risultati",
      "type": "n8n-nodes-base.aggregate",
      "typeVersion": 1,
      "position": [
        3120,
        300
      ],
      "parameters": {
        "aggregate": "aggregateAllItemData",
        "destinationFieldName": "events"
      }
    },
    {
      "id": "5573e6e8d378427e",
      "name": "✍️ Prepara Messaggio",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3380,
        300
      ],
      "parameters": {
        "jsCode": "const events = ($json.events || []).map(i => i.json || i).filter(e => !e.skip);\nif (!events.length) return [{json:{message:'📅 *Briefing Calendario*\\n\\nNessun evento nei prossimi 7 giorni.'}}];\n\nconst emojis = {work:'🗂',personal:'👨‍👩‍👧',chores:'🧹',leisure:'🏎',social:'🎂',holiday:'🎉',misc:'📌'};\nconst byCal = {};\nfor (const e of events) {\n  const k = e.calendar_name || 'Varie';\n  if (!byCal[k]) byCal[k] = {cat: e.calendar_category, evs:[]};\n  byCal[k].evs.push(e);\n}\n\nconst today = new Date().toLocaleDateString('it-IT',{weekday:'long',day:'numeric',month:'long'});\nlet msg = `📅 *Briefing Calendario — Prossimi 7 giorni*\\n_(${today})_\\n\\n`;\n\nfor (const [cal, {cat, evs}] of Object.entries(byCal)) {\n  msg += `${emojis[cat]||'📌'} *${cal}*\\n`;\n  for (const e of evs) {\n    const dt = e.all_day ? '' : (e.start_dt ? ' `' + e.start_dt.substring(11,16) + '`' : '');\n    const flag = e.action_required ? ' ⚡' : '';\n    msg += `• ${e.summary}${dt}${flag}\\n`;\n  }\n  msg += '\\n';\n}\nmsg += `_${events.length} eventi — generato da Pompeo_`;\nreturn [{json:{message:msg}}];"
      }
    },
    {
      "id": "420db8250bf9492f",
      "name": "📱 Telegram - Briefing Giornaliero",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1,
      "position": [
        3640,
        300
      ],
      "parameters": {
        "resource": "message",
        "operation": "sendMessage",
        "chatId": "-4814221197",
        "text": "={{ $json.message }}",
        "additionalFields": {
          "parse_mode": "Markdown"
        }
      },
      "credentials": {
        "telegramApi": {
          "id": "uTXHLqcCJxbOvqN3",
          "name": "Telegram account"
        }
      }
    },
    {
      "id": "7fd81a1892184b61",
      "name": "🗑️ Cleanup Cancellati",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2,
      "position": [
        2340,
        300
      ],
      "parameters": {
        "operation": "executeQuery",
        "query": "DELETE FROM memory_facts\nWHERE source = 'calendar'\n  AND source_ref IS NOT NULL\n  AND (detail->>'start')::timestamptz >= '{{ $('📅 Imposta Range').first().json.start }}'::timestamptz\n  AND (detail->>'start')::timestamptz < '{{ $('📅 Imposta Range').first().json.end }}'::timestamptz\n  AND source_ref NOT IN ({{ $('📋 Parse Risposta GPT').all().filter(i => i.json.uid).map(i => \"'\" + String(i.json.uid).replace(/'/g,\"''\") + \"'\").join(',') || \"'__no_uid__'\" }})",
        "options": {}
      },
      "credentials": {
        "postgres": {
          "id": "mRqzxhSboGscolqI",
          "name": "Pompeo — PostgreSQL"
        }
      }
    },
    {
      "id": "f8922396b255475a",
      "name": "🔀 Riemetti Eventi",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2600,
        300
      ],
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "return $('📋 Parse Risposta GPT').all().filter(i => !i.json.skip);"
      }
    }
  ],
  "connections": {
    "⏰ Schedule": {
      "main": [
        [
          {
            "node": "📅 Imposta Range",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📅 Imposta Range": {
      "main": [
        [
          {
            "node": "🔑 Ottieni Token Copilot",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔑 Ottieni Token Copilot": {
      "main": [
        [
          {
            "node": "📋 Prepara Calendari",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📋 Prepara Calendari": {
      "main": [
        [
          {
            "node": "📡 HA - Scarica Calendario",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📡 HA - Scarica Calendario": {
      "main": [
        [
          {
            "node": "🏷️ Estrai ed Etichetta",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🏷️ Estrai ed Etichetta": {
      "main": [
        [
          {
            "node": "📝 Prepara Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📝 Prepara Prompt": {
      "main": [
        [
          {
            "node": "🤖 GPT-4.1 - Analizza Calendari",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🤖 GPT-4.1 - Analizza Calendari": {
      "main": [
        [
          {
            "node": "📋 Parse Risposta GPT",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📋 Parse Risposta GPT": {
      "main": [
        [
          {
            "node": "🗑️ Cleanup Cancellati",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "💾 Postgres - Salva Evento": {
      "main": [
        [
          {
            "node": "📦 Aggrega Risultati",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "📦 Aggrega Risultati": {
      "main": [
        [
          {
            "node": "✍️ Prepara Messaggio",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "✍️ Prepara Messaggio": {
      "main": [
        [
          {
            "node": "📱 Telegram - Briefing Giornaliero",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🗑️ Cleanup Cancellati": {
      "main": [
        [
          {
            "node": "🔀 Riemetti Eventi",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "🔀 Riemetti Eventi": {
      "main": [
        [
          {
            "node": "💾 Postgres - Salva Evento",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "callerPolicy": "workflowsFromSameOwner",
    "availableInMCP": false
  },
  "triggerCount": 1,
  "versionId": "5b03aaed-af76-4d6a-bff0-5a2949c24dfe",
  "owner": {
    "type": "personal",
    "projectId": "Hdttz401OqqtObPo",
    "projectName": "Martin Tahiraj <tahiraj.martin@gmail.com>",
    "personalEmail": "tahiraj.martin@gmail.com"
  },
  "parentFolderId": null,
  "isArchived": false
}