- Add CONTEXT.md with ADHD-optimized task management patterns - Add troubleshooting guide for productivity tools - Add n8n workflow documentation including Ko-fi integration - Document n8n at LXC 210 (10.10.0.210) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
351 lines
15 KiB
JSON
351 lines
15 KiB
JSON
{
|
|
"name": "Ko-fi to Paper Dynasty",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "kofi-paperdy",
|
|
"responseMode": "lastNode",
|
|
"options": {}
|
|
},
|
|
"id": "webhook-trigger",
|
|
"name": "Ko-fi Webhook",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 1,
|
|
"position": [250, 300],
|
|
"webhookId": "kofi-paperdy"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "// Parse Ko-fi form data\nconst formData = $input.item.json.body;\nconst kofiData = JSON.parse(formData.data);\n\n// Ko-fi verification token\nconst KOFI_TOKEN = '44d1f957-ac15-497e-8306-4dc667de55d1';\n\n// Validate token\nif (kofiData.verification_token !== KOFI_TOKEN) {\n throw new Error('Invalid Ko-fi verification token');\n}\n\n// Add metadata\nreturn [{\n json: {\n ...kofiData,\n _parsed_at: new Date().toISOString(),\n _execution_id: $execution.id\n }\n}];"
|
|
},
|
|
"id": "parse-and-validate",
|
|
"name": "Parse & Validate",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [450, 300]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"string": [
|
|
{
|
|
"value1": "={{ $json.type }}",
|
|
"operation": "equals",
|
|
"value2": "Shop Order"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"id": "filter-shop-orders",
|
|
"name": "Shop Order?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 1,
|
|
"position": [650, 300]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "// Lookup Paper Dynasty team\nconst PD_API_BASE = 'https://pd.manticorum.com/api/v2';\nconst API_TOKEN = 'Bearer Tp3aO3jhYve5NJF1IqOmJTmk';\n\nlet teamId = null;\nlet teamAbbrev = null;\nlet identificationMethod = null;\n\n// Method 1: Discord User ID -> gmid lookup\nif ($json.discord_userid) {\n try {\n const response = await $http.request({\n method: 'GET',\n url: `${PD_API_BASE}/teams?gmid=${$json.discord_userid}`,\n headers: {\n 'Authorization': API_TOKEN,\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.body && response.body.length > 0) {\n teamId = response.body[0].id;\n teamAbbrev = response.body[0].abbrev;\n identificationMethod = 'discord_userid';\n }\n } catch (error) {\n console.log('Discord lookup failed:', error.message);\n }\n}\n\n// Method 2: Extract team abbrev from message\nif (!teamId && $json.message) {\n const abbrevMatch = $json.message.match(/\\b([A-Z]{2,4})\\b/);\n \n if (abbrevMatch) {\n const extractedAbbrev = abbrevMatch[1];\n \n try {\n const response = await $http.request({\n method: 'GET',\n url: `${PD_API_BASE}/teams?abbrev=${extractedAbbrev}`,\n headers: {\n 'Authorization': API_TOKEN,\n 'Content-Type': 'application/json'\n }\n });\n\n if (response.body && response.body.length > 0) {\n teamId = response.body[0].id;\n teamAbbrev = response.body[0].abbrev;\n identificationMethod = 'message_abbrev';\n }\n } catch (error) {\n console.log('Team abbrev lookup failed:', error.message);\n }\n }\n}\n\nreturn [{\n json: {\n ...$json,\n pd_team_id: teamId,\n pd_team_abbrev: teamAbbrev,\n identification_method: identificationMethod,\n needs_manual_review: !teamId\n }\n}];"
|
|
},
|
|
"id": "lookup-team",
|
|
"name": "Lookup PD Team",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [850, 200]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"boolean": [
|
|
{
|
|
"value1": "={{ $json.needs_manual_review }}",
|
|
"value2": false
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"id": "check-team-found",
|
|
"name": "Team Found?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 1,
|
|
"position": [1050, 200]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "// Product mapping\nconst PRODUCT_MAP = {\n '61de350207': {\n name: 'Premium Pack',\n pack_type_id: 3,\n packs_per_quantity: 1\n },\n '2bdb7a4916': {\n name: 'Standard Pack',\n pack_type_id: 1,\n packs_per_quantity: 1\n },\n '3d7a4935aa': {\n name: 'In-Game Currency',\n type: 'currency',\n needs_manual_review: true\n }\n};\n\nconst shopItems = $json.shop_items;\nconst packRequests = [];\nlet unknownProducts = [];\nlet needsReview = [];\n\nfor (const item of shopItems) {\n const productConfig = PRODUCT_MAP[item.direct_link_code];\n \n if (!productConfig) {\n unknownProducts.push({\n code: item.direct_link_code,\n variation: item.variation_name,\n quantity: item.quantity\n });\n continue;\n }\n \n // Handle currency products\n if (productConfig.type === 'currency') {\n needsReview.push({\n product: productConfig.name,\n quantity: item.quantity,\n reason: 'Currency system not implemented yet'\n });\n continue;\n }\n \n // Calculate total packs\n const totalPacks = item.quantity * productConfig.packs_per_quantity;\n \n // Create pack requests\n for (let i = 0; i < totalPacks; i++) {\n packRequests.push({\n team_id: $json.pd_team_id,\n pack_type_id: productConfig.pack_type_id,\n pack_cardset_id: null\n });\n }\n}\n\nreturn [{\n json: {\n ...$json,\n pack_requests: packRequests,\n unknown_products: unknownProducts,\n needs_review_items: needsReview,\n has_unknown_products: unknownProducts.length > 0,\n has_review_items: needsReview.length > 0,\n total_packs: packRequests.length\n }\n}];"
|
|
},
|
|
"id": "map-products",
|
|
"name": "Map Products",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [1250, 100]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://pd.manticorum.com/api/v2/packs",
|
|
"authentication": "predefinedCredentialType",
|
|
"nodeCredentialType": "httpHeaderAuth",
|
|
"sendBody": true,
|
|
"bodyParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "packs",
|
|
"value": "={{ JSON.stringify($json.pack_requests) }}"
|
|
}
|
|
]
|
|
},
|
|
"options": {
|
|
"response": {
|
|
"response": {
|
|
"fullResponse": true
|
|
}
|
|
},
|
|
"retry": {
|
|
"retry": {
|
|
"maxRetries": 3,
|
|
"retryInterval": 2000
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"id": "grant-packs",
|
|
"name": "Grant Packs",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4,
|
|
"position": [1450, 100],
|
|
"credentials": {
|
|
"httpHeaderAuth": {
|
|
"id": "1",
|
|
"name": "Paper Dynasty API"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"number": [
|
|
{
|
|
"value1": "={{ $json.statusCode }}",
|
|
"operation": "between",
|
|
"value2": 200,
|
|
"value3": 299
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"id": "check-api-success",
|
|
"name": "API Success?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 1,
|
|
"position": [1650, 100]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej",
|
|
"sendBody": true,
|
|
"contentType": "json",
|
|
"body": "={{ JSON.stringify({\n content: '✅ Ko-fi Order Processed',\n embeds: [{\n title: 'Paper Dynasty Packs Granted',\n color: 3066993,\n fields: [\n {\n name: 'Customer',\n value: $('Parse & Validate').item.json.from_name,\n inline: true\n },\n {\n name: 'Team',\n value: $('Lookup PD Team').item.json.pd_team_abbrev,\n inline: true\n },\n {\n name: 'Amount',\n value: '$' + $('Parse & Validate').item.json.amount + ' ' + $('Parse & Validate').item.json.currency,\n inline: true\n },\n {\n name: 'Packs Granted',\n value: $('Map Products').item.json.total_packs + ' packs',\n inline: false\n },\n {\n name: 'Transaction ID',\n value: '`' + $('Parse & Validate').item.json.kofi_transaction_id + '`',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}"
|
|
},
|
|
"id": "discord-success",
|
|
"name": "Discord Success",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4,
|
|
"position": [1850, 0]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej",
|
|
"sendBody": true,
|
|
"contentType": "json",
|
|
"body": "={{ JSON.stringify({\n content: '@here ⚠️ Ko-fi Order Processing Error',\n embeds: [{\n title: 'Paper Dynasty API Error',\n color: 15158332,\n fields: [\n {\n name: 'Customer',\n value: $('Parse & Validate').item.json.from_name + ' (' + $('Parse & Validate').item.json.email + ')',\n inline: false\n },\n {\n name: 'Amount',\n value: '$' + $('Parse & Validate').item.json.amount,\n inline: true\n },\n {\n name: 'Team ID',\n value: String($('Lookup PD Team').item.json.pd_team_id || 'Unknown'),\n inline: true\n },\n {\n name: 'API Status',\n value: String($json.statusCode || 'Request Failed'),\n inline: true\n },\n {\n name: 'Transaction ID',\n value: '`' + $('Parse & Validate').item.json.kofi_transaction_id + '`',\n inline: false\n },\n {\n name: 'Action Required',\n value: 'Manual pack distribution needed. Use transaction ID to process manually.',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}"
|
|
},
|
|
"id": "discord-error",
|
|
"name": "Discord Error",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4,
|
|
"position": [1850, 200]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "https://discord.com/api/webhooks/1438578498108133488/MGx1zyAa1ewzVy3RqCsH50ZJUiVvT5J5Vl2jExGIkIsu6v0x8A9J3-ruRftClPEq91ej",
|
|
"sendBody": true,
|
|
"contentType": "json",
|
|
"body": "={{ JSON.stringify({\n content: '@here 🔍 Ko-fi Order Needs Manual Review',\n embeds: [{\n title: 'Unable to Identify Paper Dynasty Team',\n color: 16776960,\n fields: [\n {\n name: 'Customer',\n value: $json.from_name + ' (' + $json.email + ')',\n inline: false\n },\n {\n name: 'Amount',\n value: '$' + $json.amount + ' ' + $json.currency,\n inline: true\n },\n {\n name: 'Discord User ID',\n value: $json.discord_userid || 'Not provided',\n inline: true\n },\n {\n name: 'Message',\n value: $json.message || 'No message',\n inline: false\n },\n {\n name: 'Products',\n value: $json.shop_items.map(i => i.quantity + 'x ' + i.variation_name + ' (' + i.direct_link_code + ')').join('\\n'),\n inline: false\n },\n {\n name: 'Transaction ID',\n value: '`' + $json.kofi_transaction_id + '`',\n inline: false\n },\n {\n name: 'Action Required',\n value: '1. Identify user\\'s Paper Dynasty team\\n2. Manually grant packs using PD API\\n3. Reply when resolved',\n inline: false\n }\n ],\n timestamp: new Date().toISOString()\n }]\n}) }}"
|
|
},
|
|
"id": "discord-manual-review",
|
|
"name": "Discord Manual Review",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4,
|
|
"position": [1250, 300]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"respondWith": "json",
|
|
"responseBody": "={{ JSON.stringify({\n status: 'success',\n message: 'Order received and processed',\n execution_id: $execution.id\n}) }}"
|
|
},
|
|
"id": "respond-webhook",
|
|
"name": "Return 200 OK",
|
|
"type": "n8n-nodes-base.respondToWebhook",
|
|
"typeVersion": 1,
|
|
"position": [2050, 150]
|
|
}
|
|
],
|
|
"connections": {
|
|
"Ko-fi Webhook": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Parse & Validate",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Parse & Validate": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Shop Order?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Shop Order?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Lookup PD Team",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Return 200 OK",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Lookup PD Team": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Team Found?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Team Found?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Map Products",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Discord Manual Review",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Map Products": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Grant Packs",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Grant Packs": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "API Success?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"API Success?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Discord Success",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Discord Error",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Discord Success": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Return 200 OK",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Discord Error": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Return 200 OK",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Discord Manual Review": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Return 200 OK",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"pinData": {},
|
|
"settings": {
|
|
"executionOrder": "v1"
|
|
},
|
|
"staticData": null,
|
|
"tags": [],
|
|
"triggerCount": 0,
|
|
"updatedAt": "2025-11-13T00:00:00.000Z",
|
|
"versionId": "1"
|
|
}
|