feat(channel): add Signal channel via signal-cli JSON-RPC daemon

Adds a new Signal messaging channel that connects to a running
signal-cli daemon's native HTTP API (JSON-RPC + SSE).

  [channels_config.signal]
  http_url = "http://127.0.0.1:8686"
  account = "+1234567890"
  group_id = "group_id"  # optional, omit for all
  allowed_from = ["+1111111111"]
  ignore_attachments = true
  ignore_stories = true

Implementation:
- SSE listener at /api/v1/events for incoming messages
- JSON-RPC sends via /api/v1/rpc (method: send)
- Health check via /api/v1/check
- Typing indicators via sendTyping RPC
- Supports DMs and group messages (room_id filtering)
- Allowlist-based sender filtering (E.164 or wildcard)
- Optional attachment/story filtering
- Fixed has_supervised_channels() to include signal + irc/lark/dingtalk

Registered in channel list, doctor, start, integrations registry, and
daemon supervisor gate. Includes unit tests for config serde, sender
filtering, room matching, envelope processing, and deserialization.

No new dependencies (uses existing uuid, futures-util, reqwest).
This commit is contained in:
bhagwan 2026-02-17 08:52:49 -05:00 committed by Chummy
parent acfdc34be2
commit 55f2637cfe
6 changed files with 860 additions and 2 deletions

View file

@ -6,6 +6,7 @@ pub mod imessage;
pub mod irc;
pub mod lark;
pub mod matrix;
pub mod signal;
pub mod slack;
pub mod telegram;
pub mod traits;
@ -19,6 +20,7 @@ pub use imessage::IMessageChannel;
pub use irc::IrcChannel;
pub use lark::LarkChannel;
pub use matrix::MatrixChannel;
pub use signal::SignalChannel;
pub use slack::SlackChannel;
pub use telegram::TelegramChannel;
pub use traits::Channel;
@ -579,6 +581,7 @@ pub fn handle_command(command: crate::ChannelCommands, config: &Config) -> Resul
("Webhook", config.channels_config.webhook.is_some()),
("iMessage", config.channels_config.imessage.is_some()),
("Matrix", config.channels_config.matrix.is_some()),
("Signal", config.channels_config.signal.is_some()),
("WhatsApp", config.channels_config.whatsapp.is_some()),
("Email", config.channels_config.email.is_some()),
("IRC", config.channels_config.irc.is_some()),
@ -680,6 +683,20 @@ pub async fn doctor_channels(config: Config) -> Result<()> {
));
}
if let Some(ref sig) = config.channels_config.signal {
channels.push((
"Signal",
Arc::new(SignalChannel::new(
sig.http_url.clone(),
sig.account.clone(),
sig.group_id.clone(),
sig.allowed_from.clone(),
sig.ignore_attachments,
sig.ignore_stories,
)),
));
}
if let Some(ref wa) = config.channels_config.whatsapp {
channels.push((
"WhatsApp",
@ -957,6 +974,17 @@ pub async fn start_channels(config: Config) -> Result<()> {
)));
}
if let Some(ref sig) = config.channels_config.signal {
channels.push(Arc::new(SignalChannel::new(
sig.http_url.clone(),
sig.account.clone(),
sig.group_id.clone(),
sig.allowed_from.clone(),
sig.ignore_attachments,
sig.ignore_stories,
)));
}
if let Some(ref wa) = config.channels_config.whatsapp {
channels.push(Arc::new(WhatsAppChannel::new(
wa.access_token.clone(),