Skip to main content
Glama
event-notifications.json20.7 kB
{ "name": "Template - Gestion des notifications Google Calendar", "nodes": [ { "parameters": { "rule": { "interval": [ { "field": "minutes", "minutesInterval": 15 } ] } }, "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.isoString }}", "timeMax": "={{ $now.plus(1, 'day').isoString }}", "singleEvents": true, "orderBy": "startTime" } }, "id": "get-upcoming-events", "name": "Récupération des événements à venir", "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 les événements qui nécessitent des notifications\nconst now = new Date();\nconst events = $input.item.json;\nconst notificationEvents = [];\n\n// Configuration des délais de notification (en minutes)\nconst notificationTimes = [15, 60, 1440]; // 15 min, 1 heure, 24 heures\n\n// Vérifier chaque événement\nif (Array.isArray(events)) {\n events.forEach(event => {\n // Ignorer les événements sans date/heure de début\n if (!event.start || !event.start.dateTime) return;\n \n // Calculer le temps jusqu'à l'événement en minutes\n const eventStart = new Date(event.start.dateTime);\n const minutesUntilEvent = Math.floor((eventStart.getTime() - now.getTime()) / (1000 * 60));\n \n // Vérifier si l'événement est dans un des délais de notification\n for (const notificationTime of notificationTimes) {\n // Tolérance de 5 minutes pour le déclenchement\n if (Math.abs(minutesUntilEvent - notificationTime) <= 5) {\n // Vérifier si la notification a déjà été envoyée (via les propriétés étendues)\n const notificationKey = `notification_${notificationTime}`;\n const extendedProps = event.extendedProperties?.private || {};\n \n if (!extendedProps[notificationKey]) {\n notificationEvents.push({\n ...event,\n notificationType: notificationTime === 15 ? 'imminent' : \n notificationTime === 60 ? 'upcoming' : 'day_before',\n minutesUntilEvent,\n notificationTime,\n notificationKey\n });\n }\n }\n }\n });\n}\n\nreturn { notificationEvents };" }, "id": "filter-notification-events", "name": "Filtrer les événements à notifier", "type": "n8n-nodes-base.code", "typeVersion": 1, "position": [ 850, 400 ] }, { "parameters": { "conditions": { "number": [ { "value1": "={{ $json.notificationEvents.length }}", "operation": "larger", "value2": 0 } ] } }, "id": "check-has-notifications", "name": "A des notifications?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 1050, 400 ] }, { "parameters": { "operation": "split", "mode": "autodetect" }, "id": "split-events", "name": "Séparer les événements", "type": "n8n-nodes-base.splitInBatches", "typeVersion": 2, "position": [ 1250, 500 ] }, { "parameters": { "jsCode": "// Préparer les données pour la notification\nconst event = $input.item.json;\n\n// Formater les dates\nconst startDate = new Date(event.start.dateTime);\nconst 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// Déterminer le type de message en fonction du délai\nlet subject, message;\n\nswitch (event.notificationType) {\n case 'imminent':\n subject = `🔔 Rappel imminent: ${event.summary}`;\n message = `Votre événement \"${event.summary}\" commence dans 15 minutes (${formattedStart}).\n\nDétails: ${event.description || 'Aucune description'}\nLieu: ${event.location || 'Non spécifié'}`;\n break;\n case 'upcoming':\n subject = `🔔 Rappel: ${event.summary}`;\n message = `Votre événement \"${event.summary}\" commence dans 1 heure (${formattedStart}).\n\nDétails: ${event.description || 'Aucune description'}\nLieu: ${event.location || 'Non spécifié'}`;\n break;\n case 'day_before':\n subject = `📅 Rappel pour demain: ${event.summary}`;\n message = `Rappel: Vous avez l'événement \"${event.summary}\" prévu demain à ${startDate.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}.\n\nDétails: ${event.description || 'Aucune description'}\nLieu: ${event.location || 'Non spécifié'}`;\n break;\n default:\n subject = `🔔 Rappel: ${event.summary}`;\n message = `Rappel pour votre événement \"${event.summary}\" prévu le ${formattedStart}.\n\nDétails: ${event.description || 'Aucune description'}\nLieu: ${event.location || 'Non spécifié'}`;\n}\n\n// Récupérer les destinataires\nconst attendees = event.attendees || [];\nconst emails = attendees.map(attendee => attendee.email).filter(Boolean);\n\n// Ajouter l'organisateur si présent\nif (event.organizer && event.organizer.email) {\n emails.push(event.organizer.email);\n}\n\n// Déduplicater les emails\nconst uniqueEmails = [...new Set(emails)];\n\nreturn {\n ...event,\n notificationSubject: subject,\n notificationMessage: message,\n recipients: uniqueEmails,\n eventLink: event.htmlLink || ''\n};" }, "id": "prepare-notification", "name": "Préparation de la notification", "type": "n8n-nodes-base.code", "typeVersion": 1, "position": [ 1450, 500 ] }, { "parameters": { "conditions": { "number": [ { "value1": "={{ $json.recipients.length }}", "operation": "larger", "value2": 0 } ] } }, "id": "check-has-recipients", "name": "A des destinataires?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 1650, 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": "=<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;\">{{ $json.notificationSubject }}</h2>\n </div>\n <div style=\"padding: 20px;\">\n <p>Bonjour,</p>\n <p>{{ $json.notificationMessage.split('\\n\\n')[0] }}</p>\n \n <div style=\"background-color: #f9f9f9; padding: 15px; border-radius: 5px; margin: 20px 0;\">\n <p><strong>Détails:</strong> {{ $json.description || 'Aucune description' }}</p>\n <p><strong>Lieu:</strong> {{ $json.location || 'Non spécifié' }}</p>\n <p><strong>Date et heure:</strong> {{ new Date($json.start.dateTime).toLocaleString('fr-FR') }}</p>\n </div>\n \n <div style=\"text-align: center; margin: 30px 0;\">\n <a href=\"{{ $json.eventLink }}\" 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>" } }, "id": "send-email", "name": "Envoi d'email", "type": "n8n-nodes-base.emailSend", "typeVersion": 2, "position": [ 1850, 400 ], "credentials": { "smtp": { "id": "1", "name": "SMTP Account" } }, "continueOnFail": true }, { "parameters": { "jsCode": "// Préparer les données pour la notification SMS\nconst event = $input.item.json;\n\n// Formater les dates de manière concise\nconst startDate = new Date(event.start.dateTime);\nconst formattedTime = startDate.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' });\n\n// Créer un message SMS court\nlet message;\n\nswitch (event.notificationType) {\n case 'imminent':\n message = `Rappel: \"${event.summary}\" commence dans 15min (${formattedTime}). ${event.location ? 'Lieu: ' + event.location : ''}`;\n break;\n case 'upcoming':\n message = `Rappel: \"${event.summary}\" commence dans 1h (${formattedTime}). ${event.location ? 'Lieu: ' + event.location : ''}`;\n break;\n case 'day_before':\n message = `Rappel pour demain: \"${event.summary}\" à ${formattedTime}. ${event.location ? 'Lieu: ' + event.location : ''}`;\n break;\n default:\n message = `Rappel: \"${event.summary}\" le ${startDate.toLocaleDateString('fr-FR')} à ${formattedTime}`;\n}\n\n// Limiter la longueur du message SMS à 160 caractères\nif (message.length > 157) {\n message = message.substring(0, 157) + '...';\n}\n\n// Récupérer les numéros de téléphone (simulé - dans un cas réel, il faudrait les récupérer d'une base de données)\n// Ici, on simule en utilisant des propriétés étendues qui contiendraient les numéros\nconst phoneNumbers = [];\nconst extendedProps = event.extendedProperties?.private || {};\n\n// Simuler la récupération des numéros de téléphone\nif (extendedProps.organizerPhone) {\n phoneNumbers.push(extendedProps.organizerPhone);\n}\n\n// Ajouter les numéros des participants (simulé)\nif (extendedProps.attendeePhones) {\n try {\n const attendeePhones = JSON.parse(extendedProps.attendeePhones);\n phoneNumbers.push(...attendeePhones);\n } catch (e) {\n // Ignorer les erreurs de parsing\n }\n}\n\nreturn {\n ...event,\n smsMessage: message,\n phoneNumbers: phoneNumbers,\n hasSmsRecipients: phoneNumbers.length > 0\n};" }, "id": "prepare-sms", "name": "Préparation du SMS", "type": "n8n-nodes-base.code", "typeVersion": 1, "position": [ 1850, 600 ] }, { "parameters": { "conditions": { "boolean": [ { "value1": "={{ $json.hasSmsRecipients }}", "value2": true } ] } }, "id": "check-has-sms-recipients", "name": "A des destinataires SMS?", "type": "n8n-nodes-base.if", "typeVersion": 1, "position": [ 2050, 600 ] }, { "parameters": { "httpMethod": "POST", "url": "=https://api.example.com/sms/send", "options": {}, "bodyParametersUi": { "parameter": [ { "name": "to", "value": "={{ $json.phoneNumbers[0] }}" }, { "name": "message", "value": "={{ $json.smsMessage }}" } ] } }, "id": "send-sms", "name": "Envoi de SMS", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ 2250, 500 ], "continueOnFail": true }, { "parameters": { "resource": "event", "operation": "update", "calendarId": "={{ $json.calendarId || 'primary' }}", "eventId": "={{ $json.id }}", "options": { "extendedProperties": "={{ {\n private: {\n ...($json.extendedProperties?.private || {}),\n [$json.notificationKey]: new Date().toISOString()\n }\n} }}" } }, "id": "mark-notification-sent", "name": "Marquer la notification comme envoyée", "type": "n8n-nodes-base.googleCalendar", "typeVersion": 2, "position": [ 2450, 600 ], "credentials": { "googleCalendarOAuth2Api": { "id": "1", "name": "Google Calendar OAuth2 API" } }, "continueOnFail": true }, { "parameters": { "httpMethod": "POST", "url": "=https://api.example.com/webhooks/calendar-notification", "options": {}, "bodyParametersUi": { "parameter": [ { "name": "eventId", "value": "={{ $json.id }}" }, { "name": "eventSummary", "value": "={{ $json.summary }}" }, { "name": "notificationType", "value": "={{ $json.notificationType }}" }, { "name": "startTime", "value": "={{ $json.start.dateTime }}" }, { "name": "endTime", "value": "={{ $json.end.dateTime }}" } ] } }, "id": "send-webhook", "name": "Envoi de webhook", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [ 2250, 700 ], "continueOnFail": true } ], "connections": { "schedule-trigger": { "main": [ [ { "node": "get-upcoming-events", "type": "main", "index": 0 } ] ] }, "get-upcoming-events": { "main": [ [ { "node": "check-events-error", "type": "main", "index": 0 } ] ] }, "check-events-error": { "main": [ [ { "node": "analyze-error", "type": "main", "index": 0 } ], [ { "node": "filter-notification-events", "type": "main", "index": 0 } ] ] }, "analyze-error": { "main": [ [ { "node": "refresh-oauth-token", "type": "main", "index": 0 } ] ] }, "filter-notification-events": { "main": [ [ { "node": "check-has-notifications", "type": "main", "index": 0 } ] ] }, "check-has-notifications": { "main": [ [], [ { "node": "split-events", "type": "main", "index": 0 } ] ] }, "split-events": { "main": [ [ { "node": "prepare-notification", "type": "main", "index": 0 } ] ] }, "prepare-notification": { "main": [ [ { "node": "check-has-recipients", "type": "main", "index": 0 } ] ] }, "check-has-recipients": { "main": [ [ { "node": "send-email", "type": "main", "index": 0 } ], [ { "node": "prepare-sms", "type": "main", "index": 0 } ] ] }, "send-email": { "main": [ [ { "node": "prepare-sms", "type": "main", "index": 0 } ] ] }, "prepare-sms": { "main": [ [ { "node": "check-has-sms-recipients", "type": "main", "index": 0 } ] ] }, "check-has-sms-recipients": { "main": [ [ { "node": "send-sms", "type": "main", "index": 0 } ], [ { "node": "send-webhook", "type": "main", "index": 0 } ] ] }, "send-sms": { "main": [ [ { "node": "mark-notification-sent", "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", "notifications"], "pinData": {}, "versionId": "1", "meta": { "templateCreatedBy": "MCP n8n Server", "templateDescription": "Workflow pour envoyer des notifications avant et après les événements Google Calendar.", "templateName": "Gestion des notifications Google Calendar", "templateCategory": "calendar", "templateVersion": "1.0.0", "templateDocumentation": "Ce template permet d'envoyer des notifications par email, SMS et webhook pour les événements Google Calendar à venir. Il gère différents délais de notification (15 minutes, 1 heure, 24 heures) et marque les notifications comme envoyées pour éviter les doublons." } }

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