beeper_desktop_api/models/
chat.rs

1//! Chat models
2
3use serde::{Deserialize, Serialize};
4use super::message::Message;
5use super::user::User;
6
7/// Chat participants with pagination
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Participants {
10    /// List of participants
11    pub items: Vec<User>,
12    /// Whether there are more participants not included
13    #[serde(rename = "hasMore")]
14    pub has_more: bool,
15    /// Total number of participants in the chat
16    pub total: u32,
17}
18
19/// A chat or conversation
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct Chat {
22    /// Unique chat ID
23    pub id: String,
24    /// Local chat ID specific to this Beeper Desktop installation
25    #[serde(skip_serializing_if = "Option::is_none")]
26    #[serde(rename = "localChatID")]
27    pub local_chat_id: Option<String>,
28    /// Account ID this chat belongs to, generaly "whatsapp" etc.
29    #[serde(rename = "accountID")]
30    pub account_id: String,
31    /// Display-only human-readable network name (e.g., 'WhatsApp', 'Messenger')
32    pub network: String,
33    /// Display title of the chat
34    pub title: String,
35    /// Chat type: 'single' for direct messages, 'group' for group chats
36    #[serde(rename = "type")]
37    pub chat_type: String,
38    /// Chat participants information
39    pub participants: Participants,
40    /// Timestamp of last activity
41    #[serde(skip_serializing_if = "Option::is_none")]
42    #[serde(rename = "lastActivity")]
43    pub last_activity: Option<String>,
44    /// Number of unread messages
45    #[serde(rename = "unreadCount")]
46    pub unread_count: u32,
47    /// Last read message sortKey
48    #[serde(skip_serializing_if = "Option::is_none")]
49    #[serde(rename = "lastReadMessageSortKey")]
50    pub last_read_message_sort_key: Option<u64>,
51    /// True if chat is archived
52    #[serde(rename = "isArchived")]
53    pub is_archived: bool,
54    /// True if chat notifications are muted
55    #[serde(rename = "isMuted")]
56    pub is_muted: bool,
57    /// True if chat is pinned
58    #[serde(rename = "isPinned")]
59    pub is_pinned: bool,
60    /// Last message preview for this chat, if available
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub preview: Option<Box<Message>>,
63}
64
65impl Chat {
66    /// Get a display name for the chat
67    /// 
68    /// For direct messages ('single'), returns the participant's full name or username.
69    /// For group chats, returns the chat title.
70    pub fn display_name(&self) -> String {
71        if self.chat_type == "single" {
72            // For direct messages, try to add the other person's name
73            if let Some(first_participant) = self.participants.items.iter().filter(|p| !p.is_self.unwrap_or(false)).next() {
74                if let Some(full_name) = &first_participant.full_name {
75                    return full_name.clone();
76                }
77                if let Some(username) = &first_participant.username {
78                    return username.clone();
79                }
80            }
81        }
82        
83        // Return the chat title as-is
84        self.title.clone()
85    }
86}
87
88/// Input for creating a chat
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct CreateChatInput {
91    /// Account ID to create chat on
92    #[serde(rename = "accountID")]
93    pub account_id: String,
94    /// Participant IDs for the chat
95    #[serde(rename = "participantIDs")]
96    pub participant_ids: Vec<String>,
97    /// Optional chat title for group chats
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub title: Option<String>,
100}
101
102/// Output from creating a chat
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct CreateChatOutput {
105    /// Newly created chat ID
106    #[serde(rename = "chatID")]
107    pub chat_id: String,
108}
109
110/// Output from listing chats
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct ListChatsOutput {
113    /// List of chats
114    pub items: Vec<Chat>,
115    /// Whether there are more chats
116    #[serde(rename = "hasMore")]
117    pub has_more: bool,
118    /// Cursor for fetching older results
119    #[serde(skip_serializing_if = "Option::is_none")]
120    #[serde(rename = "oldestCursor")]
121    pub oldest_cursor: Option<String>,
122    /// Cursor for fetching newer results
123    #[serde(skip_serializing_if = "Option::is_none")]
124    #[serde(rename = "newestCursor")]
125    pub newest_cursor: Option<String>,
126}
127
128/// Output from searching chats
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct SearchChatsOutput {
131    /// Matching chats
132    pub items: Vec<Chat>,
133    /// Map of chat ID -> chat details
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub chats: Option<std::collections::HashMap<String, Chat>>,
136    /// Whether there are more results
137    #[serde(rename = "hasMore")]
138    pub has_more: bool,
139    /// Cursor for older results
140    #[serde(skip_serializing_if = "Option::is_none")]
141    #[serde(rename = "oldestCursor")]
142    pub oldest_cursor: Option<String>,
143    /// Cursor for newer results
144    #[serde(skip_serializing_if = "Option::is_none")]
145    #[serde(rename = "newestCursor")]
146    pub newest_cursor: Option<String>,
147}