Skip to main content

rustant_core/channels/
mod.rs

1//! # Channel System
2//!
3//! Multi-platform communication channels for the Rustant agent.
4//! Each channel implements the `Channel` trait to provide a uniform
5//! interface for sending and receiving messages across platforms.
6
7pub mod agent_bridge;
8pub mod auto_reply;
9pub mod digest;
10pub mod discord;
11pub mod email;
12pub mod email_intelligence;
13pub mod imessage;
14pub mod intelligence;
15pub mod irc;
16pub mod manager;
17pub mod matrix;
18pub mod normalize;
19pub mod routing;
20pub mod scheduler_bridge;
21pub mod signal;
22pub mod slack;
23pub mod sms;
24pub mod teams;
25pub mod telegram;
26pub mod types;
27pub mod webchat;
28pub mod webhook;
29pub mod whatsapp;
30
31pub mod cdc;
32pub mod style_tracker;
33
34pub use agent_bridge::ChannelAgentBridge;
35pub use auto_reply::{AutoReplyEngine, PendingReply, ReplyStatus};
36pub use digest::{ChannelDigest, DigestActionItem, DigestCollector, DigestHighlight};
37pub use email_intelligence::{
38    EmailCategory, EmailClassification, EmailIntelligence, SenderProfile,
39};
40pub use imessage::{IMessageChannel, IMessageConfig, ResolvedContact};
41pub use intelligence::{
42    ClassificationCache, ClassifiedMessage, IntelligenceResult, LlmClassificationResponse,
43    MessageClassifier, MessageType, SuggestedAction,
44};
45pub use irc::{IrcChannel, IrcConfig};
46pub use manager::{ChannelManager, build_channel_manager};
47pub use normalize::MessageNormalizer;
48pub use routing::{ChannelRouter, RoutingCondition, RoutingRule};
49pub use scheduler_bridge::{FollowUpReminder, ReminderStatus, SchedulerBridge};
50pub use sms::{SmsChannel, SmsConfig};
51pub use teams::{TeamsChannel, TeamsConfig};
52pub use types::{
53    ChannelCapabilities, ChannelMessage, ChannelStatus, ChannelType, ChannelUser, MessageContent,
54    MessageId, StreamingMode, ThreadId,
55};
56pub use webhook::{WebhookChannel, WebhookConfig};
57
58pub use cdc::{CdcAction, CdcConfig, CdcProcessor, CdcState};
59pub use style_tracker::{CommunicationStyleTracker, SenderStyleProfile};
60
61use crate::error::RustantError;
62use async_trait::async_trait;
63
64/// Core trait that all channel implementations must satisfy.
65#[async_trait]
66pub trait Channel: Send + Sync {
67    /// Human-readable name of this channel instance.
68    fn name(&self) -> &str;
69
70    /// The platform type.
71    fn channel_type(&self) -> ChannelType;
72
73    /// Connect to the channel's platform.
74    async fn connect(&mut self) -> Result<(), RustantError>;
75
76    /// Disconnect from the channel's platform.
77    async fn disconnect(&mut self) -> Result<(), RustantError>;
78
79    /// Send a message through this channel. Returns the platform message ID.
80    async fn send_message(&self, msg: ChannelMessage) -> Result<MessageId, RustantError>;
81
82    /// Poll for new incoming messages.
83    async fn receive_messages(&self) -> Result<Vec<ChannelMessage>, RustantError>;
84
85    /// Current connection status.
86    fn status(&self) -> ChannelStatus;
87
88    /// Convenience: whether the channel is connected.
89    fn is_connected(&self) -> bool {
90        self.status() == ChannelStatus::Connected
91    }
92
93    /// The capabilities that this channel supports.
94    fn capabilities(&self) -> ChannelCapabilities {
95        ChannelCapabilities::default()
96    }
97
98    /// How this channel receives incoming messages.
99    fn streaming_mode(&self) -> StreamingMode {
100        StreamingMode::default()
101    }
102
103    /// Poll for new messages since a given cursor position.
104    /// Returns (messages, new_cursor). Default falls back to receive_messages().
105    async fn receive_messages_since(
106        &self,
107        _cursor: Option<&str>,
108    ) -> Result<(Vec<ChannelMessage>, Option<String>), RustantError> {
109        let msgs = self.receive_messages().await?;
110        Ok((msgs, None))
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn test_channel_type_reexported() {
120        let _ = ChannelType::Telegram;
121        let _ = ChannelStatus::Connected;
122    }
123
124    /// A minimal mock channel to test default trait methods.
125    struct DefaultTestChannel;
126
127    #[async_trait]
128    impl Channel for DefaultTestChannel {
129        fn name(&self) -> &str {
130            "default-test"
131        }
132        fn channel_type(&self) -> ChannelType {
133            ChannelType::WebChat
134        }
135        async fn connect(&mut self) -> Result<(), RustantError> {
136            Ok(())
137        }
138        async fn disconnect(&mut self) -> Result<(), RustantError> {
139            Ok(())
140        }
141        async fn send_message(&self, _msg: ChannelMessage) -> Result<MessageId, RustantError> {
142            Ok(MessageId::new("test"))
143        }
144        async fn receive_messages(&self) -> Result<Vec<ChannelMessage>, RustantError> {
145            Ok(Vec::new())
146        }
147        fn status(&self) -> ChannelStatus {
148            ChannelStatus::Disconnected
149        }
150    }
151
152    #[test]
153    fn test_default_capabilities() {
154        let ch = DefaultTestChannel;
155        let caps = ch.capabilities();
156        assert!(!caps.supports_threads);
157        assert!(!caps.supports_reactions);
158        assert!(!caps.supports_files);
159        assert!(!caps.supports_voice);
160        assert!(!caps.supports_video);
161        assert!(caps.max_message_length.is_none());
162        assert!(!caps.supports_editing);
163        assert!(!caps.supports_deletion);
164    }
165
166    #[test]
167    fn test_default_streaming_mode() {
168        let ch = DefaultTestChannel;
169        assert_eq!(
170            ch.streaming_mode(),
171            StreamingMode::Polling { interval_ms: 5000 }
172        );
173    }
174}