Skip to content

Instantly share code, notes, and snippets.

@Varshithvhegde
Last active August 27, 2025 12:39
Show Gist options
  • Select an option

  • Save Varshithvhegde/a40820f639034bac181e93146a9a21a1 to your computer and use it in GitHub Desktop.

Select an option

Save Varshithvhegde/a40820f639034bac181e93146a9a21a1 to your computer and use it in GitHub Desktop.
{
"name": "NearBy Event Notifier",
"nodes": [
{
"parameters": {
"zone": {
"__rl": true,
"mode": "list",
"value": "web_unlocker1"
},
"country": {
"__rl": true,
"mode": "list",
"value": "us"
},
"url": "https://www.eventbrite.com/d/india--bangalore/technical/",
"requestOptions": {}
},
"type": "@brightdata/n8n-nodes-brightdata.brightData",
"typeVersion": 1,
"position": [
-976,
144
],
"id": "f60d93a8-46fe-495d-841f-6c96246aa325",
"name": "Access and extract data from a specific URL",
"credentials": {
"brightdataApi": {
"id": "NcHygh2TuGIohtq8",
"name": "BrightData account"
}
}
},
{
"parameters": {
"operation": "extractHtmlContent",
"extractionValues": {
"values": [
{
"key": "event_cards",
"cssSelector": "section.event-card-details",
"returnArray": true
}
]
},
"options": {}
},
"id": "5184ec17-8680-4acd-978a-2af807932a1d",
"name": "Extract Event Cards",
"type": "n8n-nodes-base.html",
"position": [
-528,
144
],
"typeVersion": 1.2
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "1edf66e5-2535-4c5c-a7e3-67425668816e",
"name": "event_title",
"value": "={{ $json.event_title }}",
"type": "string"
},
{
"id": "b374026a-e71e-48cf-b497-fdb5bb587e42",
"name": "event_date",
"value": "={{ $json.event_date }}",
"type": "string"
},
{
"id": "ebdc0473-b7d9-4844-93af-3abc01ecf910",
"name": "event_url",
"value": "={{ $json.event_url }}",
"type": "string"
},
{
"id": "e88228a4-f4d1-418f-aaf2-4ca6270918a4",
"name": "event_price",
"value": "={{ $json.event_price }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
-80,
144
],
"id": "77dd5078-d8d8-485f-8b90-0cfe5951f1ad",
"name": "Edit Fields"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "8b4dc0aa-3170-47d5-8de3-2cce57d8b25e",
"name": "postdata",
"value": "=Event Title: {{ $json.event_title }}\nUrl: {{ $json.event_url }}\nDate: {{ $json.event_date }}\nPrice: {{ $json.event_price }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
144,
144
],
"id": "f6884d56-5a7b-42c9-a6a2-5b4e21e825a5",
"name": "Reduce objects to 1"
},
{
"parameters": {
"promptType": "define",
"text": "={{ $json.postdata.join('\\n\\n') }}",
"options": {
"systemMessage": "Return only RAW HTML\n\nYou are an email editor that converts raw event cards into a Morning Brew–style HTML newsletter.\n\n## Your Tasks\n\n### Parse Input\nInput arrives under key postdata (array of event card strings).\n\nEach string may contain:\n- Event Title: …\n- Url: …\n- Date: …\n- Price: …\n\n### Deduplicate Events\n- If two events share the same URL, keep only one.\n- If two events have very similar titles (ignoring case and bracketed extras), keep the one with a more specific date or a non-empty price.\n\n### Normalize Fields\n- **Price**: If missing, output `—`.\n- **Date**: Keep exactly as provided (don't convert timezones).\n- Trim whitespace, collapse multiple spaces.\n\n### Sort & Bucket Events\nIf you can parse a concrete date → sort chronologically.\n\nBucket into sections:\n- **Today**\n- **Tomorrow**\n- **This Week** (next 7 days, excluding Today/Tomorrow)\n- **Upcoming** (everything else, including relative dates you cannot parse)\n\nIf you cannot parse a date, put the event in Upcoming.\n\n### Add Quips (Morning Brew Tone)\nFor each event, include a one-line witty, concise quip under it.\nKeep quips neutral, clever, but never overpromise.\n\n### Generate Newsletter Content\n- **Newsletter Date**: Use today's date in format \"Wednesday, August 27, 2025\"\n- **Intro Line**: Create a short 1–2 sentence intro that's engaging and matches Morning Brew's conversational tone\n\n### Output Format\nReturn only one valid HTML document (no markdown, no JSON, no code fences).\n\nUse the HTML email template provided below.\nInline CSS only (email-safe).\n\n**CRITICAL**: Replace ALL placeholders with actual content:\n- Replace `{{newsletter_date}}` → today's formatted date\n- Replace `{{intro_line}}` → your generated intro\n- For each section (Today / Tomorrow / This Week / Upcoming), only include if it has events\n- For each event: replace `{{event_title}}`, `{{event_url}}`, `{{event_datetime}}`, `{{event_price}}`, `{{event_quip}}` with actual data\n\n## HTML Template\n```html\n<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n <title>Daily Events Brew</title>\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n <style>\n body { margin:0; padding:0; background:#f6f7fb; }\n .wrap { width:100%; background:#f6f7fb; }\n .container { max-width:640px; margin:0 auto; background:#ffffff; }\n .pad { padding:24px; }\n .header { font-family: Arial, sans-serif; font-size:26px; font-weight:700; line-height:1.2; }\n .sub { font-family: Arial, sans-serif; color:#667085; font-size:14px; margin-top:4px; }\n .intro { font-family: Arial, sans-serif; font-size:16px; line-height:1.5; color:#101828; margin:16px 0 8px; }\n .section { border-top:1px solid #eee; padding-top:16px; margin-top:16px; }\n .section-title { font-family: Arial, sans-serif; font-size:14px; font-weight:700; color:#6941C6; text-transform:uppercase; letter-spacing:0.04em; margin-bottom:8px; }\n .card { border:1px solid #eef0f4; border-radius:10px; padding:16px; margin-bottom:12px; }\n .title a { font-family: Arial, sans-serif; font-size:16px; font-weight:700; color:#0B5FFF; text-decoration:none; }\n .meta { font-family: Arial, sans-serif; font-size:13px; color:#475467; margin-top:6px; }\n .price { display:inline-block; font-family: Arial, sans-serif; font-size:12px; color:#027A48; background:#ECFDF3; border:1px solid #ABEFC6; border-radius:999px; padding:2px 8px; margin-left:8px; }\n .quip { font-family: Arial, sans-serif; font-size:13px; color:#344054; margin-top:8px; }\n .footer { font-family: Arial, sans-serif; font-size:12px; color:#98A2B3; text-align:center; padding:16px; }\n @media (prefers-color-scheme: dark) {\n body, .wrap { background:#0b0c10; }\n .container { background:#0f1217; }\n .header, .intro, .title a { color:#eaeef6; }\n .sub, .meta, .quip, .footer { color:#bcc3cf; }\n .section { border-top-color:#1f2630; }\n .card { border-color:#1f2630; }\n }\n </style>\n </head>\n <body>\n <table role=\"presentation\" class=\"wrap\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\">\n <tr>\n <td align=\"center\">\n <table role=\"presentation\" class=\"container\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\">\n <tr><td class=\"pad\">\n <div class=\"header\">Daily Events Brew</div>\n <div class=\"sub\">{{newsletter_date}}</div>\n <div class=\"intro\">{{intro_line}}</div>\n\n <!-- Today Section - Only include if events exist -->\n <div class=\"section\">\n <div class=\"section-title\">Today</div>\n <div class=\"card\">\n <div class=\"title\"><a href=\"{{event_url}}\" target=\"_blank\" rel=\"noopener\">{{event_title}}</a></div>\n <div class=\"meta\">{{event_datetime}}<span class=\"price\">{{event_price}}</span></div>\n <div class=\"quip\">{{event_quip}}</div>\n </div>\n </div>\n\n <!-- Tomorrow Section - Only include if events exist -->\n <div class=\"section\">\n <div class=\"section-title\">Tomorrow</div>\n <div class=\"card\">\n <div class=\"title\"><a href=\"{{event_url}}\" target=\"_blank\" rel=\"noopener\">{{event_title}}</a></div>\n <div class=\"meta\">{{event_datetime}}<span class=\"price\">{{event_price}}</span></div>\n <div class=\"quip\">{{event_quip}}</div>\n </div>\n </div>\n\n <!-- This Week Section - Only include if events exist -->\n <div class=\"section\">\n <div class=\"section-title\">This Week</div>\n <div class=\"card\">\n <div class=\"title\"><a href=\"{{event_url}}\" target=\"_blank\" rel=\"noopener\">{{event_title}}</a></div>\n <div class=\"meta\">{{event_datetime}}<span class=\"price\">{{event_price}}</span></div>\n <div class=\"quip\">{{event_quip}}</div>\n </div>\n </div>\n\n <!-- Upcoming Section - Only include if events exist -->\n <div class=\"section\">\n <div class=\"section-title\">Upcoming</div>\n <div class=\"card\">\n <div class=\"title\"><a href=\"{{event_url}}\" target=\"_blank\" rel=\"noopener\">{{event_title}}</a></div>\n <div class=\"meta\">{{event_datetime}}<span class=\"price\">{{event_price}}</span></div>\n <div class=\"quip\">{{event_quip}}</div>\n </div>\n </div>\n\n <div class=\"footer\">\n You're receiving this because you opted into daily event updates. Want fewer pings? Hit digest in your settings.\n </div>\n </td></tr>\n </table>\n </td>\n </tr>\n </table>\n </body>\n</html>\n```\n\n## Implementation Notes\n- Replace ALL `{{placeholders}}` with actual content\n- Only include sections that have events\n- Duplicate card HTML for multiple events in each section\n- Generate witty, Morning Brew-style quips for each event\n- Keep prices as `—` if missing\n- Sort events chronologically within each section"
}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2.2,
"position": [
592,
144
],
"id": "b408117b-d551-4336-acb5-d75d6f7f1ddf",
"name": "AI Agent"
},
{
"parameters": {
"sendTo": "[email protected]",
"subject": "⚡ Tech Events Digest - What's Happening Today",
"message": "={{ $json.output }}",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
1168,
240
],
"id": "a2f87dcf-7642-491b-b9c7-86a19e9b69fb",
"name": "Send a message",
"webhookId": "f51572f8-b029-4f34-896e-2344a07e3cb7",
"credentials": {
"gmailOAuth2": {
"id": "VlTTYiRPeMEYKVIk",
"name": "Gmail account"
}
}
},
{
"parameters": {
"jsCode": "// Get the input data\nconst inputData = $input.first().json;\nconst events = [];\n\n// Check if we have event_cards array\nif (inputData.event_cards && Array.isArray(inputData.event_cards)) {\n \n inputData.event_cards.forEach((cardText, index) => {\n // Skip empty cards\n if (!cardText || cardText.trim().length < 10) return;\n \n const event = {\n event_title: '',\n event_url: '',\n event_date: '',\n event_time: '',\n event_timezone: '',\n event_price: '',\n organizer: '',\n followers: '',\n status: '',\n is_promoted: false,\n raw_text: cardText\n };\n \n const lines = cardText.split('\\n').filter(line => line.trim().length > 0);\n \n lines.forEach(line => {\n const cleanLine = line.trim();\n \n // Extract title (usually in ALL CAPS and longer than 20 chars)\n if (cleanLine === cleanLine.toUpperCase() && cleanLine.length > 20 && !event.event_title) {\n if (!cleanLine.includes('Sales end') && !cleanLine.includes('Almost full') && !cleanLine.includes('Going fast')) {\n event.event_title = cleanLine;\n }\n }\n \n // Extract URL (between square brackets)\n if (cleanLine.startsWith('[https://') && cleanLine.endsWith(']')) {\n event.event_url = cleanLine.slice(1, -1);\n }\n \n // Extract date and time\n if (cleanLine.includes('AM ') || cleanLine.includes('PM ')) {\n event.event_date = cleanLine;\n \n // Try to parse time and timezone\n const timeMatch = cleanLine.match(/(\\d{1,2}:\\d{2}\\s+(AM|PM))\\s+([A-Z]{3,4})/i);\n if (timeMatch) {\n event.event_time = timeMatch[1];\n event.event_timezone = timeMatch[3];\n }\n }\n \n // Extract price\n if (cleanLine.toLowerCase() === 'free') {\n event.event_price = 'Free';\n } else if (cleanLine.startsWith('From $') || cleanLine.startsWith('$')) {\n event.event_price = cleanLine;\n }\n \n // Extract followers\n if (cleanLine.includes('followers')) {\n const followersMatch = cleanLine.match(/(\\d+(?:\\.\\d+)?k?)\\s+followers/i);\n if (followersMatch) {\n event.followers = followersMatch[1];\n }\n }\n \n // Extract status\n if (cleanLine.includes('Sales end soon')) {\n event.status = 'Sales end soon';\n } else if (cleanLine.includes('Almost full')) {\n event.status = 'Almost full';\n } else if (cleanLine.includes('Going fast')) {\n event.status = 'Going fast';\n }\n \n // Check if promoted\n if (cleanLine.toLowerCase().includes('promoted')) {\n event.is_promoted = true;\n }\n });\n \n // Only add events with a title\n if (event.event_title) {\n events.push(event);\n }\n });\n}\n\n// Return each event as a separate item\nreturn events.map(event => ({ json: event }));"
},
"id": "84ab133e-1a9a-4784-946f-20ec9536da9b",
"name": "Parse Events",
"type": "n8n-nodes-base.code",
"position": [
-304,
144
],
"typeVersion": 2
},
{
"parameters": {
"modelName": "models/gemini-2.0-flash-lite",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
"typeVersion": 1,
"position": [
664,
368
],
"id": "83cd2ba3-51e2-42e2-98c2-a05105195e8b",
"name": "Google Gemini Chat Model",
"credentials": {
"googlePalmApi": {
"id": "N5H0dKNiVMCrAj7q",
"name": "Google Gemini(PaLM) Api account"
}
}
},
{
"parameters": {
"content": "# N8N Workflow Documentation: NearBy Event Notifier\n\n## Overview\nThis workflow automatically scrapes technical events from Eventbrite, processes the data using AI, and sends a formatted newsletter to your email every morning.\n\n## Node-by-Node Documentation\n\n### 1. 🕐 **Trigger - Daily Run**\n**Purpose:** Initiates the workflow automatically\n- **Schedule:** Runs daily at 8:00 AM\n- **Note:** \"Daily morning trigger to start event collection and newsletter generation\"\n- **Why this timing:** Ensures fresh events are delivered before the workday starts\n\n### 2. 🌐 **Access and extract data from a specific URL**\n**Purpose:** Fetches raw HTML content from Eventbrite\n- **Service:** BrightData proxy service\n- **Target:** Eventbrite technical events page (Bangalore, India)\n- **Note:** \"Scrapes Eventbrite using BrightData to bypass rate limiting and get fresh event data\"\n- **Important:** Uses proxy to avoid IP blocking from frequent requests\n\n### 3. 📊 **Aggregate**\n**Purpose:** Consolidates scraped data into single item\n- **Function:** Combines multiple data points into one manageable object\n- **Note:** \"Merges all scraped HTML content into single item for processing\"\n- **Why needed:** Simplifies data handling for subsequent parsing steps\n\n### 4. 🏷️ **Extract Event Cards**\n**Purpose:** Extracts individual event information from HTML\n- **Method:** CSS selector `section.event-card-details`\n- **Output:** Raw text for each event card\n- **Note:** \"Uses CSS selector to isolate individual event cards from the page HTML\"\n- **Tip:** If events stop appearing, check if Eventbrite changed their HTML structure\n\n### 5. 🔍 **Parse Events**\n**Purpose:** Converts raw text into structured data\n- **Type:** Code node with custom JavaScript\n- **Extracts:** Title, URL, date, price, location details\n- **Note:** \"Custom parser that extracts structured fields from raw event text using regex and string manipulation\"\n- **Maintenance:** May need updates if Eventbrite changes their text format\n\n### 6. ✏️ **Edit Fields**\n**Purpose:** Standardizes event data structure\n- **Function:** Ensures consistent field names across all events\n- **Fields:** event_title, event_date, event_url, event_price\n- **Note:** \"Standardizes field names and cleans data for consistent processing\"\n- **Benefit:** Makes downstream processing more reliable\n",
"height": 1248,
"width": 880
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-1232,
-1200
],
"id": "332ebcd6-ee1a-4a20-9429-0db5b5da8097",
"name": "Sticky Note"
},
{
"parameters": {
"content": "### 7. 🔄 **Reduce objects to 1**\n**Purpose:** Formats each event for AI processing\n- **Action:** Combines event fields into single `postdata` string\n- **Format:** Structured text format suitable for AI parsing\n- **Note:** \"Converts structured event data into text format optimized for AI agent processing\"\n- **Output:** Each event becomes a formatted text block\n\n### 8. 📚 **Aggregate1**\n**Purpose:** Collects all events into array for AI agent\n- **Function:** Bundles all `postdata` strings into single array\n- **Note:** \"Aggregates all formatted events into single payload for AI newsletter generation\"\n- **Result:** One item containing all events ready for AI processing\n\n### 9. 🤖 **AI Agent**\n**Purpose:** Generates formatted HTML newsletter using AI\n- **Model:** Google Gemini Chat Model\n- **Prompt Style:** Morning Brew-inspired newsletter format\n- **Features:** \n - Deduplication of similar events\n - Time-based categorization (Today, Tomorrow, This Week, Upcoming)\n - Witty commentary and quips\n - Professional HTML formatting\n- **Note:** \"AI agent creates engaging HTML newsletter with event categorization, deduplication, and editorial commentary in Morning Brew style\"\n- **Customization:** Prompt can be modified to change tone, format, or categorization logic\n\n### 10. 📧 **Send a message**\n**Purpose:** Delivers newsletter via email\n- **Service:** Gmail integration\n- **Recipient:** [email protected]\n- **Subject:** \"DailyEvents\"\n- **Format:** HTML email with styled newsletter\n- **Note:** \"Sends AI-generated HTML newsletter via Gmail with professional formatting\"\n- **Settings:** Ensure Gmail integration is properly authenticated",
"height": 864,
"width": 832
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-272,
-1040
],
"id": "b73fb1b8-a0d7-433f-b296-9d3f9afb2d11",
"name": "Sticky Note1"
},
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 8
}
]
}
},
"id": "be1e55e4-8f20-4bf4-bd64-181532af9a5c",
"name": "Trigger - Daily Run",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-1200,
144
],
"typeVersion": 1.2
},
{
"parameters": {
"fieldsToAggregate": {
"fieldToAggregate": [
{
"fieldToAggregate": "postdata"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [
368,
144
],
"id": "a13f1435-b512-4727-b684-f60c11cdbae9",
"name": "Aggregate Final Data"
},
{
"parameters": {
"aggregate": "aggregateAllItemData",
"options": {}
},
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [
-752,
144
],
"id": "cfb536cc-c2f6-49d1-992e-e501eb1e9555",
"name": "Aggregate each html component into single"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "817025e2-3ee2-4b20-b1cd-d175e3f9e603",
"leftValue": "={{ $json.output }}",
"rightValue": "```html",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
944,
144
],
"id": "33023d3b-cdfb-4065-94e6-9deb3c92f6c6",
"name": "If"
},
{
"parameters": {
"jsCode": "const htmlContent = $input.first().json.output;\n\n// Clean the HTML\nconst cleaned_html = htmlContent\n .replace(/^```html\\s*/i, '')\n .replace(/```\\s*$/i, '');\n\nreturn [{ json: { output:cleaned_html } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1168,
48
],
"id": "c19878e1-22cd-47a7-8f3d-cb6875d4aef0",
"name": "Restructure HTML"
},
{
"parameters": {
"sendTo": "[email protected]",
"subject": "⚡ Tech Events Digest - What's Happening Today",
"message": "={{ $json.output }}",
"options": {}
},
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
1392,
48
],
"id": "26521418-1f2f-4e16-bc26-36be8729384e",
"name": "Send a message1",
"webhookId": "f51572f8-b029-4f34-896e-2344a07e3cb7",
"credentials": {
"gmailOAuth2": {
"id": "VlTTYiRPeMEYKVIk",
"name": "Gmail account"
}
}
}
],
"pinData": {},
"connections": {
"Access and extract data from a specific URL": {
"main": [
[
{
"node": "Aggregate each html component into single",
"type": "main",
"index": 0
}
]
]
},
"Extract Event Cards": {
"main": [
[
{
"node": "Parse Events",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[
{
"node": "Reduce objects to 1",
"type": "main",
"index": 0
}
]
]
},
"Reduce objects to 1": {
"main": [
[
{
"node": "Aggregate Final Data",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Parse Events": {
"main": [
[
{
"node": "Edit Fields",
"type": "main",
"index": 0
}
]
]
},
"Google Gemini Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Trigger - Daily Run": {
"main": [
[
{
"node": "Access and extract data from a specific URL",
"type": "main",
"index": 0
}
]
]
},
"Aggregate Final Data": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Aggregate each html component into single": {
"main": [
[
{
"node": "Extract Event Cards",
"type": "main",
"index": 0
}
]
]
},
"If": {
"main": [
[
{
"node": "Restructure HTML",
"type": "main",
"index": 0
}
],
[
{
"node": "Send a message",
"type": "main",
"index": 0
}
]
]
},
"Restructure HTML": {
"main": [
[
{
"node": "Send a message1",
"type": "main",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "2b63628a-75a9-4041-b050-d55343eed0c4",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "c779ac62b612b52239571188fe56566fe15b3c0c517bf32e0c1fff3bb183ad97"
},
"id": "ROA9J3ZABXwCTaYd",
"tags": []
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment