Telegram notification mode to suppress intermediate pushes
Cluster 22771. Data as of
Nothing open on GitHub. Fix merged in #22793 on 9 May 2026. Check it covers your case.
The short answer
- Known?
- Yes. 3 related items grouped here, 1 issue already closed.
- Fix event
- A fix was merged to main in PR #22793. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
resolved, deciding PR #22793. That is separate from the fix event. - Still open
- Nothing open on GitHub.
These members all concern adding a notification mode to the Telegram gateway integration that suppresses intermediate push notifications. The work appears across a feature request and at least two related implementation attempts.
Open issues0
Open PRs0
Fixes on record
-
#22793 feat(gateway): add Telegram notification mode (salvage #22772)
files:
gateway/platforms/base.py,gateway/platforms/telegram.py,gateway/run.py,scripts/release.py,tests/gateway/test_base_topic_sessions.pyShow the diff
-
gateway/platforms/base.py@@ -2950,6 +2950,18 @@ async def _stop_typing_task() -> None: if text_content: logger.info("[%s] Sending response (%d chars) to %s", self.name, len(text_content), event.source.chat_id) _reply_anchor = _reply_anchor_for_event(event) + # Mark final response messages for notification delivery. + # Platform adapters that support per-message notification + # control (e.g. Telegram's disable_notification) use this + # flag to override silent-mode and ensure the final + # response triggers a push notification. + # Clone to avoid mutating the metadata shared with the + # typing-indicator task (which must remain unmarked). + if _thread_metadata is not None: + _thread_metadata = dict(_thread_metadata) + _thread_metadata["notify"] = True + else: + _thread_metadata = {"notify": True} result = await self._send_with_retry( chat_id=event.source.chat_id, content=text_content, -
gateway/platforms/telegram.py@@ -319,6 +319,30 @@ def __init__(self, config: PlatformConfig): # Slash-confirm button state: confirm_id → session_key (for /reload-mcp # and any other slash-confirm prompts; see GatewayRunner._request_slash_confirm). self._slash_confirm_state: Dict[str, str] = {} + # Notification mode for message sends. + # "important" — only final responses, approvals, and slash confirmations + # trigger notifications; tool progress, streaming, status + # messages are delivered silently via disable_notification. + # This is the default — Telegram users found per-tool-call + # push notifications too noisy. + # "all" — every message triggers a push notification (legacy + # behavior; opt-in via display.platforms.telegram.notifications). + self._notifications_mode: str = "important" + + def _notification_kwargs( + self, metadata: Optional[Dict[str, Any]] + ) -> Dict[str, Any]: + """Return disable_notification kwargs when the adapter is in silent mode. + + In "important" mode, all message sends are silently delivered + (disable_notification=True) unless the caller explicitly requests a + notification by setting ``metadata["notify"] = True``. + """ + if getattr(self, "_notifications_mode", "important") != "important": + return {} + if (metadata or {}).get("notify"): + return {} + return {"disable_notification": True} def _is_callback_user_authorized( self, @@ -1414,6 +1438,7 @@ async def send( reply_to_message_id=reply_to_id, **thread_kwargs, **self._link_preview_kwargs(), + **self._notification_kwargs(metadata), ) except Exception as md_error: # Markdown parsing failed, try plain text @@ -1427,6 +1452,7 @@ async def send( reply_to_message_id=reply_to_id, **thread_kwargs, **self._link_preview_kwargs(), + **self._notification_kwargs(metadata), ) else: raise @@ -2374,6 +2400,7 @@ async def send_voice( "caption": caption[:1024] if caption else None, "reply_to_message_id": reply_to_id, **voice_thread_kwargs, + **self._notification_kwargs(metadata), }, metadata, reply_to_id, @@ -2398,6 +2425,7 @@ async def send_voice( "caption": caption[:1024] if caption else None, "reply_to_message_id": reply_to_id, **audio_thread_kwargs, + **self._notification_kwargs(metadata), }, metadata, reply_to_id, @@ -2534,6 +2562,7 @@ def _reset_opened_files() -> None: "media": media, "reply_to_message_id": reply_to_id, **thread_kwargs, + **self._notification_kwargs(metadata), }, metadata, reply_to_id, @@ -2591,6 +2620,7 @@ async def send_image_file( "caption": caption[:1024] if caption else None, "reply_to_message_id": reply_to_id, **thread_kwargs, + **self._notification_kwargs(metadata), }, metadata, reply_to_id, @@ -2686,6 +2716,7 @@ async def send_document( "caption": caption[:1024] if caption else None, "reply_to_message_id": reply_to_id, **thread_kwargs, + **self._notification_kwargs(metadata), }, metadata, reply_to_id, @@ -2731,6 +2762,7 @@ async def send_video( "caption": caption[:1024] if caption else None, "reply_to_message_id": reply_to_id, **thread_kwargs, + **self._notification_kwargs(metadata), }, metadata, reply_to_id, @@ -2781,6 +2813,7 @@ async def send_image( "caption": caption[:1024] if caption else None, "reply_to_message_id": reply_to_id, **photo_thread_kwargs, + **self._notification_kwargs(metadata), }, metadata, reply_to_id, @@ -2816,6 +2849,7 @@ async def send_image( "caption": caption[:1024] if caption else None, "reply_to_message_id": reply_to_id, **upload_thread_kwargs, + **self._notification_kwargs(metadata), }, metadata, reply_to_id, @@ -2861,6 +2895,7 @@ async def send_animation( "caption": caption[:1024] if caption else None, "reply_to_message_id": reply_to_id, **animation_thread_kwargs, + **self._notification_kwargs(metadata), }, metadata, reply_to_id, -
gateway/run.py@@ -4649,7 +4649,29 @@ def _create_adapter( if not check_telegram_requirements(): logger.warning("Telegram: python-telegram-bot not installed") return None - return TelegramAdapter(config) + adapter = TelegramAdapter(config) + # Apply Telegram notification mode from config. Controls whether + # intermediate messages (tool progress, streaming, status) trigger + # push notifications. Supports ENV override for quick testing. + _notify_mode = os.getenv("HERMES_TELEGRAM_NOTIFICATIONS", "") + if not _notify_mode: + try: + _gw_cfg = _load_gateway_config() + _raw = cfg_get(_gw_cfg, "display", "platforms", "telegram", "notifications") + if _raw not in (None, ""): + _notify_mode = str(_raw).strip().lower() + except Exception: + pass + _notify_mode = _notify_mode or "important" + if _notify_mode not in ("all", "important"): + logger.warning( + "Unknown telegram notifications mode '%s', " + "defaulting to 'important' (valid: all, important)", + _notify_mode, + ) + _notify_mode = "important" + adapter._notifications_mode = _notify_mode + return adapter elif platform == Platform.DISCORD: from gateway.platforms.discord import DiscordAdapter, check_discord_requirements -
scripts/release.py@@ -65,6 +65,7 @@ "[redacted:email]": "subtract0", "[redacted:email]": "qWaitCrypto", "[redacted:email]": "Qwinty", + "[redacted:email]": "CalmProton", "[redacted:email]": "wesleysimplicio", "[redacted:email]": "SiliconID", "[redacted:email]": "xieNniu", -
tests/gateway/test_base_topic_sessions.py@@ -130,8 +130,8 @@ async def hold_typing(_chat_id, interval=2.0, metadata=None): { "chat_id": "-1001", "content": "ack", - "reply_to": "1", - "metadata": {"thread_id": "17585"}, + "reply_to": None, + "metadata": {"thread_id": "17585", "notify": True}, } ] assert typing_calls == [
-
1 PR closed without merging
From the close comments: merged via another change 1
-
#22772 feat(gateway): add Telegram notification mode to suppress intermediate push notifications · merged via another change →
#22793
Closed issues · 1
- Closed#22771Telegram: add notification mode to suppress intermediate push notifications
How this cluster was grouped
-
Link evidence (
structural links throughout): every member is connected by structural evidence: shared changed files, or a closing/superseded reference. -
Pairwise link density
0.67: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the LabAt a glance
- Merged PRs
- #22793
- Closed issues
- 1
- Attempts
- 1 PR closed without merging
- Fix link checked
- 9 May 2026