464 lines
18 KiB
JSON
464 lines
18 KiB
JSON
{
|
|
"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
|
|
} |