Commit Graph

44 Commits

Author SHA1 Message Date
Lucas Berger 5471feec25 fix(07-02): correct Telegram credential name and ID
- Change credential name from "Telegram API" to "Telegram account"
- Update credential ID from placeholder to actual n8n ID (I0xTTiASl7C1NZhJ)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 09:18:13 -05:00
Lucas Berger 12bdd9802b refactor(07-02): migrate n8n workflow from Docker socket to proxy
- Replace all --unix-socket /var/run/docker.sock with docker-socket-proxy:2375
- Add --max-time 5 timeout to all commands (except image pull which keeps 600s)
- Update 16 curl commands across all bot operations (status, start, stop, restart, update, logs)
- No functional changes, all commands routed through security proxy
2026-02-03 09:09:48 -05:00
Lucas Berger c979a7fe7b feat(update): notify user when container is already up to date
Previously the "no update needed" branch was empty, leaving users
with no feedback after the update check completed.

Added Format No Update and Send No Update nodes to display
"<container> is already up to date" message.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 21:39:08 -05:00
Lucas Berger 808d1afe21 fix(logs): correct property name for line count parameter
Parse Logs Command returned 'lineCount' but Match Logs Container
expected 'lines', causing the line count to always be undefined
and Docker to return its default.

Renamed to 'lines' for consistency throughout the logs flow.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 21:33:39 -05:00
Lucas Berger 287c7220cb fix(logs): escape HTML entities in log output
Log content may contain <, >, & characters (like "<computed>") which
Telegram interprets as HTML tags when using parse_mode=HTML.

Added escapeHtml() function to convert:
- & → &amp;
- < → &lt;
- > → &gt;

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 21:31:22 -05:00
Lucas Berger 74dd8f1a94 fix(update): ensure image tag is specified to prevent pulling all tags
When Config.Image has no tag (e.g., "nitnelave/lldap" instead of
"nitnelave/lldap:latest"), Docker's API pulls ALL tags for that image.
This caused massive downloads and rate limit hits.

Now appends ":latest" if no tag or digest is present in the image name.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 21:21:57 -05:00
Lucas Berger 3e3b9ae47f fix(update): pipe pull output through tail to prevent memory exhaustion
Docker's /images/create API streams progress JSON for every layer.
For large images, this can be gigabytes of output that was being
buffered by curl and n8n, causing hangs and disk usage spikes.

Now pipes through `tail -c 10000` to only keep the last 10KB where
error/success messages appear. Discards the streaming progress data.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 21:14:25 -05:00
Lucas Berger 88830a8b61 fix(update): correct image removal node data reference
- Reference $('Format Update Result') instead of $json for currentImageId
- The Telegram node doesn't pass through input data, it returns API response
- Also add no-op command fallback when currentImageId is missing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 20:56:00 -05:00
Lucas Berger 4f85e00dc4 fix(update): add timeout to prevent pull command from hanging
- Add --max-time 600 (10 min) to curl pull command
- Add timeout: 660 to n8n executeCommand node

Docker's /images/create API streams progress until complete.
Without timeout, large image pulls could hang indefinitely.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 20:50:22 -05:00
Lucas Berger d03e79cc7f feat(05): add update acknowledgment and pull error handling
1. Send "Updating <container>..." message immediately when update starts
   so user knows the command was received during long image pulls

2. Check pull response for rate limiting and other errors before
   continuing with update. Errors like "toomanyrequests" now show
   a proper error message instead of silently failing to update.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 22:07:40 -05:00
Lucas Berger 0839c44b50 feat(05): remove old image after successful container update
Mimics Unraid's update behavior by removing the orphan image after
the new container is started. The old image ID is now passed through
the entire update flow and used to call DELETE /images/{id} at the end.

Removal is fire-and-forget with force=false so it will fail gracefully
if another container still uses the image.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 21:58:41 -05:00
Lucas Berger 004911ea32 fix(05): improve container matching to prioritize exact matches
Fixed matching in Match Container, Match Update Container, and Match
Logs Container nodes:
1. First check for exact name match (e.g., "jellyplex" matches only jellyplex)
2. Then fall back to substring matching (container name contains query)
3. Removed reverse matching (query contains container name) which caused
   "jellyplex" to incorrectly match "plex"

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 21:41:27 -05:00
Lucas Berger 0b6dfe69d9 fix(05-01): escape angle brackets in Show Menu text
Changed <name> to [name] to avoid HTML parse errors in Telegram.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 21:36:23 -05:00
Lucas Berger 0b140c4f1e fix(05-01): simplify Show Menu to text-only help message
Native Telegram node replyKeyboard wasn't displaying. Fallback to
simple text message listing available commands. Can revisit persistent
keyboard feature later.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 21:34:45 -05:00
Lucas Berger 6defb2daaf fix(05-01): switch Show Menu to native Telegram node
HTTP Request node with $credentials reference was causing 404 errors.
Native Telegram node handles credentials more reliably for static menus.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 21:31:16 -05:00
Lucas Berger 32fd965a2f fix(05-02): revert credential reference to hardcoded user ID
n8n IF nodes don't support credentials - $credentials syntax only works
in nodes that make external calls. Reverted to direct user ID in the
IF conditions and updated README with simpler configuration instructions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 21:28:20 -05:00
Lucas Berger 1e6c31f790 feat(05-02): migrate user ID to n8n credentials system
- Auth nodes now reference $credentials.telegramAuth.userId
- Added telegramAuth credential reference to both IF nodes
- Removed hardcoded user ID from workflow JSON
- Workflow can now be safely exported/shared
2026-01-31 21:15:20 -05:00
Lucas Berger cab0914788 chore(05-02): standardize error messages to terse format
- Docker socket errors now show 'Cannot connect to Docker'
- Action failures now show 'Failed to {action} {container}'
- Removed HTTP status codes and technical details from error messages
- Simplified callback action error handling
2026-01-31 21:14:36 -05:00
Lucas Berger a29f444e08 feat(05-01): replace NLU/Claude with keyword routing
- Remove Prepare Claude Request, Claude Intent Parser, Parse Intent,
  Intent Router, Send Unknown Intent, Send Intent Error nodes
- Remove Anthropic API credential reference
- Rename Route Message to Keyword Router with updated rules
- Update IF User Authenticated to connect to Keyword Router
- Update Parse and Match to work without NLU context
- Update Parse Action Command to parse from message text directly
- Update Match Container to reference Parse Action Command
- Update Parse Logs Command to work with keyword routing

Keyword Router handles: /start, status, restart, start, stop,
update, logs with fallback to menu

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 21:09:33 -05:00
Lucas Berger 5e7cab54ca fix(batch): convert Send Batch Confirmation to native Telegram node
HTTP Request node with credential access via URL expression was
failing. Native Telegram node handles credentials internally and
is more reliable for this use case.
2026-01-30 22:37:09 -05:00
Lucas Berger 23ce6a88d5 fix(batch): add chatId validation in Build Batch Keyboard
Add defensive error handling to throw clear error if chatId is
missing before attempting to send Telegram message. This helps
debug 'resource not found' errors by identifying data flow issues.
2026-01-30 22:36:44 -05:00
Lucas Berger 5d55bdecac fix(04): fix type validation in all Switch nodes with number comparisons
Fixed Check Match Count and Check Update Match Count nodes:
- Changed typeValidation from 'strict' to 'loose'
- Changed rightValue from strings ('0', '1') to numbers (0, 1)
- Removed empty leftValue from options

This fixes 'Wrong type: string but expecting number' errors.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:26:08 -05:00
Lucas Berger aa677703c9 fix(04): update Match Container to use Parse Intent
Changed Match Container to get action data from Parse Intent node
instead of the old Parse Action node which isn't executed in the
intent-based routing flow.

Mapping:
- action -> intent.parameters.action
- containerQuery -> intent.container
- chatId -> intent.original_message.chat.id

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:21:37 -05:00
Lucas Berger d842efbf17 fix(04): bypass Format Response for intent-based status queries
Parse and Match now returns formatted text directly, so Format Response
node is redundant. Connect Parse and Match directly to Send Docker Response
to avoid 'Unexpected response format' error.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:18:44 -05:00
Lucas Berger ada8800bff fix(04): fix Check Logs Match Count type validation
Changed typeValidation from 'strict' to 'loose' and rightValue from
strings to numbers to fix type mismatch error in Switch node conditions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:13:56 -05:00
Lucas Berger f423f4ae4c fix(04): add Prepare Claude Request node for robust API body
Split Claude API call into two nodes:
- Prepare Claude Request: Code node that builds the request body
- Claude Intent Parser: HTTP Request node that sends the request

This fixes the 'model: Field Required' error caused by complex
expression evaluation issues in the HTTP Request node's jsonBody.

Also updated Parse Intent to get original message from the new node.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:10:50 -05:00
Lucas Berger a08b8dba6b fix(04): fix Route Update Type conversion error
Removed empty leftValue from options and changed condition check from
object notEmpty to string notEmpty using optional chaining on specific
properties (message?.text, callback_query?.id).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:05:34 -05:00
Lucas Berger 87c7bf75e5 fix(04): correct connection source names for logs nodes
Connection keys referenced hyphenated IDs (code-parse-logs) instead of
actual node names (Parse Logs Command). Fixed 10 connection sources to
use proper node names.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 22:01:27 -05:00
Lucas Berger 01667b0bdb feat(04-02): integrate Claude API for natural language understanding
Tasks 1-3 complete:

Task 1: Claude API HTTP Request node
- POST to api.anthropic.com/v1/messages
- Uses claude-sonnet-4-5-20250929 with prompt caching
- System prompt defines 6 intent actions
- 30s timeout, 3 retries

Task 2: Intent parsing and validation
- Parse Intent Code node validates Claude response
- Strips markdown, validates actions, normalizes container names
- Error handling for malformed responses
- Preserves original message for fallback

Task 3: Intent-first routing
- Rewired auth -> Claude Intent Parser flow
- Intent Router Switch with 7 outputs
- Routes to existing handlers: logs, actions, status
- New handlers: unknown intent, stats placeholder, error
- Updated Parse and Match to use intent.container
- Updated Parse Action to use intent data
- Updated Parse Logs Command to use intent structure
2026-01-30 21:45:49 -05:00
Lucas Berger 93c40fed66 feat(04-01): add logs command routing to workflow
- Add logs command route in Route Message switch node
- Pattern matches: 'logs <container>' or 'show logs <container>'
- Create Parse Logs Command node to extract container name and line count
- Support optional line count parameter (default 50, max 1000)
- Examples: 'logs plex', 'show logs sonarr', 'logs nginx 100'

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 21:42:48 -05:00
Lucas Berger 04321c1c9a feat(03-04): implement container update workflow
Add complete update command flow with image pull and container recreation:

- Parse update command and extract container query
- Match container using fuzzy matching (single match only for update)
- Handle no-match and multiple-match cases with appropriate messages
- Inspect container to extract current config
- Pull latest image from registry
- Compare image digests to detect if update is available
- Stay silent if no update needed (per CONTEXT.md)
- Stop container with graceful 10-second timeout
- Remove old container
- Create new container preserving Config, HostConfig, Networks
- Start new container
- Report version change (from image labels or ID substring)

Nodes added:
- Parse Update Command, Docker List for Update, Match Update Container
- Check Update Match Count, Handle Update Multiple, Send Update Error/No Match/Multiple
- Build Inspect Command, Inspect Container, Parse Container Config
- Build Pull Command, Pull Image, Build Image Inspect, Inspect New Image
- Compare Digests, Check If Update Needed
- Build Stop Command, Stop Container, Verify Stop Build Remove
- Remove Container, Build Create Body, Build Create Command
- Create Container, Parse Create Response
- Build Start Command, Start New Container
- Format Update Result, Send Update Result

