1use anyhow::{Result, anyhow};
7use base64::{Engine as _, engine::general_purpose::STANDARD};
8use bytes::{Buf, BufMut, Bytes, BytesMut};
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(tag = "command")]
14pub enum Message {
15 #[serde(rename = "logon")]
17 Logon {
18 seq: u32,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 username: Option<String>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 password: Option<String>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 auth_token: Option<String>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 channels: Option<Vec<String>>,
27 },
28
29 #[serde(rename = "send_text_message")]
31 SendTextMessage {
32 seq: u32,
33 channel: String,
34 #[serde(rename = "for")]
35 for_user: Option<String>,
36 text: String,
37 },
38
39 #[serde(rename = "start_stream")]
41 StartStream {
42 seq: u32,
43 channel: String,
44 #[serde(rename = "for")]
45 for_user: Option<String>,
46 codec: String,
47 codec_header: Option<String>,
48 packet_duration: u32,
49 },
50
51 #[serde(rename = "stop_stream")]
53 StopStream { seq: u32, stream_id: u32 },
54}
55
56impl Message {
57 #[must_use]
59 pub fn logon_password(
60 seq: u32,
61 username: String,
62 password: String,
63 auth_token: String,
64 channel: String,
65 ) -> Self {
66 Self::Logon {
67 seq,
68 username: Some(username),
69 password: Some(password),
70 auth_token: Some(auth_token),
71 channels: Some(vec![channel]),
72 }
73 }
74
75 #[must_use]
77 pub fn logon_token(seq: u32, auth_token: String, channel: String) -> Self {
78 Self::Logon {
79 seq,
80 username: None,
81 password: None,
82 auth_token: Some(auth_token),
83 channels: Some(vec![channel]),
84 }
85 }
86
87 #[must_use]
89 pub fn send_text(seq: u32, channel: String, text: String) -> Self {
90 Self::SendTextMessage {
91 seq,
92 channel,
93 for_user: None,
94 text,
95 }
96 }
97
98 #[must_use]
100 pub fn send_text_for_callsign(
101 seq: u32,
102 channel: String,
103 text: String,
104 for_user: String,
105 ) -> Self {
106 Self::SendTextMessage {
107 seq,
108 channel,
109 for_user: Some(for_user),
110 text,
111 }
112 }
113
114 #[must_use]
116 pub fn start_stream(seq: u32, channel: String, codec: String, packet_duration: u32) -> Self {
117 Self::StartStream {
118 seq,
119 channel,
120 for_user: None,
121 codec,
122 codec_header: None,
123 packet_duration,
124 }
125 }
126
127 #[must_use]
129 pub fn stop_stream(seq: u32, stream_id: u32) -> Self {
130 Self::StopStream { seq, stream_id }
131 }
132
133 #[must_use]
135 pub fn seq(&self) -> Option<u32> {
136 match self {
137 Self::Logon { seq, .. }
138 | Self::SendTextMessage { seq, .. }
139 | Self::StartStream { seq, .. }
140 | Self::StopStream { seq, .. } => Some(*seq),
141 }
142 }
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
147#[serde(untagged)]
148pub enum Response {
149 Logon {
151 seq: u32,
152 success: bool,
153 refresh_token: String,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 error: Option<String>,
156 },
157
158 Generic {
160 seq: u32,
161 success: bool,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 error: Option<String>,
164 },
165}
166
167impl Response {
168 #[must_use]
170 pub fn seq(&self) -> Option<u32> {
171 match self {
172 Self::Logon { seq, .. } | Self::Generic { seq, .. } => Some(*seq),
173 }
174 }
175
176 #[must_use]
178 pub fn is_success(&self) -> bool {
179 match self {
180 Self::Logon { success, .. } | Self::Generic { success, .. } => *success,
181 }
182 }
183
184 #[must_use]
186 pub fn error(&self) -> Option<&str> {
187 match self {
188 Self::Logon { error, .. } | Self::Generic { error, .. } => error.as_deref(),
189 }
190 }
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(tag = "command")]
196pub enum Error {
197 #[serde(rename = "on_error")]
199 Error { error: String },
200}
201
202impl Error {
203 #[must_use]
205 pub fn error(&self) -> &str {
206 match self {
207 Self::Error { error } => error,
208 }
209 }
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214#[serde(tag = "command")]
215pub enum Event {
216 #[serde(rename = "on_text_message")]
218 TextMessage {
219 message_id: u64,
220 channel: String,
221 from: String,
222 #[serde(rename = "for")]
223 for_user: Option<String>,
224 text: String,
225 #[serde(skip_serializing_if = "Option::is_none")]
226 author: Option<String>,
227 },
228
229 #[serde(rename = "on_stream_start")]
231 AudioStart {
232 stream_id: u32,
233 channel: String,
234 from: String,
235 #[serde(rename = "for")]
236 for_user: Option<String>,
237 codec: String,
238 codec_header: Option<String>,
239 packet_duration: u32,
240 },
241
242 #[serde(rename = "on_stream_data")]
244 AudioData {
245 stream_id: u32,
246 packet_id: u32,
247 data: Vec<u8>,
248 },
249
250 #[serde(rename = "on_stream_stop")]
252 AudioStop { stream_id: u32 },
253
254 #[serde(rename = "on_channel_status")]
256 ChannelStatus {
257 channel: String,
258 status: String,
259 users_online: u32,
260 #[serde(skip_serializing_if = "Option::is_none")]
261 images: Option<Vec<ChannelImage>>,
262 },
263
264 #[serde(rename = "on_online_status")]
266 OnlineStatus {
267 channel: String,
268 from: String,
269 online: bool,
270 },
271}
272
273#[derive(Debug, Clone, Serialize, Deserialize)]
275#[serde(untagged)]
276pub enum IncomingMessage {
277 Response(Response),
278 Error(Error),
279 Event(Event),
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct ChannelImage {
285 pub url: String,
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub thumbnail_url: Option<String>,
288}
289
290#[derive(Debug, Clone)]
292pub struct CodecHeader {
293 pub sample_rate_hz: u16,
294 pub frames_per_packet: u8,
295 pub frame_size_ms: u8,
296}
297
298impl CodecHeader {
299 pub fn from_base64(encoded: &str) -> Result<Self> {
305 let bytes = STANDARD.decode(encoded)?;
306 Self::from_bytes(Bytes::from(bytes))
307 }
308
309 pub fn from_bytes(mut bytes: Bytes) -> Result<Self> {
315 if bytes.len() != 4 {
316 return Err(anyhow!(
317 "Invalid codec header length: expected 4 bytes, got {}",
318 bytes.len()
319 ));
320 }
321
322 let sample_rate_hz = bytes.get_u16_le();
323 let frames_per_packet = bytes.get_u8();
324 let frame_size_ms = bytes.get_u8();
325
326 Ok(Self {
327 sample_rate_hz,
328 frames_per_packet,
329 frame_size_ms,
330 })
331 }
332
333 #[must_use]
335 pub fn to_base64(&self) -> String {
336 STANDARD.encode(self.to_bytes())
337 }
338
339 #[must_use]
341 pub fn to_bytes(&self) -> Bytes {
342 let mut buf = BytesMut::with_capacity(4);
343 buf.put_u16_le(self.sample_rate_hz);
344 buf.put_u8(self.frames_per_packet);
345 buf.put_u8(self.frame_size_ms);
346 buf.freeze()
347 }
348}
349
350impl Default for CodecHeader {
351 fn default() -> Self {
352 Self {
353 sample_rate_hz: 16000,
354 frames_per_packet: 1,
355 frame_size_ms: 60,
356 }
357 }
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 #[test]
365 fn test_message_serialization() {
366 let msg = Message::send_text(1, "test_channel".to_string(), "Hello".to_string());
367 let json = serde_json::to_string(&msg).expect("Failed to serialize");
368 assert!(json.contains("send_text_message"));
369 assert!(json.contains("Hello"));
370 }
371
372 #[test]
373 fn test_message_seq() {
374 let msg = Message::send_text(42, "channel".to_string(), "test".to_string());
375 assert_eq!(msg.seq(), Some(42));
376 }
377}