From 6d2727ef1ce1c431e8c6119a8fd10867991c7004 Mon Sep 17 00:00:00 2001 From: quen0xi Date: Fri, 29 May 2026 13:34:21 +0300 Subject: [PATCH] fix(discord): bridge explicit allow_from configuration to env var mapping --- plugins/platforms/discord/adapter.py | 37 ++++++++++++++------ tests/gateway/test_config.py | 50 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/plugins/platforms/discord/adapter.py b/plugins/platforms/discord/adapter.py index c58afffcd..12cf05c38 100644 --- a/plugins/platforms/discord/adapter.py +++ b/plugins/platforms/discord/adapter.py @@ -6093,16 +6093,17 @@ def _apply_yaml_config(yaml_cfg: dict, discord_cfg: dict) -> dict | None: ``gateway/config.py::load_gateway_config()`` before this migration. The DiscordAdapter reads its runtime configuration via ``os.getenv()`` - throughout the connect / handle code paths (``DISCORD_REQUIRE_MENTION``, - ``DISCORD_FREE_RESPONSE_CHANNELS``, ``DISCORD_AUTO_THREAD``, - ``DISCORD_REACTIONS``, ``DISCORD_IGNORED_CHANNELS``, - ``DISCORD_ALLOWED_CHANNELS``, ``DISCORD_NO_THREAD_CHANNELS``, - ``DISCORD_HISTORY_BACKFILL``, ``DISCORD_HISTORY_BACKFILL_LIMIT``, - ``DISCORD_ALLOW_MENTION_*``, ``DISCORD_REPLY_TO_MODE``, - ``DISCORD_THREAD_REQUIRE_MENTION``). Rather than rewrite ~50 call sites - inside the adapter to read from ``PlatformConfig.extra`` instead, this - hook keeps the existing env-driven model and merely owns the - YAML→env translation here, next to the adapter that consumes it. + throughout the connect / handle code paths (``DISCORD_ALLOWED_USERS``, + ``DISCORD_REQUIRE_MENTION``, ``DISCORD_FREE_RESPONSE_CHANNELS``, + ``DISCORD_AUTO_THREAD``, ``DISCORD_REACTIONS``, + ``DISCORD_IGNORED_CHANNELS``, ``DISCORD_ALLOWED_CHANNELS``, + ``DISCORD_NO_THREAD_CHANNELS``, ``DISCORD_HISTORY_BACKFILL``, + ``DISCORD_HISTORY_BACKFILL_LIMIT``, ``DISCORD_ALLOW_MENTION_*``, + ``DISCORD_REPLY_TO_MODE``, ``DISCORD_THREAD_REQUIRE_MENTION``). + Rather than rewrite ~50 call sites inside the adapter to read from + ``PlatformConfig.extra`` instead, this hook keeps the existing + env-driven model and merely owns the YAML→env translation here, next to + the adapter that consumes it. Env vars take precedence over YAML — every assignment is guarded by ``not os.getenv(...)`` so explicit env vars survive a config.yaml @@ -6113,6 +6114,22 @@ def _apply_yaml_config(yaml_cfg: dict, discord_cfg: dict) -> dict | None: os.environ["DISCORD_REQUIRE_MENTION"] = str(discord_cfg["require_mention"]).lower() if "thread_require_mention" in discord_cfg and not os.getenv("DISCORD_THREAD_REQUIRE_MENTION"): os.environ["DISCORD_THREAD_REQUIRE_MENTION"] = str(discord_cfg["thread_require_mention"]).lower() + platforms_cfg = yaml_cfg.get("platforms") + platform_extra_cfg = {} + if isinstance(platforms_cfg, dict): + discord_platform_cfg = platforms_cfg.get("discord") + if isinstance(discord_platform_cfg, dict): + candidate_extra = discord_platform_cfg.get("extra") + if isinstance(candidate_extra, dict): + platform_extra_cfg = candidate_extra + allowed_users_cfg = ( + discord_cfg["allow_from"] if "allow_from" in discord_cfg + else platform_extra_cfg.get("allow_from") + ) + if allowed_users_cfg is not None and not os.getenv("DISCORD_ALLOWED_USERS"): + if isinstance(allowed_users_cfg, list): + allowed_users_cfg = ",".join(str(v) for v in allowed_users_cfg) + os.environ["DISCORD_ALLOWED_USERS"] = str(allowed_users_cfg) frc = discord_cfg.get("free_response_channels") if frc is not None and not os.getenv("DISCORD_FREE_RESPONSE_CHANNELS"): if isinstance(frc, list): diff --git a/tests/gateway/test_config.py b/tests/gateway/test_config.py index d336f54a9..da970eccf 100644 --- a/tests/gateway/test_config.py +++ b/tests/gateway/test_config.py @@ -343,6 +343,56 @@ class TestLoadGatewayConfig: # Env value preserved, not clobbered by yaml. assert os.environ.get("DISCORD_THREAD_REQUIRE_MENTION") == "true" + def test_bridges_discord_allow_from_from_config_yaml(self, tmp_path, monkeypatch): + """discord.allow_from should populate DISCORD_ALLOWED_USERS for auth.""" + hermes_home = tmp_path / ".hermes" + hermes_home.mkdir() + config_path = hermes_home / "config.yaml" + config_path.write_text( + "discord:\n" + " allow_from:\n" + " - \"123456789012345678\"\n" + " - \"999888777666555444\"\n", + encoding="utf-8", + ) + + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + monkeypatch.delenv("DISCORD_ALLOWED_USERS", raising=False) + + config = load_gateway_config() + + assert config.platforms[Platform.DISCORD].extra["allow_from"] == [ + "123456789012345678", + "999888777666555444", + ] + assert os.environ.get("DISCORD_ALLOWED_USERS") == ( + "123456789012345678,999888777666555444" + ) + + def test_bridges_discord_platform_extra_allow_from_to_env(self, tmp_path, monkeypatch): + """platforms.discord.extra.allow_from should reach DISCORD_ALLOWED_USERS too.""" + hermes_home = tmp_path / ".hermes" + hermes_home.mkdir() + config_path = hermes_home / "config.yaml" + config_path.write_text( + "platforms:\n" + " discord:\n" + " extra:\n" + " allow_from:\n" + " - \"123456789012345678\"\n", + encoding="utf-8", + ) + + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + monkeypatch.delenv("DISCORD_ALLOWED_USERS", raising=False) + + config = load_gateway_config() + + assert config.platforms[Platform.DISCORD].extra["allow_from"] == [ + "123456789012345678", + ] + assert os.environ.get("DISCORD_ALLOWED_USERS") == "123456789012345678" + def test_bridges_quoted_false_platform_enabled_from_config_yaml(self, tmp_path, monkeypatch): hermes_home = tmp_path / ".hermes" hermes_home.mkdir()