Closes all 3 tasks of 03-04-PLAN.md
2026-01-30 13:26:32 -05:00
Lucas Berger 6737ef09c8 feat(03-03): clean up UI after batch action
- Add Answer Batch Query to dismiss callback loading state
- Add Delete Batch Confirm Message to remove confirmation message
- Add Send Batch Result to display final success/failure message
- Wire up complete flow: Format Result -> Answer -> Delete -> Send
- UI cleanup keeps chat clean with only result message remaining
2026-01-30 08:48:04 -05:00
Lucas Berger 25a7994fcb feat(03-03): handle batch confirmation callback execution
- Update Parse Callback Data to detect batch (c is array) vs single
- Add isBatch and containerIds fields to callback data
- Add 'batch' route in Route Callback switch (output 2)
- Add Build Batch Commands node to prepare curl commands for each container
- Add Prepare Batch Execution to combine commands with result markers
- Add Execute Batch Action to run all container actions sequentially
- Add Parse Batch Result to parse RESULT_N:statusCode output
- Add Format Batch Result to build success/failure message
2026-01-30 08:47:27 -05:00
Lucas Berger ab8d5282c0 feat(03-03): build batch confirmation keyboard with inline buttons
- Replace placeholder 'Format Multiple Matches' with 'Build Batch Keyboard'
- Create inline_keyboard with 'Yes, <action> N containers' and 'Cancel' buttons
- Encode batch container IDs in callback_data (limit 4 for 64-byte constraint)
- Use HTTP Request for sendMessage (same pattern as suggestion flow)
- Format container list with bullet points in confirmation message
2026-01-30 08:45:56 -05:00
Lucas Berger 768d7584e2 feat(03-02): handle suggestion callback and execute action
- Add Parse Callback Data code node to decode callback_query JSON
- Add Route Callback switch for cancel/expired/execute branches
- Add Handle Cancel with answer query and delete message
- Add Handle Expired with alert message and delete message
- Add Build Callback Action to construct curl command from callback
- Add Execute Callback Action to run Docker API call
- Add Parse Callback Result to check status and build response
- Add Answer Action Query, Delete Suggestion Message, Send Callback Result
- 2-minute timeout enforced via timestamp in callback_data
2026-01-30 08:42:43 -05:00
Lucas Berger 56eea26d44 feat(03-02): implement suggestion flow for no-match cases
- Replace Format No Match with Find Closest Match code node
- Add Check Suggestion IF node to route based on hasSuggestion
- Add Build Suggestion Keyboard code node for inline button payload
- Add Send Suggestion HTTP Request to Telegram API with inline_keyboard
- Update Match Container to include allContainers for suggestion logic
- Suggestion shown when score >= 2 (partial match found)
2026-01-30 08:41:27 -05:00
Lucas Berger 2cbf6e7ec7 feat(03-02): configure Telegram Trigger for callback queries
- Update Telegram Trigger to receive both message and callback_query updates
- Add Route Update Type switch to route messages vs callbacks
- Add IF Callback Authenticated node for callback query auth
- Restructure connections: message flow through auth, callback through separate auth
2026-01-30 08:40:15 -05:00
Lucas Berger 2bd90c8a0c feat(03-01): add error handling for action flow
- Add docker-error route to Check Match Count switch (matchCount < 0)
- Add Send Docker Error node for Docker connection failures
- Route Docker API errors to user-facing error messages
- Ensure all error paths reach a Send Message node (no silent failures)
- Error messages include diagnostic details per CONTEXT.md requirements
2026-01-30 08:36:44 -05:00
Lucas Berger f466a2916e feat(03-01): implement container matching and action execution
- Add Docker List for Action node to get container list
- Add Match Container node with fuzzy matching (substring, prefix stripping)
- Add Check Match Count Switch node to route 0/1/>1 matches
- Add Build Action Command node to construct curl POST command
- Add Execute Action node to call Docker API start/stop/restart
- Add Parse Action Result node handling 204/304 success and error codes
- Add Send Action Result node for Telegram response
- Add placeholder nodes for No Match and Multiple Matches branches
- Use graceful ?t=10 timeout for stop/restart actions
2026-01-30 08:35:38 -05:00
Lucas Berger 4848e7db16 feat(03-01): add action command routing to workflow
- Add new route in Switch node for start/stop/restart commands
- Route matches case-insensitive startsWith for each action
- Add Parse Action Code node to extract action type and container name
- Action branch routes to Parse Action, ready for container matching
2026-01-30 08:33:47 -05:00
Lucas Berger 8e155c55de fix(02-02): fix false positive docker connection error
- Add -s (silent) flag to curl to suppress progress output
- Change error handling to only check stdout for valid JSON
- Ignore stderr which contains curl progress info
2026-01-29 14:39:14 -05:00
Lucas Berger 1252ff42f4 feat(02-02): add Docker query branch to workflow
- Add Switch node to route messages based on content
- Add Execute Command node with curl to Docker API
- Add Parse and Match code node with fuzzy matching
- Add Format Response code node with emoji mapping
- Preserve echo branch for non-docker messages
- Handle summary, single match, multiple matches, and errors
2026-01-29 14:28:01 -05:00
Lucas Berger 23c5705050 fix(01-01): hardcode user ID instead of env var
n8n community edition blocks env var access in expressions.
Hardcoded TELEGRAM_USER_ID for single-user authentication.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 20:25:33 -05:00
Lucas Berger 9d503bb9a8 feat(01-01): create n8n workflow for telegram echo bot
- Telegram Trigger node listens for messages
- IF node authenticates via $env.TELEGRAM_USER_ID
- Code node formats echo with timestamp
- Telegram Send node replies to authorized users
- False branch has no connections (silent ignore)
2026-01-28 19:47:39 -05:00