init after v1 corruption

This commit is contained in:
2026-01-02 22:48:32 +00:00
parent 305f69836c
commit 166ee1fc73
91 changed files with 15801 additions and 0 deletions

View File

@@ -0,0 +1,829 @@
{
"id": "2tDusEAKIJ6bwDl2",
"name": "EVE Character Bazar Monitor",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "minutes",
"minutesInterval": 15
}
]
}
},
"id": "Schedule Trigger",
"name": "Schedule Trigger",
"position": [
2464,
16
],
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.3
},
{
"parameters": {
"jsCode": "return $input.all().map(item => {\n return {\n json: {\n ...item.json,\n // Ensure character_id is treated as null if missing/empty\n character_id: item.json.character_id || null\n }\n };\n});"
},
"id": "Check ID Code",
"name": "Check ID Code",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2912,
-80
],
"notes": "Debug: Check logic"
},
{
"parameters": {
"url": "={{ $json.character_id ? 'https://evewho.com/api/character/' + $json.character_id : 'http://skip-verification' }}",
"options": {}
},
"id": "Verify Character",
"name": "Verify Character",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.3,
"position": [
3360,
-8
],
"continueOnFail": true
},
{
"parameters": {
"jsCode": "const items = $input.all();\nreturn items.map(item => {\n const eveData = item.json.data; // Response from EveWho\n const compliance = {\n status: 'compliant',\n issues: []\n };\n\n if (!eveData) {\n // If no EveWho data (e.g. no character ID found or API failed), we can't verify mechanics.\n // But we can still let the AI check the text.\n compliance.status = 'manual_review';\n compliance.issues.push('No character ID found or verification failed');\n } else {\n // Check Wallet Balance (must be positive, but API doesn't show wallet)\n // Check Kill Rights (must be 0)\n // EveWho API returns: { info: { kill_rights: ... }, ... }\n // Note: EveWho API structure might vary. Assuming standard ESI-like or EveWho structure.\n \n // Actually, EveWho API is often just scraping or basic info.\n // Let's assume we check what we can.\n \n if (eveData.kill_rights && eveData.kill_rights > 0) {\n compliance.status = 'non_compliant';\n compliance.issues.push('Character has kill rights');\n }\n \n // Security Status check?\n if (eveData.sec_status && eveData.sec_status < -2.0) {\n // compliance.issues.push('Low security status');\n }\n }\n\n item.json.compliance_check = compliance;\n return item;\n});"
},
"id": "Compliance Logic",
"name": "Compliance Logic",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
3584,
-80
]
},
{
"parameters": {
"jsCode": "const items = $input.all();\nreturn items.map(item => {\n const json = item.json;\n const newItemJson = { ...json }; // Clone to avoid mutation issues\n\n // Ensure post_id is preserved and valid\n if (!newItemJson.post_id) {\n // Try to recover from other fields if possible, or generate debug ID\n newItemJson.post_id = json.id || json.guid || (\"MISSING_\" + Math.random().toString(36).substring(7));\n }\n\n // Map AI output to status/issues\n if (newItemJson.output) {\n let aiData = {};\n if (typeof newItemJson.output === 'string') {\n try {\n aiData = JSON.parse(newItemJson.output);\n } catch (e) {\n aiData = { compliance_status: 'error', compliance_issues: ['Invalid JSON from AI'] };\n }\n } else if (typeof newItemJson.output === 'object') {\n aiData = newItemJson.output;\n }\n \n newItemJson.post_status = aiData.compliance_status || 'pending_manual_review';\n \n const issues = aiData.compliance_issues || [];\n newItemJson.compliance_issues = { issues: Array.isArray(issues) ? issues : [issues] };\n \n if (aiData.character_name) {\n newItemJson.character_name = aiData.character_name;\n }\n } else if (newItemJson.post_status) {\n // Bypass items (already have status)\n if (!newItemJson.compliance_issues) {\n newItemJson.compliance_issues = { issues: [] };\n }\n } else {\n // Fallback\n newItemJson.post_status = 'pending_manual_review';\n newItemJson.compliance_issues = { issues: ['No status determined'] };\n }\n\n return { json: newItemJson, pairedItem: item.pairedItem };\n});"
},
"id": "Prepare Data",
"name": "Prepare Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
4608,
-80
]
},
{
"parameters": {
"promptType": "define",
"text": "=You are a Compliance Officer for the EVE Online Character Bazaar.\nAnalyze the following forum post content.\nRules:\n1. Wallet balance must be positive.\n2. Kill rights must be disclosed (usually 'No kill rights').\n3. Jump clones must be disclosed (location and implants).\n4. Character location must be disclosed.\n\nReturn ONLY a JSON object with this structure:\n{\n \"compliance_status\": \"compliant\" | \"non_compliant\",\n \"compliance_issues\": [\"issue 1\", \"issue 2\"],\n \"character_name\": \"extracted name if found, else null\"\n}\nIf compliant, 'compliance_issues' must be an empty array.\nDo not include markdown formatting (```json). Just the raw JSON string.\n\nPost content is below\n--------------------------\n\n{{ $json.content }}",
"options": {}
},
"id": "AI Compliance Agent",
"name": "AI Compliance Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.7,
"position": [
4032,
-400
]
},
{
"parameters": {
"model": "z-ai/glm-4.6",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"typeVersion": 1,
"position": [
4104,
-176
],
"id": "98305d4e-91f6-4ca5-9883-326c4b4941d2",
"name": "OpenRouter Chat Model",
"credentials": {
"openRouterApi": {
"id": "ERgv4rlSLgq8fsK9",
"name": "OpenRouter account"
}
}
},
{
"parameters": {
"jsCode": "const items = $input.all();\nreturn items.map(item => {\n // This node was previously extracting topic IDs from GUID/Link.\n // We want to keep that behavior for the \"Lookup Posts\" node which uses post_id (topic ID).\n \n const guid = item.json.guid || item.json.link || \"\";\n const match = guid.match(/topic[-/](\\d+)/) || guid.match(/\\/t\\/[^\\/]+\\/(\\d+)/);\n \n if (match) {\n item.json.post_id = match[1];\n }\n \n // Ensure 'ids' field is NOT overwritten here if it's meant for Character IDs later.\n // But wait, 'Prepare ID List' is BEFORE 'Lookup Posts'.\n // And 'Extract Character ID' is BEFORE 'Check ID Code'.\n \n // Let's check the workflow flow again.\n // RSS -> Filter WTS -> Extract Character ID -> Check ID Code -> Verify Character\n // RSS -> Filter WTS -> Prepare ID List -> Lookup Posts\n \n // Ah! There are PARALLEL branches!\n \n return item;\n});"
},
"id": "Prepare ID List",
"name": "Prepare ID List",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2016,
-176
]
},
{
"parameters": {
"jsCode": "return $input.all();"
},
"id": "Filter New/Updated",
"name": "Filter New/Updated",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2688,
-176
]
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"id": "1",
"leftValue": "={{ $json.post_status }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "isNotEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "Check Pre-Filter",
"name": "Check Pre-Filter",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
3808,
-80
]
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"id": "1",
"leftValue": "={{ $json.extracted_char_id }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "isNotEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "Split by ID",
"name": "Split by ID",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
3136,
-80
]
},
{
"parameters": {
"jsCode": "const items = $('Prepare ID List').all();\nconst lookupResults = $input.all();\n\nreturn items.map(item => {\n const postId = item.json.post_id;\n // Ensure we handle potential type mismatches (string vs number)\n const match = lookupResults.find(r => String(r.json.post_id) === String(postId));\n \n item.json.exists = !!match;\n if (match) {\n item.json.db_status = match.json.post_status;\n item.json.db_compliance_issues = match.json.compliance_issues;\n item.json.last_reviewed_at = match.json.last_reviewed_at;\n }\n \n return item;\n});"
},
"id": "9d0240b8-2a28-46a1-828d-75bcc0e1fe89",
"name": "Merge Manual",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
2464,
-176
]
},
{
"parameters": {
"jsCode": "const aiResults = $input.all(0);\nconst bypassItems = $input.all(1);\nconst originalAiItems = $input.all(2);\n\n// Merge AI results with originals to restore fields\nconst mergedAiItems = originalAiItems.map((item, index) => {\n // Assuming 1-to-1 order with AI results\n const aiResult = aiResults[index]; \n if (aiResult) {\n // Copy AI output fields to original item\n item.json.output = aiResult.json.output;\n item.json.text = aiResult.json.text;\n }\n return item;\n});\n\n// Combine with bypass items\nreturn [...bypassItems, ...mergedAiItems];"
},
"id": "b9a2804d-fc92-492c-8855-f94f4cb180ed",
"name": "Merge AI Data Code",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
4384,
-176
]
},
{
"parameters": {},
"id": "f1d2a3f6-7d92-46e5-b192-b1df531581a6",
"name": "Pass Through",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
4096,
112
]
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst newItems = [];\nfor (const item of items) {\n const json = item.json;\n // Strict check for post_id\n if (json.post_id && json.post_id !== \"null\" && String(json.post_id).trim() !== \"\") {\n newItems.push({\n json: {\n id: json.id, // Preserve DB ID for update logic\n post_id: String(json.post_id),\n post_status: json.post_status || 'pending_manual_review',\n compliance_issues: json.compliance_issues || { issues: [] },\n last_reviewed_at: new Date().toISOString()\n }\n });\n }\n}\nreturn newItems;"
},
"id": "4dd8d766-7b16-4d4f-b84a-07954c90c42d",
"name": "Clean Data Code",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
4832,
-80
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "=SELECT * FROM character_bazaar_posts WHERE post_id IN ('{{ $json.post_id }}')",
"options": {}
},
"id": "Lookup Posts",
"name": "Lookup Posts",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.4,
"position": [
2240,
-176
],
"credentials": {
"postgres": {
"id": "5ENroY8sSuOXtF9l",
"name": "n8n-games"
}
}
},
{
"parameters": {
"operation": "upsert",
"schema": {
"__rl": true,
"mode": "list",
"value": "public"
},
"table": {
"__rl": true,
"mode": "list",
"value": "character_bazaar_posts"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"character_name": "={{ $json.character_name }}",
"compliance_issues": "={{ $json.compliance_issues }}",
"last_reviewed_at": "={{ $now }}",
"post_id": "={{ $json.post_id }}",
"post_status": "={{ $json.post_status }}"
}
},
"options": {}
},
"id": "Store Results",
"name": "Store Results",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.4,
"position": [
5056,
-80
],
"credentials": {
"postgres": {
"id": "5ENroY8sSuOXtF9l",
"name": "n8n-games"
}
}
},
{
"parameters": {
"url": "={{ $json.link }}.rss",
"options": {}
},
"id": "8417128a-ba4f-48a0-92a7-c5e7e54db8e8",
"name": "Fetch Forum Post",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
0,
-176
]
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nreturn items.map(item => {\n const rss = item.json.data;\n \n // DEBUG: Log the actual structure we're working with\n console.log('=== Parse Forum Post DEBUG ===');\n console.log('Raw data type:', typeof rss);\n console.log('Raw data length:', rss ? rss.length : 'null');\n console.log('First 500 chars:', rss ? rss.substring(0, 500) : 'null');\n \n // Extract Creator - try multiple patterns\n let creator = null;\n const creatorPatterns = [\n /<dc:creator><!\\[CDATA\\[(.*?)\\]\\]><\\/dc:creator>/,\n /<dc:creator>(.*?)<\\/dc:creator>/,\n /<span class=\"creator\"[^>]*>.*?<span[^>]* itemprop=\"name\">(.*?)<\\/span>/,\n /itemprop=\"author\"[^>]*>.*?<span[^>]* itemprop=\"name\">(.*?)<\\/span>/\n ];\n \n for (const pattern of creatorPatterns) {\n const match = rss.match(pattern);\n if (match) {\n creator = match[1];\n console.log('Creator found with pattern:', pattern.toString(), 'Value:', creator);\n break;\n }\n }\n \n // Extract Description - try multiple patterns\n let description = \"\";\n const descPatterns = [\n /<description><!\\[CDATA\\[([\\s\\S]*?)\\]\\]><\\/description>/,\n /<description>([\\s\\S]*?)<\\/description>/,\n /<div class=\"post\"[^>]* itemprop=\"text\">([\\s\\S]*?)<\\/div>/\n ];\n \n for (const pattern of descPatterns) {\n const match = rss.match(pattern);\n if (match) {\n description = match[1];\n console.log('Description found with pattern:', pattern.toString(), 'Length:', description.length);\n break;\n }\n }\n \n // Clean HTML entities\n const cleanDescription = description\n .replace(/&amp;/g, '&')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\");\n \n // Extract Qsna IDs - search in both raw and cleaned content\n const qsnaRegex = /qsna\\.eu\\/eve\\/characters\\/(\\d+)/g;\n const qsnaMatchesRaw = [...rss.matchAll(qsnaRegex)];\n const qsnaMatchesClean = [...cleanDescription.matchAll(qsnaRegex)];\n \n const qsnaIds = [...new Set([\n ...qsnaMatchesRaw.map(m => m[1]),\n ...qsnaMatchesClean.map(m => m[1])\n ])];\n \n console.log('Qsna matches in raw:', qsnaMatchesRaw.length);\n console.log('Qsna matches in clean:', qsnaMatchesClean.length);\n console.log('Final Qsna IDs:', qsnaIds);\n \n // Extract SkillQ Links - search in both raw and cleaned content\n const skillqRegex = /https?:\\/\\/skillq\\.net\\/char\\/[^\\/\\s]+\\/share\\/[a-f0-9\\-]+/g;\n const skillqMatchesRaw = rss.match(skillqRegex) || [];\n const skillqMatchesClean = cleanDescription.match(skillqRegex) || [];\n \n const skillqLinks = [...new Set([...skillqMatchesRaw, ...skillqMatchesClean])];\n \n console.log('SkillQ matches in raw:', skillqMatchesRaw.length);\n console.log('SkillQ matches in clean:', skillqMatchesClean.length);\n console.log('Final SkillQ links:', skillqLinks);\n \n item.json.creator_name = creator;\n item.json.qsna_ids = qsnaIds;\n item.json.skillq_links = skillqLinks;\n \n console.log('=== END DEBUG ===');\n \n return item;\n});"
},
"id": "ad7ed8fd-8460-4659-b9e2-ed0c50483b6c",
"name": "Parse Forum Post",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
-176
]
},
{
"parameters": {
"method": "POST",
"url": "https://esi.evetech.net/latest/universe/ids/",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify([$json.creator_name]) }}",
"options": {}
},
"id": "b3cd5b60-30d6-4089-b51f-3ccad131d548",
"name": "Resolve Creator ID",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
448,
-296
]
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst result = [];\n\n// DEBUG: Log input data structure\nconsole.log('=== Split SkillQ Links DEBUG ===');\nconsole.log('Input items length:', items.length);\nif (items.length > 0) {\n console.log('First item keys:', Object.keys(items[0].json));\n console.log('First item skillq_links:', items[0].json.skillq_links);\n console.log('First item post_id:', items[0].json.post_id);\n console.log('First item temp_id:', items[0].json.temp_id);\n}\n\nfor (const item of items) {\n const links = item.json.skillq_links || [];\n // Add temp_id to group back later\n item.json.temp_id = item.json.post_id || Math.random().toString(36).substring(7);\n\n console.log('Processing item with temp_id:', item.json.temp_id, 'links count:', links.length);\n\n if (links.length === 0) {\n item.json.scrape_skipped = true;\n result.push(item);\n console.log('No links found, marking as scrape_skipped');\n } else {\n console.log('Found links:', links);\n for (const link of links) {\n const newItem = {\n json: {\n ...item.json,\n skillq_url_to_scrape: link,\n scrape_skipped: false\n }\n };\n result.push(newItem);\n console.log('Created item for URL:', link);\n }\n }\n}\n\nconsole.log('Total output items:', result.length);\nconsole.log('=== END Split SkillQ Links DEBUG ===');\nreturn result;"
},
"id": "21333d91-85ba-4baa-a482-ec59241a076f",
"name": "Split SkillQ Links",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
896,
-176
]
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"conditions": [
{
"id": "1",
"leftValue": "={{ $json.scrape_skipped }}.toString()",
"rightValue": "true",
"operator": {
"type": "string",
"operation": "contains"
}
}
],
"combinator": "and"
},
"options": {}
},
"id": "67ec4026-ee76-4818-9018-1a289caefb76",
"name": "If Scrape Needed",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1120,
-176
]
},
{
"parameters": {
"url": "={{ $json.skillq_url_to_scrape }}",
"options": {}
},
"id": "36697258-f606-436e-a173-74577c77f502",
"name": "Scrape SkillQ",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [
1344,
-296
]
},
{
"parameters": {
"jsCode": "const items = $input.all();\nconst groups = {};\n\nfor (const item of items) {\n const id = item.json.temp_id;\n if (!groups[id]) {\n groups[id] = { ...item.json, scraped_ids: [], skillq_404: false };\n }\n \n if (!item.json.scrape_skipped) {\n const html = item.json.data || \"\";\n \n // Check for 404\n if (html.includes('<meta name=\"title\" content=\"SkillQ.net - 404\">')) {\n groups[id].skillq_404 = true;\n } else {\n // Extract ID\n const idMatch = html.match(/images\\.evetech\\.net\\/characters\\/(\\d+)\\/portrait/);\n if (idMatch) {\n groups[id].scraped_ids.push(idMatch[1]);\n }\n }\n }\n}\n\nreturn Object.values(groups).map(json => ({ json }));"
},
"id": "69371473-c0d1-400a-b7cc-6a3d4f996f87",
"name": "Aggregate SkillQ",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1568,
-176
]
},
{
"parameters": {
"jsCode": "const items = $input.all();\n\nreturn items.map(item => {\n const creatorId = item.json.characters ? item.json.characters[0].id : null;\n const creatorName = item.json.characters ? item.json.characters[0].name : item.json.creator_name;\n const qsnaIds = item.json.qsna_ids || [];\n const scrapedIds = item.json.scraped_ids || [];\n const linkIds = [...new Set([...qsnaIds, ...scrapedIds])]; // Unique IDs\n const title = (item.json.title || \"\").toUpperCase();\n \n let characterId = creatorId;\n let characterName = creatorName;\n let status = \"compliant\"; // Default\n \n // Compliance Logic\n if (item.json.skillq_404) {\n status = \"non_compliant_skillq_404\";\n } else if (linkIds.length > 0) {\n // Check if Creator ID matches any Link ID\n const match = linkIds.some(id => String(id) === String(creatorId));\n if (match) {\n status = \"compliant\";\n characterId = creatorId; // Confirmed\n } else {\n status = \"non_compliant_mismatch\";\n item.json.mismatch_ids = linkIds;\n }\n } else {\n // No Links\n if (title.includes(\"WTS\")) {\n status = \"non_compliant_missing_link\";\n } else if (title.includes(\"WTB\")) {\n status = \"ignored_wtb\";\n }\n }\n \n item.json.character_id = characterId;\n item.json.character_name = characterName;\n item.json.compliance_status_initial = status;\n \n return item;\n});"
},
"id": "91757929-87e4-480c-b2ca-6addf4e26542",
"name": "Consolidate Data",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1792,
-176
]
},
{
"parameters": {
"jsCode": "const creators = $input.all(0).map(i => i.json);\nconst posts = $input.all(1).map(i => i.json);\n\n// DEBUG: Log input structures\nconsole.log('=== Merge Creator Code DEBUG ===');\nconsole.log('Creators input length:', creators.length);\nconsole.log('Posts input length:', posts.length);\nconsole.log('First creator keys:', creators[0] ? Object.keys(creators[0]) : 'null');\nconsole.log('First post keys:', posts[0] ? Object.keys(posts[0]) : 'null');\nconsole.log('First post skillq_links:', posts[0] ? posts[0].skillq_links : 'null');\nconsole.log('First creator skillq_links:', creators[0] ? creators[0].skillq_links : 'null');\n\nconst result = posts.map((post, index) => {\n const creator = creators[index] || {};\n \n // CRITICAL FIX: Preserve ALL post data first, then add creator data\n // This ensures skillq_links and other post fields are not overwritten\n const merged = {\n json: {\n ...post, // Post data takes priority (includes skillq_links, creator_name, qsna_ids, etc.)\n ...creator // Creator data added (only characters array, should not overwrite existing fields)\n }\n };\n \n // DEBUG: Log merge result\n if (index === 0) {\n console.log('Merged result keys:', Object.keys(merged.json));\n console.log('Merged skillq_links:', merged.json.skillq_links);\n console.log('Merged creator_name:', merged.json.creator_name);\n console.log('Merged qsna_ids:', merged.json.qsna_ids);\n }\n \n return merged;\n});\n\nconsole.log('=== END Merge Creator Code DEBUG ===');\nreturn result;"
},
"id": "dac9a7f5-bff6-48de-a8a2-8a9222cb0b8a",
"name": "Merge Creator Code",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
672,
-176
]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT * FROM character_bazaar_posts WHERE post_status IN ('new', 'updated') ORDER BY last_reviewed_at ASC NULLS FIRST LIMIT 10;",
"additionalFields": {}
},
"id": "Poll Pending Posts",
"name": "Poll Pending Posts",
"type": "n8n-nodes-base.postgres",
"typeVersion": 1,
"position": [
2688,
16
],
"credentials": {
"postgres": {
"id": "5ENroY8sSuOXtF9l",
"name": "n8n-games"
}
}
}
],
"connections": {
"Check ID Code": {
"main": [
[
{
"node": "Split by ID",
"type": "main",
"index": 0
}
]
]
},
"Verify Character": {
"main": [
[
{
"node": "Compliance Logic",
"type": "main",
"index": 0
}
]
]
},
"Compliance Logic": {
"main": [
[
{
"node": "Check Pre-Filter",
"type": "main",
"index": 0
}
]
]
},
"OpenRouter Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Compliance Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Filter New/Updated": {
"main": [
[
{
"node": "Check ID Code",
"type": "main",
"index": 0
}
]
]
},
"Check Pre-Filter": {
"main": [
[
{
"node": "Prepare Data",
"type": "main",
"index": 0
},
{
"node": "Merge AI Data Code",
"type": "main",
"index": 0
},
{
"node": "Pass Through",
"type": "main",
"index": 0
}
],
[
{
"node": "AI Compliance Agent",
"type": "main",
"index": 0
}
]
]
},
"Split by ID": {
"main": [
[
{
"node": "Compliance Logic",
"type": "main",
"index": 0
}
],
[
{
"node": "Verify Character",
"type": "main",
"index": 0
}
]
]
},
"Merge Manual": {
"main": [
[
{
"node": "Filter New/Updated",
"type": "main",
"index": 0
}
]
]
},
"Merge AI Data Code": {
"main": [
[
{
"node": "Prepare Data",
"type": "main",
"index": 0
}
]
]
},
"AI Compliance Agent": {
"main": [
[
{
"node": "Merge AI Data Code",
"type": "main",
"index": 0
}
]
]
},
"Pass Through": {
"main": [
[
{
"node": "Merge AI Data Code",
"type": "main",
"index": 0
}
]
]
},
"Prepare Data": {
"main": [
[
{
"node": "Clean Data Code",
"type": "main",
"index": 0
}
]
]
},
"Prepare ID List": {
"main": [
[
{
"node": "Lookup Posts",
"type": "main",
"index": 0
}
]
]
},
"Lookup Posts": {
"main": [
[
{
"node": "Merge Manual",
"type": "main",
"index": 0
}
]
]
},
"Clean Data Code": {
"main": [
[
{
"node": "Store Results",
"type": "main",
"index": 0
}
]
]
},
"Fetch Forum Post": {
"main": [
[
{
"node": "Parse Forum Post",
"type": "main",
"index": 0
}
]
]
},
"Parse Forum Post": {
"main": [
[
{
"node": "Resolve Creator ID",
"type": "main",
"index": 0
},
{
"node": "Merge Creator Code",
"type": "main",
"index": 1
}
]
]
},
"Split SkillQ Links": {
"main": [
[
{
"node": "If Scrape Needed",
"type": "main",
"index": 0
}
]
]
},
"If Scrape Needed": {
"main": [
[
{
"node": "Scrape SkillQ",
"type": "main",
"index": 0
}
],
[
{
"node": "Aggregate SkillQ",
"type": "main",
"index": 0
}
]
]
},
"Scrape SkillQ": {
"main": [
[
{
"node": "Aggregate SkillQ",
"type": "main",
"index": 0
}
]
]
},
"Aggregate SkillQ": {
"main": [
[
{
"node": "Consolidate Data",
"type": "main",
"index": 0
}
]
]
},
"Consolidate Data": {
"main": [
[
{
"node": "Prepare ID List",
"type": "main",
"index": 0
}
]
]
},
"Resolve Creator ID": {
"main": [
[
{
"node": "Merge Creator Code",
"type": "main",
"index": 0
}
]
]
},
"Merge Creator Code": {
"main": [
[
{
"node": "Split SkillQ Links",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger": {
"main": [
[
{
"node": "Poll Pending Posts",
"type": "main",
"index": 0
}
]
]
},
"Poll Pending Posts": {
"main": [
[
{
"node": "Check ID Code",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all",
"saveManualExecutions": true,
"saveExecutionProgress": true,
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
},
"triggerCount": 0,
"versionId": "87cd1f48-d40b-4a15-81e0-0c4e17058983",
"owner": {
"type": "personal",
"projectId": "FeLO36wNUAcn61Wj",
"projectName": "Ben W <admin@ben.io>",
"personalEmail": "admin@ben.io"
},
"parentFolderId": "HWgaFb7kLF649L7l",
"isArchived": false
}