Skip to main content

roblox_api/api/groups/
v1.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{DateTime, Paging, endpoint};
4
5pub const URL: &str = "https://groups.roblox.com/v1";
6
7#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
8#[serde(rename_all = "camelCase")]
9pub struct GroupUser {
10    #[serde(rename = "userId")]
11    pub id: u64,
12    #[serde(rename = "username")]
13    pub name: String,
14    pub display_name: String,
15    #[serde(rename = "hasVerifiedBadge")]
16    pub is_verified: bool,
17}
18
19#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
20#[serde(rename_all = "camelCase")]
21pub struct GroupRole {
22    pub id: u64,
23    pub name: String,
24    pub rank: u8,
25
26    /// How many users have the role
27    pub member_count: Option<u64>,
28    pub description: Option<String>,
29}
30
31#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
32pub struct UserRole {
33    pub user: GroupUser,
34    pub role: GroupRole,
35}
36
37#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
38pub struct GroupShout {
39    pub body: String,
40    pub poster: GroupUser,
41    pub created: DateTime,
42    pub updated: DateTime,
43}
44
45#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
46#[serde(rename_all = "camelCase")]
47pub struct NameHistory {
48    pub names: Vec<(String, DateTime)>,
49    pub next_cursor: Option<String>,
50    pub previous_cursor: Option<String>,
51}
52
53#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
54pub struct WallPost {
55    pub id: u64,
56    pub body: String,
57    pub created: DateTime,
58    pub updated: DateTime,
59    pub poster: GroupUser,
60}
61
62#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
63#[serde(rename_all = "camelCase")]
64pub struct WallPosts {
65    #[serde(rename = "data")]
66    pub posts: Vec<WallPost>,
67    pub next_cursor: Option<String>,
68    pub previous_cursor: Option<String>,
69}
70
71#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
72#[serde(rename_all = "camelCase")]
73pub struct PostPermissions {
74    pub view_wall: bool,
75    pub post_to_wall: bool,
76    pub delete_from_wall: bool,
77
78    pub view_status: bool,
79    pub post_to_status: bool,
80}
81
82#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
83#[serde(rename_all = "camelCase")]
84pub struct ForumsPermissions {
85    pub pin_posts: bool,
86    pub lock_posts: bool,
87    pub create_posts: bool,
88    pub remove_posts: bool,
89
90    pub create_comments: bool,
91    pub remove_comments: bool,
92
93    pub manage_categories: bool,
94}
95
96#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
97#[serde(rename_all = "camelCase")]
98pub struct ContentModerationPermissions {
99    pub manage_keyword_block_list: bool,
100    pub view_keyword_block_list: bool,
101}
102
103#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
104#[serde(rename_all = "camelCase")]
105pub struct MembershipPermissions {
106    pub change_rank: bool,
107    pub ban_members: bool,
108    pub invite_members: bool,
109    pub remove_members: bool,
110}
111
112#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
113#[serde(rename_all = "camelCase")]
114pub struct ManagementPermissions {
115    pub manage_clan: bool,
116    pub manage_relationships: bool,
117    pub view_audit_logs: bool,
118}
119
120#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
121#[serde(rename_all = "camelCase")]
122pub struct EconomyPermissions {
123    pub create_items: bool,
124    pub manage_items: bool,
125    pub advertise_group: bool,
126    pub add_group_places: bool,
127    pub spend_group_funds: bool,
128    pub manage_group_games: bool,
129
130    pub view_group_payouts: bool,
131    pub view_analytics: bool,
132}
133
134#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
135#[serde(rename_all = "camelCase")]
136pub struct OpenCloudPermissions {
137    pub use_cloud_authentication: bool,
138    pub administer_cloud_authentication: bool,
139}
140
141#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
142#[serde(rename_all = "camelCase")]
143pub struct Permissions {
144    // TODO: all these fields are HashMap<String, bool>, but I can't figure a way to snakecase the hashmap keys, so.
145    #[serde(rename = "groupPostsPermissions")]
146    pub posts: PostPermissions,
147    #[serde(rename = "groupForumsPermissions")]
148    pub forums: ForumsPermissions,
149    #[serde(rename = "groupContentModerationPermissions")]
150    pub content_moderation: ContentModerationPermissions,
151    #[serde(rename = "groupMembershipPermissions")]
152    pub membership: MembershipPermissions,
153    #[serde(rename = "groupManagementPermissions")]
154    pub management: ManagementPermissions,
155    #[serde(rename = "groupEconomyPermissions")]
156    pub economy: EconomyPermissions,
157    #[serde(rename = "groupOpenCloudPermissions")]
158    pub open_cloud: OpenCloudPermissions,
159}
160
161#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
162#[serde(rename_all = "camelCase")]
163pub struct GroupInformation {
164    pub id: u64,
165    pub name: String,
166    pub description: String,
167
168    pub owner: Option<GroupUser>,
169    pub shout: Option<GroupShout>,
170
171    pub member_count: Option<u64>,
172    #[serde(rename = "isBuildersClubOnly")]
173    pub premium_only: bool,
174    #[serde(rename = "publicEntryAllowed")]
175    pub is_public: bool,
176    #[serde(rename = "hasVerifiedBadge")]
177    pub is_verified: bool,
178}
179
180#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
181pub struct NotificationPreference {
182    #[serde(rename = "type")]
183    pub name: String,
184    pub description: String,
185
186    pub kind: String,
187    pub enabled: bool,
188}
189
190#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
191#[serde(rename_all = "camelCase")]
192pub struct Membership {
193    #[serde(rename = "groupId")]
194    pub id: u64,
195
196    /// GroupUser is the authenticated user
197    pub user_role: UserRole,
198    pub permissions: Permissions,
199
200    pub is_primary: bool,
201    pub is_pending_join: bool,
202
203    pub are_enemies_allowed: bool,
204    pub are_group_games_visible: bool,
205    pub are_group_funds_visible: bool,
206
207    pub can_configure: bool,
208
209    pub notification_preferences: Option<Vec<NotificationPreference>>,
210}
211
212#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
213pub struct RolePermissions {
214    #[serde(rename = "groupId")]
215    pub id: u64,
216    pub role: GroupRole,
217    pub permissions: Permissions,
218}
219
220#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
221#[serde(rename_all = "camelCase")]
222pub struct GroupUsers {
223    pub users: Vec<(GroupUser, GroupRole)>,
224    pub next_cursor: Option<String>,
225    pub previous_cursor: Option<String>,
226}
227
228endpoint! {
229    information(id: u64) -> GroupInformation {
230        GET "{URL}/groups/{id}";
231    }
232
233    /// Gets group membership information in the context of the authenticated user
234    membership(id: u64, notification_preferences: bool) -> Membership {
235        GET "{URL}/groups/{id}/membership";
236        prelude {
237            let notification_preferences = notification_preferences.to_string();
238        }
239        query {
240            "includeNotificationPreferences" => &notification_preferences,
241        }
242    }
243
244    /// Gets the Group's name change history
245    name_history(id: u64) -> NameHistory {
246        GET "{URL}/groups/{id}/name-history";
247        types {
248            NameHistoryItem {
249                name: String,
250                created: DateTime,
251            }
252            Response {
253                items("data"): Vec<NameHistoryItem>,
254                next_cursor("nextPageCursor"): Option<String>,
255                previous_cursor("previousPageCursor"): Option<String>,
256            }
257        }
258        map |r: Response| NameHistory {
259            names: r.items.into_iter().map(|x| (x.name, x.created)).collect(),
260            next_cursor: r.next_cursor,
261            previous_cursor: r.previous_cursor,
262        }
263    }
264
265    /// Gets groups that the authenticated user has requested to join
266    pending_join_requests() -> Vec<GroupInformation> {
267        GET "{URL}/user/groups/pending";
268        types {
269            Response {
270                groups("data"): Vec<GroupInformation>,
271            }
272        }
273        map |r: Response| r.groups
274    }
275
276    roles(id: u64) -> Vec<GroupRole> {
277        GET "{URL}/groups/{id}/roles";
278        types {
279            Response {
280                roles: Vec<GroupRole>,
281            }
282        }
283        map |r: Response| r.roles
284    }
285
286    user_roles(id: u64) -> Vec<(GroupInformation, GroupRole)> {
287        GET "{URL}/users/{id}/groups/roles";
288        types {
289            GroupAndRole {
290                group: GroupInformation,
291                role: GroupRole,
292            }
293            Response {
294                items("data"): Vec<GroupAndRole>,
295            }
296        }
297        map |r: Response| r.items.into_iter().map(|x| (x.group, x.role)).collect()
298    }
299
300    /// Gets the permissions for a group's roleset. The authorized user must either be the group owner or the roleset being requested, except for guest roles, which can be viewed by all (members and guests).
301    roleset_permissions(id: u64, roleset_id: u64) -> RolePermissions {
302        GET "{URL}/groups/{id}/roles/{roleset_id}/permissions";
303    }
304
305    /// Gets all permissions for each role
306    role_permissions(id: u64) -> Vec<RolePermissions> {
307        GET "{URL}/groups/{id}/roles/permissions";
308        types {
309            Response {
310                items("data"): Vec<RolePermissions>,
311            }
312        }
313        map |r: Response| r.items
314    }
315
316    users(id: u64, paging: Paging<'_>) -> GroupUsers {
317        GET "{URL}/groups/{id}/users";
318        paging_query { paging, limit = 10 }
319        types {
320            User {
321                user: GroupUser,
322                role: GroupRole,
323            }
324            Response {
325                users("data"): Vec<User>,
326                next_cursor("nextPageCursor"): Option<String>,
327                previous_cursor("previousPageCursor"): Option<String>,
328            }
329        }
330        map |r: Response| GroupUsers {
331            users: r.users.into_iter().map(|u| (u.user, u.role)).collect(),
332            next_cursor: r.next_cursor,
333            previous_cursor: r.previous_cursor,
334        }
335    }
336
337    /// Gets a list of group wall posts
338    wall_posts(id: u64, paging: Paging<'_>) -> WallPosts {
339        GET "{URL}/groups/{id}/wall/posts";
340        paging_query { paging, limit = 10 }
341    }
342
343    join(id: u64) -> () {
344        POST "{URL}/groups/{id}/users";
345        types {
346            Request<'a> {
347                session_id: &'a str,
348                redemption_token: &'a str,
349            }
350        }
351        body_serialize { Request { session_id: "", redemption_token: "" } }
352    }
353
354    remove_join_request(id: u64, user_id: u64) -> () {
355        DELETE "{URL}/groups/{id}/join-requests/users/{user_id}";
356        types { Request {} }
357        body_serialize { Request {} }
358    }
359
360    remove(id: u64, user_id: u64) -> () {
361        DELETE "{URL}/groups/{id}/users/{user_id}";
362        types { Request {} }
363        body_serialize { Request {} }
364    }
365}