Skip to main content

webex_message_handler/
types.rs

1//! Data types for webex-message-handler.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::future::Future;
6use std::pin::Pin;
7use std::sync::Arc;
8
9/// Networking mode for the handler.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum NetworkMode {
12    /// Use built-in HTTP and WebSocket libraries (default).
13    Native,
14    /// Use provided fetch and WebSocket factory functions.
15    Injected,
16}
17
18impl Default for NetworkMode {
19    fn default() -> Self {
20        Self::Native
21    }
22}
23
24/// HTTP request for injected fetch function.
25#[derive(Debug, Clone)]
26pub struct FetchRequest {
27    pub url: String,
28    pub method: String,
29    pub headers: HashMap<String, String>,
30    pub body: Option<String>,
31}
32
33/// HTTP response from injected fetch function.
34pub struct FetchResponse {
35    pub status: u16,
36    pub ok: bool,
37    pub body: Vec<u8>,
38}
39
40/// Custom fetch function for injected mode.
41pub type FetchFn = Arc<
42    dyn Fn(FetchRequest) -> Pin<Box<dyn Future<Output = Result<FetchResponse, Box<dyn std::error::Error + Send + Sync>>> + Send>>
43        + Send
44        + Sync,
45>;
46
47/// WebSocket interface for injected mode.
48pub trait InjectedWebSocket: Send + Sync {
49    fn send(&self, data: String) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + '_>>;
50    fn receive(&self) -> Pin<Box<dyn Future<Output = Result<String, Box<dyn std::error::Error + Send + Sync>>> + Send + '_>>;
51    fn close(&self) -> Pin<Box<dyn Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send + '_>>;
52}
53
54/// WebSocket factory for injected mode.
55pub type WebSocketFactory = Arc<
56    dyn Fn(String) -> Pin<Box<dyn Future<Output = Result<Box<dyn InjectedWebSocket>, Box<dyn std::error::Error + Send + Sync>>> + Send>>
57        + Send
58        + Sync,
59>;
60
61/// Configuration for WebexMessageHandler.
62#[derive(Clone)]
63pub struct Config {
64    /// Webex bot or user access token (required).
65    pub token: String,
66
67    /// Networking mode: Native or Injected (default: Native).
68    pub mode: NetworkMode,
69
70    /// Optional HTTP client for proxy support (native mode only).
71    /// If None, a default client will be created.
72    pub client: Option<reqwest::Client>,
73
74    /// Custom fetch function for all HTTP requests (injected mode).
75    pub fetch: Option<FetchFn>,
76
77    /// Custom WebSocket factory (injected mode).
78    pub web_socket_factory: Option<WebSocketFactory>,
79
80    /// Mercury ping interval in seconds (default: 15).
81    pub ping_interval: f64,
82
83    /// Pong response timeout in seconds (default: 14).
84    pub pong_timeout: f64,
85
86    /// Max reconnect backoff in seconds (default: 32).
87    pub reconnect_backoff_max: f64,
88
89    /// Max consecutive reconnection attempts (default: 10).
90    pub max_reconnect_attempts: u32,
91
92    /// Automatically filter out messages sent by this bot to prevent loops (default: true).
93    pub ignore_self_messages: bool,
94}
95
96impl Default for Config {
97    fn default() -> Self {
98        Self {
99            token: String::new(),
100            mode: NetworkMode::Native,
101            client: None,
102            fetch: None,
103            web_socket_factory: None,
104            ping_interval: 15.0,
105            pong_timeout: 14.0,
106            reconnect_backoff_max: 32.0,
107            max_reconnect_attempts: 10,
108            ignore_self_messages: true,
109        }
110    }
111}
112
113/// Result of WDM device registration.
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct DeviceRegistration {
116    /// Mercury WebSocket URL.
117    #[serde(rename = "webSocketUrl")]
118    pub web_socket_url: String,
119
120    /// Device URL (used as clientId for KMS).
121    #[serde(rename = "url")]
122    pub device_url: String,
123
124    /// Bot's user ID.
125    #[serde(rename = "userId")]
126    pub user_id: String,
127
128    /// Service catalog from WDM.
129    #[serde(default)]
130    pub services: HashMap<String, String>,
131
132    /// Encryption service URL extracted from services.
133    #[serde(skip)]
134    pub encryption_service_url: String,
135}
136
137/// Actor in a Mercury activity.
138#[derive(Debug, Clone, Default, Serialize, Deserialize)]
139pub struct MercuryActor {
140    #[serde(default)]
141    pub id: String,
142
143    #[serde(rename = "objectType", default)]
144    pub object_type: String,
145
146    #[serde(rename = "emailAddress", default)]
147    pub email_address: Option<String>,
148}
149
150/// Object in a Mercury activity.
151#[derive(Debug, Clone, Default, Serialize, Deserialize)]
152pub struct MercuryObject {
153    #[serde(default)]
154    pub id: String,
155
156    #[serde(rename = "objectType", default)]
157    pub object_type: String,
158
159    #[serde(rename = "displayName", default)]
160    pub display_name: Option<String>,
161
162    #[serde(default)]
163    pub content: Option<String>,
164
165    #[serde(rename = "encryptionKeyUrl", default)]
166    pub encryption_key_url: Option<String>,
167}
168
169/// Target in a Mercury activity.
170#[derive(Debug, Clone, Default, Serialize, Deserialize)]
171pub struct MercuryTarget {
172    #[serde(default)]
173    pub id: String,
174
175    #[serde(rename = "objectType", default)]
176    pub object_type: String,
177
178    #[serde(rename = "encryptionKeyUrl", default)]
179    pub encryption_key_url: Option<String>,
180
181    #[serde(default)]
182    pub tags: Vec<String>,
183}
184
185/// A Mercury conversation activity.
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct MercuryActivity {
188    #[serde(default)]
189    pub id: String,
190
191    #[serde(default)]
192    pub verb: String,
193
194    #[serde(default)]
195    pub actor: MercuryActor,
196
197    #[serde(default)]
198    pub object: MercuryObject,
199
200    #[serde(default)]
201    pub target: MercuryTarget,
202
203    #[serde(default)]
204    pub published: String,
205
206    #[serde(rename = "encryptionKeyUrl", default)]
207    pub encryption_key_url: Option<String>,
208}
209
210/// A decrypted Webex message.
211#[derive(Debug, Clone)]
212pub struct DecryptedMessage {
213    /// Unique message ID.
214    pub id: String,
215
216    /// Conversation/space ID.
217    pub room_id: String,
218
219    /// Sender's user ID.
220    pub person_id: String,
221
222    /// Sender's email address.
223    pub person_email: String,
224
225    /// Decrypted plain text.
226    pub text: String,
227
228    /// Decrypted HTML content (rich text messages).
229    pub html: Option<String>,
230
231    /// ISO 8601 timestamp.
232    pub created: String,
233
234    /// "direct", "group", or None.
235    pub room_type: Option<String>,
236
237    /// Full decrypted activity for advanced use.
238    pub raw: MercuryActivity,
239}
240
241/// A deleted Webex message notification.
242#[derive(Debug, Clone)]
243pub struct DeletedMessage {
244    pub message_id: String,
245    pub room_id: String,
246    pub person_id: String,
247}
248
249/// Overall connection state.
250#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub enum ConnectionStatus {
252    Connected,
253    Connecting,
254    Reconnecting,
255    Disconnected,
256}
257
258impl std::fmt::Display for ConnectionStatus {
259    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260        match self {
261            Self::Connected => write!(f, "connected"),
262            Self::Connecting => write!(f, "connecting"),
263            Self::Reconnecting => write!(f, "reconnecting"),
264            Self::Disconnected => write!(f, "disconnected"),
265        }
266    }
267}
268
269/// Structured health check of all connection subsystems.
270#[derive(Debug, Clone)]
271pub struct HandlerStatus {
272    /// Overall connection state.
273    pub status: ConnectionStatus,
274
275    /// Whether the Mercury WebSocket is currently open.
276    pub web_socket_open: bool,
277
278    /// Whether the KMS encryption context has been established.
279    pub kms_initialized: bool,
280
281    /// Whether the device is registered with WDM.
282    pub device_registered: bool,
283
284    /// Current auto-reconnect attempt number (0 if not reconnecting).
285    pub reconnect_attempt: u32,
286}