- ReadBooks.tsx: remove line-clamp-3, readMore button, and review modal - Show full review text inline instead of truncated snippets - Remove unused AnimatePresence, X import, selectedReview state - Fix typo in 6 handler nodes - Fix Markdown/HTML mix (*text*</b> → <b>text</b>) - Fix Switch condition syntax (.action → .action) - Fix position collision (Review Info Handler) - Hardcode Telegram bot token, fix response handling in Publish Handler - Add AI-generated questions for .review flow (was .review HC_ID TEXT) - New .answer command for submitting review answers - Create/Refine Review: POST new translations if missing instead of skipping - Remove all substring truncations from Telegram messages
740 lines
52 KiB
JSON
740 lines
52 KiB
JSON
{
|
|
"name": "🎯 ULTIMATE Telegram CMS COMPLETE",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"updates": [
|
|
"message",
|
|
"callback_query"
|
|
],
|
|
"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 input = $input.first().json;\nconst token = '8166414331:AAGNQ6fn2juD5esaTRxPjtTdSMkwq_oASIc';\n\nif (input.callback_query) {\n const cbq = input.callback_query;\n const chatId = cbq.message.chat.id;\n const data = cbq.data;\n const callbackQueryId = cbq.id;\n \n if (token) {\n try {\n await this.helpers.httpRequest({ \n method: 'POST', \n url: 'https://api.telegram.org/bot' + token + '/answerCallbackQuery', \n headers: { 'Content-Type': 'application/json' }, \n body: { callback_query_id: callbackQueryId } \n });\n } catch(e) {}\n }\n \n const parts = data.split(':');\n const action = parts[0];\n \n if (action === 'start') return [{ json: { action: 'start', chatId } }];\n if (action === 'stats') return [{ json: { action: 'stats', chatId } }];\n if (action === 'list') return [{ json: { action: 'list', type: parts[1], page: parseInt(parts[2] || '1'), chatId } }];\n if (action === 'preview') return [{ json: { action: 'preview', collectionType: parts[1], id: parts[2], chatId } }];\n if (action === 'publish') return [{ json: { action: 'publish', collectionType: parts[1], id: parts[2], chatId } }];\n if (action === 'delete') return [{ json: { action: 'delete', collectionType: parts[1], id: parts[2], chatId } }];\n if (action === 'review_info') return [{ json: { action: 'review_info', id: parts[1], chatId } }];\n \n return [{ json: { action: 'unknown', chatId } }];\n}\n\nconst text = input.message?.text ?? '';\nconst chatId = input.message?.chat?.id;\nlet match;\n\nif (text === '/start') return [{ json: { action: 'start', chatId } }];\nif (text === '/stats') return [{ json: { action: 'stats', chatId } }];\n\nmatch = text.match(/^\\/list\\s+(projects|books)(?:\\s+(\\d+))?/);\nif (match) return [{ json: { action: 'list', type: match[1], page: parseInt(match[2] || '1'), chatId } }];\n\nmatch = text.match(/^\\/preview\\s*(project|book)?(\\d+)/);\nif (match) {\n const typePrefix = match[1] === 'project' ? 'projects' : match[1] === 'book' ? 'book_reviews' : 'projects';\n return [{ json: { action: 'preview', collectionType: typePrefix, id: match[2], chatId } }];\n}\n\nmatch = text.match(/^\\/search\\s+(.+)/);\nif (match) return [{ json: { action: 'search', query: match[1].trim(), chatId } }];\n\nmatch = text.match(/^\\/publish\\s*(project|book)?(\\d+)/);\nif (match) {\n const typePrefix = match[1] === 'book' ? 'books' : 'projects';\n return [{ json: { action: 'publish', collectionType: typePrefix, id: match[2], chatId } }];\n}\n\nmatch = text.match(/^\\/delete\\s*(project|book)?(\\d+)/);\nif (match) {\n const typePrefix = match[1] === 'book' ? 'books' : 'projects';\n return [{ json: { action: 'delete', collectionType: typePrefix, id: match[2], chatId } }];\n}\n\n// .review HC_ID [RATING] -> starts review process with AI questions\nmatch = text.match(/^\\.review\\s+(\\d+)(?:\\s+([1-5]))?/);\nif (match) return [{ json: { action: 'review_info', hardcoverId: match[1], rating: match[2] ? parseInt(match[2]) : 0, chatId } }];\n\n// .answer BOOK_ID RATING your answers -> submit review answers\nmatch = text.match(/^\\.answer\\s+(\\d+)\\s+([1-5])\\s+(.+)/);\nif (match) return [{ json: { action: 'answer_review', bookId: match[1], rating: parseInt(match[2]), answers: match[3].trim(), chatId } }];\n\nmatch = text.match(/^\\.refine\\s+(\\d+)\\s+(.+)/);\nif (match) return [{ json: { action: 'refine_review', id: match[1], feedback: match[2].trim(), chatId } }];\n\nreturn [{ json: { action: 'unknown', chatId } }];\n"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
240,
|
|
240
|
|
],
|
|
"id": "global-parser-001",
|
|
"name": "Global Parser"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"rules": {
|
|
"values": [
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "start",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "start"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "list",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "list"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "search",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "search"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "stats",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "stats"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "preview",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "preview"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "publish",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "publish"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "delete",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "delete"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "delete_review",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "delete_review"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "answer_review",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "answer_review"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
},
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "refine_review",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "refine_review"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "unknown",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "unknown"
|
|
},
|
|
{
|
|
"conditions": {
|
|
"conditions": [
|
|
{
|
|
"leftValue": "={{ $json.action }}",
|
|
"rightValue": "review_info",
|
|
"operator": {
|
|
"type": "string",
|
|
"operation": "equals"
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and",
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": ""
|
|
}
|
|
},
|
|
"renameOutput": true,
|
|
"outputKey": "review_info"
|
|
}
|
|
]
|
|
},
|
|
"options": {}
|
|
},
|
|
"type": "n8n-nodes-base.switch",
|
|
"typeVersion": 3.2,
|
|
"position": [
|
|
480,
|
|
240
|
|
],
|
|
"id": "router-001",
|
|
"name": "Command Router"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "\ntry {\n var chatId = $input.first().json.chatId;\n var projectsResp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/projects?aggregate[count]=id&filter[status][_eq]=draft', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var draftProjects = (projectsResp && projectsResp.data && projectsResp.data[0] && projectsResp.data[0].count && projectsResp.data[0].count.id) || 0;\n var booksResp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews?aggregate[count]=id&filter[status][_eq]=draft', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var draftBooks = (booksResp && booksResp.data && booksResp.data[0] && booksResp.data[0].count && booksResp.data[0].count.id) || 0;\n var message = '\\u{1F3AF} <b>DK0 Portfolio CMS</b>\\n\\n\\u{1F4CA} <b>Status:</b>\\n\\u2022 Draft Projects: ' + draftProjects + '\\n\\u2022 Draft Reviews: ' + draftBooks + '\\n\\nTap a button to navigate.';\n var keyboard = [\n [{ text: '\\u{1F4CB} Projects', callback_data: 'list:projects:1' }, { text: '\\u{1F4DA} Books', callback_data: 'list:books:1' }],\n [{ text: '\\u{1F4CA} Stats', callback_data: 'stats' }]\n ];\n return [{ json: { chatId: chatId, message: message, parseMode: 'HTML', keyboard: keyboard } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Error loading dashboard: ' + error.message, parseMode: 'HTML' } }];\n}\n"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
-120
|
|
],
|
|
"id": "dashboard-001",
|
|
"name": "Dashboard Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "\ntry {\n var input = $input.first().json;\n var type = input.type;\n var page = input.page || 1;\n var chatId = input.chatId;\n var limit = 5;\n var offset = (page - 1) * limit;\n var collection = type === 'projects' ? 'projects' : 'book_reviews';\n var fields = type === 'projects' ? 'id,slug,category,status,date_created,translations.*' : 'id,book_title,rating,status,finished_at';\n var url = 'https://cms.dk0.dev/items/' + collection + '?limit=' + limit + '&offset=' + offset + '&sort=' + (type === 'projects' ? '-date_created' : '-finished_at') + '&fields=' + fields;\n var response = await this.helpers.httpRequest({ method: 'GET', url: url, headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var items = (response && response.data) || [];\n if (items.length === 0) {\n return [{ json: { chatId: chatId, message: 'No ' + type + ' found.', parseMode: 'HTML', keyboard: [[{ text: '\\u{1F3E0} Home', callback_data: 'start' }]] } }];\n }\n var message = '<b>' + type.toUpperCase() + ' (Page ' + page + ')</b>\\n\\n';\n var keyboard = [];\n items.forEach(function(item, idx) {\n var num = idx + 1;\n var displayNum = (offset || 0) + num;\n if (type === 'projects') {\n var title = (item.translations && item.translations[0] && item.translations[0].title) || item.slug || 'Untitled';\n message += displayNum + '. <b>' + title + '</b>\\n ' + (item.category || 'N/A') + ' | ' + item.status + '\\n\\n';\n } else {\n var stars = '';\n for (var s = 0; s < (item.rating || 0); s++) { stars += '\\u2B50'; }\n message += displayNum + '. <b>' + (item.book_title || 'Untitled') + '</b>\\n ' + stars + ' | ' + item.status + '\\n\\n';\n }\n var row = [\n { text: '\\u{1F441} #' + displayNum, callback_data: 'preview:' + type + ':' + item.id },\n { text: '\\u2705 Pub #' + displayNum, callback_data: 'publish:' + type + ':' + item.id }\n ];\n if (type === 'books' && item.status === 'draft') {\n row.push({ text: '\\u270D\\uFE0F Review #' + displayNum, callback_data: 'review_info:' + item.id });\n }\n row.push({ text: '\\u{1F5D1} Del #' + displayNum, callback_data: 'delete:' + type + ':' + item.id });\n keyboard.push(row);\n });\n var navRow = [];\n if (page > 1) { navRow.push({ text: '\\u2190 Prev', callback_data: 'list:' + type + ':' + (page - 1) }); }\n if (items.length === limit) { navRow.push({ text: 'Next \\u2192', callback_data: 'list:' + type + ':' + (page + 1) }); }\n navRow.push({ text: '\\u{1F3E0} Home', callback_data: 'start' });\n keyboard.push(navRow);\n return [{ json: { chatId: chatId, message: message, parseMode: 'HTML', keyboard: keyboard } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Error fetching list: ' + error.message, parseMode: 'HTML' } }];\n}\n"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
0
|
|
],
|
|
"id": "list-handler-001",
|
|
"name": "List Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "try {\n var input = $input.first().json;\n var query = input.query;\n var chatId = input.chatId;\n var encoded = encodeURIComponent(query);\n var projectsResp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/projects?filter[translations][title][_contains]=' + encoded + '&limit=5&fields=id,slug,category,translations.*', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var booksResp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews?filter[book_title][_contains]=' + encoded + '&limit=5&fields=id,book_title,book_author,rating', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var projects = (projectsResp && projectsResp.data) || [];\n var books = (booksResp && booksResp.data) || [];\n if (projects.length === 0 && books.length === 0) {\n return [{ json: { chatId: chatId, message: '\\u{1F50D} No results for \"' + query + '\"', parseMode: 'HTML', keyboard: [[{ text: '\\u{1F3E0} Home', callback_data: 'start' }]] } }];\n }\n var message = '\\u{1F50D} <b>Search: \"' + query + '\"</b>\\n\\n';\n var keyboard = [];\n if (projects.length > 0) {\n message += '\\u{1F4C1} <b>Projects (' + projects.length + '):</b>\\n';\n projects.forEach(function(p) {\n var title = (p.translations && p.translations[0] && p.translations[0].title) || p.slug || 'Untitled';\n message += '\\u2022 ' + title + '\\n';\n keyboard.push([{ text: '\\u{1F441} ' + title, callback_data: 'preview:projects:' + p.id }]);\n });\n message += '\\n';\n }\n if (books.length > 0) {\n message += '\\u{1F4DA} <b>Books (' + books.length + '):</b>\\n';\n books.forEach(function(b) {\n message += '\\u2022 ' + b.book_title + ' by ' + b.book_author + '\\n';\n keyboard.push([{ text: '\\u{1F441} ' + b.book_title, callback_data: 'preview:books:' + b.id }]);\n });\n }\n keyboard.push([{ text: '\\u{1F3E0} Home', callback_data: 'start' }]);\n return [{ json: { chatId: chatId, message: message, parseMode: 'HTML', keyboard: keyboard } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Error searching: ' + error.message, parseMode: 'HTML' } }];\n}"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
120
|
|
],
|
|
"id": "search-handler-001",
|
|
"name": "Search Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "try {\n var chatId = $input.first().json.chatId;\n var projectsResp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/projects?fields=id,category,status,date_created', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var booksResp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews?fields=id,rating,status,finished_at', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var projects = (projectsResp && projectsResp.data) || [];\n var books = (booksResp && booksResp.data) || [];\n var pPublished = projects.filter(function(p) { return p.status === 'published'; }).length;\n var pDraft = projects.filter(function(p) { return p.status === 'draft'; }).length;\n var pArchived = projects.filter(function(p) { return p.status === 'archived'; }).length;\n var bPublished = books.filter(function(b) { return b.status === 'published'; }).length;\n var bDraft = books.filter(function(b) { return b.status === 'draft'; }).length;\n var bAvg = books.length > 0 ? (books.reduce(function(sum, b) { return sum + (b.rating || 0); }, 0) / books.length).toFixed(1) : 0;\n var categories = {};\n projects.forEach(function(p) { if (p.category) { categories[p.category] = (categories[p.category] || 0) + 1; } });\n var message = '\\u{1F4CA} <b>DK0 Portfolio Statistics</b>\\n\\n\\u{1F4C1} <b>Projects:</b>\\n\\u2022 Total: ' + projects.length + '\\n\\u2022 Published: ' + pPublished + '\\n\\u2022 Draft: ' + pDraft + '\\n\\u2022 Archived: ' + pArchived + '\\n\\n\\u{1F4DA} <b>Book Reviews:</b>\\n\\u2022 Total: ' + books.length + '\\n\\u2022 Published: ' + bPublished + '\\n\\u2022 Draft: ' + bDraft + '\\n\\u2022 Avg Rating: ' + bAvg + '/5\\n';\n var catEntries = Object.entries(categories).sort(function(a, b) { return b[1] - a[1]; });\n if (catEntries.length > 0) {\n message += '\\n\\u{1F3F7}\\uFE0F <b>Categories:</b>\\n';\n catEntries.forEach(function(entry) { message += '\\u2022 ' + entry[0] + ': ' + entry[1] + '\\n'; });\n }\n var keyboard = [\n [{ text: '\\u{1F4CB} Projects', callback_data: 'list:projects:1' }, { text: '\\u{1F4DA} Books', callback_data: 'list:books:1' }],\n [{ text: '\\u{1F3E0} Home', callback_data: 'start' }]\n ];\n return [{ json: { chatId: chatId, message: message, parseMode: 'HTML', keyboard: keyboard } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Error loading stats: ' + error.message, parseMode: 'HTML' } }];\n}"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
240
|
|
],
|
|
"id": "stats-handler-001",
|
|
"name": "Stats Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "\ntry {\n var input = $input.first().json;\n var id = input.id;\n var chatId = input.chatId;\n var collectionType = input.collectionType;\n \n var response, collection;\n \n if (collectionType === 'projects' || collectionType === 'project') {\n response = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/projects/' + id + '?fields=id,slug,category,status,date_created,translations.*', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, returnFullResponse: true }).catch(function() { return null; });\n collection = 'projects';\n } else if (collectionType === 'books' || collectionType === 'book') {\n response = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews/' + id + '?fields=id,book_title,book_author,rating,status,hardcover_id,translations.*', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, returnFullResponse: true }).catch(function() { return null; });\n collection = 'book_reviews';\n } else {\n response = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/projects/' + id + '?fields=id,slug,category,status,date_created,translations.*', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, returnFullResponse: true }).catch(function() { return null; });\n collection = 'projects';\n var itemTry = response && response.body && response.body.data;\n if (!itemTry) {\n response = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews/' + id + '?fields=id,book_title,book_author,rating,status,hardcover_id,translations.*', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, returnFullResponse: true }).catch(function() { return null; });\n collection = 'book_reviews';\n }\n }\n\n var item = response && response.body && response.body.data;\n if (!item) {\n return [{ json: { chatId: chatId, message: '\\u274C Item #' + id + ' not found.', parseMode: 'HTML', keyboard: [[{ text: '\\u{1F3E0} Home', callback_data: 'start' }]] } }];\n }\n \n var message = '\\u{1F441}\\uFE0F <b>Preview #' + id + '</b>\\n\\n';\n if (collection === 'projects') {\n message += '\\u{1F4C1} <b>Type:</b> Project\\n\\u{1F516} <b>Slug:</b> ' + item.slug + '\\n\\u{1F3F7}\\uFE0F <b>Category:</b> ' + (item.category || 'N/A') + '\\n\\u{1F4CA} <b>Status:</b> ' + item.status + '\\n\\n';\n var translations = item.translations || [];\n translations.forEach(function(t) {\n var lang = t.languages_code === 'en-US' ? '\\u{1F1EC}\\u{1F1E7} EN' : '\\u{1F1E9}\\u{1F1EA} DE';\n message += lang + ':\\n<b>Title:</b> ' + (t.title || 'N/A') + '\\n<b>Desc:</b> ' + ((t.description || 'N/A')) + '...\\n\\n';\n });\n } else {\n message += '\\u{1F4DA} <b>Type:</b> Book Review\\n\\u{1F4D6} <b>Title:</b> ' + item.book_title + '\\n\\u270D\\uFE0F <b>Author:</b> ' + item.book_author + '\\n\\u2B50 <b>Rating:</b> ' + item.rating + '/5\\n\\u{1F4CA} <b>Status:</b> ' + item.status + '\\n\\u{1F517} <b>HC-ID:</b> ' + item.hardcover_id + '\\n\\n';\n var translations = item.translations || [];\n translations.forEach(function(t) {\n var lang = t.languages_code === 'en-US' ? '\\u{1F1EC}\\u{1F1E7} EN' : '\\u{1F1E9}\\u{1F1EA} DE';\n message += lang + ':\\n' + ((t.review || 'No review')) + '...\\n\\n';\n });\n }\n var listType = collection === 'projects' ? 'projects' : 'books';\n var keyboard = [\n [{ text: '\\u2705 Publish', callback_data: 'publish:' + listType + ':' + id }, { text: '\\u{1F5D1} Delete', callback_data: 'delete:' + listType + ':' + id }],\n [{ text: '\\u2190 Back', callback_data: 'list:' + listType + ':1' }, { text: '\\u{1F3E0} Home', callback_data: 'start' }]\n ];\n return [{ json: { chatId: chatId, message: message, parseMode: 'HTML', keyboard: keyboard } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Error loading preview: ' + error.message, parseMode: 'HTML' } }];\n}\n"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
360
|
|
],
|
|
"id": "preview-handler-001",
|
|
"name": "Preview Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "try {\n var input = $input.first().json;\n var id = input.id;\n var chatId = input.chatId;\n var collectionType = input.collectionType;\n \n var url, title, listType;\n \n if (collectionType === 'projects' || collectionType === 'project') {\n url = 'https://cms.dk0.dev/items/projects/' + id;\n title = 'Project';\n listType = 'projects';\n } else {\n url = 'https://cms.dk0.dev/items/book_reviews/' + id;\n title = 'Book Review';\n listType = 'books';\n }\n \n var response;\n try {\n response = await this.helpers.httpRequest({\n method: 'PATCH',\n url: url,\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB'\n },\n body: { status: 'published' }\n });\n } catch(e) {\n return [{ json: { chatId: chatId, message: '\\u274C <b>Publish fehlgeschlagen</b>\\n\\n' + e.message, parseMode: 'HTML', keyboard: [[{ text: '\\u{1F3E0} Home', callback_data: 'start' }]] } }];\n }\n \n var result = response.data || response;\n if (!result || !result.id) {\n return [{ json: { chatId: chatId, message: '\\u274C <b>Publish fehlgeschlagen</b>\\n\\nKeine Bestaetigung von Directus.', parseMode: 'HTML', keyboard: [[{ text: '\\u{1F3E0} Home', callback_data: 'start' }]] } }];\n }\n \n var keyboard = [[{ text: '\\u{1F4CB} ' + (listType === 'projects' ? 'Projects' : 'Books'), callback_data: 'list:' + listType + ':1' }, { text: '\\u{1F3E0} Home', callback_data: 'start' }]];\n return [{ json: { chatId: chatId, message: '\\u2705 <b>' + title + ' #' + id + ' Published!</b>\\n\\nNow live on dk0.dev.', parseMode: 'HTML', keyboard: keyboard } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Error publishing: ' + error.message, parseMode: 'HTML' } }];\n}"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
480
|
|
],
|
|
"id": "publish-handler-001",
|
|
"name": "Publish Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "try {\n var input = $input.first().json;\n var id = input.id;\n var chatId = input.chatId;\n var collectionType = input.collectionType;\n \n var response, collection, title;\n \n if (collectionType === 'projects' || collectionType === 'project') {\n response = await this.helpers.httpRequest({ method: 'DELETE', url: 'https://cms.dk0.dev/items/projects/' + id, headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, returnFullResponse: true }).catch(function() { return null; });\n collection = 'projects';\n title = 'Project';\n } else if (collectionType === 'books' || collectionType === 'book') {\n response = await this.helpers.httpRequest({ method: 'DELETE', url: 'https://cms.dk0.dev/items/book_reviews/' + id, headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, returnFullResponse: true }).catch(function() { return null; });\n collection = 'book_reviews';\n title = 'Book Review';\n } else {\n // Fallback\n response = await this.helpers.httpRequest({ method: 'DELETE', url: 'https://cms.dk0.dev/items/projects/' + id, headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, returnFullResponse: true }).catch(function() { return null; });\n collection = 'projects';\n title = 'Project';\n if (!response || response.statusCode >= 400) {\n response = await this.helpers.httpRequest({ method: 'DELETE', url: 'https://cms.dk0.dev/items/book_reviews/' + id, headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, returnFullResponse: true }).catch(function() { return null; });\n collection = 'book_reviews';\n title = 'Book Review';\n }\n }\n\n if (!response || response.statusCode >= 400) {\n return [{ json: { chatId: chatId, message: '\\u274C Item #' + id + ' could not be deleted.', parseMode: 'HTML', keyboard: [[{ text: '\\u{1F3E0} Home', callback_data: 'start' }]] } }];\n }\n var listType = collection === 'projects' ? 'projects' : 'books';\n var keyboard = [[{ text: (collection === 'projects' ? '\\u{1F4CB} Projects' : '\\u{1F4DA} Books'), callback_data: 'list:' + listType + ':1' }, { text: '\\u{1F3E0} Home', callback_data: 'start' }]];\n return [{ json: { chatId: chatId, message: '\\u{1F5D1}\\uFE0F *' + title + ' #' + id + ' Deleted*', parseMode: 'HTML', keyboard: keyboard, collection: collection, itemId: id } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Error deleting: ' + error.message, parseMode: 'HTML' } }];\n}"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
600
|
|
],
|
|
"id": "delete-handler-001",
|
|
"name": "Delete Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "try {\n var input = $input.first().json;\n var id = input.id;\n var chatId = input.chatId;\n var bookResp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews/' + id + '?fields=id,book_title,translations.id', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var book = bookResp && bookResp.data;\n if (!book) {\n return [{ json: { chatId: chatId, message: '\\u274C Book review #' + id + ' not found.', parseMode: 'HTML', keyboard: [[{ text: '\\u{1F3E0} Home', callback_data: 'start' }]] } }];\n }\n var translations = book.translations || [];\n var deletedCount = 0;\n for (var i = 0; i < translations.length; i++) {\n await this.helpers.httpRequest({ method: 'DELETE', url: 'https://cms.dk0.dev/items/book_reviews_translations/' + translations[i].id, headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } }).catch(function() {});\n deletedCount++;\n }\n var keyboard = [[{ text: '\\u{1F4DA} Books', callback_data: 'list:books:1' }, { text: '\\u{1F3E0} Home', callback_data: 'start' }]];\n return [{ json: { chatId: chatId, message: '\\u{1F5D1}\\uFE0F Deleted ' + deletedCount + ' review translations for \"' + book.book_title + '\".\\n\\nBook entry still exists.', parseMode: 'HTML', keyboard: keyboard, itemId: id, deletedCount: deletedCount } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Error deleting review: ' + error.message, parseMode: 'HTML' } }];\n}"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
720
|
|
],
|
|
"id": "delete-review-handler-001",
|
|
"name": "Delete Review Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "try {\n var input = $input.first().json;\n var bookId = input.bookId;\n var rating = input.rating;\n var answers = input.answers;\n var chatId = input.chatId;\n\n var bookResp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews/' + bookId + '?fields=id,book_title,book_author,rating,translations.id,translations.languages_code,translations.review', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var bookData = bookResp && bookResp.data ? bookResp.data : bookResp;\n if (!bookData || !bookData.id) {\n return [{ json: { chatId: chatId, message: '\\u274C Buch #' + bookId + ' nicht gefunden.', parseMode: 'HTML', keyboard: [[{ text: '\\u{1F3E0} Home', callback_data: 'start' }]] } }];\n }\n\n var prompt = 'Schreibe eine authentische Buchbewertung. Buch: ' + bookData.book_title + ' von ' + bookData.book_author + '. Rating: ' + rating + '/5. Antworten des Lesers auf Fragen zum Buch: ' + answers + ' Schreibe Ich-Perspektive, 4-6 Saetze pro Sprache. Verwende keine Bindestriche, Em-Dashes oder Gedankenstriche. Antworte NUR als JSON: {\"review_en\": \"English review\", \"review_de\": \"Deutsche Bewertung\"}';\n\n var aiResp = await this.helpers.httpRequest({ method: 'POST', url: 'https://openrouter.ai/api/v1/chat/completions', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer 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' }, body: { model: 'openrouter/free', messages: [{ role: 'user', content: prompt }] } });\n var aiText = (aiResp && aiResp.choices && aiResp.choices[0] && aiResp.choices[0].message && aiResp.choices[0].message.content) || '{}';\n var jsonMatch = aiText.match(/\\{[\\s\\S]*\\}/);\n var ai = jsonMatch ? JSON.parse(jsonMatch[0]) : { review_en: answers, review_de: answers };\n\n // Update rating\n await this.helpers.httpRequest({ method: 'PATCH', url: 'https://cms.dk0.dev/items/book_reviews/' + bookData.id, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, body: { rating: rating } });\n\n var translations = bookData.translations || [];\n var enTrans = null, deTrans = null;\n for (var i = 0; i < translations.length; i++) {\n if (translations[i].languages_code === 'en-US') enTrans = translations[i];\n if (translations[i].languages_code === 'de-DE') deTrans = translations[i];\n }\n\n var reviewEn = ai.review_en || answers;\n var reviewDe = ai.review_de || answers;\n\n // Update existing translations (PATCH) or create new ones (POST)\n if (enTrans) {\n await this.helpers.httpRequest({ method: 'PATCH', url: 'https://cms.dk0.dev/items/book_reviews_translations/' + enTrans.id, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, body: { review: reviewEn } });\n } else {\n await this.helpers.httpRequest({ method: 'POST', url: 'https://cms.dk0.dev/items/book_reviews_translations', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, body: { book_reviews_id: bookData.id, languages_code: 'en-US', review: reviewEn } });\n }\n\n if (deTrans) {\n await this.helpers.httpRequest({ method: 'PATCH', url: 'https://cms.dk0.dev/items/book_reviews_translations/' + deTrans.id, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, body: { review: reviewDe } });\n } else {\n await this.helpers.httpRequest({ method: 'POST', url: 'https://cms.dk0.dev/items/book_reviews_translations', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, body: { book_reviews_id: bookData.id, languages_code: 'de-DE', review: reviewDe } });\n }\n\n var showEn = reviewEn;\n var showDe = reviewDe;\n var msg = '\\u2705 <b>Review erstellt!</b>\\n\\n\\u{1F4DA} ' + bookData.book_title + ' (' + rating + '/5)\\n\\n<b>EN:</b> ' + showEn + '\\n\\n<b>DE:</b> ' + showDe;\n var keyboard = [[{ text: '\\u{1F441} Preview', callback_data: 'preview:books:' + bookData.id }, { text: '\\u2705 Publish', callback_data: 'publish:books:' + bookData.id }], [{ text: '\\u{1F4DA} Books', callback_data: 'list:books:1' }]];\n return [{ json: { chatId: chatId, message: msg, parseMode: 'HTML', keyboard: keyboard } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Fehler beim Erstellen der Review: ' + error.message, parseMode: 'HTML' } }];\n}"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
840
|
|
],
|
|
"id": "create-review-handler-001",
|
|
"name": "Create Review Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "var chatId = $input.first().json.chatId;\nvar message = '\\u2753 <b>Unknown Command</b>\\n\\nUse the buttons below or type:\\n<code>.review HC_ID [RATING]</code> - Start review with AI questions\\n<code>.answer BOOK_ID RATING your answers</code> - Submit review answers\\n<code>.refine ID FEEDBACK</code> - Refine existing review';\nvar keyboard = [\n [{ text: '\\u{1F4CB} Projects', callback_data: 'list:projects:1' }, { text: '\\u{1F4DA} Books', callback_data: 'list:books:1' }],\n [{ text: '\\u{1F4CA} Stats', callback_data: 'stats' }, { text: '\\u{1F3E0} Dashboard', callback_data: 'start' }]\n];\nreturn [{ json: { chatId: chatId, message: message, parseMode: 'HTML', keyboard: keyboard } }];"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
960
|
|
],
|
|
"id": "unknown-handler-001",
|
|
"name": "Unknown Command Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "={{ 'https://api.telegram.org/bot8166414331:AAGNQ6fn2juD5esaTRxPjtTdSMkwq_oASIc/sendMessage' }}",
|
|
"authentication": "none",
|
|
"sendBody": true,
|
|
"contentType": "json",
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ { chat_id: $json.chatId, text: $json.message, parse_mode: $json.parseMode || 'HTML', reply_markup: ($json.keyboard && $json.keyboard.length > 0) ? { inline_keyboard: $json.keyboard } : undefined } }}"
|
|
},
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
960,
|
|
420
|
|
],
|
|
"id": "send-message-001",
|
|
"name": "Send Message",
|
|
"options": {}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "try {\n var input = $input.first().json;\n var id = input.id;\n var feedback = input.feedback;\n var chatId = input.chatId;\n var bookResp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews/' + id + '?fields=id,book_title,book_author,rating,translations.id,translations.languages_code,translations.review', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n var bookData = bookResp && bookResp.data ? bookResp.data : bookResp;\n if (!bookData || !bookData.id) {\n return [{ json: { chatId: chatId, message: 'Review #' + id + ' nicht gefunden.', parseMode: 'HTML', keyboard: [[{ text: '\\u{1F3E0} Home', callback_data: 'start' }]] } }];\n }\n var translations = bookData.translations || [];\n var enTrans = null, deTrans = null;\n for (var i = 0; i < translations.length; i++) {\n if (translations[i].languages_code === 'en-US') enTrans = translations[i];\n if (translations[i].languages_code === 'de-DE') deTrans = translations[i];\n }\n var currentEn = enTrans ? enTrans.review : '';\n var currentDe = deTrans ? deTrans.review : '';\n var prompt = 'Du hast eine Buchbewertung fuer \"' + bookData.book_title + '\" von \"' + bookData.book_author + '\" geschrieben. Rating: ' + bookData.rating + '/5. Aktuelle EN-Bewertung: ' + currentEn + ' Aktuelle DE-Bewertung: ' + currentDe + ' Feedback des Lesers: ' + feedback + ' Wichtig: EN und DE sind immer inhaltlich identisch, nur die Sprache unterscheidet sich. Feedback gilt fuer BEIDE Versionen, auch wenn es nur eine Sprache erwaehnt. Ueberarbeite daher immer beide synchron. Ich-Perspektive, 4-6 Saetze pro Sprache. Verwende keine Bindestriche, Em-Dashes oder Gedankenstriche. Antworte NUR als JSON: {\"review_en\": \"...\", \"review_de\": \"...\"}';\n var aiResp = await this.helpers.httpRequest({ method: 'POST', url: 'https://openrouter.ai/api/v1/chat/completions', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer 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' }, body: { model: 'openrouter/free', messages: [{ role: 'user', content: prompt }] } });\n var aiText = (aiResp && aiResp.choices && aiResp.choices[0] && aiResp.choices[0].message && aiResp.choices[0].message.content) || '{}';\n var jsonMatch = aiText.match(/\\{[\\s\\S]*\\}/);\n var ai = jsonMatch ? JSON.parse(jsonMatch[0]) : { review_en: feedback, review_de: feedback };\n var reviewEn = ai.review_en || feedback;\n var reviewDe = ai.review_de || feedback;\n\n // Update existing translations (PATCH) or create new ones (POST)\n if (enTrans) {\n await this.helpers.httpRequest({ method: 'PATCH', url: 'https://cms.dk0.dev/items/book_reviews_translations/' + enTrans.id, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, body: { review: reviewEn } });\n } else {\n await this.helpers.httpRequest({ method: 'POST', url: 'https://cms.dk0.dev/items/book_reviews_translations', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, body: { book_reviews_id: bookData.id, languages_code: 'en-US', review: reviewEn } });\n }\n if (deTrans) {\n await this.helpers.httpRequest({ method: 'PATCH', url: 'https://cms.dk0.dev/items/book_reviews_translations/' + deTrans.id, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, body: { review: reviewDe } });\n } else {\n await this.helpers.httpRequest({ method: 'POST', url: 'https://cms.dk0.dev/items/book_reviews_translations', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' }, body: { book_reviews_id: bookData.id, languages_code: 'de-DE', review: reviewDe } });\n }\n\n var showEn = reviewEn;\n var showDe = reviewDe;\n var msg = '\\u270F\\uFE0F <b>Review aktualisiert!</b>\\n\\n\\u{1F4DA} ' + bookData.book_title + '\\n\\n<b>EN:</b> ' + showEn + '\\n\\n<b>DE:</b> ' + showDe;\n var keyboard = [[{ text: '\\u{1F441} Preview', callback_data: 'preview:books:' + id }, { text: '\\u2705 Publish', callback_data: 'publish:books:' + id }], [{ text: '\\u{1F4DA} Books', callback_data: 'list:books:1' }]];\n return [{ json: { chatId: chatId, message: msg, parseMode: 'HTML', keyboard: keyboard } }];\n} catch (error) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Fehler beim Aktualisieren: ' + error.message, parseMode: 'HTML' } }];\n}"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
1080
|
|
],
|
|
"id": "refine-review-handler-001",
|
|
"name": "Refine Review Handler"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "try {\n var input = $input.first().json;\n var chatId = input.chatId;\n var bookId = input.id;\n var hardcoverId = input.hardcoverId;\n var rating = input.rating || 0;\n var book;\n\n if (bookId) {\n var resp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews/' + bookId + '?fields=id,book_title,book_author,hardcover_id,rating', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n book = resp && resp.data;\n } else if (hardcoverId) {\n var resp = await this.helpers.httpRequest({ method: 'GET', url: 'https://cms.dk0.dev/items/book_reviews?filter[hardcover_id][_eq]=' + hardcoverId + '&fields=id,book_title,book_author,hardcover_id,rating&limit=1', headers: { 'Authorization': 'Bearer RF2QytqhcLXuVy6FO3PzWlsoR-ysCTwB' } });\n book = resp && resp.data && resp.data[0];\n }\n\n if (!book) {\n return [{ json: { chatId: chatId, message: '\\u274C Buch nicht gefunden. Pr\\u00fcfe die ID.', parseMode: 'HTML', keyboard: [[{ text: '\\u{1F4DA} Books', callback_data: 'list:books:1' }]] } }];\n }\n\n var prompt = 'Du bist ein Leseberater. Generiere genau 4 persoenliche, tiefgruendige Fragen zum Buch \"' + book.book_title + '\" von ' + book.book_author + ', die einem helfen, eine authentische Bewertung zu schreiben. Die Fragen sollen spezifisch zum Buch sein und zum Nachdenken anregen. Antworte NUR als JSON-Array, keine Erklaerung davor: [\"Frage 1\", \"Frage 2\", \"Frage 3\", \"Frage 4\"]';\n\n var aiResp = await this.helpers.httpRequest({ method: 'POST', url: 'https://openrouter.ai/api/v1/chat/completions', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer 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' }, body: { model: 'openrouter/free', messages: [{ role: 'user', content: prompt }] } });\n\n var aiText = (aiResp && aiResp.choices && aiResp.choices[0] && aiResp.choices[0].message && aiResp.choices[0].message.content) || '[]';\n var questions;\n try {\n var jsonMatch = aiText.match(/\\[[\\s\\S]*\\]/);\n questions = jsonMatch ? JSON.parse(jsonMatch[0]) : ['Was hat dir am besten gefallen?', 'Was hat dich gestoert?', 'Wuerdest du es weiterempfehlen?', 'Welche Szene ist dir im Gedaechtnis geblieben?'];\n } catch(e) {\n questions = ['Was hat dir am besten gefallen?', 'Was hat dich gestoert?', 'Wuerdest du es weiterempfehlen?', 'Welche Szene ist dir im Gedaechtnis geblieben?'];\n }\n\n var ratingInfo = rating > 0 ? '\\n\\u2B50 Dein Rating: ' + rating + '/5' : '\\n\\u2B50 Gib dein Rating (1-5) an';\n var msg = '\\u{1F4D6} <b>Review: ' + book.book_title + '</b>\\n' + book.book_author + ratingInfo + '\\n\\n\\u2753 <b>Beantworte diese Fragen:</b>\\n\\n';\n for (var i = 0; i < questions.length; i++) {\n msg += (i + 1) + '. ' + questions[i] + '\\n';\n }\n msg += '\\n\\u270D\\uFE0F Antworte mit:\\n<code>.answer ' + book.id + ' ' + (rating > 0 ? rating : '5') + ' deine Antworten hier</code>';\n msg += '\\n\\n<i>Beispiel: .answer ' + book.id + ' 4 Die Charakterentwicklung war super...</i>';\n\n var keyboard = [[{ text: '\\u{1F4DA} Books', callback_data: 'list:books:1' }, { text: '\\u{1F3E0} Home', callback_data: 'start' }]];\n return [{ json: { chatId: chatId, message: msg, parseMode: 'HTML', keyboard: keyboard } }];\n} catch(e) {\n return [{ json: { chatId: $input.first().json.chatId, message: '\\u274C Error: ' + e.message, parseMode: 'HTML' } }];\n}\n"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
720,
|
|
960
|
|
],
|
|
"id": "review-info-handler-001",
|
|
"name": "Review Info Handler"
|
|
}
|
|
],
|
|
"connections": {
|
|
"Telegram Trigger": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Global Parser",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Global Parser": {
|
|
"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": "Refine Review Handler",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Unknown Command Handler",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Review Info Handler",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Dashboard Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"List Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Search Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Stats Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preview Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Publish Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Delete Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Delete Review Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Create Review Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Unknown Command Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Refine Review Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Review Info Handler": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Send Message",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"pinData": {},
|
|
"settings": {
|
|
"executionOrder": "v1"
|
|
},
|
|
"staticData": null,
|
|
"tags": [],
|
|
"triggerCount": 1,
|
|
"updatedAt": "2025-01-21T00:00:00.000Z",
|
|
"versionId": "1"
|
|
} |