feat(08-02): route action callbacks to container operations
- Add Answer Action Callback node to answer query immediately - Add Route Action Type switch with start/restart/stop/update/logs outputs - Wire start/restart to immediate action flow (Get Container, Build Command, Execute, Format Result) - Wire logs to logs action flow with 30-line display - Wire stop/update to confirmation dialog builders with 30s timeout - All action results update message in-place with editMessageText Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+605
-1
@@ -2659,6 +2659,422 @@
|
|||||||
"name": "Telegram account"
|
"name": "Telegram account"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "=https://api.telegram.org/bot{{ $credentials.telegramApi.accessToken }}/answerCallbackQuery",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify({ callback_query_id: $json.queryId }) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "http-answer-action-callback",
|
||||||
|
"name": "Answer Action Callback",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
1340,
|
||||||
|
1200
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"telegramApi": {
|
||||||
|
"id": "I0xTTiASl7C1NZhJ",
|
||||||
|
"name": "Telegram account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"rules": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"id": "action-start",
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"typeValidation": "loose"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "is-start",
|
||||||
|
"leftValue": "={{ $('Parse Callback Data').item.json.action }}",
|
||||||
|
"rightValue": "start",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"renameOutput": true,
|
||||||
|
"outputKey": "start"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "action-restart",
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"typeValidation": "loose"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "is-restart",
|
||||||
|
"leftValue": "={{ $('Parse Callback Data').item.json.action }}",
|
||||||
|
"rightValue": "restart",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"renameOutput": true,
|
||||||
|
"outputKey": "restart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "action-stop",
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"typeValidation": "loose"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "is-stop",
|
||||||
|
"leftValue": "={{ $('Parse Callback Data').item.json.action }}",
|
||||||
|
"rightValue": "stop",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"renameOutput": true,
|
||||||
|
"outputKey": "stop"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "action-update",
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"typeValidation": "loose"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "is-update",
|
||||||
|
"leftValue": "={{ $('Parse Callback Data').item.json.action }}",
|
||||||
|
"rightValue": "update",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"renameOutput": true,
|
||||||
|
"outputKey": "update"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "action-logs",
|
||||||
|
"conditions": {
|
||||||
|
"options": {
|
||||||
|
"caseSensitive": true,
|
||||||
|
"typeValidation": "loose"
|
||||||
|
},
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"id": "is-logs",
|
||||||
|
"leftValue": "={{ $('Parse Callback Data').item.json.action }}",
|
||||||
|
"rightValue": "logs",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"combinator": "and"
|
||||||
|
},
|
||||||
|
"renameOutput": true,
|
||||||
|
"outputKey": "logs"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"fallbackOutput": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "switch-route-action-type",
|
||||||
|
"name": "Route Action Type",
|
||||||
|
"type": "n8n-nodes-base.switch",
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"position": [
|
||||||
|
1560,
|
||||||
|
1200
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Build start/restart action command for inline keyboard\nconst data = $('Parse Callback Data').item.json;\nconst containerName = data.containerName;\nconst action = data.action; // 'start' or 'restart'\nconst chatId = data.chatId;\nconst messageId = data.messageId;\n\n// For restart, use ?t=10 for graceful timeout\nconst timeout = action === 'restart' ? '?t=10' : '';\n\nreturn {\n json: {\n containerName,\n action,\n chatId,\n messageId\n }\n};"
|
||||||
|
},
|
||||||
|
"id": "code-prepare-immediate-action",
|
||||||
|
"name": "Prepare Immediate Action",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1780,
|
||||||
|
1100
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "=http://docker-socket-proxy:2375/containers/json?all=true",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "http-get-container-for-action",
|
||||||
|
"name": "Get Container For Action",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
2000,
|
||||||
|
1100
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Find container and execute action\nconst containers = $input.item.json;\nconst prevData = $('Prepare Immediate Action').item.json;\nconst containerName = prevData.containerName.toLowerCase();\nconst action = prevData.action;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\n\n// Function to normalize container names\nfunction normalizeName(name) {\n return name\n .replace(/^\\//, '')\n .replace(/^(linuxserver[-_]|binhex[-_])/i, '')\n .toLowerCase();\n}\n\n// Find the matching container\nconst container = containers.find(c => normalizeName(c.Names[0]) === containerName);\n\nif (!container) {\n return {\n json: {\n error: true,\n chatId,\n messageId,\n text: `Container \"${containerName}\" not found`\n }\n };\n}\n\nconst containerId = container.Id;\nconst timeout = action === 'restart' ? '?t=10' : '';\n\nreturn {\n json: {\n cmd: `curl -s -o /dev/null -w \"%{http_code}\" --max-time 15 -X POST 'http://docker-socket-proxy:2375/v1.47/containers/${containerId}/${action}${timeout}'`,\n containerId,\n containerName: normalizeName(container.Names[0]),\n action,\n chatId,\n messageId\n }\n};"
|
||||||
|
},
|
||||||
|
"id": "code-build-immediate-action-cmd",
|
||||||
|
"name": "Build Immediate Action Command",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
2220,
|
||||||
|
1100
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"command": "={{ $json.cmd }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "exec-immediate-action",
|
||||||
|
"name": "Execute Immediate Action",
|
||||||
|
"type": "n8n-nodes-base.executeCommand",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
2440,
|
||||||
|
1100
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Parse immediate action result and update submenu\nconst stdout = $input.item.json.stdout;\nconst prevData = $('Build Immediate Action Command').item.json;\nconst containerName = prevData.containerName;\nconst action = prevData.action;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\n\nconst statusCode = parseInt(stdout.trim());\n\n// 204: Success, 304: Already in state (also success)\nconst success = statusCode === 204 || statusCode === 304;\n\nconst verb = action === 'start' ? 'started' : 'restarted';\nconst icon = success ? '\\u2705' : '\\u274C';\nconst resultText = success ? `${containerName} ${verb} successfully` : `Failed to ${action} ${containerName}`;\n\n// Build updated keyboard based on new state (assume action succeeded)\nconst keyboard = [];\nif (action === 'start') {\n // Container is now running\n keyboard.push([\n { text: '\\u23F9\\uFE0F Stop', callback_data: `action:stop:${containerName}` },\n { text: '\\u{1F504} Restart', callback_data: `action:restart:${containerName}` }\n ]);\n} else {\n // Restart - container is still running\n keyboard.push([\n { text: '\\u23F9\\uFE0F Stop', callback_data: `action:stop:${containerName}` },\n { text: '\\u{1F504} Restart', callback_data: `action:restart:${containerName}` }\n ]);\n}\n\nkeyboard.push([\n { text: '\\u{1F4CB} Logs', callback_data: `action:logs:${containerName}` },\n { text: '\\u2B06\\uFE0F Update', callback_data: `action:update:${containerName}` }\n]);\n\nkeyboard.push([\n { text: '\\u25C0\\uFE0F Back to List', callback_data: 'list:0' }\n]);\n\nconst stateIcon = action === 'start' || success ? '\\u{1F7E2}' : '\\u26AA';\nlet text = `${stateIcon} <b>${containerName}</b>\\n\\n`;\ntext += `${icon} ${resultText}`;\n\nreturn {\n json: {\n chatId,\n messageId,\n text,\n reply_markup: { inline_keyboard: keyboard }\n }\n};"
|
||||||
|
},
|
||||||
|
"id": "code-format-immediate-result",
|
||||||
|
"name": "Format Immediate Result",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
2660,
|
||||||
|
1100
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "=https://api.telegram.org/bot{{ $credentials.telegramApi.accessToken }}/editMessageText",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chatId, message_id: $json.messageId, text: $json.text, parse_mode: 'HTML', reply_markup: $json.reply_markup }) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "http-send-immediate-result",
|
||||||
|
"name": "Send Immediate Result",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
2880,
|
||||||
|
1100
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"telegramApi": {
|
||||||
|
"id": "I0xTTiASl7C1NZhJ",
|
||||||
|
"name": "Telegram account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Prepare logs action\nconst data = $('Parse Callback Data').item.json;\nreturn {\n json: {\n containerName: data.containerName,\n chatId: data.chatId,\n messageId: data.messageId,\n lines: 30\n }\n};"
|
||||||
|
},
|
||||||
|
"id": "code-prepare-logs-action",
|
||||||
|
"name": "Prepare Logs Action",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1780,
|
||||||
|
1300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "GET",
|
||||||
|
"url": "=http://docker-socket-proxy:2375/containers/json?all=true",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "http-get-container-for-logs",
|
||||||
|
"name": "Get Container For Logs",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
2000,
|
||||||
|
1300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Find container and build logs command\nconst containers = $input.item.json;\nconst prevData = $('Prepare Logs Action').item.json;\nconst containerName = prevData.containerName.toLowerCase();\nconst lines = prevData.lines;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\n\n// Function to normalize container names\nfunction normalizeName(name) {\n return name\n .replace(/^\\//, '')\n .replace(/^(linuxserver[-_]|binhex[-_])/i, '')\n .toLowerCase();\n}\n\n// Find the matching container\nconst container = containers.find(c => normalizeName(c.Names[0]) === containerName);\n\nif (!container) {\n return {\n json: {\n error: true,\n chatId,\n messageId,\n text: `Container \"${containerName}\" not found`\n }\n };\n}\n\nconst containerId = container.Id;\n\nreturn {\n json: {\n cmd: `curl -s --max-time 10 'http://docker-socket-proxy:2375/v1.47/containers/${containerId}/logs?stdout=true&stderr=true&tail=${lines}'`,\n containerId,\n containerName: normalizeName(container.Names[0]),\n containerState: container.State,\n lines,\n chatId,\n messageId\n }\n};"
|
||||||
|
},
|
||||||
|
"id": "code-build-logs-action-cmd",
|
||||||
|
"name": "Build Logs Action Command",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
2220,
|
||||||
|
1300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"command": "={{ $json.cmd }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "exec-logs-action",
|
||||||
|
"name": "Execute Logs Action",
|
||||||
|
"type": "n8n-nodes-base.executeCommand",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
2440,
|
||||||
|
1300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Parse and format logs output\nconst stdout = $input.item.json.stdout || '';\nconst prevData = $('Build Logs Action Command').item.json;\nconst containerName = prevData.containerName;\nconst containerState = prevData.containerState;\nconst lines = prevData.lines;\nconst chatId = prevData.chatId;\nconst messageId = prevData.messageId;\n\n// Docker logs API returns binary stream with 8-byte header per line\n// Strip non-printable characters and clean up\nlet logs = stdout\n .split('\\n')\n .map(line => {\n // Remove Docker stream header (first 8 bytes of each frame)\n // The header contains stream type and length info\n if (line.length > 8) {\n const cleaned = line.substring(8).replace(/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/g, '');\n return cleaned;\n }\n return line.replace(/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/g, '');\n })\n .filter(line => line.trim().length > 0)\n .slice(-lines)\n .join('\\n');\n\nif (!logs || logs.trim().length === 0) {\n logs = '(no recent logs)';\n}\n\n// Truncate if too long for Telegram (max ~4096 chars)\nif (logs.length > 3800) {\n logs = '...' + logs.slice(-3800);\n}\n\n// Build keyboard for navigation\nconst keyboard = [];\nif (containerState === 'running') {\n keyboard.push([\n { text: '\\u23F9\\uFE0F Stop', callback_data: `action:stop:${containerName}` },\n { text: '\\u{1F504} Restart', callback_data: `action:restart:${containerName}` }\n ]);\n} else {\n keyboard.push([\n { text: '\\u25B6\\uFE0F Start', callback_data: `action:start:${containerName}` }\n ]);\n}\n\nkeyboard.push([\n { text: '\\u{1F4CB} Refresh Logs', callback_data: `action:logs:${containerName}` },\n { text: '\\u2B06\\uFE0F Update', callback_data: `action:update:${containerName}` }\n]);\n\nkeyboard.push([\n { text: '\\u25C0\\uFE0F Back to List', callback_data: 'list:0' }\n]);\n\nconst stateIcon = containerState === 'running' ? '\\u{1F7E2}' : '\\u26AA';\nlet text = `${stateIcon} <b>${containerName}</b> - Logs\\n\\n<pre>${logs}</pre>`;\n\nreturn {\n json: {\n chatId,\n messageId,\n text,\n reply_markup: { inline_keyboard: keyboard }\n }\n};"
|
||||||
|
},
|
||||||
|
"id": "code-format-logs-action-result",
|
||||||
|
"name": "Format Logs Action Result",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
2660,
|
||||||
|
1300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "=https://api.telegram.org/bot{{ $credentials.telegramApi.accessToken }}/editMessageText",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chatId, message_id: $json.messageId, text: $json.text, parse_mode: 'HTML', reply_markup: $json.reply_markup }) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "http-send-logs-result",
|
||||||
|
"name": "Send Logs Result",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
2880,
|
||||||
|
1300
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"telegramApi": {
|
||||||
|
"id": "I0xTTiASl7C1NZhJ",
|
||||||
|
"name": "Telegram account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Build Stop Confirmation dialog with timestamp for 30-second timeout\nconst data = $('Parse Callback Data').item.json;\nconst containerName = data.containerName;\nconst chatId = data.chatId;\nconst messageId = data.messageId;\nconst timestamp = Math.floor(Date.now() / 1000); // Unix timestamp\n\nconst keyboard = {\n inline_keyboard: [\n [\n { text: '\\u2705 Yes, Stop', callback_data: `confirm:stop:${containerName}:${timestamp}` },\n { text: '\\u274C Cancel', callback_data: `cancel:${containerName}` }\n ]\n ]\n};\n\nconst text = `\\u26A0\\uFE0F <b>Stop ${containerName}?</b>\\n\\nThis will stop the container immediately.\\n\\n<i>Confirmation expires in 30 seconds.</i>`;\n\nreturn {\n json: {\n chatId,\n messageId,\n text,\n reply_markup: keyboard\n }\n};"
|
||||||
|
},
|
||||||
|
"id": "code-build-stop-confirmation",
|
||||||
|
"name": "Build Stop Confirmation",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1780,
|
||||||
|
1400
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "=https://api.telegram.org/bot{{ $credentials.telegramApi.accessToken }}/editMessageText",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chatId, message_id: $json.messageId, text: $json.text, parse_mode: 'HTML', reply_markup: $json.reply_markup }) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "http-send-stop-confirmation",
|
||||||
|
"name": "Send Stop Confirmation",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
2000,
|
||||||
|
1400
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"telegramApi": {
|
||||||
|
"id": "I0xTTiASl7C1NZhJ",
|
||||||
|
"name": "Telegram account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"jsCode": "// Build Update Confirmation dialog with timestamp for 30-second timeout\nconst data = $('Parse Callback Data').item.json;\nconst containerName = data.containerName;\nconst chatId = data.chatId;\nconst messageId = data.messageId;\nconst timestamp = Math.floor(Date.now() / 1000); // Unix timestamp\n\nconst keyboard = {\n inline_keyboard: [\n [\n { text: '\\u2705 Yes, Update', callback_data: `confirm:update:${containerName}:${timestamp}` },\n { text: '\\u274C Cancel', callback_data: `cancel:${containerName}` }\n ]\n ]\n};\n\nconst text = `\\u26A0\\uFE0F <b>Update ${containerName}?</b>\\n\\nThis will pull the latest image and recreate the container.\\n\\n<i>Confirmation expires in 30 seconds.</i>`;\n\nreturn {\n json: {\n chatId,\n messageId,\n text,\n reply_markup: keyboard\n }\n};"
|
||||||
|
},
|
||||||
|
"id": "code-build-update-confirmation",
|
||||||
|
"name": "Build Update Confirmation",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1780,
|
||||||
|
1500
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "=https://api.telegram.org/bot{{ $credentials.telegramApi.accessToken }}/editMessageText",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify({ chat_id: $json.chatId, message_id: $json.messageId, text: $json.text, parse_mode: 'HTML', reply_markup: $json.reply_markup }) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "http-send-update-confirmation",
|
||||||
|
"name": "Send Update Confirmation",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.2,
|
||||||
|
"position": [
|
||||||
|
2000,
|
||||||
|
1500
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"telegramApi": {
|
||||||
|
"id": "I0xTTiASl7C1NZhJ",
|
||||||
|
"name": "Telegram account"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"connections": {
|
"connections": {
|
||||||
@@ -2763,7 +3179,13 @@
|
|||||||
"index": 0
|
"index": 0
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
[],
|
[
|
||||||
|
{
|
||||||
|
"node": "Answer Action Callback",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"node": "Answer Noop Callback",
|
"node": "Answer Noop Callback",
|
||||||
@@ -3823,6 +4245,188 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"Answer Action Callback": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Route Action Type",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Route Action Type": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Prepare Immediate Action",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Prepare Immediate Action",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Build Stop Confirmation",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Build Update Confirmation",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Prepare Logs Action",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Prepare Immediate Action": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get Container For Action",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get Container For Action": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Build Immediate Action Command",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Build Immediate Action Command": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Execute Immediate Action",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Execute Immediate Action": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Format Immediate Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Format Immediate Result": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send Immediate Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Prepare Logs Action": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Get Container For Logs",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Get Container For Logs": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Build Logs Action Command",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Build Logs Action Command": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Execute Logs Action",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Execute Logs Action": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Format Logs Action Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Format Logs Action Result": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send Logs Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Build Stop Confirmation": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send Stop Confirmation",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Build Update Confirmation": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send Update Confirmation",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pinData": {},
|
"pinData": {},
|
||||||
|
|||||||
Reference in New Issue
Block a user