This workflow corresponds to n8n.io template #12337 — we link there as the canonical source.
This workflow follows the Discord → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"meta": {
"templateCredsSetupCompleted": true
},
"nodes": [
{
"id": "05e93c77-cf6a-4f19-8261-3e7593d13ed4",
"name": "Sticky Note - Main",
"type": "n8n-nodes-base.stickyNote",
"position": [
2512,
816
],
"parameters": {
"width": 440,
"height": 1308,
"content": "## Visual Daily Journal\n\n**Automatic diary entries from your Discord conversations**\n\nEvery day at your chosen time, this workflow:\n1. Fetches messages from a Discord channel\n2. Summarizes your day with GPT-4\n3. Generates a representative image with DALL-E\n4. Creates a beautiful entry in Notion\n\n---\n\n### How it works\n\n1. **Schedule Trigger** runs daily at your chosen time (default: 11pm)\n2. **Discord node** fetches the last 100 messages from your channel\n3. **Filter node** keeps only today's messages\n4. **Check node** skips if no messages found\n5. **GPT-4** analyzes conversations and creates:\n - Creative title\n - Reflective summary\n - Mood assessment\n - Relevant tags\n - Image prompt\n6. **DALL-E 3** generates an artistic image for the day\n7. **Cloudinary** uploads the image (Notion needs a public URL)\n8. **Notion** creates a beautiful diary entry with everything\n\n---\n\n### Setup steps (15-20 min)\n\n1. **Discord credentials**\n - Go to discord.com/developers/applications\n - Create New Application \u2192 Bot \u2192 Copy Token\n - Add bot to server with Read Message History permission\n - In n8n: Settings \u2192 Credentials \u2192 Discord Bot API\n\n2. **OpenAI credentials**\n - Settings \u2192 Credentials \u2192 OpenAI API\n\n3. **Cloudinary** (free account)\n - Sign up at cloudinary.com\n - Copy Cloud Name, API Key, API Secret\n - Update values in \"Upload to Cloudinary\" node\n\n4. **Notion credentials**\n - Create integration at notion.so/my-integrations\n - Settings \u2192 Credentials \u2192 Notion API\n - Share your database with the integration\n\n5. **Configure nodes**\n - Select your Discord server and channel\n - Update OPENAI_API_KEY in DALL-E node\n - Set your Notion database ID"
},
"typeVersion": 1
},
{
"id": "80279469-95ca-4ce7-82b0-19797b0215f0",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
3008,
928
],
"parameters": {
"color": 7,
"width": 160,
"height": 224,
"content": "**Step 1: Schedule**\nDaily trigger"
},
"typeVersion": 1
},
{
"id": "56796dd8-6c4b-4bff-951f-932fe7095cd8",
"name": "Sticky Note2",
"type": "n8n-nodes-base.stickyNote",
"position": [
3200,
928
],
"parameters": {
"color": 7,
"width": 180,
"height": 224,
"content": "**Step 2: Discord**\nOfficial node"
},
"typeVersion": 1
},
{
"id": "4b339b36-7efe-404c-a73f-eeec51b21f2b",
"name": "Sticky Note3",
"type": "n8n-nodes-base.stickyNote",
"position": [
3408,
928
],
"parameters": {
"color": 7,
"width": 196,
"height": 224,
"content": "**Step 3: Filter**\nToday's messages"
},
"typeVersion": 1
},
{
"id": "5601ab8a-7e26-427f-833e-d200ce5d2ee3",
"name": "Sticky Note4",
"type": "n8n-nodes-base.stickyNote",
"position": [
3648,
928
],
"parameters": {
"color": 7,
"width": 160,
"height": 224,
"content": "**Step 4: Check**\nHas messages?"
},
"typeVersion": 1
},
{
"id": "e442bc4c-5974-4114-bded-831d35dfc7bb",
"name": "Sticky Note5",
"type": "n8n-nodes-base.stickyNote",
"position": [
3840,
928
],
"parameters": {
"color": 7,
"width": 176,
"height": 208,
"content": "**Step 5: Format**\nPrepare text"
},
"typeVersion": 1
},
{
"id": "1eca36c0-1c38-4eb7-b432-4f34f555d496",
"name": "Sticky Note6",
"type": "n8n-nodes-base.stickyNote",
"position": [
4032,
928
],
"parameters": {
"color": 7,
"width": 196,
"height": 240,
"content": "**Step 6: GPT-4**\nAnalyze day"
},
"typeVersion": 1
},
{
"id": "35baedcf-01c2-4302-96f5-92afd00e0463",
"name": "Sticky Note7",
"type": "n8n-nodes-base.stickyNote",
"position": [
4256,
928
],
"parameters": {
"color": 7,
"width": 160,
"content": "**Step 7: Parse**\nExtract data"
},
"typeVersion": 1
},
{
"id": "ecba201d-d6c6-4a23-a97f-1c3396e5997a",
"name": "Sticky Note8",
"type": "n8n-nodes-base.stickyNote",
"position": [
4464,
928
],
"parameters": {
"color": 7,
"width": 180,
"height": 224,
"content": "**Step 8: DALL-E**\nGenerate image"
},
"typeVersion": 1
},
{
"id": "7c3452c2-9d92-4541-baa5-85f74cf7c6a1",
"name": "Sticky Note9",
"type": "n8n-nodes-base.stickyNote",
"position": [
4672,
928
],
"parameters": {
"color": 7,
"width": 196,
"height": 224,
"content": "**Step 9: Upload**\nCloudinary"
},
"typeVersion": 1
},
{
"id": "59ee1280-1530-414b-9017-b41185aaa7b8",
"name": "Sticky Note10",
"type": "n8n-nodes-base.stickyNote",
"position": [
4896,
928
],
"parameters": {
"color": 7,
"width": 196,
"height": 224,
"content": "**Step 10: Notion**\nCreate entry"
},
"typeVersion": 1
},
{
"id": "df177715-c451-4120-be5d-4ec306d2a45c",
"name": "Daily Trigger (11pm)",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
3040,
1008
],
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 24
}
]
}
},
"typeVersion": 1.2
},
{
"id": "888d0e4d-77bf-43a3-8b7e-1f1ee083c511",
"name": "Get Discord Messages",
"type": "n8n-nodes-base.discord",
"position": [
3248,
1008
],
"parameters": {
"guildId": {
"__rl": true,
"mode": "list",
"value": ""
},
"options": {},
"resource": "message",
"channelId": {
"__rl": true,
"mode": "list",
"value": ""
},
"operation": "getAll"
},
"typeVersion": 2
},
{
"id": "74f3c044-3985-42be-b802-4c9cafef1778",
"name": "Filter Today's Messages",
"type": "n8n-nodes-base.code",
"position": [
3456,
1008
],
"parameters": {
"jsCode": "// Filter messages from today only\nconst allMessages = $input.all();\nconst now = new Date();\nconst todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);\nconst todayEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59);\n\n// Filter to today's messages\nconst todayMessages = allMessages.filter(item => {\n const msgDate = new Date(item.json.timestamp);\n return msgDate >= todayStart && msgDate <= todayEnd;\n});\n\n// Format the messages\nconst formattedMessages = todayMessages.map(item => ({\n id: item.json.id,\n content: item.json.content || '',\n author: item.json.author?.username || 'Unknown',\n timestamp: item.json.timestamp,\n attachments: item.json.attachments?.length || 0\n})).reverse(); // Chronological order\n\nreturn {\n json: {\n messages: formattedMessages,\n messageCount: formattedMessages.length,\n date: now.toISOString().split('T')[0],\n fetchedAt: now.toISOString()\n }\n};"
},
"typeVersion": 2
},
{
"id": "e96b9987-dbdb-4e44-beaf-b48963e77ef6",
"name": "Has Messages?",
"type": "n8n-nodes-base.if",
"position": [
3680,
1008
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "condition-check-messages",
"operator": {
"type": "number",
"operation": "gt"
},
"leftValue": "={{ $json.messageCount }}",
"rightValue": 0
}
]
}
},
"typeVersion": 2
},
{
"id": "b1f6d4c8-b876-4e7a-9ee6-abd27b503070",
"name": "Format for GPT-4",
"type": "n8n-nodes-base.code",
"position": [
3888,
1008
],
"parameters": {
"jsCode": "// Format messages for GPT-4 analysis\nconst data = $input.first().json;\nconst messages = data.messages;\n\nif (!messages || messages.length === 0) {\n return { json: { error: 'No messages to process' } };\n}\n\n// Build conversation text\nconst conversationText = messages.map(msg => {\n const time = new Date(msg.timestamp).toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit'\n });\n return `[${time}] ${msg.author}: ${msg.content}`;\n}).join('\\n');\n\n// Extract unique participants\nconst participants = [...new Set(messages.map(m => m.author))];\n\n// Count messages per person\nconst messageCounts = {};\nmessages.forEach(msg => {\n messageCounts[msg.author] = (messageCounts[msg.author] || 0) + 1;\n});\n\nreturn {\n json: {\n conversationText: conversationText,\n participants: participants,\n messageCounts: messageCounts,\n totalMessages: data.messageCount,\n date: data.date,\n dateFormatted: new Date(data.date).toLocaleDateString('en-US', {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric'\n })\n }\n};"
},
"typeVersion": 2
},
{
"id": "01b52d05-4eb6-4534-b733-85115d9cd85f",
"name": "GPT-4 Analyze Day",
"type": "n8n-nodes-base.httpRequest",
"position": [
4080,
1008
],
"parameters": {
"url": "https://api.openai.com/v1/chat/completions",
"method": "POST",
"options": {},
"jsonBody": "={\n \"model\": \"gpt-4o\",\n \"messages\": [\n {\n \"role\": \"system\",\n \"content\": \"You are a thoughtful journal assistant. Analyze the day's Discord conversations and create a personal diary entry. Be warm, reflective, and capture the essence of the day. Focus on themes, emotions, achievements, and memorable moments.\"\n },\n {\n \"role\": \"user\",\n \"content\": \"Here are today's Discord messages ({{ $json.dateFormatted }}):\\n\\n{{ $json.conversationText }}\\n\\nParticipants: {{ $json.participants.join(', ') }}\\nTotal messages: {{ $json.totalMessages }}\\n\\nPlease analyze this and provide:\\n1. A diary entry title (creative, captures the day's essence)\\n2. A reflective summary (2-3 paragraphs, personal tone)\\n3. Mood assessment (one of: Great, Good, Neutral, Low, Productive)\\n4. 3-5 relevant tags (topics discussed)\\n5. An image prompt to visually represent this day (artistic, atmospheric, no text)\"\n }\n ],\n \"response_format\": {\n \"type\": \"json_schema\",\n \"json_schema\": {\n \"name\": \"diary_entry\",\n \"strict\": true,\n \"schema\": {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"title\": { \"type\": \"string\" },\n \"summary\": { \"type\": \"string\" },\n \"mood\": { \"type\": \"string\" },\n \"tags\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n },\n \"imagePrompt\": { \"type\": \"string\" },\n \"highlights\": {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n }\n },\n \"required\": [\"title\", \"summary\", \"mood\", \"tags\", \"imagePrompt\", \"highlights\"]\n }\n }\n }\n}",
"sendBody": true,
"specifyBody": "json",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "openAiApi"
},
"typeVersion": 4.3
},
{
"id": "e0a6c9e0-46d3-4cb8-a958-fcccb0980682",
"name": "Parse Analysis",
"type": "n8n-nodes-base.code",
"position": [
4304,
1008
],
"parameters": {
"jsCode": "// Parse GPT-4 response and prepare for image generation\nconst response = $input.first().json;\nconst previousData = $('Format for GPT-4').first().json;\n\nconst analysis = JSON.parse(response.choices[0].message.content);\n\n// Map mood to emoji\nconst moodEmojis = {\n 'Great': '\ud83d\ude0a',\n 'Good': '\ud83d\ude0c',\n 'Neutral': '\ud83d\ude10',\n 'Low': '\ud83d\ude14',\n 'Productive': '\ud83d\udd25'\n};\n\nconst moodWithEmoji = `${moodEmojis[analysis.mood] || '\ud83d\ude10'} ${analysis.mood}`;\n\nreturn {\n json: {\n title: analysis.title,\n summary: analysis.summary,\n mood: analysis.mood,\n moodWithEmoji: moodWithEmoji,\n tags: analysis.tags,\n imagePrompt: analysis.imagePrompt,\n highlights: analysis.highlights,\n date: previousData.date,\n dateFormatted: previousData.dateFormatted,\n totalMessages: previousData.totalMessages,\n participants: previousData.participants\n }\n};"
},
"typeVersion": 2
},
{
"id": "b71d6b2f-76a3-40fe-b4c6-2e5ac31e423a",
"name": "Generate Image (DALL-E)",
"type": "n8n-nodes-base.code",
"position": [
4496,
1008
],
"parameters": {
"jsCode": "// ============================================\n// GENERATE IMAGE WITH DALL-E 3\n// ============================================\n// \u26a0\ufe0f UPDATE THIS API KEY\nconst OPENAI_API_KEY = 'YOUR_OPENAI_API_KEY';\n// ============================================\n\nconst data = $input.first().json;\n\n// Enhance the prompt for better artistic results\nconst enhancedPrompt = `${data.imagePrompt}. Artistic digital illustration, dreamy atmosphere, soft lighting, no text or words, suitable for a personal journal.`;\n\ntry {\n const response = await fetch('https://api.openai.com/v1/images/generations', {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${OPENAI_API_KEY}`,\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n model: 'dall-e-3',\n prompt: enhancedPrompt.substring(0, 4000),\n n: 1,\n size: '1024x1024',\n quality: 'standard',\n response_format: 'b64_json'\n })\n });\n \n const result = await response.json();\n \n if (result.error) {\n return {\n json: {\n ...data,\n imageBase64: null,\n imageError: result.error.message\n }\n };\n }\n \n return {\n json: {\n ...data,\n imageBase64: result.data[0].b64_json,\n imageError: null\n }\n };\n \n} catch (error) {\n return {\n json: {\n ...data,\n imageBase64: null,\n imageError: error.message\n }\n };\n}"
},
"typeVersion": 2
},
{
"id": "a6b403d9-1ab9-4bcf-a2cb-69862e2f9dac",
"name": "Upload to Cloudinary",
"type": "n8n-nodes-base.code",
"position": [
4720,
1008
],
"parameters": {
"jsCode": "// ============================================\n// UPLOAD IMAGE TO CLOUDINARY\n// ============================================\n// \u26a0\ufe0f UPDATE THESE VALUES\nconst CLOUDINARY_CLOUD_NAME = 'YOUR_CLOUD_NAME';\nconst CLOUDINARY_API_KEY = 'YOUR_API_KEY';\nconst CLOUDINARY_API_SECRET = 'YOUR_API_SECRET';\n// ============================================\n\nconst data = $input.first().json;\n\nif (!data.imageBase64) {\n return {\n json: {\n ...data,\n imageUrl: null,\n uploadError: data.imageError || 'No image to upload'\n }\n };\n}\n\ntry {\n // Create signature for Cloudinary\n const timestamp = Math.floor(Date.now() / 1000);\n const folder = 'visual-journal';\n const publicId = `diary_${data.date}`;\n \n // Build signature string\n const signatureString = `folder=${folder}&public_id=${publicId}×tamp=${timestamp}${CLOUDINARY_API_SECRET}`;\n \n // Create SHA1 hash\n const crypto = require('crypto');\n const signature = crypto.createHash('sha1').update(signatureString).digest('hex');\n \n // Upload to Cloudinary\n const formData = new URLSearchParams();\n formData.append('file', `data:image/png;base64,${data.imageBase64}`);\n formData.append('api_key', CLOUDINARY_API_KEY);\n formData.append('timestamp', timestamp.toString());\n formData.append('signature', signature);\n formData.append('folder', folder);\n formData.append('public_id', publicId);\n \n const response = await fetch(\n `https://api.cloudinary.com/v1_1/${CLOUDINARY_CLOUD_NAME}/image/upload`,\n {\n method: 'POST',\n body: formData\n }\n );\n \n const result = await response.json();\n \n if (result.error) {\n return {\n json: {\n ...data,\n imageUrl: null,\n uploadError: result.error.message\n }\n };\n }\n \n return {\n json: {\n ...data,\n imageUrl: result.secure_url,\n imagePublicId: result.public_id,\n uploadError: null\n }\n };\n \n} catch (error) {\n return {\n json: {\n ...data,\n imageUrl: null,\n uploadError: error.message\n }\n };\n}"
},
"typeVersion": 2
},
{
"id": "cd529bb8-3553-4e91-b97a-bc4d13f07b22",
"name": "Create Notion Entry",
"type": "n8n-nodes-base.notion",
"position": [
4944,
1008
],
"parameters": {
"blockUi": {
"blockValues": [
{
"type": "image"
},
{
"type": "heading_2",
"textContent": "\ud83d\udcdd Summary"
},
{
"textContent": "={{ $json.summary }}"
},
{
"type": "heading_2",
"textContent": "\u2728 Highlights"
},
{
"type": "bulleted_list_item",
"textContent": "={{ $json.highlights[0] || '' }}"
},
{
"type": "bulleted_list_item",
"textContent": "={{ $json.highlights[1] || '' }}"
},
{
"type": "bulleted_list_item",
"textContent": "={{ $json.highlights[2] || '' }}"
},
{
"type": "divider"
},
{
"textContent": "={{ '\ud83d\udc65 Participants: ' + $json.participants.join(', ') }}"
},
{
"textContent": "={{ '\ud83d\udcac Total messages: ' + $json.totalMessages }}"
}
]
},
"options": {},
"resource": "databasePage",
"databaseId": {
"__rl": true,
"mode": "list",
"value": ""
},
"propertiesUi": {
"propertyValues": [
{
"key": "Title|title",
"title": "={{ $json.title }}"
},
{
"key": "Date|date",
"date": "={{ $json.date }}",
"includeTime": false
},
{
"key": "Summary|rich_text",
"textContent": "={{ $json.summary }}"
},
{
"key": "Mood|select",
"selectValue": "={{ $json.moodWithEmoji }}"
},
{
"key": "Message Count|number",
"numberValue": "={{ $json.totalMessages }}"
}
]
}
},
"typeVersion": 2.2
},
{
"id": "5cef8e68-3870-47ad-8b88-2ca60945f301",
"name": "Success Response",
"type": "n8n-nodes-base.code",
"position": [
5168,
1008
],
"parameters": {
"jsCode": "// Final response - entry created successfully\nconst data = $input.first().json;\nconst previousData = $('Upload to Cloudinary').first().json;\n\nreturn {\n json: {\n success: true,\n message: 'Diary entry created successfully!',\n entry: {\n title: previousData.title,\n date: previousData.date,\n mood: previousData.moodWithEmoji,\n messageCount: previousData.totalMessages,\n hasImage: !!previousData.imageUrl,\n notionPageId: data.id,\n notionUrl: data.url\n }\n }\n};"
},
"typeVersion": 2
},
{
"id": "032d91c1-3994-4511-b895-9e8a34030265",
"name": "No Messages Today",
"type": "n8n-nodes-base.code",
"position": [
3888,
1184
],
"parameters": {
"jsCode": "// No messages today - skip entry\nconst data = $input.first().json;\n\nreturn {\n json: {\n success: false,\n message: 'No messages found for today. Skipping diary entry.',\n date: data.date || new Date().toISOString().split('T')[0]\n }\n};"
},
"typeVersion": 2
}
],
"connections": {
"Has Messages?": {
"main": [
[
{
"node": "Format for GPT-4",
"type": "main",
"index": 0
}
],
[
{
"node": "No Messages Today",
"type": "main",
"index": 0
}
]
]
},
"Parse Analysis": {
"main": [
[
{
"node": "Generate Image (DALL-E)",
"type": "main",
"index": 0
}
]
]
},
"Format for GPT-4": {
"main": [
[
{
"node": "GPT-4 Analyze Day",
"type": "main",
"index": 0
}
]
]
},
"GPT-4 Analyze Day": {
"main": [
[
{
"node": "Parse Analysis",
"type": "main",
"index": 0
}
]
]
},
"Create Notion Entry": {
"main": [
[
{
"node": "Success Response",
"type": "main",
"index": 0
}
]
]
},
"Daily Trigger (11pm)": {
"main": [
[
{
"node": "Get Discord Messages",
"type": "main",
"index": 0
}
]
]
},
"Get Discord Messages": {
"main": [
[
{
"node": "Filter Today's Messages",
"type": "main",
"index": 0
}
]
]
},
"Upload to Cloudinary": {
"main": [
[
{
"node": "Create Notion Entry",
"type": "main",
"index": 0
}
]
]
},
"Filter Today's Messages": {
"main": [
[
{
"node": "Has Messages?",
"type": "main",
"index": 0
}
]
]
},
"Generate Image (DALL-E)": {
"main": [
[
{
"node": "Upload to Cloudinary",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
I wanted a journal but never had the discipline to write one. Most of my day happens in Discord anyway, so I built this to do it for me.
Source: https://n8n.io/workflows/12337/ — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
🌸 Affirmation Sender + Weekly Gratitude Digest v2
Automatically fetch existing domains from Notion's Database and verify the validity of SSL certificates through SSL-Checker. If the validity period is less than 14 days, send a Telegram message notifi
This workflow monitors product prices from BooksToScrape and sends alerts to a Discord channel via webhook when competitor's prices are lower than our prices. Schedule (for daily or required schedule)
This workflow provides an automated, intelligent solution for global weather monitoring. It goes beyond simple data fetching by calculating a custom "Comfort Index" and using AI to provide human-like
AmazonLuna-Games-Fetch. Uses httpRequest, scheduleTrigger, googleSheets, stickyNote. Scheduled trigger; 16 nodes.