feat(gateway): private notice delivery and Slack format_message fixes
Adds platform-level private notice delivery abstraction so operational messages (e.g. sethome prompt) can be sent ephemerally on Slack when configured with `slack.notice_delivery: private`. Changes: - gateway/config.py: _normalize_notice_delivery() + GatewayConfig.get_notice_delivery() with per-platform config bridging - gateway/platforms/base.py: send_private_notice() default implementation (falls through to send()) - gateway/platforms/slack.py: send_private_notice() via chat_postEphemeral - gateway/run.py: _deliver_platform_notice() helper replaces direct adapter.send() for the sethome notice, with private→public fallback - gateway/platforms/slack.py: app_mention handler now forwards to _handle_slack_message (safe due to ts-based dedup) instead of no-op pass, fixing edge-case Slack configs where mentions arrive only as app_mention - gateway/platforms/slack.py format_message: negative lookbehind prevents markdown images (![]()) from becoming broken Slack links; italic regex now requires non-whitespace boundaries so 'a * b * c' stays literal Based on PR #9340 by @probepark.
This commit is contained in:
@@ -4294,6 +4294,33 @@ class GatewayRunner:
|
||||
|
||||
return "pair"
|
||||
|
||||
async def _deliver_platform_notice(self, source, content: str) -> None:
|
||||
"""Deliver a setup/operational notice using platform-specific privacy rules."""
|
||||
adapter = self.adapters.get(source.platform)
|
||||
if not adapter:
|
||||
return
|
||||
|
||||
config = getattr(self, "config", None)
|
||||
notice_delivery = "public"
|
||||
if config and hasattr(config, "get_notice_delivery"):
|
||||
notice_delivery = config.get_notice_delivery(source.platform)
|
||||
|
||||
metadata = {"thread_id": source.thread_id} if getattr(source, "thread_id", None) else None
|
||||
if notice_delivery == "private" and getattr(source, "user_id", None):
|
||||
try:
|
||||
result = await adapter.send_private_notice(
|
||||
source.chat_id,
|
||||
source.user_id,
|
||||
content,
|
||||
metadata=metadata,
|
||||
)
|
||||
if getattr(result, "success", False):
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
await adapter.send(source.chat_id, content, metadata=metadata)
|
||||
|
||||
async def _handle_message(self, event: MessageEvent) -> Optional[str]:
|
||||
"""
|
||||
Handle an incoming message from any platform.
|
||||
@@ -5953,24 +5980,22 @@ class GatewayRunner:
|
||||
platform_name = source.platform.value
|
||||
env_key = _home_target_env_var(platform_name)
|
||||
if not os.getenv(env_key):
|
||||
adapter = self.adapters.get(source.platform)
|
||||
if adapter:
|
||||
# Slack dispatches all Hermes commands through a single
|
||||
# parent slash command `/hermes`; bare `/sethome` is not
|
||||
# registered and would fail with "app did not respond".
|
||||
sethome_cmd = (
|
||||
"/hermes sethome"
|
||||
if source.platform == Platform.SLACK
|
||||
else "/sethome"
|
||||
)
|
||||
await adapter.send(
|
||||
source.chat_id,
|
||||
f"📬 No home channel is set for {platform_name.title()}. "
|
||||
f"A home channel is where Hermes delivers cron job results "
|
||||
f"and cross-platform messages.\n\n"
|
||||
f"Type {sethome_cmd} to make this chat your home channel, "
|
||||
f"or ignore to skip."
|
||||
)
|
||||
# Slack dispatches all Hermes commands through a single
|
||||
# parent slash command `/hermes`; bare `/sethome` is not
|
||||
# registered and would fail with "app did not respond".
|
||||
sethome_cmd = (
|
||||
"/hermes sethome"
|
||||
if source.platform == Platform.SLACK
|
||||
else "/sethome"
|
||||
)
|
||||
notice = (
|
||||
f"📬 No home channel is set for {platform_name.title()}. "
|
||||
f"A home channel is where Hermes delivers cron job results "
|
||||
f"and cross-platform messages.\n\n"
|
||||
f"Type {sethome_cmd} to make this chat your home channel, "
|
||||
f"or ignore to skip."
|
||||
)
|
||||
await self._deliver_platform_notice(source, notice)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# Voice channel awareness — inject current voice channel state
|
||||
|
||||
Reference in New Issue
Block a user