From e7ceca0f811c847e31534bac5e2c935a4205af8f Mon Sep 17 00:00:00 2001 From: sunba91-su Date: Mon, 8 Jun 2026 19:28:01 +0330 Subject: [PATCH] fix: support special chars in password and exit on auth failure Two fixes: 1. Add ROCKETCHAT_BOT_PASSWORD_FILE env var support (Docker _FILE pattern). Reads password from a file, avoiding shell/env escaping issues with characters like #, $, &, (, ), etc. 2. Exit immediately on authentication errors (401, User not found, IP blocked) instead of retrying with backoff. Prevents the bot account from being temporarily locked due to rapid retries. Also: document the _FILE option in .env.example and README.md. Closes #33 --- .env.example | 4 ++++ README.md | 3 ++- internal/config/config.go | 14 +++++++++++++- internal/rocket/client.go | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 390c9b0..dc6957b 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,10 @@ ROCKETCHAT_SERVER_URL=https://chat.yourcompany.com ROCKETCHAT_BOT_USERNAME=geekbot ROCKETCHAT_BOT_PASSWORD=your-password + +# Alternative: read password from a file (avoids shell/env escaping issues) +# ROCKETCHAT_BOT_PASSWORD_FILE=/run/secrets/bot_password + ROCKETCHAT_MAIN_ADMIN=admin_username STANDUP_DB_PATH=/data/standup-bot.db diff --git a/README.md b/README.md index bcb90ce..e2d0261 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ cp .env.example .env |----------|----------|---------|-------------| | `ROCKETCHAT_SERVER_URL` | Yes | — | Rocket.Chat server URL | | `ROCKETCHAT_BOT_USERNAME` | Yes | — | Bot account username | -| `ROCKETCHAT_BOT_PASSWORD` | Yes | — | Bot account password | +| `ROCKETCHAT_BOT_PASSWORD` | One of | — | Bot account password (use if password has no special chars) | +| `ROCKETCHAT_BOT_PASSWORD_FILE` | One of | — | Path to file containing the bot password (avoids shell/env escaping) | | `ROCKETCHAT_MAIN_ADMIN` | Yes | — | Rocket.Chat username of the main bot administrator | | `STANDUP_DB_PATH` | No | `~/standup-bot.db` | Path to the SQLite database file | diff --git a/internal/config/config.go b/internal/config/config.go index c62526b..5ad9034 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -3,6 +3,7 @@ package config import ( "fmt" "os" + "strings" ) type Config struct { @@ -13,10 +14,21 @@ type Config struct { } func Load() (*Config, error) { + botPass := os.Getenv("ROCKETCHAT_BOT_PASSWORD") + if botPass == "" { + if path := os.Getenv("ROCKETCHAT_BOT_PASSWORD_FILE"); path != "" { + b, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("reading ROCKETCHAT_BOT_PASSWORD_FILE: %w", err) + } + botPass = strings.TrimSpace(string(b)) + } + } + cfg := &Config{ ServerURL: os.Getenv("ROCKETCHAT_SERVER_URL"), BotUser: os.Getenv("ROCKETCHAT_BOT_USERNAME"), - BotPass: os.Getenv("ROCKETCHAT_BOT_PASSWORD"), + BotPass: botPass, MainAdmin: os.Getenv("ROCKETCHAT_MAIN_ADMIN"), } diff --git a/internal/rocket/client.go b/internal/rocket/client.go index c2b5003..1bc068a 100644 --- a/internal/rocket/client.go +++ b/internal/rocket/client.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "net/url" + "strings" "sync" "time" @@ -75,6 +76,9 @@ func (c *Client) Connect(user, password string) error { log.Printf("Connecting to Rocket.Chat at %s", c.serverURL.String()) if err := c.connect(); err != nil { + if isAuthError(err) { + return fmt.Errorf("authentication failed — check bot credentials: %w", err) + } return err } @@ -117,6 +121,17 @@ func (c *Client) connect() error { return nil } +func isAuthError(err error) bool { + if err == nil { + return false + } + s := err.Error() + return strings.Contains(s, "401") || + strings.Contains(s, "User not found") || + strings.Contains(s, "error-login-blocked") || + strings.Contains(s, "Login has been temporarily blocked") +} + func (c *Client) Disconnect() { c.mu.Lock() defer c.mu.Unlock() @@ -244,6 +259,10 @@ func (c *Client) watchConnection() { err := c.connect() c.mu.Unlock() + if isAuthError(err) { + log.Fatalf("Authentication failed — check bot credentials: %v", err) + } + if err == nil { log.Println("Reconnected successfully")