{ "name": "šŸŽÆ ULTIMATE Telegram CMS COMPLETE", "nodes": [ { "parameters": { "updates": ["message"], "additionalFields": {} }, "type": "n8n-nodes-base.telegramTrigger", "typeVersion": 1.2, "position": [0, 240], "id": "telegram-trigger-001", "name": "Telegram Trigger", "webhookId": "telegram-cms-webhook-001", "credentials": { "telegramApi": { "id": "ADurvy9EKUDzbDdq", "name": "DK0_Server" } } }, { "parameters": { "jsCode": "const text = $input.first().json.message?.text ?? '';\nconst chatId = $input.first().json.message?.chat?.id;\nlet match;\n\n// /start - Dashboard\nif (text === '/start') {\n return [{ json: { action: 'start', chatId } }];\n}\n\n// /list projects|books\nmatch = text.match(/^\\/list\\s+(projects|books)/);\nif (match) {\n return [{ json: { action: 'list', type: match[1], page: 1, chatId } }];\n}\n\n// /search \nmatch = text.match(/^\\/search\\s+(.+)/);\nif (match) {\n return [{ json: { action: 'search', query: match[1], chatId } }];\n}\n\n// /stats\nif (text === '/stats') {\n return [{ json: { action: 'stats', chatId } }];\n}\n\n// /preview \nmatch = text.match(/^\\/preview(\\d+)/);\nif (match) {\n return [{ json: { action: 'preview', id: match[1], chatId } }];\n}\n\n// /publish or /publishproject or /publishbook\nmatch = text.match(/^\\/publish(?:project|book)?(\\d+)/);\nif (match) {\n return [{ json: { action: 'publish', id: match[1], chatId } }];\n}\n\n// /delete or /deleteproject or /deletebook\nmatch = text.match(/^\\/delete(?:project|book)?(\\d+)/);\nif (match) {\n return [{ json: { action: 'delete', id: match[1], chatId } }];\n}\n\n// /deletereview\nmatch = text.match(/^\\/deletereview(\\d+)/);\nif (match) {\n return [{ json: { action: 'delete_review', id: match[1], chatId } }];\n}\n\n// .review \nif (text.startsWith('.review') || text.startsWith('/review')) {\n const rest = text.replace(/^[\\.|\\/]review/, '').trim();\n match = rest.match(/^([0-9]+)\\s+([0-9]+)\\s+(.+)/);\n if (match) {\n return [{ json: { action: 'create_review', hardcoverId: match[1], rating: parseInt(match[2]), answers: match[3], chatId } }];\n }\n}\n\n// Unknown\nreturn [{ json: { action: 'unknown', chatId, text } }];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [240, 240], "id": "parse-command-001", "name": "Parse Command" }, { "parameters": { "rules": { "values": [ { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "start", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "start" }, { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "list", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "list" }, { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "search", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "search" }, { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "stats", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "stats" }, { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "preview", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "preview" }, { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "publish", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "publish" }, { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "delete", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "delete" }, { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "delete_review", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "delete_review" }, { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "create_review", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "create_review" }, { "conditions": { "conditions": [ { "leftValue": "={{ $json.action }}", "rightValue": "unknown", "operator": { "type": "string", "operation": "equals" } } ] }, "renameOutput": true, "outputKey": "unknown" } ] }, "options": {} }, "type": "n8n-nodes-base.switch", "typeVersion": 3.4, "position": [480, 240], "id": "router-001", "name": "Command Router" }, { "parameters": { "jsCode": "try {\n const chatId = $input.first().json.chatId;\n \n // Fetch projects count\n const projectsResp = await this.helpers.httpRequest({\n method: 'GET',\n url: 'https://cms.dk0.dev/items/projects?aggregate[count]=id&filter[status][_eq]=draft',\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n });\n const draftProjects = projectsResp?.data?.[0]?.count?.id || 0;\n \n // Fetch books count\n const booksResp = await this.helpers.httpRequest({\n method: 'GET',\n url: 'https://cms.dk0.dev/items/book_reviews?aggregate[count]=id&filter[status][_eq]=draft',\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n });\n const draftBooks = booksResp?.data?.[0]?.count?.id || 0;\n \n const message = `šŸŽÆ *DK0 Portfolio CMS*\\n\\n` +\n `šŸ“Š *Stats:*\\n` +\n `• Draft Projects: ${draftProjects}\\n` +\n `• Draft Reviews: ${draftBooks}\\n\\n` +\n `šŸ’” *Quick Actions:*\\n` +\n `/list projects - View all projects\\n` +\n `/list books - View book reviews\\n` +\n `/search - Search content\\n` +\n `/stats - Detailed statistics\\n\\n` +\n `šŸ“ *Management:*\\n` +\n `/preview - Preview item\\n` +\n `/publish - Publish item\\n` +\n `/delete - Delete item\\n\\n` +\n `āœļø *Create Review:*\\n` +\n \\`.review \\`;\n \n return [{ json: { chatId, message, parseMode: 'Markdown' } }];\n} catch (error) {\n console.error('Dashboard Error:', error);\n return [{ json: { \n chatId: $input.first().json.chatId, \n message: 'āŒ Error loading dashboard: ' + error.message,\n parseMode: 'Markdown'\n } }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, -120], "id": "dashboard-001", "name": "Dashboard Handler" }, { "parameters": { "jsCode": "try {\n const { type, page = 1, chatId } = $input.first().json;\n const limit = 5;\n const offset = (page - 1) * limit;\n const collection = type === 'projects' ? 'projects' : 'book_reviews';\n \n // Fetch items\n const response = await this.helpers.httpRequest({\n method: 'GET',\n url: `https://cms.dk0.dev/items/${collection}?limit=${limit}&offset=${offset}&sort=-date_created&fields=id,${type === 'projects' ? 'slug,category' : 'book_title,rating'},status,date_created,translations.*`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n });\n \n const items = response?.data || [];\n const total = items.length;\n \n if (total === 0) {\n return [{ json: { chatId, message: `šŸ“­ No ${type} found.`, parseMode: 'Markdown' } }];\n }\n \n let message = `šŸ“‹ *${type.toUpperCase()} (Page ${page})*\\n\\n`;\n \n items.forEach((item, idx) => {\n const num = offset + idx + 1;\n if (type === 'projects') {\n const title = item.translations?.[0]?.title || item.slug || 'Untitled';\n message += `${num}. *${title}*\\n`;\n message += ` Category: ${item.category || 'N/A'}\\n`;\n message += ` Status: ${item.status}\\n`;\n message += ` /preview${item.id} | /publish${item.id} | /delete${item.id}\\n\\n`;\n } else {\n message += `${num}. *${item.book_title || 'Untitled'}*\\n`;\n message += ` Rating: ${'⭐'.repeat(item.rating || 0)}/5\\n`;\n message += ` Status: ${item.status}\\n`;\n message += ` /preview${item.id} | /publish${item.id} | /delete${item.id}\\n\\n`;\n }\n });\n \n if (total === limit) {\n message += `\\nāž”ļø More items available. Use /list ${type} for next page.`;\n }\n \n return [{ json: { chatId, message, parseMode: 'Markdown' } }];\n} catch (error) {\n console.error('List Error:', error);\n return [{ json: { \n chatId: $input.first().json.chatId, \n message: 'āŒ Error fetching list: ' + error.message,\n parseMode: 'Markdown'\n } }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, 0], "id": "list-handler-001", "name": "List Handler" }, { "parameters": { "jsCode": "try {\n const { query, chatId } = $input.first().json;\n \n // Search projects\n const projectsResp = await this.helpers.httpRequest({\n method: 'GET',\n url: `https://cms.dk0.dev/items/projects?filter[translations][title][_contains]=${encodeURIComponent(query)}&limit=5&fields=id,slug,category,translations.*`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n });\n \n // Search books\n const booksResp = await this.helpers.httpRequest({\n method: 'GET',\n url: `https://cms.dk0.dev/items/book_reviews?filter[book_title][_contains]=${encodeURIComponent(query)}&limit=5&fields=id,book_title,book_author,rating`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n });\n \n const projects = projectsResp?.data || [];\n const books = booksResp?.data || [];\n \n if (projects.length === 0 && books.length === 0) {\n return [{ json: { chatId, message: `šŸ” No results for \"${query}\"`, parseMode: 'Markdown' } }];\n }\n \n let message = `šŸ” *Search Results: \"${query}\"*\\n\\n`;\n \n if (projects.length > 0) {\n message += `šŸ“ *Projects (${projects.length}):*\\n`;\n projects.forEach(p => {\n const title = p.translations?.[0]?.title || p.slug || 'Untitled';\n message += `• ${title} - /preview${p.id}\\n`;\n });\n message += '\\n';\n }\n \n if (books.length > 0) {\n message += `šŸ“š *Books (${books.length}):*\\n`;\n books.forEach(b => {\n message += `• ${b.book_title} by ${b.book_author} - /preview${b.id}\\n`;\n });\n }\n \n return [{ json: { chatId, message, parseMode: 'Markdown' } }];\n} catch (error) {\n console.error('Search Error:', error);\n return [{ json: { \n chatId: $input.first().json.chatId, \n message: 'āŒ Error searching: ' + error.message,\n parseMode: 'Markdown'\n } }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, 120], "id": "search-handler-001", "name": "Search Handler" }, { "parameters": { "jsCode": "try {\n const chatId = $input.first().json.chatId;\n \n // Fetch projects stats\n const projectsResp = await this.helpers.httpRequest({\n method: 'GET',\n url: 'https://cms.dk0.dev/items/projects?fields=id,category,status,date_created',\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n });\n \n // Fetch books stats\n const booksResp = await this.helpers.httpRequest({\n method: 'GET',\n url: 'https://cms.dk0.dev/items/book_reviews?fields=id,rating,status,date_created',\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n });\n \n const projects = projectsResp?.data || [];\n const books = booksResp?.data || [];\n \n // Calculate stats\n const projectStats = {\n total: projects.length,\n published: projects.filter(p => p.status === 'published').length,\n draft: projects.filter(p => p.status === 'draft').length,\n archived: projects.filter(p => p.status === 'archived').length\n };\n \n const bookStats = {\n total: books.length,\n published: books.filter(b => b.status === 'published').length,\n draft: books.filter(b => b.status === 'draft').length,\n avgRating: books.length > 0 ? (books.reduce((sum, b) => sum + (b.rating || 0), 0) / books.length).toFixed(1) : 0\n };\n \n // Category breakdown\n const categories = {};\n projects.forEach(p => {\n if (p.category) {\n categories[p.category] = (categories[p.category] || 0) + 1;\n }\n });\n \n let message = `šŸ“Š *DK0 Portfolio Statistics*\\n\\n`;\n message += `šŸ“ *Projects:*\\n`;\n message += `• Total: ${projectStats.total}\\n`;\n message += `• Published: ${projectStats.published}\\n`;\n message += `• Draft: ${projectStats.draft}\\n`;\n message += `• Archived: ${projectStats.archived}\\n\\n`;\n \n message += `šŸ“š *Book Reviews:*\\n`;\n message += `• Total: ${bookStats.total}\\n`;\n message += `• Published: ${bookStats.published}\\n`;\n message += `• Draft: ${bookStats.draft}\\n`;\n message += `• Avg Rating: ${bookStats.avgRating}/5 ⭐\\n\\n`;\n \n if (Object.keys(categories).length > 0) {\n message += `šŸ·ļø *Project Categories:*\\n`;\n Object.entries(categories).sort((a, b) => b[1] - a[1]).forEach(([cat, count]) => {\n message += `• ${cat}: ${count}\\n`;\n });\n }\n \n return [{ json: { chatId, message, parseMode: 'Markdown' } }];\n} catch (error) {\n console.error('Stats Error:', error);\n return [{ json: { \n chatId: $input.first().json.chatId, \n message: 'āŒ Error loading stats: ' + error.message,\n parseMode: 'Markdown'\n } }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, 240], "id": "stats-handler-001", "name": "Stats Handler" }, { "parameters": { "jsCode": "try {\n const { id, chatId } = $input.first().json;\n \n // Try projects first\n let response = await this.helpers.httpRequest({\n method: 'GET',\n url: `https://cms.dk0.dev/items/projects/${id}?fields=id,slug,category,status,date_created,translations.*`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' },\n returnFullResponse: true\n }).catch(() => null);\n \n let collection = 'projects';\n let item = response?.body?.data;\n \n // If not found in projects, try books\n if (!item) {\n response = await this.helpers.httpRequest({\n method: 'GET',\n url: `https://cms.dk0.dev/items/book_reviews/${id}?fields=id,book_title,book_author,book_image,rating,status,hardcover_id,translations.*`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' },\n returnFullResponse: true\n }).catch(() => null);\n collection = 'book_reviews';\n item = response?.body?.data;\n }\n \n if (!item) {\n return [{ json: { chatId, message: `āŒ Item #${id} not found in any collection.`, parseMode: 'Markdown' } }];\n }\n \n let message = `šŸ‘ļø *Preview #${id}*\\n\\n`;\n \n if (collection === 'projects') {\n message += `šŸ“ *Type:* Project\\n`;\n message += `šŸ”– *Slug:* ${item.slug}\\n`;\n message += `šŸ·ļø *Category:* ${item.category || 'N/A'}\\n`;\n message += `šŸ“Š *Status:* ${item.status}\\n\\n`;\n \n const translations = item.translations || [];\n translations.forEach(t => {\n const lang = t.languages_code === 'en-US' ? 'šŸ‡¬šŸ‡§ EN' : 'šŸ‡©šŸ‡Ŗ DE';\n message += `${lang}:\\n`;\n message += `*Title:* ${t.title || 'N/A'}\\n`;\n message += `*Description:* ${(t.description || 'N/A').substring(0, 100)}...\\n\\n`;\n });\n } else {\n message += `šŸ“š *Type:* Book Review\\n`;\n message += `šŸ“– *Title:* ${item.book_title}\\n`;\n message += `āœļø *Author:* ${item.book_author}\\n`;\n message += `⭐ *Rating:* ${item.rating}/5\\n`;\n message += `šŸ“Š *Status:* ${item.status}\\n`;\n message += `šŸ”— *Hardcover ID:* ${item.hardcover_id}\\n\\n`;\n \n const translations = item.translations || [];\n translations.forEach(t => {\n const lang = t.languages_code === 'en-US' ? 'šŸ‡¬šŸ‡§ EN' : 'šŸ‡©šŸ‡Ŗ DE';\n message += `${lang}:\\n`;\n message += `${(t.review || 'No review').substring(0, 200)}...\\n\\n`;\n });\n }\n \n message += `\\n*Actions:*\\n`;\n message += `/publish${id} - Publish\\n`;\n message += `/delete${id} - Delete`;\n \n return [{ json: { chatId, message, parseMode: 'Markdown', collection, itemId: id } }];\n} catch (error) {\n console.error('Preview Error:', error);\n return [{ json: { \n chatId: $input.first().json.chatId, \n message: 'āŒ Error loading preview: ' + error.message,\n parseMode: 'Markdown'\n } }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, 360], "id": "preview-handler-001", "name": "Preview Handler" }, { "parameters": { "jsCode": "try {\n const { id, chatId } = $input.first().json;\n \n // Try projects first\n let response = await this.helpers.httpRequest({\n method: 'PATCH',\n url: `https://cms.dk0.dev/items/projects/${id}`,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB'\n },\n body: { status: 'published' },\n returnFullResponse: true\n }).catch(() => null);\n \n let collection = 'projects';\n let title = 'Project';\n \n // If not found in projects, try books\n if (!response || response.statusCode >= 400) {\n response = await this.helpers.httpRequest({\n method: 'PATCH',\n url: `https://cms.dk0.dev/items/book_reviews/${id}`,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB'\n },\n body: { status: 'published' },\n returnFullResponse: true\n }).catch(() => null);\n collection = 'book_reviews';\n title = 'Book Review';\n }\n \n if (!response || response.statusCode >= 400) {\n return [{ json: { chatId, message: `āŒ Item #${id} not found or could not be published.`, parseMode: 'Markdown' } }];\n }\n \n const message = `āœ… *${title} #${id} Published!*\\n\\nThe item is now live on dk0.dev.`;\n \n return [{ json: { chatId, message, parseMode: 'Markdown', collection, itemId: id } }];\n} catch (error) {\n console.error('Publish Error:', error);\n return [{ json: { \n chatId: $input.first().json.chatId, \n message: 'āŒ Error publishing item: ' + error.message,\n parseMode: 'Markdown'\n } }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, 480], "id": "publish-handler-001", "name": "Publish Handler" }, { "parameters": { "jsCode": "try {\n const { id, chatId } = $input.first().json;\n \n // Try projects first\n let response = await this.helpers.httpRequest({\n method: 'DELETE',\n url: `https://cms.dk0.dev/items/projects/${id}`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' },\n returnFullResponse: true\n }).catch(() => null);\n \n let collection = 'projects';\n let title = 'Project';\n \n // If not found in projects, try books\n if (!response || response.statusCode >= 400) {\n response = await this.helpers.httpRequest({\n method: 'DELETE',\n url: `https://cms.dk0.dev/items/book_reviews/${id}`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' },\n returnFullResponse: true\n }).catch(() => null);\n collection = 'book_reviews';\n title = 'Book Review';\n }\n \n if (!response || response.statusCode >= 400) {\n return [{ json: { chatId, message: `āŒ Item #${id} not found or could not be deleted.`, parseMode: 'Markdown' } }];\n }\n \n const message = `šŸ—‘ļø *${title} #${id} Deleted*\\n\\nThe item has been permanently removed from the CMS.`;\n \n return [{ json: { chatId, message, parseMode: 'Markdown', collection, itemId: id } }];\n} catch (error) {\n console.error('Delete Error:', error);\n return [{ json: { \n chatId: $input.first().json.chatId, \n message: 'āŒ Error deleting item: ' + error.message,\n parseMode: 'Markdown'\n } }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, 600], "id": "delete-handler-001", "name": "Delete Handler" }, { "parameters": { "jsCode": "try {\n const { id, chatId } = $input.first().json;\n \n // Fetch the book review to get translation IDs\n const bookResp = await this.helpers.httpRequest({\n method: 'GET',\n url: `https://cms.dk0.dev/items/book_reviews/${id}?fields=id,book_title,translations.id`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n });\n \n const book = bookResp?.data;\n if (!book) {\n return [{ json: { chatId, message: `āŒ Book review #${id} not found.`, parseMode: 'Markdown' } }];\n }\n \n const translations = book.translations || [];\n let deletedCount = 0;\n \n // Delete each translation\n for (const trans of translations) {\n await this.helpers.httpRequest({\n method: 'DELETE',\n url: `https://cms.dk0.dev/items/book_reviews_translations/${trans.id}`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n }).catch(() => {});\n deletedCount++;\n }\n \n const message = `šŸ—‘ļø *Deleted ${deletedCount} review translations for \"${book.book_title}\"*\\n\\nThe review text has been removed. The book entry still exists.`;\n \n return [{ json: { chatId, message, parseMode: 'Markdown', itemId: id, deletedCount } }];\n} catch (error) {\n console.error('Delete Review Error:', error);\n return [{ json: { \n chatId: $input.first().json.chatId, \n message: 'āŒ Error deleting review: ' + error.message,\n parseMode: 'Markdown'\n } }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, 720], "id": "delete-review-handler-001", "name": "Delete Review Handler" }, { "parameters": { "jsCode": "try {\n const { hardcoverId, rating, answers, chatId } = $input.first().json;\n \n // Check if book exists\n const checkResp = await this.helpers.httpRequest({\n method: 'GET',\n url: `https://cms.dk0.dev/items/book_reviews?filter[hardcover_id][_eq]=${hardcoverId}&fields=id,book_title,book_author,book_image,finished_at&limit=1`,\n headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }\n });\n \n const book = checkResp?.data?.[0];\n if (!book) {\n return [{ json: { chatId, message: `āŒ Book with Hardcover ID ${hardcoverId} not found.`, parseMode: 'Markdown' } }];\n }\n \n // Build AI prompt\n const promptParts = [\n 'Schreibe eine authentische Buchbewertung.',\n `Buch: ${book.book_title} von ${book.book_author}`,\n `Rating: ${rating}/5`,\n `Antworten des Lesers: ${answers}`,\n 'Schreibe Ich-Perspektive, 4-6 Saetze pro Sprache.',\n 'Antworte NUR als JSON:',\n '{\"review_en\": \"English\", \"review_de\": \"Deutsch\"}'\n ];\n const prompt = promptParts.join(' ');\n \n // Call AI\n const aiResp = await this.helpers.httpRequest({\n method: 'POST',\n url: 'https://openrouter.ai/api/v1/chat/completions',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer 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'\n },\n body: {\n model: 'google/gemini-2.0-flash-exp:free',\n messages: [{ role: 'user', content: prompt }]\n }\n });\n \n const aiText = aiResp?.choices?.[0]?.message?.content || '{}';\n const match = aiText.match(/\\{[\\s\\S]*\\}/);\n const ai = match ? JSON.parse(match[0]) : { review_en: answers, review_de: answers };\n \n // Update book review with translations\n const updateResp = await this.helpers.httpRequest({\n method: 'PATCH',\n url: `https://cms.dk0.dev/items/book_reviews/${book.id}`,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB'\n },\n body: {\n rating: rating,\n status: 'draft',\n translations: {\n create: [\n { languages_code: 'en-US', review: ai.review_en },\n { languages_code: 'de-DE', review: ai.review_de }\n ]\n }\n }\n });\n \n const message = `āœ… *Review created for \"${book.book_title}\"*\\n\\n` +\n `⭐ Rating: ${rating}/5\\n\\n` +\n `šŸ‡¬šŸ‡§ EN: ${ai.review_en.substring(0, 100)}...\\n\\n` +\n `šŸ‡©šŸ‡Ŗ DE: ${ai.review_de.substring(0, 100)}...\\n\\n` +\n `*Actions:*\\n` +\n `/publishbook${book.id} - Publish\\n` +\n `/deletebook${book.id} - Delete`;\n \n return [{ json: { chatId, message, parseMode: 'Markdown', bookId: book.id, rating } }];\n} catch (error) {\n console.error('Create Review Error:', error);\n return [{ json: { \n chatId: $input.first().json.chatId, \n message: 'āŒ Error creating review: ' + error.message,\n parseMode: 'Markdown'\n } }];\n}" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, 840], "id": "create-review-handler-001", "name": "Create Review Handler" }, { "parameters": { "jsCode": "const { chatId } = $input.first().json;\nconst message = `ā“ *Unknown Command*\\n\\nAvailable commands:\\n` +\n `/start - Dashboard\\n` +\n `/list projects|books - List items\\n` +\n `/search - Search\\n` +\n `/stats - Statistics\\n` +\n `/preview - Preview item\\n` +\n `/publish - Publish item\\n` +\n `/delete - Delete item\\n` +\n `/deletereview - Delete review translations\\n` +\n \\`.review - Create review\\`;\n\nreturn [{ json: { chatId, message, parseMode: 'Markdown' } }];" }, "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [720, 960], "id": "unknown-handler-001", "name": "Unknown Command Handler" }, { "parameters": { "chatId": "={{ $json.chatId }}", "text": "={{ $json.message }}", "additionalFields": { "parse_mode": "={{ $json.parseMode || 'Markdown' }}" } }, "type": "n8n-nodes-base.telegram", "typeVersion": 1.2, "position": [960, 420], "id": "send-message-001", "name": "Send Telegram Message", "credentials": { "telegramApi": { "id": "ADurvy9EKUDzbDdq", "name": "DK0_Server" } } } ], "connections": { "Telegram Trigger": { "main": [ [ { "node": "Parse Command", "type": "main", "index": 0 } ] ] }, "Parse Command": { "main": [ [ { "node": "Command Router", "type": "main", "index": 0 } ] ] }, "Command Router": { "main": [ [ { "node": "Dashboard Handler", "type": "main", "index": 0 } ], [ { "node": "List Handler", "type": "main", "index": 0 } ], [ { "node": "Search Handler", "type": "main", "index": 0 } ], [ { "node": "Stats Handler", "type": "main", "index": 0 } ], [ { "node": "Preview Handler", "type": "main", "index": 0 } ], [ { "node": "Publish Handler", "type": "main", "index": 0 } ], [ { "node": "Delete Handler", "type": "main", "index": 0 } ], [ { "node": "Delete Review Handler", "type": "main", "index": 0 } ], [ { "node": "Create Review Handler", "type": "main", "index": 0 } ], [ { "node": "Unknown Command Handler", "type": "main", "index": 0 } ] ] }, "Dashboard Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] }, "List Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] }, "Search Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] }, "Stats Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] }, "Preview Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] }, "Publish Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] }, "Delete Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] }, "Delete Review Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] }, "Create Review Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] }, "Unknown Command Handler": { "main": [ [ { "node": "Send Telegram Message", "type": "main", "index": 0 } ] ] } }, "pinData": {}, "settings": { "executionOrder": "v1" }, "staticData": null, "tags": [], "triggerCount": 1, "updatedAt": "2025-01-21T00:00:00.000Z", "versionId": "1" }