use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::{DateTime, Paging, endpoint};
pub const URL: &str = "https://apis.roblox.com/platform-chat-api/v1";
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ConversationType {
OneToOne,
Group,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ConversationSource {
Channels,
Friends,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ConversationUser {
pub id: u64,
pub name: String,
pub display_name: String,
pub combined_name: String,
pub is_verified: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Message {
pub id: String,
pub content: String,
#[serde(rename = "type")]
pub kind: String,
#[serde(rename = "sender_user_id")]
pub sender_id: u64,
pub replies_to: Option<()>,
#[serde(rename = "created_at")]
pub created: DateTime,
pub moderation_type: String,
pub is_deleted: bool,
pub is_badgeable: bool,
pub is_previewable: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Conversation {
pub id: Option<String>,
pub name: String,
#[serde(rename = "type")]
pub kind: ConversationType,
pub source: String,
#[serde(rename = "created_by")]
pub creator_id: Option<u64>,
#[serde(rename = "participant_user_ids")]
pub participants: Vec<u64>,
#[serde(rename = "user_data")]
pub users: HashMap<String, ConversationUser>,
pub messages: Vec<Message>,
pub preview_message: Option<Message>,
pub sort_index: u64,
pub unread_message_count: u64,
#[serde(rename = "created_at")]
pub created: DateTime,
#[serde(rename = "updated_at")]
pub updated: DateTime,
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,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Conversations {
pub conversations: Vec<Conversation>,
pub next_cursor: Option<String>,
pub previous_cursor: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ConversationMessages {
pub messages: Vec<Message>,
pub next_cursor: Option<String>,
pub previous_cursor: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ConversationMetadata {
pub global_unread_count: u64,
pub global_unread_message_count: u64,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ParticipantMetadata {
pub id: u64,
pub is_pending: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ConversationsParticipantMetadata {
pub id: String,
pub participants: Vec<ParticipantMetadata>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ConversationMarkedStatus {
#[serde(rename = "conversation_id")]
pub id: String,
pub status: String, }
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct ConversationCreateRequest {
pub name: String,
pub users: Vec<u64>,
}
endpoint! {
conversation_metadata() -> ConversationMetadata {
GET "{URL}/get-conversation-metadata";
}
conversations_participant_metadata(ids: &[&str]) -> Vec<ConversationsParticipantMetadata> {
POST "{URL}/get-conversations-participants-metadata";
types {
Request<'a> { ids("conversation_ids"): &'a [&'a str] }
Response { metadata("conversation_participants_metadata"): HashMap<String, ParticipantsMetadata> }
ParticipantsMetadata { participants_metadata: HashMap<String, ParticipantPending> }
ParticipantPending { is_pending: bool }
}
body_serialize { Request { ids } }
map |r: Response| {
let mut metadata = Vec::new();
for (conv_id, v) in &r.metadata {
let mut participants = Vec::new();
for (user_id, pending) in &v.participants_metadata {
participants.push(ParticipantMetadata {
id: user_id.parse().unwrap(),
is_pending: pending.is_pending,
});
}
metadata.push(ConversationsParticipantMetadata {
id: conv_id.to_owned(),
participants,
});
}
metadata
}
}
conversations(ids: &[&str]) -> Conversations {
POST "{URL}/get-conversations";
types {
Request<'a> {
ids: &'a [&'a str],
include_messages: bool,
include_user_data: bool,
include_participants: bool,
}
}
body_serialize {
Request { ids, include_messages: true, include_user_data: true, include_participants: true }
}
}
user_conversations(paging: Paging<'_>) -> Conversations {
GET "{URL}/get-user-conversations";
prelude {
let limit = paging.limit.unwrap_or(20).to_string();
let cursor_str = match paging.cursor {
Some(c) => c.to_string(),
None => String::new(),
};
}
query {
"cursor" => &cursor_str,
"include_user_data" => "true",
"pageSize" => &limit,
}
}
conversation_messages(id: &str) -> ConversationMessages {
GET "{URL}/get-conversation-messages";
query { "conversation_id" => id }
}
send_messages_in_conversation(id: &str, messages: &[&str]) -> ConversationMessages {
POST "{URL}/send-messages";
types {
MessageToPost { content: String }
Request<'a> { id("conversation_id"): &'a str, messages: &'a [MessageToPost] }
}
prelude {
let msgs: Vec<MessageToPost> = messages.iter().map(|x| MessageToPost { content: x.to_string() }).collect();
}
body_serialize { Request { id, messages: &msgs } }
}
update_typing_status_in_conversation(id: &str) -> String {
POST "{URL}/update-typing-status";
types {
Request<'a> { id("conversation_id"): &'a str }
Response { status: String }
}
body_serialize { Request { id } }
map |r: Response| r.status
}
add_users_to_conversation(id: &str, users: &[u64]) -> String {
POST "{URL}/add-users";
types {
Request<'a> { id("conversation_id"): &'a str, users("user_ids"): &'a [u64] }
Response { status: String }
}
body_serialize { Request { id, users } }
map |r: Response| r.status
}
remove_users_from_conversation(id: &str, users: &[u64]) -> String {
POST "{URL}/remove-users";
types {
Request<'a> { id("conversation_id"): &'a str, users("user_ids"): &'a [u64] }
Response { status: String }
}
body_serialize { Request { id, users } }
map |r: Response| r.status
}
create_conversations(conversations: &[ConversationCreateRequest]) -> Conversations {
POST "{URL}/create-conversations";
types {
ConversationToCreate {
name: String,
kind("type"): String,
users("participant_user_ids"): Vec<u64>,
}
Request<'a> { conversations: &'a [ConversationToCreate], include_user_data: bool }
}
prelude {
let convs: Vec<ConversationToCreate> = conversations.iter().map(|x| ConversationToCreate {
name: x.name.clone(),
kind: "group".to_string(),
users: x.users.clone(),
}).collect();
}
body_serialize { Request { conversations: &convs, include_user_data: true } }
}
rename_conversations(ids: &[&str], names: &[&str]) -> Conversations {
POST "{URL}/update-conversations";
types {
ConversationToUpdate { id: String, name: String }
Request<'a> { conversations: &'a [ConversationToUpdate] }
}
prelude {
let updates: Vec<ConversationToUpdate> = ids.iter()
.zip(names.iter())
.map(|(id, name)| ConversationToUpdate { id: id.to_string(), name: name.to_string() })
.collect();
}
body_serialize { Request { conversations: &updates } }
}
mark_conversations_as_read(ids: &[&str]) -> Vec<ConversationMarkedStatus> {
POST "{URL}/mark-conversations";
types {
Request<'a> { ids("conversation_ids"): &'a [&'a str] }
Response { results: Vec<ConversationMarkedStatus> }
}
body_serialize { Request { ids } }
map |r: Response| r.results
}
}