mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-31 06:51:29 +00:00
fix(telegram): gate profile bots by allowed topics
This commit is contained in:
parent
efc37409aa
commit
46ce3453c1
3 changed files with 77 additions and 1 deletions
|
|
@ -828,6 +828,10 @@ def load_gateway_config() -> GatewayConfig:
|
|||
bridged["reply_in_thread"] = platform_cfg["reply_in_thread"]
|
||||
if "require_mention" in platform_cfg:
|
||||
bridged["require_mention"] = platform_cfg["require_mention"]
|
||||
if plat == Platform.TELEGRAM and "allowed_chats" in platform_cfg:
|
||||
bridged["allowed_chats"] = platform_cfg["allowed_chats"]
|
||||
if plat == Platform.TELEGRAM and "allowed_topics" in platform_cfg:
|
||||
bridged["allowed_topics"] = platform_cfg["allowed_topics"]
|
||||
if "free_response_channels" in platform_cfg:
|
||||
bridged["free_response_channels"] = platform_cfg["free_response_channels"]
|
||||
if "mention_patterns" in platform_cfg:
|
||||
|
|
@ -1017,6 +1021,11 @@ def load_gateway_config() -> GatewayConfig:
|
|||
if isinstance(ac, list):
|
||||
ac = ",".join(str(v) for v in ac)
|
||||
os.environ["TELEGRAM_ALLOWED_CHATS"] = str(ac)
|
||||
allowed_topics = telegram_cfg.get("allowed_topics")
|
||||
if allowed_topics is not None and not os.getenv("TELEGRAM_ALLOWED_TOPICS"):
|
||||
if isinstance(allowed_topics, list):
|
||||
allowed_topics = ",".join(str(v) for v in allowed_topics)
|
||||
os.environ["TELEGRAM_ALLOWED_TOPICS"] = str(allowed_topics)
|
||||
ignored_threads = telegram_cfg.get("ignored_threads")
|
||||
if ignored_threads is not None and not os.getenv("TELEGRAM_IGNORED_THREADS"):
|
||||
if isinstance(ignored_threads, list):
|
||||
|
|
|
|||
|
|
@ -3974,6 +3974,21 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||
return {str(part).strip() for part in raw if str(part).strip()}
|
||||
return {part.strip() for part in str(raw).split(",") if part.strip()}
|
||||
|
||||
def _telegram_allowed_topics(self) -> set[str]:
|
||||
"""Return the whitelist of Telegram forum topic IDs this bot handles.
|
||||
|
||||
When non-empty, group/supergroup messages from other topics are
|
||||
silently ignored. DMs are never filtered by topic. Telegram may omit
|
||||
``message_thread_id`` for the forum General topic, so ``None`` is
|
||||
treated as topic ``1`` for matching purposes.
|
||||
"""
|
||||
raw = self.config.extra.get("allowed_topics")
|
||||
if raw is None:
|
||||
raw = os.getenv("TELEGRAM_ALLOWED_TOPICS", "")
|
||||
if isinstance(raw, list):
|
||||
return {str(part).strip() for part in raw if str(part).strip()}
|
||||
return {part.strip() for part in str(raw).split(",") if part.strip()}
|
||||
|
||||
def _telegram_ignored_threads(self) -> set[int]:
|
||||
raw = self.config.extra.get("ignored_threads")
|
||||
if raw is None:
|
||||
|
|
@ -4165,6 +4180,12 @@ class TelegramAdapter(BasePlatformAdapter):
|
|||
return True
|
||||
|
||||
thread_id = getattr(message, "message_thread_id", None)
|
||||
allowed_topics = self._telegram_allowed_topics()
|
||||
if allowed_topics:
|
||||
topic_id = str(thread_id) if thread_id is not None else self._GENERAL_TOPIC_THREAD_ID
|
||||
if topic_id not in allowed_topics:
|
||||
return False
|
||||
|
||||
if thread_id is not None:
|
||||
try:
|
||||
if int(thread_id) in self._telegram_ignored_threads():
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ def _make_adapter(
|
|||
free_response_chats=None,
|
||||
mention_patterns=None,
|
||||
ignored_threads=None,
|
||||
allowed_topics=None,
|
||||
allow_from=None,
|
||||
group_allow_from=None,
|
||||
allowed_chats=None,
|
||||
|
|
@ -26,12 +27,24 @@ def _make_adapter(
|
|||
extra["mention_patterns"] = mention_patterns
|
||||
if ignored_threads is not None:
|
||||
extra["ignored_threads"] = ignored_threads
|
||||
if allowed_topics is not None:
|
||||
extra["allowed_topics"] = allowed_topics
|
||||
else:
|
||||
# Keep unit tests isolated from TELEGRAM_ALLOWED_TOPICS in the parent
|
||||
# environment; production adapters without this explicit key still fall
|
||||
# back to the env var.
|
||||
extra["allowed_topics"] = []
|
||||
if allow_from is not None:
|
||||
extra["allow_from"] = allow_from
|
||||
if group_allow_from is not None:
|
||||
extra["group_allow_from"] = group_allow_from
|
||||
if allowed_chats is not None:
|
||||
extra["allowed_chats"] = allowed_chats
|
||||
else:
|
||||
# Keep unit tests isolated from TELEGRAM_ALLOWED_CHATS in the parent
|
||||
# environment; production adapters without this explicit key still fall
|
||||
# back to the env var.
|
||||
extra["allowed_chats"] = []
|
||||
if guest_mode is not None:
|
||||
extra["guest_mode"] = guest_mode
|
||||
|
||||
|
|
@ -216,6 +229,29 @@ def test_ignored_threads_drop_group_messages_before_other_gates():
|
|||
assert adapter._should_process_message(_group_message("hello everyone", chat_id=-200, thread_id=99)) is True
|
||||
|
||||
|
||||
def test_allowed_topics_drop_other_forum_topics_before_other_gates():
|
||||
adapter = _make_adapter(require_mention=False, allowed_chats=["-100"], allowed_topics=["8"])
|
||||
|
||||
assert adapter._should_process_message(_group_message("hello", chat_id=-100, thread_id=8)) is True
|
||||
assert adapter._should_process_message(_group_message("hello", chat_id=-100, thread_id=11)) is False
|
||||
assert adapter._should_process_message(
|
||||
_group_message("hi @hermes_bot", chat_id=-100, thread_id=11, entities=[_mention_entity("hi @hermes_bot")])
|
||||
) is False
|
||||
|
||||
|
||||
def test_allowed_topics_do_not_filter_dms():
|
||||
adapter = _make_adapter(require_mention=False, allowed_topics=["8"])
|
||||
|
||||
assert adapter._should_process_message(_dm_message("hello")) is True
|
||||
|
||||
|
||||
def test_allowed_topics_treat_missing_thread_as_general_topic():
|
||||
adapter = _make_adapter(require_mention=False, allowed_topics=["1"])
|
||||
|
||||
assert adapter._should_process_message(_group_message("hello", thread_id=None)) is True
|
||||
assert adapter._should_process_message(_group_message("hello", thread_id=8)) is False
|
||||
|
||||
|
||||
def test_regex_mention_patterns_allow_custom_wake_words():
|
||||
adapter = _make_adapter(require_mention=True, mention_patterns=[r"^\s*chompy\b"])
|
||||
|
||||
|
|
@ -241,7 +277,11 @@ def test_config_bridges_telegram_group_settings(monkeypatch, tmp_path):
|
|||
" mention_patterns:\n"
|
||||
" - \"^\\\\s*chompy\\\\b\"\n"
|
||||
" free_response_chats:\n"
|
||||
" - \"-123\"\n",
|
||||
" - \"-123\"\n"
|
||||
" allowed_chats:\n"
|
||||
" - \"-100\"\n"
|
||||
" allowed_topics:\n"
|
||||
" - 8\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
|
|
@ -250,6 +290,8 @@ def test_config_bridges_telegram_group_settings(monkeypatch, tmp_path):
|
|||
monkeypatch.delenv("TELEGRAM_MENTION_PATTERNS", raising=False)
|
||||
monkeypatch.delenv("TELEGRAM_GUEST_MODE", raising=False)
|
||||
monkeypatch.delenv("TELEGRAM_FREE_RESPONSE_CHATS", raising=False)
|
||||
monkeypatch.delenv("TELEGRAM_ALLOWED_CHATS", raising=False)
|
||||
monkeypatch.delenv("TELEGRAM_ALLOWED_TOPICS", raising=False)
|
||||
|
||||
config = load_gateway_config()
|
||||
|
||||
|
|
@ -258,9 +300,13 @@ def test_config_bridges_telegram_group_settings(monkeypatch, tmp_path):
|
|||
assert __import__("os").environ["TELEGRAM_GUEST_MODE"] == "true"
|
||||
assert json.loads(__import__("os").environ["TELEGRAM_MENTION_PATTERNS"]) == [r"^\s*chompy\b"]
|
||||
assert __import__("os").environ["TELEGRAM_FREE_RESPONSE_CHATS"] == "-123"
|
||||
assert __import__("os").environ["TELEGRAM_ALLOWED_CHATS"] == "-100"
|
||||
assert __import__("os").environ["TELEGRAM_ALLOWED_TOPICS"] == "8"
|
||||
tg_cfg = config.platforms.get(Platform.TELEGRAM)
|
||||
assert tg_cfg is not None
|
||||
assert tg_cfg.extra.get("guest_mode") is True
|
||||
assert tg_cfg.extra.get("allowed_chats") == ["-100"]
|
||||
assert tg_cfg.extra.get("allowed_topics") == [8]
|
||||
|
||||
|
||||
def test_config_bridges_telegram_user_allowlists(monkeypatch, tmp_path):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue