Skip to main content

beeper_desktop_api/models/
message.rs

1//! Message, attachment, and reaction models
2
3use serde::{Deserialize, Serialize};
4
5/// File attachment or media
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Attachment {
8    /// Attachment type
9    #[serde(rename = "type")]
10    pub typ: String,
11    /// Public URL or local file path
12    #[serde(skip_serializing_if = "Option::is_none")]
13    #[serde(rename = "srcURL")]
14    pub src_url: Option<String>,
15    /// MIME type
16    #[serde(skip_serializing_if = "Option::is_none")]
17    #[serde(rename = "mimeType")]
18    pub mime_type: Option<String>,
19    /// Original filename
20    #[serde(skip_serializing_if = "Option::is_none")]
21    #[serde(rename = "fileName")]
22    pub file_name: Option<String>,
23    /// File size in bytes
24    #[serde(skip_serializing_if = "Option::is_none")]
25    #[serde(rename = "fileSize")]
26    pub file_size: Option<u64>,
27    /// True if GIF
28    #[serde(skip_serializing_if = "Option::is_none")]
29    #[serde(rename = "isGif")]
30    pub is_gif: Option<bool>,
31    /// True if sticker
32    #[serde(skip_serializing_if = "Option::is_none")]
33    #[serde(rename = "isSticker")]
34    pub is_sticker: Option<bool>,
35    /// True if voice note
36    #[serde(skip_serializing_if = "Option::is_none")]
37    #[serde(rename = "isVoiceNote")]
38    pub is_voice_note: Option<bool>,
39    /// Duration in seconds
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub duration: Option<f64>,
42    /// Preview image URL for videos
43    #[serde(skip_serializing_if = "Option::is_none")]
44    #[serde(rename = "posterImg")]
45    pub poster_img: Option<String>,
46}
47
48/// Emoji reaction to a message
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct Reaction {
51    /// Reaction ID
52    pub id: String,
53    /// The reaction key (emoji or shortcode)
54    #[serde(rename = "reactionKey")]
55    pub reaction_key: String,
56    /// URL to reaction image
57    #[serde(skip_serializing_if = "Option::is_none")]
58    #[serde(rename = "imgURL")]
59    pub img_url: Option<String>,
60    /// User ID of participant who reacted
61    #[serde(rename = "participantID")]
62    pub participant_id: String,
63    /// True if the reactionKey is an emoji
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub emoji: Option<bool>,
66}
67
68/// A message in a chat
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct Message {
71    /// Message ID
72    pub id: String,
73    /// Chat ID this message belongs to
74    #[serde(rename = "chatID")]
75    pub chat_id: String,
76    /// Account ID the message belongs to
77    #[serde(skip_serializing_if = "Option::is_none")]
78    #[serde(rename = "accountID")]
79    pub account_id: Option<String>,
80    /// Sender user ID
81    #[serde(rename = "senderID")]
82    pub sender_id: String,
83    /// Sender display name
84    #[serde(skip_serializing_if = "Option::is_none")]
85    #[serde(rename = "senderName")]
86    pub sender_name: Option<String>,
87    /// Message text content
88    #[serde(skip_serializing_if = "Option::is_none")]
89    pub text: Option<String>,
90    /// Timestamp in ISO 8601 format
91    pub timestamp: String,
92    /// Sort key for pagination
93    #[serde(rename = "sortKey")]
94    pub sort_key: String,
95    /// Is this message edited?
96    #[serde(skip_serializing_if = "Option::is_none")]
97    #[serde(rename = "isEdited")]
98    pub is_edited: Option<bool>,
99    /// Attachments
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub attachments: Option<Vec<Attachment>>,
102    /// True if the message is unread
103    #[serde(skip_serializing_if = "Option::is_none")]
104    #[serde(rename = "isUnread")]
105    pub is_unread: Option<bool>,
106    /// Reactions to this message
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub reactions: Option<Vec<Reaction>>,
109    /// Message ID this message replies to
110    #[serde(skip_serializing_if = "Option::is_none")]
111    #[serde(rename = "replyToID")]
112    pub reply_to_id: Option<String>,
113    /// Is this message from the current user (for previews)
114    #[serde(skip_serializing_if = "Option::is_none")]
115    #[serde(rename = "isSender")]
116    pub is_sender: Option<bool>,
117}
118
119/// Input for sending a message
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct SendMessageInput {
122    /// Message text
123    pub text: String,
124    /// ID of message to reply to (optional)
125    #[serde(skip_serializing_if = "Option::is_none")]
126    #[serde(rename = "replyToID")]
127    pub reply_to_id: Option<String>,
128}
129
130/// Output from sending a message
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct SendMessageOutput {
133    /// Chat ID where message was sent
134    #[serde(rename = "chatID")]
135    pub chat_id: String,
136    /// Pending message ID
137    #[serde(rename = "pendingMessageID")]
138    pub pending_message_id: String,
139}
140
141/// Output from listing messages
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct ListMessagesOutput {
144    /// Messages in the chat
145    pub items: Vec<Message>,
146    /// Whether there are more results
147    #[serde(rename = "hasMore")]
148    pub has_more: bool,
149}
150
151/// Output from searching messages
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct SearchMessagesOutput {
154    /// Matching messages
155    pub items: Vec<Message>,
156    /// Map of chat ID -> chat details for chats referenced in items
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub chats: Option<std::collections::HashMap<String, crate::models::Chat>>,
159    /// Whether there are more results
160    #[serde(rename = "hasMore")]
161    pub has_more: bool,
162    /// Cursor for older messages
163    #[serde(skip_serializing_if = "Option::is_none")]
164    #[serde(rename = "oldestCursor")]
165    pub oldest_cursor: Option<String>,
166    /// Cursor for newer messages
167    #[serde(skip_serializing_if = "Option::is_none")]
168    #[serde(rename = "newestCursor")]
169    pub newest_cursor: Option<String>,
170}