Skip to main content

rustyclaw_core/messengers/
mod.rs

1//! Messenger implementations for various chat platforms.
2//!
3//! **DEPRECATION NOTICE**: Native messenger integrations are being phased out
4//! in favor of skill-based messaging via [Beeper](https://www.beeper.com) and
5//! the `claw-me-maybe` skill from ClawHub. The skill approach:
6//! - Requires no recompilation for new platforms
7//! - Handles 15+ platforms through one API
8//! - Is the recommended path in `rustyclaw onboard`
9//!
10//! The native messengers below are retained for backwards compatibility but
11//! are largely untested. New development should focus on skill-based approaches.
12//!
13//! Each messenger implements the `Messenger` trait and can be enabled
14//! via feature flags in Cargo.toml.
15
16use anyhow::Result;
17use async_trait::async_trait;
18use serde::{Deserialize, Serialize};
19
20// ── Core types ──────────────────────────────────────────────────────────────
21
22/// Represents a message in the messenger system
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct Message {
25    pub id: String,
26    pub sender: String,
27    pub content: String,
28    pub timestamp: i64,
29    #[serde(default)]
30    pub channel: Option<String>,
31    #[serde(default)]
32    pub reply_to: Option<String>,
33    #[serde(default)]
34    pub media: Option<Vec<MediaAttachment>>,
35}
36
37/// Media attachment in a message
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct MediaAttachment {
40    pub url: Option<String>,
41    pub path: Option<String>,
42    pub mime_type: Option<String>,
43    pub filename: Option<String>,
44}
45
46/// Options for sending a message
47#[derive(Debug, Default)]
48pub struct SendOptions<'a> {
49    pub recipient: &'a str,
50    pub content: &'a str,
51    pub reply_to: Option<&'a str>,
52    pub silent: bool,
53    pub media: Option<&'a str>,
54}
55
56// ── Messenger trait ─────────────────────────────────────────────────────────
57
58/// Trait for messenger implementations (OpenClaw compatible)
59#[async_trait]
60pub trait Messenger: Send + Sync {
61    /// Get the messenger name
62    fn name(&self) -> &str;
63
64    /// Get the messenger type (telegram, discord, signal, matrix, etc.)
65    fn messenger_type(&self) -> &str;
66
67    /// Initialize the messenger (connect, authenticate, etc.)
68    async fn initialize(&mut self) -> Result<()>;
69
70    /// Send a message to a recipient/channel
71    async fn send_message(&self, recipient: &str, content: &str) -> Result<String>;
72
73    /// Send a message with additional options
74    async fn send_message_with_options(&self, opts: SendOptions<'_>) -> Result<String> {
75        // Default implementation ignores options
76        self.send_message(opts.recipient, opts.content).await
77    }
78
79    /// Receive pending messages (non-blocking poll)
80    async fn receive_messages(&self) -> Result<Vec<Message>>;
81
82    /// Check if the messenger is connected
83    fn is_connected(&self) -> bool;
84
85    /// Disconnect the messenger
86    async fn disconnect(&mut self) -> Result<()>;
87
88    /// Set typing indicator (optional - default is no-op)
89    async fn set_typing(&self, _channel: &str, _typing: bool) -> Result<()> {
90        Ok(())
91    }
92}
93
94// ── Messenger manager ───────────────────────────────────────────────────────
95
96/// Manager for multiple messengers
97pub struct MessengerManager {
98    messengers: Vec<Box<dyn Messenger>>,
99}
100
101impl MessengerManager {
102    pub fn new() -> Self {
103        Self {
104            messengers: Vec::new(),
105        }
106    }
107
108    /// Add a messenger to the manager
109    pub fn add_messenger(&mut self, messenger: Box<dyn Messenger>) {
110        self.messengers.push(messenger);
111    }
112
113    /// Initialize all messengers
114    pub async fn initialize_all(&mut self) -> Result<()> {
115        for messenger in &mut self.messengers {
116            messenger.initialize().await?;
117        }
118        Ok(())
119    }
120
121    /// Get all messengers
122    pub fn get_messengers(&self) -> &[Box<dyn Messenger>] {
123        &self.messengers
124    }
125
126    /// Get a messenger by name
127    pub fn get_messenger(&self, name: &str) -> Option<&dyn Messenger> {
128        self.messengers
129            .iter()
130            .find(|m| m.name() == name)
131            .map(|b| &**b)
132    }
133
134    /// Get a messenger by type
135    pub fn get_messenger_by_type(&self, msg_type: &str) -> Option<&dyn Messenger> {
136        self.messengers
137            .iter()
138            .find(|m| m.messenger_type() == msg_type)
139            .map(|b| &**b)
140    }
141
142    /// Disconnect all messengers
143    pub async fn disconnect_all(&mut self) -> Result<()> {
144        for messenger in &mut self.messengers {
145            messenger.disconnect().await?;
146        }
147        Ok(())
148    }
149}
150
151impl Default for MessengerManager {
152    fn default() -> Self {
153        Self::new()
154    }
155}
156
157// ── Built-in messengers ─────────────────────────────────────────────────────
158
159mod console;
160mod discord;
161mod google_chat;
162pub mod group_chat;
163mod imessage;
164mod irc;
165pub mod media;
166mod slack;
167pub mod streaming;
168mod teams;
169mod telegram;
170mod webhook;
171
172pub use console::ConsoleMessenger;
173pub use discord::DiscordMessenger;
174pub use google_chat::GoogleChatMessenger;
175pub use group_chat::GroupChatConfig;
176pub use imessage::IMessageMessenger;
177pub use irc::IrcMessenger;
178pub use media::{MediaConfig, MediaType};
179pub use slack::SlackMessenger;
180pub use streaming::{StreamBuffer, StreamConfig, StreamStrategy};
181pub use teams::TeamsMessenger;
182pub use telegram::TelegramMessenger;
183pub use webhook::WebhookMessenger;
184
185// ── Additional messengers ───────────────────────────────────────────────────
186// Note: These are deprecated in favor of skill-based messaging (see module docs).
187// They remain compiled-in for backwards compatibility but are feature-gated.
188
189#[cfg(feature = "matrix")]
190mod matrix;
191#[cfg(feature = "matrix")]
192pub use matrix::MatrixMessenger;
193
194// Signal messenger removed — was incomplete and never had its dependencies (presage) added.
195// Use the signal-messenger-standalone skill or claw-me-maybe for Signal integration.
196// Now replaced with signal-cli based implementation below.
197
198#[cfg(feature = "whatsapp")]
199mod whatsapp;
200#[cfg(feature = "whatsapp")]
201pub use whatsapp::WhatsAppMessenger;
202
203// ── Tier 1: CLI-based messengers (lightweight HTTP/process wrappers) ────────
204
205#[cfg(feature = "signal-cli")]
206mod signal_cli;
207#[cfg(feature = "signal-cli")]
208pub use signal_cli::SignalCliMessenger;
209
210#[cfg(feature = "matrix-cli")]
211mod matrix_cli;
212#[cfg(feature = "matrix-cli")]
213pub use matrix_cli::{MatrixCliMessenger, MatrixDmConfig};
214
215#[cfg(feature = "telegram-cli")]
216mod telegram_cli;
217#[cfg(feature = "telegram-cli")]
218pub use telegram_cli::TelegramCliMessenger;
219
220#[cfg(feature = "discord-cli")]
221mod discord_cli;
222#[cfg(feature = "discord-cli")]
223pub use discord_cli::DiscordCliMessenger;
224
225#[cfg(feature = "slack-cli")]
226mod slack_cli;
227#[cfg(feature = "slack-cli")]
228pub use slack_cli::SlackCliMessenger;