From 38d694f55919c2aba1396450ba8b12db5545828b Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sun, 29 Mar 2026 15:48:51 -0700 Subject: [PATCH] fix(gateway): apply home channel env overrides consistently (#3808) Home channel env vars (SLACK_HOME_CHANNEL, SIGNAL_HOME_CHANNEL, etc.) for Slack, Signal, Mattermost, Matrix, Email, and SMS were nested inside the credential-env blocks, so they were ignored when the platform was already configured via config.yaml. Moved the home channel handling outside the credential blocks with a Platform.X in config.platforms guard, matching the existing pattern for Telegram and Discord. Co-authored-by: cutepawss --- gateway/config.py | 85 ++++++++++++++++++------------------ tests/gateway/test_config.py | 76 ++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 43 deletions(-) diff --git a/gateway/config.py b/gateway/config.py index f93c6905a..5dbc81c86 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -647,14 +647,13 @@ def _apply_env_overrides(config: GatewayConfig) -> None: config.platforms[Platform.SLACK] = PlatformConfig() config.platforms[Platform.SLACK].enabled = True config.platforms[Platform.SLACK].token = slack_token - # Home channel - slack_home = os.getenv("SLACK_HOME_CHANNEL") - if slack_home: - config.platforms[Platform.SLACK].home_channel = HomeChannel( - platform=Platform.SLACK, - chat_id=slack_home, - name=os.getenv("SLACK_HOME_CHANNEL_NAME", ""), - ) + slack_home = os.getenv("SLACK_HOME_CHANNEL") + if slack_home and Platform.SLACK in config.platforms: + config.platforms[Platform.SLACK].home_channel = HomeChannel( + platform=Platform.SLACK, + chat_id=slack_home, + name=os.getenv("SLACK_HOME_CHANNEL_NAME", ""), + ) # Signal signal_url = os.getenv("SIGNAL_HTTP_URL") @@ -668,13 +667,13 @@ def _apply_env_overrides(config: GatewayConfig) -> None: "account": signal_account, "ignore_stories": os.getenv("SIGNAL_IGNORE_STORIES", "true").lower() in ("true", "1", "yes"), }) - signal_home = os.getenv("SIGNAL_HOME_CHANNEL") - if signal_home: - config.platforms[Platform.SIGNAL].home_channel = HomeChannel( - platform=Platform.SIGNAL, - chat_id=signal_home, - name=os.getenv("SIGNAL_HOME_CHANNEL_NAME", "Home"), - ) + signal_home = os.getenv("SIGNAL_HOME_CHANNEL") + if signal_home and Platform.SIGNAL in config.platforms: + config.platforms[Platform.SIGNAL].home_channel = HomeChannel( + platform=Platform.SIGNAL, + chat_id=signal_home, + name=os.getenv("SIGNAL_HOME_CHANNEL_NAME", "Home"), + ) # Mattermost mattermost_token = os.getenv("MATTERMOST_TOKEN") @@ -687,13 +686,13 @@ def _apply_env_overrides(config: GatewayConfig) -> None: config.platforms[Platform.MATTERMOST].enabled = True config.platforms[Platform.MATTERMOST].token = mattermost_token config.platforms[Platform.MATTERMOST].extra["url"] = mattermost_url - mattermost_home = os.getenv("MATTERMOST_HOME_CHANNEL") - if mattermost_home: - config.platforms[Platform.MATTERMOST].home_channel = HomeChannel( - platform=Platform.MATTERMOST, - chat_id=mattermost_home, - name=os.getenv("MATTERMOST_HOME_CHANNEL_NAME", "Home"), - ) + mattermost_home = os.getenv("MATTERMOST_HOME_CHANNEL") + if mattermost_home and Platform.MATTERMOST in config.platforms: + config.platforms[Platform.MATTERMOST].home_channel = HomeChannel( + platform=Platform.MATTERMOST, + chat_id=mattermost_home, + name=os.getenv("MATTERMOST_HOME_CHANNEL_NAME", "Home"), + ) # Matrix matrix_token = os.getenv("MATRIX_ACCESS_TOKEN") @@ -715,13 +714,13 @@ def _apply_env_overrides(config: GatewayConfig) -> None: config.platforms[Platform.MATRIX].extra["password"] = matrix_password matrix_e2ee = os.getenv("MATRIX_ENCRYPTION", "").lower() in ("true", "1", "yes") config.platforms[Platform.MATRIX].extra["encryption"] = matrix_e2ee - matrix_home = os.getenv("MATRIX_HOME_ROOM") - if matrix_home: - config.platforms[Platform.MATRIX].home_channel = HomeChannel( - platform=Platform.MATRIX, - chat_id=matrix_home, - name=os.getenv("MATRIX_HOME_ROOM_NAME", "Home"), - ) + matrix_home = os.getenv("MATRIX_HOME_ROOM") + if matrix_home and Platform.MATRIX in config.platforms: + config.platforms[Platform.MATRIX].home_channel = HomeChannel( + platform=Platform.MATRIX, + chat_id=matrix_home, + name=os.getenv("MATRIX_HOME_ROOM_NAME", "Home"), + ) # Home Assistant hass_token = os.getenv("HASS_TOKEN") @@ -748,13 +747,13 @@ def _apply_env_overrides(config: GatewayConfig) -> None: "imap_host": email_imap, "smtp_host": email_smtp, }) - email_home = os.getenv("EMAIL_HOME_ADDRESS") - if email_home: - config.platforms[Platform.EMAIL].home_channel = HomeChannel( - platform=Platform.EMAIL, - chat_id=email_home, - name=os.getenv("EMAIL_HOME_ADDRESS_NAME", "Home"), - ) + email_home = os.getenv("EMAIL_HOME_ADDRESS") + if email_home and Platform.EMAIL in config.platforms: + config.platforms[Platform.EMAIL].home_channel = HomeChannel( + platform=Platform.EMAIL, + chat_id=email_home, + name=os.getenv("EMAIL_HOME_ADDRESS_NAME", "Home"), + ) # SMS (Twilio) twilio_sid = os.getenv("TWILIO_ACCOUNT_SID") @@ -763,13 +762,13 @@ def _apply_env_overrides(config: GatewayConfig) -> None: config.platforms[Platform.SMS] = PlatformConfig() config.platforms[Platform.SMS].enabled = True config.platforms[Platform.SMS].api_key = os.getenv("TWILIO_AUTH_TOKEN", "") - sms_home = os.getenv("SMS_HOME_CHANNEL") - if sms_home: - config.platforms[Platform.SMS].home_channel = HomeChannel( - platform=Platform.SMS, - chat_id=sms_home, - name=os.getenv("SMS_HOME_CHANNEL_NAME", "Home"), - ) + sms_home = os.getenv("SMS_HOME_CHANNEL") + if sms_home and Platform.SMS in config.platforms: + config.platforms[Platform.SMS].home_channel = HomeChannel( + platform=Platform.SMS, + chat_id=sms_home, + name=os.getenv("SMS_HOME_CHANNEL_NAME", "Home"), + ) # API Server api_server_enabled = os.getenv("API_SERVER_ENABLED", "").lower() in ("true", "1", "yes") diff --git a/tests/gateway/test_config.py b/tests/gateway/test_config.py index 8dbb725d8..8f24faa99 100644 --- a/tests/gateway/test_config.py +++ b/tests/gateway/test_config.py @@ -1,11 +1,15 @@ """Tests for gateway configuration management.""" +import os +from unittest.mock import patch + from gateway.config import ( GatewayConfig, HomeChannel, Platform, PlatformConfig, SessionResetPolicy, + _apply_env_overrides, load_gateway_config, ) @@ -192,3 +196,75 @@ class TestLoadGatewayConfig: assert config.unauthorized_dm_behavior == "ignore" assert config.platforms[Platform.WHATSAPP].extra["unauthorized_dm_behavior"] == "pair" + + +class TestHomeChannelEnvOverrides: + """Home channel env vars should apply even when the platform was already + configured via config.yaml (not just when credential env vars create it).""" + + def test_existing_platform_configs_accept_home_channel_env_overrides(self): + cases = [ + ( + Platform.SLACK, + PlatformConfig(enabled=True, token="xoxb-from-config"), + {"SLACK_HOME_CHANNEL": "C123", "SLACK_HOME_CHANNEL_NAME": "Ops"}, + ("C123", "Ops"), + ), + ( + Platform.SIGNAL, + PlatformConfig( + enabled=True, + extra={"http_url": "http://localhost:9090", "account": "+15551234567"}, + ), + {"SIGNAL_HOME_CHANNEL": "+1555000", "SIGNAL_HOME_CHANNEL_NAME": "Phone"}, + ("+1555000", "Phone"), + ), + ( + Platform.MATTERMOST, + PlatformConfig( + enabled=True, + token="mm-token", + extra={"url": "https://mm.example.com"}, + ), + {"MATTERMOST_HOME_CHANNEL": "ch_abc123", "MATTERMOST_HOME_CHANNEL_NAME": "General"}, + ("ch_abc123", "General"), + ), + ( + Platform.MATRIX, + PlatformConfig( + enabled=True, + token="syt_abc123", + extra={"homeserver": "https://matrix.example.org"}, + ), + {"MATRIX_HOME_ROOM": "!room123:example.org", "MATRIX_HOME_ROOM_NAME": "Bot Room"}, + ("!room123:example.org", "Bot Room"), + ), + ( + Platform.EMAIL, + PlatformConfig( + enabled=True, + extra={ + "address": "hermes@test.com", + "imap_host": "imap.test.com", + "smtp_host": "smtp.test.com", + }, + ), + {"EMAIL_HOME_ADDRESS": "user@test.com", "EMAIL_HOME_ADDRESS_NAME": "Inbox"}, + ("user@test.com", "Inbox"), + ), + ( + Platform.SMS, + PlatformConfig(enabled=True, api_key="token_abc"), + {"SMS_HOME_CHANNEL": "+15559876543", "SMS_HOME_CHANNEL_NAME": "My Phone"}, + ("+15559876543", "My Phone"), + ), + ] + + for platform, platform_config, env, expected in cases: + config = GatewayConfig(platforms={platform: platform_config}) + with patch.dict(os.environ, env, clear=True): + _apply_env_overrides(config) + + home = config.platforms[platform].home_channel + assert home is not None, f"{platform.value}: home_channel should not be None" + assert (home.chat_id, home.name) == expected, platform.value