Skip to main content

openclaw_channels/
traits.rs

1//! Channel traits.
2
3use async_trait::async_trait;
4use thiserror::Error;
5
6use openclaw_core::types::{Attachment, DeliveryResult, Message};
7
8/// Channel errors.
9#[derive(Error, Debug)]
10pub enum ChannelError {
11    /// Channel not connected.
12    #[error("Channel not connected")]
13    NotConnected,
14
15    /// Authentication failed.
16    #[error("Authentication failed: {0}")]
17    AuthFailed(String),
18
19    /// Message delivery failed.
20    #[error("Delivery failed: {0}")]
21    DeliveryFailed(String),
22
23    /// Rate limited.
24    #[error("Rate limited")]
25    RateLimited,
26
27    /// Network error.
28    #[error("Network error: {0}")]
29    Network(String),
30
31    /// Configuration error.
32    #[error("Configuration error: {0}")]
33    Config(String),
34}
35
36/// Channel capabilities.
37#[derive(Debug, Clone, Default, serde::Serialize)]
38pub struct ChannelCapabilities {
39    /// Supports text messages.
40    pub text: bool,
41    /// Supports images.
42    pub images: bool,
43    /// Supports videos.
44    pub videos: bool,
45    /// Supports voice messages.
46    pub voice: bool,
47    /// Supports files.
48    pub files: bool,
49    /// Supports threads.
50    pub threads: bool,
51    /// Supports reactions.
52    pub reactions: bool,
53    /// Supports editing messages.
54    pub editing: bool,
55    /// Supports deleting messages.
56    pub deletion: bool,
57}
58
59/// Channel health probe result.
60#[derive(Debug, Clone)]
61pub struct ChannelProbe {
62    /// Whether channel is connected.
63    pub connected: bool,
64    /// Account/bot identifier.
65    pub account_id: Option<String>,
66    /// Account display name.
67    pub display_name: Option<String>,
68    /// Error message if not connected.
69    pub error: Option<String>,
70}
71
72/// Delivery mode for outbound messages.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum DeliveryMode {
75    /// Send immediately.
76    Immediate,
77    /// Queue for batched delivery.
78    Batched,
79    /// Webhook-based delivery.
80    Webhook,
81}
82
83/// Context for outbound messages.
84#[derive(Debug, Clone)]
85pub struct OutboundContext {
86    /// Target chat/channel ID.
87    pub chat_id: String,
88    /// Reply to message ID.
89    pub reply_to: Option<String>,
90    /// Thread ID.
91    pub thread_id: Option<String>,
92}
93
94/// Context for channel operations.
95#[derive(Debug, Clone)]
96pub struct ChannelContext {
97    /// Agent ID for routing.
98    pub agent_id: String,
99    /// Account ID on the channel.
100    pub account_id: String,
101}
102
103/// Core channel trait.
104#[async_trait]
105pub trait Channel: Send + Sync {
106    /// Channel identifier (e.g., "telegram", "discord").
107    fn id(&self) -> &str;
108
109    /// Human-readable label.
110    fn label(&self) -> &str;
111
112    /// Channel capabilities.
113    fn capabilities(&self) -> ChannelCapabilities;
114
115    /// Start monitoring for inbound messages.
116    async fn start(&self, ctx: ChannelContext) -> Result<(), ChannelError>;
117
118    /// Stop monitoring.
119    async fn stop(&self) -> Result<(), ChannelError>;
120
121    /// Check if channel is ready.
122    async fn probe(&self) -> Result<ChannelProbe, ChannelError>;
123}
124
125/// Outbound message delivery trait.
126#[async_trait]
127pub trait ChannelOutbound: Channel {
128    /// Send a text message.
129    async fn send_text(
130        &self,
131        ctx: OutboundContext,
132        text: &str,
133    ) -> Result<DeliveryResult, ChannelError>;
134
135    /// Send media attachments.
136    async fn send_media(
137        &self,
138        ctx: OutboundContext,
139        media: &[Attachment],
140    ) -> Result<DeliveryResult, ChannelError>;
141
142    /// Maximum text message length.
143    fn text_chunk_limit(&self) -> usize;
144
145    /// Delivery mode.
146    fn delivery_mode(&self) -> DeliveryMode;
147}
148
149/// Inbound message handling trait.
150#[async_trait]
151pub trait ChannelInbound: Channel {
152    /// Raw message type from the platform.
153    type RawMessage;
154
155    /// Normalize raw message to common format.
156    fn normalize(&self, raw: Self::RawMessage) -> Result<Message, ChannelError>;
157
158    /// Acknowledge message receipt.
159    async fn acknowledge(&self, message_id: &str) -> Result<(), ChannelError>;
160}