fix: Signal adapter parity pass — integration gaps, clawdbot features, env var simplification
Integration gaps fixed (7 files missing Signal): - cron/scheduler.py: Signal in platform_map (cron delivery was broken) - agent/prompt_builder.py: PLATFORM_HINTS for Signal (agent knows it's on Signal) - toolsets.py: hermes-signal toolset + added to hermes-gateway composite - hermes_cli/status.py: Signal + Slack in platform status display - tools/send_message_tool.py: Signal example in target description - tools/cronjob_tools.py: Signal in delivery option docs + schema - gateway/channel_directory.py: Signal in session-based channel discovery Clawdbot parity features added to signal.py: - Self-message filtering: prevents reply loops by checking sender != account - SyncMessage filtering: ignores sync envelopes (sent transcripts, read receipts) - Edit message support: reads dataMessage from editMessage envelope - Mention rendering: replaces \uFFFC placeholders with @identifier text - Jitter in SSE reconnection backoff (20% randomization, prevents thundering herd) Env var simplification (7 → 4): - Removed SIGNAL_DM_POLICY (DM auth follows standard platform pattern via SIGNAL_ALLOWED_USERS + DM pairing, same as Telegram/Discord) - Removed SIGNAL_GROUP_POLICY (derived from SIGNAL_GROUP_ALLOWED_USERS: not set = disabled, set with IDs = allowlist, set with * = open) - Removed SIGNAL_DEBUG (was setting root logger, removed entirely) - Remaining: SIGNAL_HTTP_URL, SIGNAL_ACCOUNT (required), SIGNAL_ALLOWED_USERS, SIGNAL_GROUP_ALLOWED_USERS (optional) Updated all docs (website, AGENTS.md, signal.md) to match.
This commit is contained in:
parent
0c4cff352a
commit
b7d6eae64c
14 changed files with 645 additions and 621 deletions
|
|
@ -23,8 +23,6 @@ class TestSignalConfigLoading:
|
|||
def test_apply_env_overrides_signal(self, monkeypatch):
|
||||
monkeypatch.setenv("SIGNAL_HTTP_URL", "http://localhost:9090")
|
||||
monkeypatch.setenv("SIGNAL_ACCOUNT", "+15551234567")
|
||||
monkeypatch.setenv("SIGNAL_DM_POLICY", "open")
|
||||
monkeypatch.setenv("SIGNAL_GROUP_POLICY", "allowlist")
|
||||
|
||||
from gateway.config import GatewayConfig, _apply_env_overrides
|
||||
config = GatewayConfig()
|
||||
|
|
@ -35,8 +33,6 @@ class TestSignalConfigLoading:
|
|||
assert sc.enabled is True
|
||||
assert sc.extra["http_url"] == "http://localhost:9090"
|
||||
assert sc.extra["account"] == "+15551234567"
|
||||
assert sc.extra["dm_policy"] == "open"
|
||||
assert sc.extra["group_policy"] == "allowlist"
|
||||
|
||||
def test_signal_not_loaded_without_both_vars(self, monkeypatch):
|
||||
monkeypatch.setenv("SIGNAL_HTTP_URL", "http://localhost:9090")
|
||||
|
|
@ -71,49 +67,44 @@ class TestSignalAdapterInit:
|
|||
config.extra = {
|
||||
"http_url": "http://localhost:8080",
|
||||
"account": "+15551234567",
|
||||
"dm_policy": "pairing",
|
||||
"group_policy": "disabled",
|
||||
**extra,
|
||||
}
|
||||
return config
|
||||
|
||||
def test_init_parses_config(self, monkeypatch):
|
||||
monkeypatch.setenv("SIGNAL_ALLOWED_USERS", "+15559876543,+15551111111")
|
||||
monkeypatch.setenv("SIGNAL_GROUP_ALLOWED_USERS", "group123,group456")
|
||||
monkeypatch.delenv("SIGNAL_DEBUG", raising=False)
|
||||
|
||||
from gateway.platforms.signal import SignalAdapter
|
||||
adapter = SignalAdapter(self._make_config())
|
||||
|
||||
assert adapter.http_url == "http://localhost:8080"
|
||||
assert adapter.account == "+15551234567"
|
||||
assert adapter.dm_policy == "pairing"
|
||||
assert adapter.group_policy == "disabled"
|
||||
assert "+15559876543" in adapter.allowed_users
|
||||
assert "+15551111111" in adapter.allowed_users
|
||||
assert "group123" in adapter.group_allow_from
|
||||
|
||||
def test_init_empty_allowlist(self, monkeypatch):
|
||||
monkeypatch.setenv("SIGNAL_ALLOWED_USERS", "")
|
||||
monkeypatch.setenv("SIGNAL_GROUP_ALLOWED_USERS", "")
|
||||
monkeypatch.delenv("SIGNAL_DEBUG", raising=False)
|
||||
|
||||
from gateway.platforms.signal import SignalAdapter
|
||||
adapter = SignalAdapter(self._make_config())
|
||||
|
||||
assert len(adapter.allowed_users) == 0
|
||||
assert len(adapter.group_allow_from) == 0
|
||||
|
||||
def test_init_strips_trailing_slash(self, monkeypatch):
|
||||
monkeypatch.setenv("SIGNAL_ALLOWED_USERS", "")
|
||||
monkeypatch.setenv("SIGNAL_GROUP_ALLOWED_USERS", "")
|
||||
monkeypatch.delenv("SIGNAL_DEBUG", raising=False)
|
||||
|
||||
from gateway.platforms.signal import SignalAdapter
|
||||
adapter = SignalAdapter(self._make_config(http_url="http://localhost:8080/"))
|
||||
|
||||
assert adapter.http_url == "http://localhost:8080"
|
||||
|
||||
def test_self_message_filtering(self, monkeypatch):
|
||||
monkeypatch.setenv("SIGNAL_GROUP_ALLOWED_USERS", "")
|
||||
|
||||
from gateway.platforms.signal import SignalAdapter
|
||||
adapter = SignalAdapter(self._make_config())
|
||||
|
||||
assert adapter._account_normalized == "+15551234567"
|
||||
|
||||
|
||||
class TestSignalHelpers:
|
||||
def test_redact_phone_long(self):
|
||||
|
|
@ -177,6 +168,20 @@ class TestSignalHelpers:
|
|||
monkeypatch.setenv("SIGNAL_ACCOUNT", "+15551234567")
|
||||
assert check_signal_requirements() is True
|
||||
|
||||
def test_render_mentions(self):
|
||||
from gateway.platforms.signal import _render_mentions
|
||||
text = "Hello \uFFFC, how are you?"
|
||||
mentions = [{"start": 6, "length": 1, "number": "+15559999999"}]
|
||||
result = _render_mentions(text, mentions)
|
||||
assert "@+15559999999" in result
|
||||
assert "\uFFFC" not in result
|
||||
|
||||
def test_render_mentions_no_mentions(self):
|
||||
from gateway.platforms.signal import _render_mentions
|
||||
text = "Hello world"
|
||||
result = _render_mentions(text, [])
|
||||
assert result == "Hello world"
|
||||
|
||||
def test_check_requirements_missing(self, monkeypatch):
|
||||
from gateway.platforms.signal import check_signal_requirements
|
||||
monkeypatch.delenv("SIGNAL_HTTP_URL", raising=False)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue