roblox_api/api/notifications/
v2.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{DateTime, Error, Paging, client::Client};
6
7pub const URL: &str = "https://notifications.roblox.com/v2";
8
9#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
10#[serde(rename_all = "camelCase")]
11pub struct NotificationUnreadCount {
12    #[serde(rename = "unreadNotifications")]
13    pub count: u64,
14    pub status_message: Option<String>,
15}
16
17#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
18#[serde(rename_all = "camelCase")]
19pub struct ClientEventsPayload {
20    #[serde(rename = "sender_userid")]
21    pub sender_user_id: Option<String>, // TODO: cast to u64
22    pub place_id: Option<String>,      // TODO: cast to u64
23    pub root_place_id: Option<String>, // TODO: cast to u64
24    pub universe_id: Option<String>,   // TODO: cast to u64
25    pub trigger: Option<String>,
26}
27
28#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
29pub enum VisualItemType {
30    Button,
31    TextBody,
32    Thumbnail,
33    MetaAction,
34}
35
36#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
37#[serde(rename_all = "camelCase")]
38pub struct VisualItemAction {
39    pub path: String,
40    pub next_state: String,     // Enum?
41    pub action_type: String,    // Enum?
42    pub fallback_state: String, // Enum?
43}
44
45#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
46#[serde(rename_all = "camelCase")]
47pub struct StyledText {
48    pub text: String,
49    pub styled_elements: Vec<StyledElement>,
50}
51
52#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
53#[serde(rename_all = "camelCase")]
54pub struct StyledElement {
55    #[serde(rename = "styledElementType")]
56    pub kind: String, // Enum?
57    pub offset: i32,
58    pub length: u32,
59}
60
61#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
62#[serde(rename_all = "camelCase")]
63pub struct VisualItemTextBody {
64    #[serde(rename = "visualItemType")]
65    pub kind: VisualItemType,
66    pub label: StyledText,
67    pub title: Option<StyledText>,
68    pub actions: Vec<VisualItemAction>,
69
70    pub event_name: String,
71    pub client_events_payload: ClientEventsPayload,
72}
73
74#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
75#[serde(rename_all = "camelCase")]
76pub struct VisualItemButton {
77    #[serde(rename = "visualItemName")]
78    pub name: String,
79
80    #[serde(rename = "visualItemType")]
81    pub kind: VisualItemType,
82    pub label: StyledText,
83    pub actions: Vec<VisualItemAction>,
84
85    pub button_style: String, // Enum?
86    pub event_name: String,
87    pub client_events_payload: ClientEventsPayload,
88}
89
90#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
91#[serde(rename_all = "camelCase")]
92pub struct VisualItemThumbnail {
93    pub id: String,
94    pub id_type: String, // Enum?
95
96    #[serde(rename = "visualItemType")]
97    pub kind: VisualItemType,
98}
99
100#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
101#[serde(rename_all = "camelCase")]
102pub struct VisualItemMetaAction {
103    #[serde(rename = "visualItemName")]
104    pub name: String,
105
106    #[serde(rename = "visualItemType")]
107    pub kind: VisualItemType,
108    pub label: StyledText,
109    pub actions: Vec<VisualItemAction>,
110
111    pub action_icon: String, // Enum?
112    pub event_name: String,
113    pub client_events_payload: ClientEventsPayload,
114}
115
116#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
117#[serde(rename_all = "camelCase")]
118pub struct VisualItems {
119    pub button: Vec<VisualItemButton>,
120    pub text_body: Vec<VisualItemTextBody>,
121    pub thumbnail: Vec<VisualItemThumbnail>,
122    pub meta_action: Vec<VisualItemMetaAction>,
123}
124
125#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
126#[serde(rename_all = "camelCase")]
127pub struct NotificationContentStateDefault {
128    pub layout_key: String, // Enum?
129    pub visual_items: VisualItems,
130}
131
132#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
133#[serde(rename_all = "camelCase")]
134pub struct NotificationContent {
135    pub notification_type: String, // Enum?
136
137    pub min_version: String,
138    pub time_before_refresh: String, // Cast to u64?
139    pub client_events_payload: ClientEventsPayload,
140    pub bundle_key: String,
141
142    pub current_state: String, // Enum?
143    pub states: HashMap<String, NotificationContentStateDefault>,
144}
145
146#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
147#[serde(rename_all = "camelCase")]
148pub struct Notification {
149    pub id: String,
150    pub notification_source_type: String, // Enum?
151    pub event_date: DateTime,
152    pub timestamp: String,
153    pub is_interacted: bool,
154
155    pub metadata_collection: Vec<u64>,
156    pub event_count: u64,
157
158    pub content: NotificationContent,
159}
160
161pub async fn unread_count(client: &mut Client) -> Result<NotificationUnreadCount, Error> {
162    let result = client
163        .requestor
164        .client
165        .get(format!("{URL}/stream-notifications/unread-count"))
166        .headers(client.requestor.default_headers.clone())
167        .send()
168        .await;
169
170    let response = client.validate_response(result).await?;
171    client
172        .requestor
173        .parse_json::<NotificationUnreadCount>(response)
174        .await
175}
176
177pub async fn recent(client: &mut Client, paging: Paging<'_>) -> Result<Vec<Notification>, Error> {
178    let limit = paging.limit.unwrap_or(20).to_string();
179    let cursor = match paging.cursor {
180        Some(cursor) => cursor.to_string(),
181        None => String::new(),
182    };
183
184    let result = client
185        .requestor
186        .client
187        .get(format!("{URL}/stream-notifications/get-recent"))
188        .query(&[("maxRows", limit), ("startIndex", cursor)])
189        .headers(client.requestor.default_headers.clone())
190        .send()
191        .await;
192
193    let response = client.validate_response(result).await?;
194    client
195        .requestor
196        .parse_json::<Vec<Notification>>(response)
197        .await
198}
199
200pub async fn clear_unread(client: &mut Client) -> Result<String, Error> {
201    let result = client
202        .requestor
203        .client
204        .post(format!("{URL}/stream-notifications/clear-unread"))
205        .headers(client.requestor.default_headers.clone())
206        .send()
207        .await;
208
209    #[derive(Debug, Deserialize)]
210    #[serde(rename_all = "camelCase")]
211    struct Response {
212        status_message: String,
213    }
214
215    let response = client.validate_response(result).await?;
216    Ok(client
217        .requestor
218        .parse_json::<Response>(response)
219        .await?
220        .status_message)
221}
222
223pub async fn dismiss(client: &mut Client, id: String) -> Result<String, Error> {
224    let result = client
225        .requestor
226        .client
227        .post(format!(
228            "{URL}/stream-notifications/clear-unread/action/{id}/SpecialItemIgnoreAction"
229        ))
230        .headers(client.requestor.default_headers.clone())
231        .send()
232        .await;
233
234    #[derive(Debug, Deserialize)]
235    #[serde(rename_all = "camelCase")]
236    struct Response {
237        status_message: String,
238    }
239
240    let response = client.validate_response(result).await?;
241    Ok(client
242        .requestor
243        .parse_json::<Response>(response)
244        .await?
245        .status_message)
246}