mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat(gateway/slack): add SLACK_REACTIONS env toggle for reaction lifecycle
Adds _reactions_enabled() gating to match Discord (DISCORD_REACTIONS) and Telegram (TELEGRAM_REACTIONS) pattern. Defaults to true to preserve existing behavior. Gates at three levels: - _handle_slack_message: skips _reacting_message_ids registration - on_processing_start: early return - on_processing_complete: early return Also adds config.yaml bridge (slack.reactions) and two new tests.
This commit is contained in:
parent
70a33708e7
commit
1f216ecbb4
3 changed files with 61 additions and 1 deletions
|
|
@ -616,6 +616,8 @@ def load_gateway_config() -> GatewayConfig:
|
|||
if isinstance(frc, list):
|
||||
frc = ",".join(str(v) for v in frc)
|
||||
os.environ["SLACK_FREE_RESPONSE_CHANNELS"] = str(frc)
|
||||
if "reactions" in slack_cfg and not os.getenv("SLACK_REACTIONS"):
|
||||
os.environ["SLACK_REACTIONS"] = str(slack_cfg["reactions"]).lower()
|
||||
|
||||
# Discord settings → env vars (env vars take precedence)
|
||||
discord_cfg = yaml_cfg.get("discord", {})
|
||||
|
|
|
|||
|
|
@ -607,8 +607,14 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
logger.debug("[Slack] reactions.remove failed (%s): %s", emoji, e)
|
||||
return False
|
||||
|
||||
def _reactions_enabled(self) -> bool:
|
||||
"""Check if message reactions are enabled via config/env."""
|
||||
return os.getenv("SLACK_REACTIONS", "true").lower() not in ("false", "0", "no")
|
||||
|
||||
async def on_processing_start(self, event: MessageEvent) -> None:
|
||||
"""Add an in-progress reaction when message processing begins."""
|
||||
if not self._reactions_enabled():
|
||||
return
|
||||
ts = getattr(event, "message_id", None)
|
||||
if not ts or ts not in self._reacting_message_ids:
|
||||
return
|
||||
|
|
@ -618,6 +624,8 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
|
||||
async def on_processing_complete(self, event: MessageEvent, outcome: ProcessingOutcome) -> None:
|
||||
"""Swap the in-progress reaction for a final success/failure reaction."""
|
||||
if not self._reactions_enabled():
|
||||
return
|
||||
ts = getattr(event, "message_id", None)
|
||||
if not ts or ts not in self._reacting_message_ids:
|
||||
return
|
||||
|
|
@ -1260,7 +1268,7 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
# Only react when bot is directly addressed (DM or @mention).
|
||||
# In listen-all channels (require_mention=false), reacting to every
|
||||
# casual message would be noisy.
|
||||
_should_react = is_dm or is_mentioned
|
||||
_should_react = (is_dm or is_mentioned) and self._reactions_enabled()
|
||||
if _should_react:
|
||||
self._reacting_message_ids.add(ts)
|
||||
|
||||
|
|
|
|||
|
|
@ -1138,6 +1138,56 @@ class TestReactions:
|
|||
adapter._app.client.reactions_add.assert_not_called()
|
||||
adapter._app.client.reactions_remove.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reactions_disabled_via_env(self, adapter, monkeypatch):
|
||||
"""SLACK_REACTIONS=false should suppress all reaction lifecycle."""
|
||||
monkeypatch.setenv("SLACK_REACTIONS", "false")
|
||||
adapter._app.client.reactions_add = AsyncMock()
|
||||
adapter._app.client.reactions_remove = AsyncMock()
|
||||
adapter._app.client.users_info = AsyncMock(return_value={
|
||||
"user": {"profile": {"display_name": "Tyler"}}
|
||||
})
|
||||
|
||||
event = {
|
||||
"text": "hello",
|
||||
"user": "U_USER",
|
||||
"channel": "C123",
|
||||
"channel_type": "im",
|
||||
"ts": "1234567890.000004",
|
||||
}
|
||||
await adapter._handle_slack_message(event)
|
||||
|
||||
# Should NOT register for reactions when toggle is off
|
||||
assert "1234567890.000004" not in adapter._reacting_message_ids
|
||||
|
||||
# Hooks should also be no-ops when disabled
|
||||
from gateway.platforms.base import MessageEvent, MessageType, SessionSource, ProcessingOutcome
|
||||
from gateway.config import Platform
|
||||
source = SessionSource(
|
||||
platform=Platform.SLACK,
|
||||
chat_id="C123",
|
||||
chat_type="dm",
|
||||
user_id="U_USER",
|
||||
)
|
||||
msg_event = MessageEvent(
|
||||
text="hello",
|
||||
message_type=MessageType.TEXT,
|
||||
source=source,
|
||||
message_id="1234567890.000004",
|
||||
)
|
||||
# Force-add to verify hooks respect the toggle independently
|
||||
adapter._reacting_message_ids.add("1234567890.000004")
|
||||
await adapter.on_processing_start(msg_event)
|
||||
await adapter.on_processing_complete(msg_event, ProcessingOutcome.SUCCESS)
|
||||
|
||||
adapter._app.client.reactions_add.assert_not_called()
|
||||
adapter._app.client.reactions_remove.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reactions_enabled_by_default(self, adapter):
|
||||
"""SLACK_REACTIONS defaults to true (matches existing behavior)."""
|
||||
assert adapter._reactions_enabled() is True
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# TestThreadReplyHandling
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue