Summary
Add native support for per-topic skill and personality binding in Telegram group forum (supergroup) chats. This allows different forum topics (threads) within the same group to automatically load different skills, effectively giving the bot different "roles" depending on which topic the user is messaging in.
Motivation
In Telegram groups with forums enabled, each topic serves a distinct purpose (e.g., "Deep Research", "Coding Help", "General Chat"). Currently, Hermes applies the same persona/skill set regardless of which topic a message comes from. This means users cannot get specialized behavior per topic without creating separate bot instances.
Use case: A single bot in a community group where:
- Thread "Deep Research" → loads
deep-research skill (thorough analysis, citations)
- Thread "Code Review" → loads
code-review skill (security-focused, line-by-line review)
- Thread "General" → default behavior (casual chat)
Proposed Configuration
Add a group_topics section under platforms.telegram.extra in config.yaml:
platforms:
telegram:
extra:
group_topics:
- chat_id: -100XXXXXXXXXX # Your group/supergroup ID
topics:
- thread_id: 42
name: "Deep Research"
skill: "deep-research"
- thread_id: 19
name: "Code Review"
skill: "code-review"
- thread_id: 7
name: "General"
# no skill = default behavior
Each topic entry supports:
thread_id (required) — The Telegram message_thread_id for the forum topic
name (optional) — Human-readable topic name, surfaced as chat_topic in the session context
skill (optional) — Skill name to auto-load when a message arrives from this topic
Implementation Details
1. Config Loading (__init__)
# In TelegramAdapter.__init__
self._group_topics_config: List[Dict[str, Any]] = self.config.extra.get("group_topics", [])
Store the config list at startup for lookup.
2. Topic Lookup Method (_get_group_topic_info)
def _get_group_topic_info(self, chat_id: str, thread_id: Optional[str]) -> Optional[Dict[str, Any]]:
"""Look up group forum topic config by chat_id and thread_id."""
if not thread_id or not self._group_topics_config:
return None
thread_id_int = int(thread_id)
for chat_entry in self._group_topics_config:
if str(chat_entry.get("chat_id")) == chat_id:
for t in chat_entry.get("topics", []):
if t.get("thread_id") == thread_id_int:
return t
return None
3. Hot-Reload (_reload_group_topics_from_config)
Re-read config.yaml at runtime so new topics can be added without restarting the gateway:
def _reload_group_topics_from_config(self) -> None:
"""Re-read group_topics from config.yaml (hot-reload without restart)."""
try:
from hermes_constants import get_hermes_home
config_path = get_hermes_home() / "config.yaml"
if not config_path.exists():
return
import yaml as _yaml
with open(config_path, "r") as f:
config = _yaml.safe_load(f) or {}
group_topics = (
config.get("platforms", {})
.get("telegram", {})
.get("extra", {})
.get("group_topics", [])
)
if group_topics:
self._group_topics_config = group_topics
except Exception as e:
logger.debug("[%s] Failed to reload group_topics from config: %s", self.name, e)
4. Integration in _build_message_event
Add a branch for chat_type == "group" after the existing DM topic resolution:
# Resolve GROUP forum topic name and skill binding
elif chat_type == "group" and thread_id_str:
group_topic_info = self._get_group_topic_info(str(chat.id), thread_id_str)
if group_topic_info:
chat_topic = group_topic_info.get("name")
topic_skill = group_topic_info.get("skill")
else:
# Hot-reload config in case topics were added after startup
self._reload_group_topics_from_config()
group_topic_info = self._get_group_topic_info(str(chat.id), thread_id_str)
if group_topic_info:
chat_topic = group_topic_info.get("name")
topic_skill = group_topic_info.get("skill")
Then pass topic_skill as auto_skill in the returned MessageEvent:
return MessageEvent(
...
auto_skill=topic_skill,
...
)
How It Works (Flow)
1. User sends message in group forum topic (thread_id=42)
2. _build_message_event() is called
3. Detects: chat_type="group" + thread_id present
4. Calls _get_group_topic_info(chat_id, thread_id)
5. Finds matching config → {name: "Deep Research", skill: "deep-research"}
6. Sets auto_skill="deep-research" on MessageEvent
7. Gateway sees auto_skill → auto-loads the skill before responding
8. Bot responds with the specialized persona
Benefits
- Single bot, multiple roles — no need for separate bot instances per function
- Hot-reloadable — add/remove topics without restarting the gateway
- Zero breaking changes — fully backward compatible; groups without
group_topics config work exactly as before
- Consistent with existing DM topics — mirrors the existing
dm_topics feature pattern (already in Hermes) for private chat topics
- Config-driven — no code changes needed to add new topic bindings
Relation to Existing Features
This mirrors the existing DM Topics feature (private chat forum topics via dm_topics config) but applies to group/supergroup forum topics. The implementation pattern is identical, just using a different config key (group_topics) and lookup path.
Testing Checklist
Summary
Add native support for per-topic skill and personality binding in Telegram group forum (supergroup) chats. This allows different forum topics (threads) within the same group to automatically load different skills, effectively giving the bot different "roles" depending on which topic the user is messaging in.
Motivation
In Telegram groups with forums enabled, each topic serves a distinct purpose (e.g., "Deep Research", "Coding Help", "General Chat"). Currently, Hermes applies the same persona/skill set regardless of which topic a message comes from. This means users cannot get specialized behavior per topic without creating separate bot instances.
Use case: A single bot in a community group where:
deep-researchskill (thorough analysis, citations)code-reviewskill (security-focused, line-by-line review)Proposed Configuration
Add a
group_topicssection underplatforms.telegram.extrainconfig.yaml:Each topic entry supports:
thread_id(required) — The Telegram message_thread_id for the forum topicname(optional) — Human-readable topic name, surfaced aschat_topicin the session contextskill(optional) — Skill name to auto-load when a message arrives from this topicImplementation Details
1. Config Loading (
__init__)Store the config list at startup for lookup.
2. Topic Lookup Method (
_get_group_topic_info)3. Hot-Reload (
_reload_group_topics_from_config)Re-read
config.yamlat runtime so new topics can be added without restarting the gateway:4. Integration in
_build_message_eventAdd a branch for
chat_type == "group"after the existing DM topic resolution:Then pass
topic_skillasauto_skillin the returnedMessageEvent:How It Works (Flow)
Benefits
group_topicsconfig work exactly as beforedm_topicsfeature pattern (already in Hermes) for private chat topicsRelation to Existing Features
This mirrors the existing DM Topics feature (private chat forum topics via
dm_topicsconfig) but applies to group/supergroup forum topics. The implementation pattern is identical, just using a different config key (group_topics) and lookup path.Testing Checklist
chat_topicname is surfaced correctly in session context