refactor(10-06): remove old batch inline path and modernize callbacks
- Update Build Batch Keyboard to use bexec: format instead of legacy JSON - Update Build Suggestion Keyboard to use action: format instead of legacy JSON - Remove 7 nodes from old batch inline execution path: - Build Batch Commands - Prepare Batch Execution - Execute Batch Action - Parse Batch Result - Format Batch Result - Send Batch Result - Answer Batch Query Node count: 199 -> 192 (-7 nodes) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+7
-209
@@ -629,7 +629,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "// Build suggestion keyboard for Telegram\nconst { chatId, query, action, suggestedName, suggestedId, timestamp } = $json;\n\n// callback_data must be <=64 bytes - use short keys\n// a=action (1 char: s=start, t=stop, r=restart)\n// c=container short ID\n// t=timestamp\nconst actionCode = action === 'start' ? 's' : action === 'stop' ? 't' : 'r';\nconst callbackData = JSON.stringify({ a: actionCode, c: suggestedId, t: timestamp });\n\nreturn {\n json: {\n chat_id: chatId,\n text: `No container '<b>${query}</b>' found.\\n\\nDid you mean <b>${suggestedName}</b>?`,\n parse_mode: \"HTML\",\n reply_markup: {\n inline_keyboard: [\n [\n { text: `Yes, ${action} ${suggestedName}`, callback_data: callbackData },\n { text: \"Cancel\", callback_data: '{\"a\":\"x\"}' }\n ]\n ]\n }\n }\n};"
|
"jsCode": "// Build suggestion keyboard for Telegram\nconst { chatId, query, action, suggestedName, suggestedId, timestamp } = $json;\n\n// Use modern action callback format: action:{action}:{containerName}\nconst callbackData = `action:${action}:${suggestedName}`;\n\nreturn {\n json: {\n chat_id: chatId,\n text: `No container '<b>${query}</b>' found.\\n\\nDid you mean <b>${suggestedName}</b>?`,\n parse_mode: \"HTML\",\n reply_markup: {\n inline_keyboard: [\n [\n { text: `Yes, ${action} ${suggestedName}`, callback_data: callbackData },\n { text: \"Cancel\", callback_data: 'batch:cancel' }\n ]\n ]\n }\n }\n};"
|
||||||
},
|
},
|
||||||
"id": "code-build-suggestion",
|
"id": "code-build-suggestion",
|
||||||
"name": "Build Suggestion Keyboard",
|
"name": "Build Suggestion Keyboard",
|
||||||
@@ -691,7 +691,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "// Build batch confirmation keyboard for multiple matches\nconst matches = $json.matches;\nconst action = $json.action;\nconst chatId = $json.chatId;\nconst query = $json.containerQuery;\n\n// Validate chatId exists - critical for Telegram API\nif (!chatId) {\n throw new Error('Missing chatId - cannot send batch confirmation. Data: ' + JSON.stringify({ matches: matches?.length, action, query }));\n}\n\n// List matched container names\nconst names = matches.map(m => m.Name);\nconst shortIds = matches.map(m => m.Id.substring(0, 12));\n\n// Build callback_data - must be <=64 bytes\n// For batch: a=action code, c=array of short IDs, t=timestamp\nconst actionCode = action === 'start' ? 's' : action === 'stop' ? 't' : 'r';\nconst timestamp = Date.now();\n\n// Check size - if too many containers, callback_data might exceed 64 bytes\n// Each short ID is 12 chars, plus overhead. Max ~3-4 containers safely\nlet callbackData;\nlet limitedCount = shortIds.length;\nif (shortIds.length <= 4) {\n callbackData = JSON.stringify({ a: actionCode, c: shortIds, t: timestamp });\n} else {\n // Too many containers - limit to first 4\n callbackData = JSON.stringify({ a: actionCode, c: shortIds.slice(0, 4), t: timestamp });\n limitedCount = 4;\n}\n\n// Format container list\nconst listText = names.map(n => ` \\u2022 ${n}`).join('\\n');\n\n// Build action verb for button\nconst actionVerb = action.charAt(0).toUpperCase() + action.slice(1);\n\nreturn {\n json: {\n chat_id: chatId,\n text: `Found <b>${matches.length}</b> containers matching '<b>${query}</b>':\\n\\n${listText}\\n\\n${actionVerb} all?`,\n parse_mode: \"HTML\",\n reply_markup: {\n inline_keyboard: [\n [\n { text: `Yes, ${action} ${limitedCount} containers`, callback_data: callbackData },\n { text: \"Cancel\", callback_data: '{\"a\":\"x\"}' }\n ]\n ]\n },\n // Store metadata for summary\n _meta: {\n action,\n containers: matches,\n timestamp,\n limitedCount\n }\n }\n};"
|
"jsCode": "// Build batch confirmation keyboard for multiple matches\nconst matches = $json.matches;\nconst action = $json.action;\nconst chatId = $json.chatId;\nconst query = $json.containerQuery;\n\n// Validate chatId exists - critical for Telegram API\nif (!chatId) {\n throw new Error('Missing chatId - cannot send batch confirmation. Data: ' + JSON.stringify({ matches: matches?.length, action, query }));\n}\n\n// List matched container names\nconst names = matches.map(m => m.Name);\nconst timestamp = Math.floor(Date.now() / 1000); // Unix timestamp for 30s timeout\n\n// Build callback_data using new bexec format\n// Format: bexec:{action}:{comma-separated-names}:{timestamp}\n// Limit to 4 containers to stay within 64-byte callback_data limit\nlet limitedNames = names;\nlet limitedCount = names.length;\nif (names.length > 4) {\n limitedNames = names.slice(0, 4);\n limitedCount = 4;\n}\n\nconst namesStr = limitedNames.join(',');\nconst callbackData = `bexec:${action}:${namesStr}:${timestamp}`;\n\n// Format container list\nconst listText = names.map(n => ` \\u2022 ${n}`).join('\\n');\n\n// Build action verb for button\nconst actionVerb = action.charAt(0).toUpperCase() + action.slice(1);\n\nreturn {\n json: {\n chat_id: chatId,\n text: `Found <b>${matches.length}</b> containers matching '<b>${query}</b>':\\n\\n${listText}\\n\\n${actionVerb} all?`,\n parse_mode: \"HTML\",\n reply_markup: {\n inline_keyboard: [\n [\n { text: `Yes, ${action} ${limitedCount} containers`, callback_data: callbackData },\n { text: \"Cancel\", callback_data: 'batch:cancel' }\n ]\n ]\n },\n // Store metadata for summary\n _meta: {\n action,\n containers: matches,\n timestamp,\n limitedCount\n }\n }\n};"
|
||||||
},
|
},
|
||||||
"id": "code-build-batch-keyboard",
|
"id": "code-build-batch-keyboard",
|
||||||
"name": "Build Batch Keyboard",
|
"name": "Build Batch Keyboard",
|
||||||
@@ -1518,96 +1518,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"jsCode": "// Execute batch action on all containers sequentially\nconst containerIds = $json.containerIds;\nconst action = $json.action;\nconst chatId = $json.chatId;\nconst messageId = $json.messageId;\nconst queryId = $json.queryId;\n\nconst timeout = (action === 'stop' || action === 'restart') ? '?t=10' : '';\nconst results = [];\n\n// Execute each container action sequentially using fetch\nfor (const containerId of containerIds) {\n try {\n // Use n8n's built-in $http or construct command for later execution\n // Since we can't use execSync easily, we'll build commands for chained execution\n results.push({\n containerId,\n cmd: `curl -s -o /dev/null -w \"%{http_code}\" --max-time 5 -X POST 'http://docker-socket-proxy:2375/v1.47/containers/${containerId}/${action}${timeout}'`\n });\n } catch (err) {\n results.push({ containerId, error: err.message });\n }\n}\n\nreturn {\n json: {\n commands: results,\n action,\n queryId,\n chatId,\n messageId,\n totalCount: containerIds.length\n }\n};"
|
|
||||||
},
|
|
||||||
"id": "code-build-batch-commands",
|
|
||||||
"name": "Build Batch Commands",
|
|
||||||
"type": "n8n-nodes-base.code",
|
|
||||||
"typeVersion": 2,
|
|
||||||
"position": [
|
|
||||||
1340,
|
|
||||||
800
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"jsCode": "// Execute all batch commands and collect results\nconst { commands, action, queryId, chatId, messageId, totalCount } = $json;\nconst results = [];\n\n// Build a single shell command that runs all curl commands sequentially\n// and outputs results in a parseable format\nconst allCommands = commands.map((c, i) => \n `echo \"RESULT_${i}:$(${c.cmd})\"`\n).join(' && ');\n\nreturn {\n json: {\n batchCmd: allCommands,\n containerIds: commands.map(c => c.containerId),\n action,\n queryId,\n chatId,\n messageId,\n totalCount\n }\n};"
|
|
||||||
},
|
|
||||||
"id": "code-prepare-batch-exec",
|
|
||||||
"name": "Prepare Batch Execution",
|
|
||||||
"type": "n8n-nodes-base.code",
|
|
||||||
"typeVersion": 2,
|
|
||||||
"position": [
|
|
||||||
1560,
|
|
||||||
800
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"command": "={{ $json.batchCmd }}",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"id": "exec-batch-action",
|
|
||||||
"name": "Execute Batch Action",
|
|
||||||
"type": "n8n-nodes-base.executeCommand",
|
|
||||||
"typeVersion": 1,
|
|
||||||
"position": [
|
|
||||||
1780,
|
|
||||||
800
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"jsCode": "// Parse batch execution results\nconst stdout = $input.item.json.stdout || '';\nconst stderr = $input.item.json.stderr || '';\nconst prevData = $('Prepare Batch Execution').item.json;\nconst { containerIds, action, queryId, chatId, messageId, totalCount } = prevData;\n\n// Parse results from output like: RESULT_0:204 RESULT_1:204 RESULT_2:304\nconst results = [];\nfor (let i = 0; i < containerIds.length; i++) {\n const match = stdout.match(new RegExp(`RESULT_${i}:(\\\\d+)`));\n if (match) {\n const statusCode = parseInt(match[1]);\n results.push({\n containerId: containerIds[i],\n success: statusCode === 204 || statusCode === 304,\n statusCode\n });\n } else {\n results.push({\n containerId: containerIds[i],\n success: false,\n error: 'No response'\n });\n }\n}\n\nconst successCount = results.filter(r => r.success).length;\nconst failCount = results.length - successCount;\n\nreturn {\n json: {\n results,\n successCount,\n failCount,\n totalCount: results.length,\n action,\n queryId,\n chatId,\n messageId\n }\n};"
|
|
||||||
},
|
|
||||||
"id": "code-parse-batch-result",
|
|
||||||
"name": "Parse Batch Result",
|
|
||||||
"type": "n8n-nodes-base.code",
|
|
||||||
"typeVersion": 2,
|
|
||||||
"position": [
|
|
||||||
2000,
|
|
||||||
800
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"jsCode": "// Format batch result message\nconst { successCount, failCount, totalCount, action } = $json;\nconst verb = action === 'start' ? 'started' :\n action === 'stop' ? 'stopped' : 'restarted';\n\nlet message;\nif (failCount === 0) {\n message = `Successfully ${verb} ${successCount} container${successCount > 1 ? 's' : ''}`;\n} else if (successCount === 0) {\n message = `Failed to ${action} all ${totalCount} containers`;\n} else {\n message = `${verb.charAt(0).toUpperCase() + verb.slice(1)} ${successCount}/${totalCount} containers (${failCount} failed)`;\n}\n\nreturn {\n json: {\n message,\n chatId: $json.chatId,\n queryId: $json.queryId,\n messageId: $json.messageId\n }\n};"
|
|
||||||
},
|
|
||||||
"id": "code-format-batch-result",
|
|
||||||
"name": "Format Batch Result",
|
|
||||||
"type": "n8n-nodes-base.code",
|
|
||||||
"typeVersion": 2,
|
|
||||||
"position": [
|
|
||||||
2220,
|
|
||||||
800
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"method": "POST",
|
|
||||||
"url": "=https://api.telegram.org/bot{{ $env.TELEGRAM_BOT_TOKEN }}/answerCallbackQuery",
|
|
||||||
"sendBody": true,
|
|
||||||
"specifyBody": "json",
|
|
||||||
"jsonBody": "={{ JSON.stringify({ callback_query_id: $json.queryId }) }}",
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
"id": "http-answer-batch-query",
|
|
||||||
"name": "Answer Batch Query",
|
|
||||||
"type": "n8n-nodes-base.httpRequest",
|
|
||||||
"typeVersion": 4.2,
|
|
||||||
"position": [
|
|
||||||
2440,
|
|
||||||
800
|
|
||||||
],
|
|
||||||
"credentials": {
|
|
||||||
"telegramApi": {
|
|
||||||
"id": "I0xTTiASl7C1NZhJ",
|
|
||||||
"name": "Telegram account"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"method": "POST",
|
"method": "POST",
|
||||||
@@ -1632,31 +1542,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"parameters": {
|
|
||||||
"resource": "message",
|
|
||||||
"operation": "sendMessage",
|
|
||||||
"chatId": "={{ $('Format Batch Result').item.json.chatId }}",
|
|
||||||
"text": "={{ $('Format Batch Result').item.json.message }}",
|
|
||||||
"additionalFields": {
|
|
||||||
"parse_mode": "HTML"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"id": "telegram-send-batch-result",
|
|
||||||
"name": "Send Batch Result",
|
|
||||||
"type": "n8n-nodes-base.telegram",
|
|
||||||
"typeVersion": 1.2,
|
|
||||||
"position": [
|
|
||||||
2880,
|
|
||||||
800
|
|
||||||
],
|
|
||||||
"credentials": {
|
|
||||||
"telegramApi": {
|
|
||||||
"id": "I0xTTiASl7C1NZhJ",
|
|
||||||
"name": "Telegram account"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"jsCode": "// Parse update command from message\nconst text = $json.message.text.toLowerCase().trim();\nconst chatId = $json.message.chat.id;\nconst messageId = $json.message.message_id;\n\n// Match update pattern: update followed by container name\nconst match = text.match(/^update\\s+(.+)$/i);\n\nif (!match) {\n return {\n json: {\n error: true,\n errorMessage: 'Invalid update format. Use: update <container-name>',\n chatId: chatId\n }\n };\n}\n\nreturn {\n json: {\n containerQuery: match[1].trim(),\n chatId: chatId,\n messageId: messageId\n }\n};"
|
"jsCode": "// Parse update command from message\nconst text = $json.message.text.toLowerCase().trim();\nconst chatId = $json.message.chat.id;\nconst messageId = $json.message.message_id;\n\n// Match update pattern: update followed by container name\nconst match = text.match(/^update\\s+(.+)$/i);\n\nif (!match) {\n return {\n json: {\n error: true,\n errorMessage: 'Invalid update format. Use: update <container-name>',\n chatId: chatId\n }\n };\n}\n\nreturn {\n json: {\n containerQuery: match[1].trim(),\n chatId: chatId,\n messageId: messageId\n }\n};"
|
||||||
@@ -5001,8 +4886,7 @@
|
|||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
[]
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"IF Callback Authenticated": {
|
"IF Callback Authenticated": {
|
||||||
@@ -5013,8 +4897,7 @@
|
|||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
[]
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Parse Callback Data": {
|
"Parse Callback Data": {
|
||||||
@@ -5058,13 +4941,7 @@
|
|||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[
|
[],
|
||||||
{
|
|
||||||
"node": "Build Batch Commands",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"node": "Answer Select Callback",
|
"node": "Answer Select Callback",
|
||||||
@@ -5461,8 +5338,7 @@
|
|||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
[]
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Find Closest Match": {
|
"Find Closest Match": {
|
||||||
@@ -5549,83 +5425,6 @@
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Build Batch Commands": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Prepare Batch Execution",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Prepare Batch Execution": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Execute Batch Action",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Execute Batch Action": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Parse Batch Result",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Parse Batch Result": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Format Batch Result",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Format Batch Result": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Answer Batch Query",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Answer Batch Query": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Delete Batch Confirm Message",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Delete Batch Confirm Message": {
|
|
||||||
"main": [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"node": "Send Batch Result",
|
|
||||||
"type": "main",
|
|
||||||
"index": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Parse Update Command": {
|
"Parse Update Command": {
|
||||||
"main": [
|
"main": [
|
||||||
[
|
[
|
||||||
@@ -5688,8 +5487,7 @@
|
|||||||
"type": "main",
|
"type": "main",
|
||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
[]
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"Handle Update Multiple": {
|
"Handle Update Multiple": {
|
||||||
|
|||||||
Reference in New Issue
Block a user