From 0267ae0b4991e325bf64d2d1d76da0fc0f6ce6ad Mon Sep 17 00:00:00 2001 From: Matteo De Agazio Date: Tue, 14 Apr 2026 21:15:55 +0200 Subject: [PATCH] fix(config): load discord.reply_to_mode from config.yaml into DISCORD_REPLY_TO_MODE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit discord.reply_to_mode (and discord.extra.reply_to_mode) in config.yaml was silently ignored — the gateway only honoured the DISCORD_REPLY_TO_MODE env var, leaving the YAML setting with no effect. Apply the same env-var bridge already used for require_mention, auto_thread, free_response_channels, etc. Also handles YAML 1.1 bare 'off' (parsed as boolean False) correctly. Top-level discord.reply_to_mode takes precedence over discord.extra.reply_to_mode; a pre-existing DISCORD_REPLY_TO_MODE env var is never overwritten. --- gateway/config.py | 11 ++++ tests/gateway/test_discord_reply_mode.py | 66 +++++++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/gateway/config.py b/gateway/config.py index 2d7407323..c53607161 100644 --- a/gateway/config.py +++ b/gateway/config.py @@ -655,6 +655,17 @@ def load_gateway_config() -> GatewayConfig: ): if yaml_key in allow_mentions_cfg and not os.getenv(env_key): os.environ[env_key] = str(allow_mentions_cfg[yaml_key]).lower() + # reply_to_mode: whether bot replies reference the triggering message (off/first/all) + # Supported at top-level (discord.reply_to_mode) or nested (discord.extra.reply_to_mode) + # Note: YAML 1.1 parses bare 'off' as boolean False, so we handle that explicitly. + _extra = discord_cfg.get("extra") if isinstance(discord_cfg.get("extra"), dict) else {} + _rtp_sources = ( + discord_cfg["reply_to_mode"] if "reply_to_mode" in discord_cfg + else _extra.get("reply_to_mode") + ) + if _rtp_sources is not None and not os.getenv("DISCORD_REPLY_TO_MODE"): + _rtp_str = "off" if _rtp_sources is False else str(_rtp_sources).lower() + os.environ["DISCORD_REPLY_TO_MODE"] = _rtp_str # Telegram settings → env vars (env vars take precedence) telegram_cfg = yaml_cfg.get("telegram", {}) diff --git a/tests/gateway/test_discord_reply_mode.py b/tests/gateway/test_discord_reply_mode.py index 9060fe294..7211fcc07 100644 --- a/tests/gateway/test_discord_reply_mode.py +++ b/tests/gateway/test_discord_reply_mode.py @@ -15,7 +15,7 @@ from unittest.mock import MagicMock, AsyncMock, patch import pytest -from gateway.config import PlatformConfig, GatewayConfig, Platform, _apply_env_overrides +from gateway.config import PlatformConfig, GatewayConfig, Platform, _apply_env_overrides, load_gateway_config def _ensure_discord_mock(): @@ -396,3 +396,67 @@ class TestReplyToText: event = reply_text_adapter.handle_message.await_args.args[0] assert event.reply_to_message_id == "555" assert event.reply_to_text is None + + +class TestYamlConfigLoading: + """Tests for reply_to_mode loaded from config.yaml discord section.""" + + def _write_config(self, tmp_path, content: str): + hermes_home = tmp_path / ".hermes" + hermes_home.mkdir() + (hermes_home / "config.yaml").write_text(content, encoding="utf-8") + return hermes_home + + def test_top_level_reply_to_mode_off(self, tmp_path, monkeypatch): + """discord.reply_to_mode: off sets DISCORD_REPLY_TO_MODE env var.""" + hermes_home = self._write_config(tmp_path, "discord:\n reply_to_mode: off\n") + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + monkeypatch.delenv("DISCORD_REPLY_TO_MODE", raising=False) + + load_gateway_config() + + assert os.environ.get("DISCORD_REPLY_TO_MODE") == "off" + + def test_top_level_reply_to_mode_all(self, tmp_path, monkeypatch): + hermes_home = self._write_config(tmp_path, "discord:\n reply_to_mode: all\n") + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + monkeypatch.delenv("DISCORD_REPLY_TO_MODE", raising=False) + + load_gateway_config() + + assert os.environ.get("DISCORD_REPLY_TO_MODE") == "all" + + def test_extra_reply_to_mode_off(self, tmp_path, monkeypatch): + """discord.extra.reply_to_mode: off is also honoured.""" + hermes_home = self._write_config( + tmp_path, "discord:\n extra:\n reply_to_mode: \"off\"\n" + ) + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + monkeypatch.delenv("DISCORD_REPLY_TO_MODE", raising=False) + + load_gateway_config() + + assert os.environ.get("DISCORD_REPLY_TO_MODE") == "off" + + def test_env_var_takes_precedence_over_yaml(self, tmp_path, monkeypatch): + """Existing DISCORD_REPLY_TO_MODE env var is not overwritten by YAML.""" + hermes_home = self._write_config(tmp_path, "discord:\n reply_to_mode: all\n") + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + monkeypatch.setenv("DISCORD_REPLY_TO_MODE", "first") + + load_gateway_config() + + assert os.environ.get("DISCORD_REPLY_TO_MODE") == "first" + + def test_top_level_takes_precedence_over_extra(self, tmp_path, monkeypatch): + """discord.reply_to_mode wins over discord.extra.reply_to_mode.""" + hermes_home = self._write_config( + tmp_path, + "discord:\n reply_to_mode: all\n extra:\n reply_to_mode: \"off\"\n", + ) + monkeypatch.setenv("HERMES_HOME", str(hermes_home)) + monkeypatch.delenv("DISCORD_REPLY_TO_MODE", raising=False) + + load_gateway_config() + + assert os.environ.get("DISCORD_REPLY_TO_MODE") == "all"