claude-home/productivity/n8n/workflows/kofi-paper-dynasty-workflow.json
Cal Corum c8dcf2b5ee CLAUDE: Add productivity tools with n8n workflow automation
- 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>
2025-12-07 00:48:28 -06:00

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"
}