Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions Other_Integrations_and_Use_Cases/glasp-highlights-export-workflow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
{
"name": "Glasp Highlights Auto Export",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "hours",
"hoursInterval": 6
}
]
}
},
"id": "schedule-trigger",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.2,
"position": [
220,
300
]
},
{
"parameters": {
"jsCode": "// Prepare the query parameters for the Glasp API\nconst sd = $getWorkflowStaticData('global');\nconst now = new Date();\nconst firstRun = !sd.lastRunAt;\n\nlet baseDate = firstRun\n ? new Date(now.getTime() - 24 * 60 * 60 * 1000)\n : new Date(sd.lastRunAt);\n\n// 5-minute buffer\nbaseDate = new Date(baseDate.getTime() - 5 * 60 * 1000);\n\nsd.exportedDocs = sd.exportedDocs || {};\n\nreturn [\n {\n json: {\n updatedAfter: baseDate.toISOString(),\n exportedDocs: sd.exportedDocs,\n },\n },\n];\n"
},
"id": "prepare-params",
"name": "Prepare Parameters",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
460,
300
]
},
{
"parameters": {
"method": "GET",
"url": "https://api.glasp.co/v1/highlights/export",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "updatedAfter",
"value": "={{ $json.updatedAfter }}"
}
]
},
"options": {
"pagination": {
"paginationMode": "responseContainsNextURL",
"nextURL": "={{ $response.body.nextPageCursor ? 'https://api.glasp.co/v1/highlights/export?updatedAfter=' + $json.updatedAfter + '&pageCursor=' + $response.body.nextPageCursor : '' }}",
"paginationCompleteWhen": "receiveSpecificStatusCodes",
"statusCodesWhenComplete": "",
"maxRequests": 100
},
"response": {
"response": {
"responseFormat": "json"
}
}
}
},
"id": "http-glasp-api",
"name": "Glasp API",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
700,
300
],
"credentials": {
"httpHeaderAuth": {
"id": "",
"name": "Glasp Token"
}
}
},
{
"parameters": {
"jsCode": "// Filter out already-exported docs, then format for export\nconst sd = $getWorkflowStaticData('global');\nsd.exportedDocs = sd.exportedDocs || {};\nconst now = new Date();\n\n// Collect all results from all paginated responses\nconst allItems = $input.all();\nlet allResults = [];\n\nfor (const item of allItems) {\n const data = item.json;\n // Handle both: direct results array or nested in response\n if (data.results && Array.isArray(data.results)) {\n allResults = allResults.concat(data.results);\n }\n}\n\n// Filter: only new docs with highlights\nconst newDocs = allResults.filter(function(doc) {\n return !sd.exportedDocs[doc.id] && doc.highlights && doc.highlights.length > 0;\n});\n\n// Mark as exported\nfor (const doc of newDocs) {\n sd.exportedDocs[doc.id] = now.toISOString();\n}\n\n// Update last run\nsd.lastRunAt = now.toISOString();\n\n// Cleanup old tracking (30 days)\nconst thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\nfor (const [docId, exportedAt] of Object.entries(sd.exportedDocs)) {\n if (new Date(exportedAt) < thirtyDaysAgo) {\n delete sd.exportedDocs[docId];\n }\n}\n\nif (newDocs.length === 0) {\n return [{ json: { message: 'No new highlights found.', count: 0 } }];\n}\n\n// Format each doc for export\nreturn newDocs.map(function(doc) {\n var lines = [];\n lines.push('## ' + (doc.title || 'Untitled'));\n lines.push('');\n lines.push('**URL:** ' + doc.url);\n lines.push('**Glasp:** ' + doc.glasp_url);\n if (doc.tags && doc.tags.length > 0) {\n lines.push('**Tags:** ' + doc.tags.join(', '));\n }\n if (doc.document_note) {\n lines.push('**Note:** ' + doc.document_note);\n }\n lines.push('');\n lines.push('### Highlights');\n lines.push('');\n\n for (var i = 0; i < doc.highlights.length; i++) {\n var h = doc.highlights[i];\n lines.push('> ' + h.text);\n if (h.note) {\n lines.push('');\n lines.push('**Note:** ' + h.note);\n }\n var d = new Date(h.highlighted_at).toLocaleDateString();\n lines.push('');\n lines.push('- *' + h.color + ' highlight, ' + d + '*');\n lines.push('');\n }\n\n var highlightsMarkdown = lines.join('\\n');\n\n var highlightsText = doc.highlights\n .map(function(h) {\n return h.text + (h.note ? ' [Note: ' + h.note + ']' : '');\n })\n .join('\\n\\n');\n\n return {\n json: {\n documentId: doc.id,\n title: doc.title || 'Untitled',\n url: doc.url,\n glasp_url: doc.glasp_url,\n domain: doc.domain,\n category: doc.category,\n tags: doc.tags || [],\n author: doc.author || '',\n thumbnail_url: doc.thumbnail_url || '',\n document_note: doc.document_note || '',\n is_favorite: doc.is_favorite || false,\n highlightCount: doc.highlights.length,\n highlightsText: highlightsText,\n highlightsMarkdown: highlightsMarkdown,\n highlights: doc.highlights.map(function(h) {\n return {\n id: h.id,\n text: h.text,\n note: h.note || '',\n color: h.color,\n highlighted_at: h.highlighted_at,\n };\n }),\n createdAt: doc.created_at,\n updatedAt: doc.updated_at,\n },\n };\n});\n"
},
"id": "filter-and-format",
"name": "Filter & Format",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
940,
300
]
},
{
"parameters": {
"content": "## [Connect Your Destination Here]\n\nAdd your preferred export node after \"Filter & Format\".\n\n**Available fields per item:**\n- `title` -- Article/page title\n- `url` -- Original URL\n- `glasp_url` -- Glasp page URL\n- `domain`, `category`, `tags`\n- `highlightCount`\n- `highlightsText` -- Plain text\n- `highlightsMarkdown` -- Markdown\n- `highlights[]` -- Array of objects\n- `createdAt`, `updatedAt`\n\n**Examples:**\n- Notion: Create Database Item\n- Slack: Send Message\n- Google Sheets: Append Row\n- Email: Send Email\n- Webhook: HTTP Request POST",
"height": 440,
"width": 360
},
"id": "sticky-note-destination",
"name": "Sticky Note - Destination Guide",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1200,
100
]
},
{
"parameters": {
"content": "## [Setup - 2 steps]\n\n**Step 1:** Get your Glasp Access Token\nhttps://glasp.co/settings/access_token\n\n**Step 2:** Create a \"Header Auth\" credential\n- Name: Authorization\n- Value: Bearer YOUR_TOKEN\nThen assign it to the \"Glasp API\" node.\n\n**Optional:** Adjust schedule frequency\nby clicking the Schedule Trigger node.\nDefault: every 6 hours.",
"height": 320,
"width": 360
},
"id": "sticky-note-setup",
"name": "Sticky Note - Setup",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
180,
-100
]
},
{
"parameters": {
"content": "## [Security]\n\n- Access token is stored in n8n's\n encrypted Credentials, never in\n the workflow JSON.\n- Exported doc tracking auto-cleans\n after 30 days.\n- No secrets in code.",
"height": 220,
"width": 360
},
"id": "sticky-note-security",
"name": "Sticky Note - Security",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
600,
-100
]
}
],
"connections": {
"Schedule Trigger": {
"main": [
[
{
"node": "Prepare Parameters",
"type": "main",
"index": 0
}
]
]
},
"Prepare Parameters": {
"main": [
[
{
"node": "Glasp API",
"type": "main",
"index": 0
}
]
]
},
"Glasp API": {
"main": [
[
{
"node": "Filter & Format",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
{
"name": "Glasp"
},
{
"name": "Highlights"
},
{
"name": "Export"
},
{
"name": "Automation"
}
],
"pinData": {}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ All automation templates in this repository were found online and are uploaded h
| Classify new bugs in Linear with OpenAI's GPT-4 | Automatically classifies and routes new bug reports in Linear using AI. | Development/QA | [Link to Template](Other_Integrations_and_Use_Cases/Classify%20new%20bugs%20in%20Linear%20with%20OpenAI_s%20GPT-4%20and%20move%20them%20to%20the%20right%20team.json) |
| Create, update, and get a profile in Humantic AI | Manages user profiles in Humantic AI platform. | Marketing/AI | [Link to Template](Other_Integrations_and_Use_Cases/Create,%20update,%20and%20get%20a%20profile%20in%20Humantic%20AI.json) |
| Enhance Customer Chat with Twilio and Redis | Implements message buffering for customer chats using Twilio and Redis. | Support/Development | [Link to Template](Other_Integrations_and_Use_Cases/Enhance%20Customer%20Chat%20by%20Buffering%20Messages%20with%20Twilio%20and%20Redis.json) |
| Glasp Highlights Auto Export | Fetches new Glasp highlights on a schedule and formats them for export to any destination (Notion, Slack, Google Sheets, etc.). | Productivity | [Link to Template](/enescingoz/awesome-n8n-templates/blob/main/Other_Integrations_and_Use_Cases/glasp-highlights-export-workflow.json) |
| Hacker News Throwback Machine | Shows what was popular on Hacker News on this day in previous years. | Development/Community | [Link to Template](Other_Integrations_and_Use_Cases/Hacker%20News%20Throwback%20Machine%20-%20See%20What%20Was%20Hot%20on%20This%20Day,%20Every%20Year!.json) |
| Handling Appointment Leads with Twilio, Cal.com and AI | Manages appointment scheduling and follow-ups using Twilio and Cal.com. | Sales/Support | [Link to Template](Other_Integrations_and_Use_Cases/Handling%20Appointment%20Leads%20and%20Follow-up%20With%20Twilio,%20Cal.com%20and%20AI.json) |
| Integrating AI with Open-Meteo API | Enhances weather forecasting with AI analysis. | Data Science/Weather | [Link to Template](Other_Integrations_and_Use_Cases/Integrating%20AI%20with%20Open-Meteo%20API%20for%20Enhanced%20Weather%20Forecasting.json) |
Expand Down