diff --git a/plugins/platforms/telegram/adapter.py b/plugins/platforms/telegram/adapter.py index a10ef88b06..07acb7ad03 100644 --- a/plugins/platforms/telegram/adapter.py +++ b/plugins/platforms/telegram/adapter.py @@ -5133,9 +5133,8 @@ class TelegramAdapter(BasePlatformAdapter): return self._extra_str_set("allowed_topics", "TELEGRAM_ALLOWED_TOPICS") def _telegram_ignored_threads(self) -> set[int]: - raw = self.config.extra.get("ignored_threads") - if raw is None: - raw = _scoped_gate_env("TELEGRAM_IGNORED_THREADS") + """Thread ids to skip: scoped ``TELEGRAM_IGNORED_THREADS`` → ``config.extra`` → none.""" + raw = _extra_or_secret(self.config.extra, "ignored_threads", "TELEGRAM_IGNORED_THREADS", "", blank_is_unset=False) raw = _decode_json_list_literal(raw) ignored: set[int] = set() for value in (raw if isinstance(raw, list) else str(raw).split(",")): @@ -5150,17 +5149,18 @@ class TelegramAdapter(BasePlatformAdapter): def _compile_mention_patterns(self) -> List[re.Pattern]: """Compile optional regex wake-word patterns for group triggers.""" - patterns = self.config.extra.get("mention_patterns") - if patterns is None: - raw = _scoped_gate_env("TELEGRAM_MENTION_PATTERNS", "").strip() - if raw: - try: - loaded = json.loads(raw) - except Exception: - loaded = [part.strip() for part in raw.splitlines() if part.strip()] - if not loaded: - loaded = [part.strip() for part in raw.split(",") if part.strip()] - patterns = loaded + # Scoped env → the profile's YAML → none. Only the env rung is a serialized string (JSON list, + # newline- or comma-separated); a YAML string is one literal pattern and is left intact. + env_raw = _scoped_gate_env("TELEGRAM_MENTION_PATTERNS", "").strip() + if env_raw: + try: + patterns = json.loads(env_raw) + except Exception: + patterns = [part.strip() for part in env_raw.splitlines() if part.strip()] + if not patterns: + patterns = [part.strip() for part in env_raw.split(",") if part.strip()] + else: + patterns = self.config.extra.get("mention_patterns") if patterns is None: return [] # before touching ``self.name``: tests build bare adapters via object.__new__ return compile_mention_patterns(patterns, log_prefix=self.name, platform_label="telegram", display_label="Telegram", logger_=logger) diff --git a/tests/plugins/platforms/matrix/test_blank_config_env_fallback.py b/tests/plugins/platforms/matrix/test_blank_config_env_fallback.py index c8c88698f8..123856ad02 100644 --- a/tests/plugins/platforms/matrix/test_blank_config_env_fallback.py +++ b/tests/plugins/platforms/matrix/test_blank_config_env_fallback.py @@ -22,19 +22,29 @@ def test_blank_yaml_values_fall_through_to_env(monkeypatch, blank): assert MatrixAdapter._extra_truthy(config, "auto_thread", "MATRIX_AUTO_THREAD", "true") is False -def test_explicit_yaml_values_still_beat_env(monkeypatch): +def test_explicit_env_beats_yaml_and_yaml_beats_default(monkeypatch): + """Per-profile precedence: explicit scoped env → the profile's YAML → default. A blank env + value is unset (it must not clobber YAML); an explicit empty list is a real "no rooms" value.""" from plugins.platforms.matrix.adapter import MatrixAdapter, _extra_csv_set, _resolve_max_message_length + yaml_config = PlatformConfig(enabled=True, extra={ + "free_response_rooms": ["!a:example.org", " !b:example.org "], "max_message_length": 4000, + "auto_thread": False}) + monkeypatch.setenv("MATRIX_FREE_RESPONSE_ROOMS", "!env:example.org") monkeypatch.setenv("MATRIX_MAX_MESSAGE_LENGTH", "9000") monkeypatch.setenv("MATRIX_AUTO_THREAD", "true") - config = PlatformConfig(enabled=True, extra={ - "free_response_rooms": ["!a:example.org", " !b:example.org "], "max_message_length": 4000, - "auto_thread": False}) + assert _extra_csv_set(yaml_config, "free_response_rooms", "MATRIX_FREE_RESPONSE_ROOMS") == {"!env:example.org"} + assert _resolve_max_message_length(yaml_config) == 9000 + assert MatrixAdapter._extra_truthy(yaml_config, "auto_thread", "MATRIX_AUTO_THREAD", "true") is True - assert _extra_csv_set(config, "free_response_rooms", "MATRIX_FREE_RESPONSE_ROOMS") == {"!a:example.org", "!b:example.org"} - assert _resolve_max_message_length(config) == 4000 - assert MatrixAdapter._extra_truthy(config, "auto_thread", "MATRIX_AUTO_THREAD", "true") is False - # An explicit empty list is a real "no rooms" value, not "unset". + for name in ("MATRIX_FREE_RESPONSE_ROOMS", "MATRIX_MAX_MESSAGE_LENGTH", "MATRIX_AUTO_THREAD"): + monkeypatch.setenv(name, " ") + assert _extra_csv_set(yaml_config, "free_response_rooms", "MATRIX_FREE_RESPONSE_ROOMS") == {"!a:example.org", "!b:example.org"} + assert _resolve_max_message_length(yaml_config) == 4000 + assert MatrixAdapter._extra_truthy(yaml_config, "auto_thread", "MATRIX_AUTO_THREAD", "true") is False + + monkeypatch.delenv("MATRIX_AUTO_THREAD") + assert MatrixAdapter._extra_truthy(PlatformConfig(enabled=True, extra={}), "auto_thread", "MATRIX_AUTO_THREAD", "true") is True assert _extra_csv_set(PlatformConfig(enabled=True, extra={"free_response_rooms": []}), "free_response_rooms", "MATRIX_FREE_RESPONSE_ROOMS") == set()