Skip to main content
Glama
event-modifications.json20.6 kB
{ "name": "Template - Gestion des annulations/modifications Google Calendar", "nodes": [ { "parameters": { "rule": { "interval": [ { "field": "minutes", "minutesInterval": 10 } ] } }, "id": "schedule-trigger", "name": "Déclencheur planifié", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1, "position": [ 250, 300 ] }, { "parameters": { "resource": "event", "operation": "getAll", "calendarId": "={{ $json.calendarId || 'primary' }}", "returnAll": true, "options": { "timeMin": "={{ $now.minus(1, 'day').isoString }}", "timeMax": "={{ $now.plus(7, 'day').isoString }}", "singleEvents": false, "updatedMin": "={{ $now.minus(30, 'minutes').isoString }}" } }, "id": "get-updated-events", "name": "Récupération des événements modifiés", "type": "n8n-nodes-base.googleCalendar", "typeVersion": 2, "position": [ 450, 300 ], "credentials": { "googleCalendarOAuth2Api": { "id": "1", "name": "Google Calendar OAuth2 API" } }, "continueOnFail": true }, { "parameters": { "conditions": { "string": [ { "value1": "={{ $json.error }}", "operation": "exists" } ] } }, "id": "check-events-error", "name": "Vérification erreur événements", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 650, 300 ] }, { "parameters": { "jsCode": "// Gestion des erreurs d'API Google Calendar\nconst error = $input.item.json.error;\n\n// Journaliser l'erreur\nconsole.error('Erreur lors de la récupération des événements:', error.message);\n\n// Vérifier si c'est une erreur d'authentification\nif (error.message && (error.message.includes('401') || error.message.includes('auth'))) {\n return {\n error: true,\n authError: true,\n message: \"Erreur d'authentification. Le token OAuth doit être rafraîchi.\",\n originalError: error.message\n };\n}\n\n// Vérifier si c'est une erreur de quota\nif (error.message && error.message.includes('quota')) {\n return {\n error: true,\n quotaError: true,\n message: \"Quota d'API Google Calendar dépassé. Réessayez plus tard.\",\n originalError: error.message\n };\n}\n\n// Erreur générique\nreturn {\n error: true,\n message: \"Erreur lors de la récupération des événements: \" + error.message,\n originalError: error.message\n};" }, "id": "analyze-error", "name": "Analyse de l'erreur", "type": "n8n-nodes-base.code", "typeVersion": 1, "position": [ 850, 200 ] }, { "parameters": { "functionCode": "// Cette fonction simule le rafraîchissement du token OAuth\n// Dans un environnement de production, implémentez la logique réelle de rafraîchissement\n\n// Journaliser la tentative de rafraîchissement\nconsole.log('Tentative de rafraîchissement du token OAuth pour Google Calendar');\n\n// Simuler un délai pour le rafraîchissement\nawait new Promise(resolve => setTimeout(resolve, 1000));\n\n// Retourner un statut de rafraîchissement\nreturn {\n tokenRefreshed: true,\n message: \"Token OAuth rafraîchi avec succès. Réessayez l'opération.\"\n};" }, "id": "refresh-oauth-token", "name": "Rafraîchissement du token OAuth", "type": "n8n-nodes-base.function", "typeVersion": 1, "position": [ 1050, 200 ] }, { "parameters": { "jsCode": "// Filtrer et analyser les événements modifiés ou annulés\nconst events = $input.item.json;\nconst modifiedEvents = [];\nconst cancelledEvents = [];\n\n// Vérifier chaque événement\nif (Array.isArray(events)) {\n events.forEach(event => {\n // Vérifier si l'événement a été récemment modifié\n const updatedTime = new Date(event.updated);\n const now = new Date();\n const minutesSinceUpdate = Math.floor((now - updatedTime) / (1000 * 60));\n \n // Ne traiter que les événements modifiés dans les 30 dernières minutes\n if (minutesSinceUpdate <= 30) {\n // Vérifier si l'événement est annulé\n if (event.status === 'cancelled') {\n cancelledEvents.push({\n ...event,\n eventType: 'cancelled',\n updateTime: updatedTime.toISOString()\n });\n } else {\n // Vérifier si l'événement a des propriétés étendues pour suivre les modifications\n const extendedProps = event.extendedProperties?.private || {};\n const lastNotifiedUpdate = extendedProps.lastNotifiedUpdate;\n \n // Si pas de notification précédente ou mise à jour plus récente\n if (!lastNotifiedUpdate || new Date(lastNotifiedUpdate) < updatedTime) {\n modifiedEvents.push({\n ...event,\n eventType: 'modified',\n updateTime: updatedTime.toISOString(),\n previousNotifiedUpdate: lastNotifiedUpdate || null\n });\n }\n }\n }\n });\n}\n\nreturn { modifiedEvents, cancelledEvents };" }, "id": "filter-modified-events", "name": "Filtrer les événements modifiés/annulés", "type": "n8n-nodes-base.code", "typeVersion": 1, "position": [ 850, 400 ] }, { "parameters": { "conditions": { "number": [ { "value1": "={{ $json.modifiedEvents.length + $json.cancelledEvents.length }}", "operation": "larger", "value2": 0 } ] } }, "id": "check-has-changes", "name": "A des changements?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 1050, 400 ] }, { "parameters": { "jsCode": "// Combiner les événements modifiés et annulés\nconst { modifiedEvents, cancelledEvents } = $input.item.json;\nconst allChangedEvents = [...modifiedEvents, ...cancelledEvents];\n\n// Ajouter des informations supplémentaires pour chaque événement\nconst processedEvents = allChangedEvents.map(event => {\n // Récupérer les destinataires\n const attendees = event.attendees || [];\n const emails = attendees.map(attendee => attendee.email).filter(Boolean);\n \n // Ajouter l'organisateur si présent\n if (event.organizer && event.organizer.email) {\n emails.push(event.organizer.email);\n }\n \n // Déduplicater les emails\n const uniqueEmails = [...new Set(emails)];\n \n // Formater les dates si disponibles\n let formattedStart = 'Non spécifié';\n let formattedEnd = 'Non spécifié';\n \n if (event.start && event.start.dateTime) {\n const startDate = new Date(event.start.dateTime);\n formattedStart = startDate.toLocaleString('fr-FR', {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit'\n });\n }\n \n if (event.end && event.end.dateTime) {\n const endDate = new Date(event.end.dateTime);\n formattedEnd = endDate.toLocaleString('fr-FR', {\n hour: '2-digit',\n minute: '2-digit'\n });\n }\n \n return {\n ...event,\n recipients: uniqueEmails,\n formattedStart,\n formattedEnd\n };\n});\n\nreturn { processedEvents };" }, "id": "prepare-events-data", "name": "Préparation des données d'événements", "type": "n8n-nodes-base.code", "typeVersion": 1, "position": [ 1250, 400 ] }, { "parameters": { "operation": "split", "mode": "autodetect" }, "id": "split-events", "name": "Séparer les événements", "type": "n8n-nodes-base.splitInBatches", "typeVersion": 2, "position": [ 1450, 400 ] }, { "parameters": { "conditions": { "string": [ { "value1": "={{ $json.eventType }}", "value2": "cancelled" } ] } }, "id": "check-event-type", "name": "Type d'événement?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 1650, 400 ] }, { "parameters": { "jsCode": "// Préparer la notification d'annulation\nconst event = $input.item.json;\n\n// Créer le sujet et le message\nconst subject = `❌ Annulation: ${event.summary}`;\nconst message = `L'événement \"${event.summary}\" prévu le ${event.formattedStart} a été annulé.\n\nDétails de l'événement annulé:\nOrganisateur: ${event.organizer?.displayName || event.organizer?.email || 'Non spécifié'}\nLieu: ${event.location || 'Non spécifié'}`;\n\n// Préparer le message HTML\nconst htmlMessage = `\n<div style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e0e0e0; border-radius: 5px;\">\n <div style=\"background-color: #db4437; padding: 15px; border-radius: 5px 5px 0 0;\">\n <h2 style=\"color: white; margin: 0;\">❌ Annulation d'événement</h2>\n </div>\n <div style=\"padding: 20px;\">\n <p>Bonjour,</p>\n <p>Nous vous informons que l'événement suivant a été <strong>annulé</strong> :</p>\n \n <div style=\"background-color: #f9f9f9; padding: 15px; border-radius: 5px; margin: 20px 0;\">\n <p><strong>Événement :</strong> ${event.summary}</p>\n <p><strong>Date et heure :</strong> ${event.formattedStart}</p>\n <p><strong>Organisateur :</strong> ${event.organizer?.displayName || event.organizer?.email || 'Non spécifié'}</p>\n <p><strong>Lieu :</strong> ${event.location || 'Non spécifié'}</p>\n </div>\n \n <p style=\"color: #666; font-size: 12px; margin-top: 30px; border-top: 1px solid #eee; padding-top: 15px;\">\n Ce message est envoyé automatiquement, merci de ne pas y répondre.\n </p>\n </div>\n</div>`;\n\nreturn {\n ...event,\n notificationSubject: subject,\n notificationMessage: message,\n htmlMessage\n};" }, "id": "prepare-cancellation-notification", "name": "Préparation notification d'annulation", "type": "n8n-nodes-base.code", "typeVersion": 1, "position": [ 1850, 300 ] }, { "parameters": { "jsCode": "// Préparer la notification de modification\nconst event = $input.item.json;\n\n// Analyser ce qui a été modifié (dans un vrai scénario, il faudrait comparer avec les données précédentes)\n// Ici, on simule en détectant simplement qu'il y a eu une modification\nconst changes = [];\n\n// Vérifier si la date/heure a été modifiée\nif (event.start && event.start.dateTime) {\n changes.push('Date/heure');\n}\n\n// Vérifier si le lieu a été modifié\nif (event.location) {\n changes.push('Lieu');\n}\n\n// Vérifier si la description a été modifiée\nif (event.description) {\n changes.push('Description');\n}\n\n// Vérifier si les participants ont été modifiés\nif (event.attendees && event.attendees.length > 0) {\n changes.push('Participants');\n}\n\n// S'il n'y a pas de changements détectés, ajouter un message générique\nif (changes.length === 0) {\n changes.push('Détails de l\\'événement');\n}\n\n// Créer le sujet et le message\nconst subject = `🔄 Modification: ${event.summary}`;\nconst message = `L'événement \"${event.summary}\" prévu le ${event.formattedStart} a été modifié.\n\nChangements apportés: ${changes.join(', ')}\n\nDétails mis à jour:\nDate et heure: ${event.formattedStart} - ${event.formattedEnd}\nLieu: ${event.location || 'Non spécifié'}\nDescription: ${event.description || 'Aucune description'}`;\n\n// Préparer le message HTML\nconst htmlMessage = `\n<div style=\"font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e0e0e0; border-radius: 5px;\">\n <div style=\"background-color: #4285f4; padding: 15px; border-radius: 5px 5px 0 0;\">\n <h2 style=\"color: white; margin: 0;\">🔄 Modification d'événement</h2>\n </div>\n <div style=\"padding: 20px;\">\n <p>Bonjour,</p>\n <p>Nous vous informons que l'événement suivant a été <strong>modifié</strong> :</p>\n \n <div style=\"background-color: #f9f9f9; padding: 15px; border-radius: 5px; margin: 20px 0;\">\n <p><strong>Événement :</strong> ${event.summary}</p>\n <p><strong>Changements apportés :</strong> ${changes.join(', ')}</p>\n </div>\n \n <div style=\"background-color: #e8f0fe; padding: 15px; border-radius: 5px; margin: 20px 0;\">\n <h3 style=\"margin-top: 0;\">Détails mis à jour</h3>\n <p><strong>Date et heure :</strong> ${event.formattedStart} - ${event.formattedEnd}</p>\n <p><strong>Lieu :</strong> ${event.location || 'Non spécifié'}</p>\n <p><strong>Description :</strong> ${event.description || 'Aucune description'}</p>\n </div>\n \n <div style=\"text-align: center; margin: 30px 0;\">\n <a href=\"${event.htmlLink || '#'}\" style=\"background-color: #4285f4; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; font-weight: bold;\">Voir l'événement</a>\n </div>\n \n <p style=\"color: #666; font-size: 12px; margin-top: 30px; border-top: 1px solid #eee; padding-top: 15px;\">\n Ce message est envoyé automatiquement, merci de ne pas y répondre.\n </p>\n </div>\n</div>`;\n\nreturn {\n ...event,\n notificationSubject: subject,\n notificationMessage: message,\n htmlMessage,\n changes\n};" }, "id": "prepare-modification-notification", "name": "Préparation notification de modification", "type": "n8n-nodes-base.code", "typeVersion": 1, "position": [ 1850, 500 ] }, { "parameters": { "operation": "send", "sender": "={{ $json.sender || 'notifications@example.com' }}", "sendTo": "={{ $json.recipients.join(',') }}", "subject": "={{ $json.notificationSubject }}", "text": "={{ $json.notificationMessage }}", "options": { "attachments": "", "cc": "", "bcc": "", "htmlMessage": "={{ $json.htmlMessage }}" } }, "id": "send-email", "name": "Envoi d'email", "type": "n8n-nodes-base.emailSend", "typeVersion": 2, "position": [ 2050, 400 ], "credentials": { "smtp": { "id": "1", "name": "SMTP Account" } }, "continueOnFail": true }, { "parameters": { "httpMethod": "POST", "url": "=https://api.example.com/webhooks/calendar-change", "options": {}, "bodyParametersUi": { "parameter": [ { "name": "eventId", "value": "={{ $json.id }}" }, { "name": "eventSummary", "value": "={{ $json.summary }}" }, { "name": "changeType", "value": "={{ $json.eventType }}" }, { "name": "changes", "value": "={{ $json.changes ? JSON.stringify($json.changes) : '' }}" }, { "name": "updateTime", "value": "={{ $json.updateTime }}" } ] } }, "id": "send-webhook", "name": "Envoi de webhook", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ 2250, 400 ], "continueOnFail": true }, { "parameters": { "resource": "event", "operation": "update", "calendarId": "={{ $json.calendarId || 'primary' }}", "eventId": "={{ $json.id }}", "options": { "extendedProperties": "={{ {\n private: {\n ...($json.extendedProperties?.private || {}),\n lastNotifiedUpdate: $json.updateTime\n }\n} }}" } }, "id": "mark-notification-sent", "name": "Marquer la notification comme envoyée", "type": "n8n-nodes-base.googleCalendar", "typeVersion": 2, "position": [ 2450, 400 ], "credentials": { "googleCalendarOAuth2Api": { "id": "1", "name": "Google Calendar OAuth2 API" } }, "continueOnFail": true } ], "connections": { "schedule-trigger": { "main": [ [ { "node": "get-updated-events", "type": "main", "index": 0 } ] ] }, "get-updated-events": { "main": [ [ { "node": "check-events-error", "type": "main", "index": 0 } ] ] }, "check-events-error": { "main": [ [ { "node": "analyze-error", "type": "main", "index": 0 } ], [ { "node": "filter-modified-events", "type": "main", "index": 0 } ] ] }, "analyze-error": { "main": [ [ { "node": "refresh-oauth-token", "type": "main", "index": 0 } ] ] }, "filter-modified-events": { "main": [ [ { "node": "check-has-changes", "type": "main", "index": 0 } ] ] }, "check-has-changes": { "main": [ [], [ { "node": "prepare-events-data", "type": "main", "index": 0 } ] ] }, "prepare-events-data": { "main": [ [ { "node": "split-events", "type": "main", "index": 0 } ] ] }, "split-events": { "main": [ [ { "node": "check-event-type", "type": "main", "index": 0 } ] ] }, "check-event-type": { "main": [ [ { "node": "prepare-cancellation-notification", "type": "main", "index": 0 } ], [ { "node": "prepare-modification-notification", "type": "main", "index": 0 } ] ] }, "prepare-cancellation-notification": { "main": [ [ { "node": "send-email", "type": "main", "index": 0 } ] ] }, "prepare-modification-notification": { "main": [ [ { "node": "send-email", "type": "main", "index": 0 } ] ] }, "send-email": { "main": [ [ { "node": "send-webhook", "type": "main", "index": 0 } ] ] }, "send-webhook": { "main": [ [ { "node": "mark-notification-sent", "type": "main", "index": 0 } ] ] } }, "active": false, "settings": { "executionOrder": "v1", "saveManualExecutions": true, "callerPolicy": "workflowsFromSameOwner", "errorWorkflow": "", "saveDataErrorExecution": "all", "saveDataSuccessExecution": "all", "saveExecutionProgress": true }, "tags": ["template", "calendar", "modifications"], "pinData": {}, "versionId": "1", "meta": { "templateCreatedBy": "MCP n8n Server", "templateDescription": "Workflow pour gérer les annulations et modifications d'événements Google Calendar.", "templateName": "Gestion des annulations/modifications Google Calendar", "templateCategory": "calendar", "templateVersion": "1.0.0", "templateDocumentation": "Ce template permet de détecter et de notifier les participants lorsqu'un événement Google Calendar est modifié ou annulé. Il envoie des notifications par email et webhook, et marque les événements pour éviter les notifications en double." } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lowprofix/n8n-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server