1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{DateTime, Error, Paging, client::Client};
6
7pub const URL: &str = "https://apis.roblox.com/platform-chat-api/v1";
8
9#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
10#[serde(rename_all = "snake_case")]
11pub enum ConversationType {
12 OneToOne,
13 Group,
14}
15
16#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
17#[serde(rename_all = "snake_case")]
18pub enum ConversationSource {
19 Channels,
20 Friends,
21}
22
23#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
24pub struct ConversationUser {
25 pub id: u64,
26 pub name: String,
27 pub display_name: String,
28 pub combined_name: String,
30 pub is_verified: bool,
31}
32
33#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
34pub struct Message {
35 pub id: String,
36 pub content: String,
37 #[serde(rename = "type")]
38 pub kind: String, #[serde(rename = "sender_user_id")]
41 pub sender_id: u64,
42 pub replies_to: Option<()>, #[serde(rename = "created_at")]
45 pub created: DateTime,
46
47 pub moderation_type: String, pub is_deleted: bool,
50 pub is_badgeable: bool,
51 pub is_previewable: bool,
52}
53
54#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
55pub struct Conversation {
56 pub id: Option<String>,
57 pub name: String,
58 #[serde(rename = "type")]
59 pub kind: ConversationType,
60 pub source: String, #[serde(rename = "created_by")]
63 pub creator_id: Option<u64>,
64 #[serde(rename = "participant_user_ids")]
65 pub participants: Vec<u64>,
66 #[serde(rename = "user_data")]
67 pub users: HashMap<String, ConversationUser>,
70
71 pub messages: Vec<Message>,
72 pub preview_message: Option<Message>,
73
74 pub sort_index: u64,
75 pub unread_message_count: u64,
76
77 #[serde(rename = "created_at")]
78 pub created: DateTime,
79 #[serde(rename = "updated_at")]
80 pub updated: DateTime,
81
82 pub status: Option<String>, pub moderation_type: Option<String>, pub user_pending_status: Option<String>, pub participant_pending_status: Option<String>, pub osa_acknowledgement_status: String, pub is_default_name: bool,
90}
91
92#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
93pub struct Conversations {
94 pub conversations: Vec<Conversation>,
95 pub next_cursor: Option<String>,
96 pub previous_cursor: Option<String>,
97}
98
99#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
100pub struct ConversationMessages {
101 pub messages: Vec<Message>,
102 pub next_cursor: Option<String>,
103 pub previous_cursor: Option<String>,
104}
105
106#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
107pub struct ConversationMetadata {
108 pub global_unread_count: u64,
109 pub global_unread_message_count: u64,
110}
111
112#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
113pub struct ParticipantMetadata {
114 pub id: u64,
115 pub is_pending: bool,
116}
117
118#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
119pub struct ConversationsParticipantMetadata {
120 pub id: String,
121 pub participants: Vec<ParticipantMetadata>,
122}
123
124#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
125pub struct ConversationMarkedStatus {
126 #[serde(rename = "conversation_id")]
127 pub id: String,
128 pub status: String, }
130
131#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
132pub struct ConversationCreateRequest {
133 pub name: String,
134 pub users: Vec<u64>,
135}
136
137pub async fn conversation_metadata(client: &mut Client) -> Result<ConversationMetadata, Error> {
138 let result = client
139 .requestor
140 .client
141 .get(format!("{URL}/get-conversation-metadata"))
142 .headers(client.requestor.default_headers.clone())
143 .send()
144 .await;
145
146 let response = client.requestor.validate_response(result).await?;
147 client
148 .requestor
149 .parse_json::<ConversationMetadata>(response)
150 .await
151}
152
153pub async fn conversations_participant_metadata(
154 client: &mut Client,
155 ids: &[&str],
156) -> Result<Vec<ConversationsParticipantMetadata>, Error> {
157 #[derive(Debug, Serialize)]
158 struct Request<'a> {
159 #[serde(rename = "conversation_ids")]
160 ids: &'a [&'a str],
161 }
162
163 let result = client
164 .requestor
165 .client
166 .post(format!("{URL}/get-conversations-participants-metadata"))
167 .json(&Request { ids })
168 .headers(client.requestor.default_headers.clone())
169 .send()
170 .await;
171
172 #[derive(Debug, Deserialize)]
173 struct ParticipantPending {
174 is_pending: bool,
175 }
176
177 #[derive(Debug, Deserialize)]
178 struct ParticipantsMetadata {
179 participants_metadata: HashMap<String, ParticipantPending>,
180 }
181
182 #[derive(Debug, Deserialize)]
183 struct Response {
184 #[serde(rename = "conversation_participants_metadata")]
185 metadata: HashMap<String, ParticipantsMetadata>,
186 }
187
188 let response = client.requestor.validate_response(result).await?;
189 let response = client.requestor.parse_json::<Response>(response).await?;
190
191 let mut metadata = Vec::new();
192 for (k, v) in &response.metadata {
193 let mut participants = Vec::new();
194 for (k, v) in &v.participants_metadata {
195 participants.push(ParticipantMetadata {
196 id: k.parse().unwrap(),
197 is_pending: v.is_pending,
198 });
199 }
200
201 metadata.push(ConversationsParticipantMetadata {
202 id: k.to_owned(),
203 participants,
204 })
205 }
206
207 Ok(metadata)
208}
209
210pub async fn conversations(client: &mut Client, ids: &[&str]) -> Result<Conversations, Error> {
211 #[derive(Debug, Serialize)]
212 struct Request<'a> {
213 ids: &'a [&'a str],
214 include_messages: bool,
215 include_user_data: bool,
216 include_participants: bool,
217 }
218
219 let result = client
220 .requestor
221 .client
222 .post(format!("{URL}/get-conversations"))
223 .json(&Request {
224 ids,
225 include_messages: true,
226 include_user_data: true,
227 include_participants: true,
228 })
229 .headers(client.requestor.default_headers.clone())
230 .send()
231 .await;
232
233 let response = client.requestor.validate_response(result).await?;
234 client.requestor.parse_json::<Conversations>(response).await
235}
236
237pub async fn user_conversations(
238 client: &mut Client,
239 paging: Paging<'_>,
240) -> Result<Conversations, Error> {
241 let limit = paging.limit.unwrap_or(20).to_string();
242 let cursor = match paging.cursor {
243 Some(cursor) => cursor.to_string(),
244 None => String::new(),
245 };
246
247 let result = client
248 .requestor
249 .client
250 .get(format!("{URL}/get-user-conversations"))
251 .query(&[
252 ("cursor", cursor),
253 ("include_user_data", true.to_string()),
254 ("pageSize", limit),
255 ])
256 .headers(client.requestor.default_headers.clone())
257 .send()
258 .await;
259
260 let response = client.requestor.validate_response(result).await?;
261 client.requestor.parse_json::<Conversations>(response).await
262}
263
264pub async fn conversation_messages(
265 client: &mut Client,
266 id: &str,
267) -> Result<ConversationMessages, Error> {
268 let result = client
269 .requestor
270 .client
271 .get(format!("{URL}/get-conversation-messages"))
272 .query(&[("conversation_id", id)])
273 .headers(client.requestor.default_headers.clone())
274 .send()
275 .await;
276
277 let response = client.requestor.validate_response(result).await?;
278 client
279 .requestor
280 .parse_json::<ConversationMessages>(response)
281 .await
282}
283
284pub async fn send_messages_in_conversation(
286 client: &mut Client,
287 id: &str,
288 messages: &[&str],
289) -> Result<ConversationMessages, Error> {
290 #[derive(Debug, Serialize)]
291 struct MessageToPost<'a> {
292 content: &'a str,
293 }
294
295 #[derive(Debug, Serialize)]
296 struct Request<'a> {
297 #[serde(rename = "conversation_id")]
298 id: &'a str,
299 messages: &'a [MessageToPost<'a>],
300 }
301
302 let messages = &messages
303 .iter()
304 .map(|x| MessageToPost { content: x })
305 .collect::<Vec<_>>();
306
307 let result = client
308 .requestor
309 .client
310 .post(format!("{URL}/send-messages"))
311 .json(&Request { id, messages })
312 .headers(client.requestor.default_headers.clone())
313 .send()
314 .await;
315
316 let response = client.requestor.validate_response(result).await?;
317 client
318 .requestor
319 .parse_json::<ConversationMessages>(response)
320 .await
321}
322
323pub async fn update_typing_status_in_conversation(
324 client: &mut Client,
325 id: &str,
326) -> Result<String, Error> {
327 #[derive(Debug, Serialize)]
328 struct Request<'a> {
329 #[serde(rename = "conversation_id")]
330 id: &'a str,
331 }
332
333 let result = client
334 .requestor
335 .client
336 .post(format!("{URL}/update-typing-status"))
337 .json(&Request { id })
338 .headers(client.requestor.default_headers.clone())
339 .send()
340 .await;
341
342 #[derive(Debug, Deserialize)]
343 struct Response {
344 status: String,
345 }
346
347 let response = client.requestor.validate_response(result).await?;
348 Ok(client
349 .requestor
350 .parse_json::<Response>(response)
351 .await?
352 .status)
353}
354
355pub async fn add_users_to_conversation(
356 client: &mut Client,
357 id: &str,
358 users: &[u64],
359) -> Result<String, Error> {
360 #[derive(Debug, Serialize)]
361 struct Request<'a> {
362 #[serde(rename = "conversation_id")]
363 id: &'a str,
364 #[serde(rename = "user_ids")]
365 users: &'a [u64],
366 }
367
368 let result = client
369 .requestor
370 .client
371 .post(format!("{URL}/add-users"))
372 .json(&Request { id, users })
373 .headers(client.requestor.default_headers.clone())
374 .send()
375 .await;
376
377 #[derive(Debug, Deserialize)]
378 struct Response {
379 status: String,
380 }
381
382 let response = client.requestor.validate_response(result).await?;
383 Ok(client
384 .requestor
385 .parse_json::<Response>(response)
386 .await?
387 .status)
388}
389
390pub async fn remove_users_from_conversation(
391 client: &mut Client,
392 id: &str,
393 users: &[u64],
394) -> Result<String, Error> {
395 #[derive(Debug, Serialize)]
396 struct Request<'a> {
397 #[serde(rename = "conversation_id")]
398 id: &'a str,
399 #[serde(rename = "user_ids")]
400 users: &'a [u64],
401 }
402
403 let result = client
404 .requestor
405 .client
406 .post(format!("{URL}/remove-users"))
407 .json(&Request { id, users })
408 .headers(client.requestor.default_headers.clone())
409 .send()
410 .await;
411
412 #[derive(Debug, Deserialize)]
413 struct Response {
414 status: String,
415 }
416
417 let response = client.requestor.validate_response(result).await?;
418 Ok(client
419 .requestor
420 .parse_json::<Response>(response)
421 .await?
422 .status)
423}
424
425pub async fn create_conversations(
426 client: &mut Client,
427 conversations: &[ConversationCreateRequest],
428) -> Result<Conversations, Error> {
429 #[derive(Debug, Serialize)]
430 struct ConversationToCreate<'a> {
431 name: &'a str,
432 #[serde(rename = "type")]
433 kind: &'a str,
434 #[serde(rename = "participant_user_ids")]
435 users: &'a [u64],
436 }
437
438 #[derive(Debug, Serialize)]
439 struct Request<'a> {
440 conversations: &'a [ConversationToCreate<'a>],
441 include_user_data: bool,
442 }
443
444 let conversations = &conversations
445 .iter()
446 .map(|x| ConversationToCreate {
447 name: &x.name,
448 kind: "group",
449 users: &x.users,
450 })
451 .collect::<Vec<_>>();
452
453 let result = client
454 .requestor
455 .client
456 .post(format!("{URL}/create-conversations"))
457 .json(&Request {
458 conversations,
459 include_user_data: true,
460 })
461 .headers(client.requestor.default_headers.clone())
462 .send()
463 .await;
464
465 let response = client.requestor.validate_response(result).await?;
466 client.requestor.parse_json::<Conversations>(response).await
467}
468
469pub async fn rename_conversations(
470 client: &mut Client,
471 ids: &[&str],
472 names: &[&str],
473) -> Result<Conversations, Error> {
474 #[derive(Debug, Serialize)]
475 struct ConversationToUpdate<'a> {
476 id: &'a str,
477 name: &'a str,
478 }
479
480 #[derive(Debug, Serialize)]
481 struct Request<'a> {
482 conversations: &'a [ConversationToUpdate<'a>],
483 }
484
485 let conversations = &ids
486 .iter()
487 .zip(names.into_iter())
488 .collect::<Vec<_>>()
489 .iter()
490 .map(|(id, name)| ConversationToUpdate { id, name })
491 .collect::<Vec<_>>();
492
493 let result = client
494 .requestor
495 .client
496 .post(format!("{URL}/update-conversations"))
497 .json(&Request { conversations })
498 .headers(client.requestor.default_headers.clone())
499 .send()
500 .await;
501
502 let response = client.requestor.validate_response(result).await?;
503 client.requestor.parse_json::<Conversations>(response).await
504}
505
506pub async fn mark_conversations_as_read(
507 client: &mut Client,
508 ids: &[&str],
509) -> Result<Vec<ConversationMarkedStatus>, Error> {
510 #[derive(Debug, Serialize)]
511 struct Request<'a> {
512 #[serde(rename = "conversation_ids")]
513 ids: &'a [&'a str],
514 }
515
516 let result = client
517 .requestor
518 .client
519 .post(format!("{URL}/mark-conversations"))
520 .json(&Request { ids })
521 .headers(client.requestor.default_headers.clone())
522 .send()
523 .await;
524
525 #[derive(Debug, Deserialize)]
526 struct Response {
527 results: Vec<ConversationMarkedStatus>,
528 }
529
530 let response = client.requestor.validate_response(result).await?;
531 Ok(client
532 .requestor
533 .parse_json::<Response>(response)
534 .await?
535 .results)
536}