Skip to main content

steam_client/services/
chatroom.rs

1//! Chat room functionality for Steam client.
2//!
3//! This module provides group chat (chat room) functionality,
4//! including creating, joining, and messaging in chat rooms.
5
6use std::collections::HashMap;
7
8use steamid::SteamID;
9
10use crate::{error::SteamError, SteamClient};
11
12/// A chat room group.
13#[derive(Debug, Clone)]
14pub struct ChatRoomGroup {
15    /// Chat group ID.
16    pub chat_group_id: String,
17    /// Group name.
18    pub chat_group_name: String,
19    /// Owner's SteamID.
20    pub steamid_owner: SteamID,
21    /// Group tagline.
22    pub tagline: String,
23    /// Avatar URL.
24    pub avatar_url: Option<String>,
25    /// App ID associated with this group.
26    pub appid: Option<u32>,
27    /// Default chat room ID.
28    pub default_chat_id: String,
29    /// Active member count.
30    pub active_member_count: u32,
31    /// Members in voice.
32    pub active_voice_member_count: u32,
33    /// Chat rooms in this group.
34    pub chat_rooms: Vec<ChatRoom>,
35    /// Members in this group.
36    pub members: Vec<ChatRoomMember>,
37    /// Roles in this group.
38    pub roles: Vec<ChatRole>,
39}
40
41/// A chat room within a group.
42#[derive(Debug, Clone)]
43pub struct ChatRoom {
44    /// Chat room ID.
45    pub chat_id: String,
46    /// Room name.
47    pub chat_name: String,
48    /// Whether voice is allowed.
49    pub voice_allowed: bool,
50    /// Members currently in voice.
51    pub members_in_voice: Vec<SteamID>,
52    /// Time of last message (Unix timestamp).
53    pub time_last_message: u32,
54    /// Sort order.
55    pub sort_order: u32,
56    /// Last message content.
57    pub last_message: String,
58    /// SteamID of last message sender.
59    pub steamid_last_message: Option<SteamID>,
60}
61
62/// A member of a chat room group.
63#[derive(Debug, Clone)]
64pub struct ChatRoomMember {
65    /// Member's SteamID.
66    pub steamid: SteamID,
67    /// Join state.
68    pub state: u32,
69    /// Member rank.
70    pub rank: u32,
71    /// Time kick expires (if kicked).
72    pub time_kick_expire: Option<u32>,
73    /// Role IDs assigned to this member.
74    pub role_ids: Vec<String>,
75}
76
77/// A role in a chat room group.
78#[derive(Debug, Clone)]
79pub struct ChatRole {
80    /// Role ID.
81    pub role_id: String,
82    /// Role name.
83    pub name: String,
84    /// Sort ordinal.
85    pub ordinal: u32,
86}
87
88/// Permissions for a chat role.
89#[derive(Debug, Clone, Default)]
90pub struct RolePermissions {
91    /// Can create, rename, delete channels.
92    pub can_create_rename_delete_channel: bool,
93    /// Can kick members.
94    pub can_kick: bool,
95    /// Can ban members.
96    pub can_ban: bool,
97    /// Can invite members.
98    pub can_invite: bool,
99    /// Can change tagline, avatar, name.
100    pub can_change_tagline_avatar_name: bool,
101    /// Can send chat messages.
102    pub can_chat: bool,
103    /// Can view chat history.
104    pub can_view_history: bool,
105    /// Can change group roles.
106    pub can_change_group_roles: bool,
107    /// Can change user roles.
108    pub can_change_user_roles: bool,
109    /// Can @mention all.
110    pub can_mention_all: bool,
111    /// Can set watching broadcast.
112    pub can_set_watching_broadcast: bool,
113}
114
115/// A message in a chat room.
116#[derive(Debug, Clone)]
117pub struct ChatRoomMessage {
118    /// Sender's SteamID.
119    pub sender: SteamID,
120    /// Message content.
121    pub message: String,
122    /// Server timestamp.
123    pub server_timestamp: u32,
124    /// Message ordinal.
125    pub ordinal: u32,
126    /// Whether message was deleted.
127    pub deleted: bool,
128}
129
130/// A banned user in a chat room group.
131#[derive(Debug, Clone)]
132pub struct ChatRoomBan {
133    /// Banned user's SteamID.
134    pub steamid: SteamID,
135    /// Actor who performed the ban.
136    pub steamid_actor: SteamID,
137    /// Time when banned.
138    pub time_banned: u32,
139    /// Ban reason.
140    pub ban_reason: String,
141}
142
143/// Information about an invite link.
144#[derive(Debug, Clone)]
145pub struct InviteLinkInfo {
146    /// Invite code.
147    pub invite_code: String,
148    /// SteamID of who sent the invite.
149    pub steamid_sender: SteamID,
150    /// When the invite expires.
151    pub time_expires: Option<u32>,
152    /// Group summary.
153    pub group_summary: Option<ChatRoomGroup>,
154    /// Whether the user is banned.
155    pub banned: bool,
156    /// When kick expires.
157    pub time_kick_expire: Option<u32>,
158    /// Chat ID if specific to a voice channel.
159    pub chat_id: Option<String>,
160}
161
162impl SteamClient {
163    /// Create a new chat room group.
164    ///
165    /// # Arguments
166    /// * `name` - Name for the group (empty for ad-hoc group)
167    /// * `invitees` - Users to invite
168    pub async fn create_chat_room_group(&mut self, name: &str, invitees: Vec<SteamID>) -> Result<ChatRoomGroup, SteamError> {
169        if !self.is_logged_in() {
170            return Err(SteamError::NotLoggedOn);
171        }
172
173        let msg = steam_protos::CChatRoomCreateChatRoomGroupRequest {
174            name: Some(name.to_string()),
175            steamid_invitees: invitees.iter().map(|s| s.steam_id64()).collect(),
176            ..Default::default()
177        };
178
179        self.send_service_method("ChatRoom.CreateChatRoomGroup#1", &msg).await?;
180
181        Ok(ChatRoomGroup {
182            chat_group_id: String::new(),
183            chat_group_name: name.to_string(),
184            steamid_owner: self.steam_id.unwrap_or_default(),
185            tagline: String::new(),
186            avatar_url: None,
187            appid: None,
188            default_chat_id: String::new(),
189            active_member_count: 0,
190            active_voice_member_count: 0,
191            chat_rooms: Vec::new(),
192            members: Vec::new(),
193            roles: Vec::new(),
194        })
195    }
196
197    /// Save an unnamed "ad-hoc" group chat as a full chat room group.
198    ///
199    /// # Arguments
200    /// * `group_id` - The group ID
201    /// * `name` - Name for the saved group
202    pub async fn save_chat_room_group(&mut self, group_id: SteamID, name: &str) -> Result<(), SteamError> {
203        if !self.is_logged_in() {
204            return Err(SteamError::NotLoggedOn);
205        }
206
207        let msg = steam_protos::CChatRoomSaveChatRoomGroupRequest { chat_group_id: Some(group_id.steam_id64()), name: Some(name.to_string()) };
208
209        self.send_service_method("ChatRoom.SaveChatRoomGroup#1", &msg).await
210    }
211
212    /// Get a list of chat room groups you're in.
213    pub async fn get_chat_room_groups(&mut self) -> Result<HashMap<String, ChatRoomGroup>, SteamError> {
214        if !self.is_logged_in() {
215            return Err(SteamError::NotLoggedOn);
216        }
217
218        let msg = steam_protos::CChatRoomGetMyChatRoomGroupsRequest {};
219        self.send_service_method("ChatRoom.GetMyChatRoomGroups#1", &msg).await?;
220
221        Ok(HashMap::new())
222    }
223
224    /// Set which groups are actively being chatted in.
225    ///
226    /// Only active groups receive some events like member state changes.
227    ///
228    /// # Arguments
229    /// * `group_ids` - Group IDs to set as active
230    pub async fn set_session_active_chat_groups(&mut self, group_ids: Vec<SteamID>) -> Result<HashMap<String, ChatRoomGroup>, SteamError> {
231        if !self.is_logged_in() {
232            return Err(SteamError::NotLoggedOn);
233        }
234
235        let chat_group_ids: Vec<u64> = group_ids.iter().map(|s| s.steam_id64()).collect();
236        let msg = steam_protos::CChatRoomSetSessionActiveChatRoomGroupsRequest { chat_group_ids: chat_group_ids.clone(), chat_groups_data_requested: chat_group_ids, ..Default::default() };
237
238        self.send_service_method("ChatRoom.SetSessionActiveChatRoomGroups#1", &msg).await?;
239
240        Ok(HashMap::new())
241    }
242
243    /// Get details from a chat group invite link.
244    ///
245    /// # Arguments
246    /// * `link_url` - The invite link URL (e.g., https://s.team/chat/XXXXX)
247    pub async fn get_chat_invite_link_info(&mut self, link_url: &str) -> Result<InviteLinkInfo, SteamError> {
248        if !self.is_logged_in() {
249            return Err(SteamError::NotLoggedOn);
250        }
251
252        let invite_code = link_url.split("/chat/").last().ok_or_else(|| SteamError::Other("Invalid invite link".into()))?.trim_end_matches('/');
253
254        let msg = steam_protos::CChatRoomGetInviteLinkInfoRequest { invite_code: Some(invite_code.to_string()) };
255
256        self.send_service_method("ChatRoom.GetInviteLinkInfo#1", &msg).await?;
257
258        Ok(InviteLinkInfo {
259            invite_code: invite_code.to_string(),
260            steamid_sender: SteamID::new(),
261            time_expires: None,
262            group_summary: None,
263            banned: false,
264            time_kick_expire: None,
265            chat_id: None,
266        })
267    }
268
269    /// Join a chat room group.
270    ///
271    /// # Arguments
272    /// * `group_id` - The group ID to join
273    /// * `invite_code` - Optional invite code if joining via invite
274    pub async fn join_chat_room_group(&mut self, group_id: SteamID, invite_code: Option<&str>) -> Result<ChatRoomGroup, SteamError> {
275        if !self.is_logged_in() {
276            return Err(SteamError::NotLoggedOn);
277        }
278
279        let msg = steam_protos::CChatRoomJoinChatRoomGroupRequest {
280            chat_group_id: Some(group_id.steam_id64()),
281            invite_code: invite_code.map(|s| s.to_string()),
282            ..Default::default()
283        };
284
285        self.send_service_method("ChatRoom.JoinChatRoomGroup#1", &msg).await?;
286
287        Ok(ChatRoomGroup {
288            chat_group_id: group_id.to_string(),
289            chat_group_name: String::new(),
290            steamid_owner: SteamID::new(),
291            tagline: String::new(),
292            avatar_url: None,
293            appid: None,
294            default_chat_id: String::new(),
295            active_member_count: 0,
296            active_voice_member_count: 0,
297            chat_rooms: Vec::new(),
298            members: Vec::new(),
299            roles: Vec::new(),
300        })
301    }
302
303    /// Leave a chat room group.
304    ///
305    /// # Arguments
306    /// * `group_id` - The group ID to leave
307    pub async fn leave_chat_room_group(&mut self, group_id: SteamID) -> Result<(), SteamError> {
308        if !self.is_logged_in() {
309            return Err(SteamError::NotLoggedOn);
310        }
311
312        let msg = steam_protos::CChatRoomLeaveChatRoomGroupRequest { chat_group_id: Some(group_id.steam_id64()) };
313
314        self.send_service_method("ChatRoom.LeaveChatRoomGroup#1", &msg).await
315    }
316
317    /// Create an invite link for a chat room group.
318    ///
319    /// # Arguments
320    /// * `group_id` - The group ID
321    /// * `seconds_valid` - Duration in seconds the link is valid (default 3600)
322    /// * `voice_chat_id` - Optional voice chat ID to link directly to
323    pub async fn create_chat_room_invite_link(&mut self, group_id: SteamID, seconds_valid: Option<u32>, voice_chat_id: Option<String>) -> Result<String, SteamError> {
324        if !self.is_logged_in() {
325            return Err(SteamError::NotLoggedOn);
326        }
327
328        let msg = steam_protos::CChatRoomCreateInviteLinkRequest {
329            chat_group_id: Some(group_id.steam_id64()),
330            seconds_valid: Some(seconds_valid.unwrap_or(3600)),
331            chat_id: voice_chat_id.and_then(|id| id.parse().ok()),
332        };
333
334        let response: steam_protos::CChatRoomCreateInviteLinkResponse = self.send_service_method_and_wait("ChatRoom.CreateInviteLink#1", &msg).await?;
335
336        Ok(response.invite_code.unwrap_or_default())
337    }
338
339    /// Get all active invite links for a chat group.
340    ///
341    /// # Arguments
342    /// * `group_id` - The group ID
343    pub async fn get_group_invite_links(&mut self, group_id: SteamID) -> Result<Vec<InviteLinkInfo>, SteamError> {
344        if !self.is_logged_in() {
345            return Err(SteamError::NotLoggedOn);
346        }
347
348        let msg = steam_protos::CChatRoomGetInviteLinksForGroupRequest { chat_group_id: Some(group_id.steam_id64()) };
349
350        let response: steam_protos::CChatRoomGetInviteLinksForGroupResponse = self.send_service_method_and_wait("ChatRoom.GetInviteLinksForGroup#1", &msg).await?;
351
352        let links = response
353            .invite_links
354            .into_iter()
355            .map(|link| InviteLinkInfo {
356                invite_code: link.invite_code.unwrap_or_default(),
357                steamid_sender: SteamID::from_steam_id64(link.steamid_creator.unwrap_or(0)),
358                time_expires: link.time_expires,
359                group_summary: None,
360                banned: false,
361                time_kick_expire: None,
362                chat_id: link.chat_id.map(|id| id.to_string()),
363            })
364            .collect();
365
366        Ok(links)
367    }
368
369    /// Revoke and delete an active invite link.
370    ///
371    /// # Arguments
372    /// * `group_id` - The group ID
373    /// * `invite_code` - The invite code to delete
374    pub async fn delete_invite_link(&mut self, group_id: SteamID, invite_code: &str) -> Result<(), SteamError> {
375        if !self.is_logged_in() {
376            return Err(SteamError::NotLoggedOn);
377        }
378
379        let msg = steam_protos::CChatRoomDeleteInviteLinkRequest { chat_group_id: Some(group_id.steam_id64()), invite_code: Some(invite_code.to_string()) };
380
381        self.send_service_method("ChatRoom.DeleteInviteLink#1", &msg).await
382    }
383
384    /// Send a message to a chat room.
385    ///
386    /// # Arguments
387    /// * `group_id` - The group ID
388    /// * `chat_id` - The chat room ID within the group
389    /// * `message` - The message to send
390    pub async fn send_chat_room_message(&mut self, group_id: SteamID, chat_id: u64, message: &str) -> Result<(), SteamError> {
391        if !self.is_logged_in() {
392            return Err(SteamError::NotLoggedOn);
393        }
394
395        let msg = steam_protos::CChatRoomSendChatMessageRequest {
396            chat_group_id: Some(group_id.steam_id64()),
397            chat_id: Some(chat_id),
398            message: Some(message.to_string()),
399            ..Default::default()
400        };
401
402        self.send_service_method("ChatRoom.SendChatMessage#1", &msg).await
403    }
404
405    /// Create a text/voice chat room in a group.
406    ///
407    /// # Arguments
408    /// * `group_id` - The group ID
409    /// * `name` - Name of the new channel
410    /// * `allow_voice` - Whether to allow voice chat
411    pub async fn create_chat_room(&mut self, group_id: SteamID, name: &str, allow_voice: bool) -> Result<ChatRoom, SteamError> {
412        if !self.is_logged_in() {
413            return Err(SteamError::NotLoggedOn);
414        }
415
416        let msg = steam_protos::CChatRoomCreateChatRoomRequest { chat_group_id: Some(group_id.steam_id64()), name: Some(name.to_string()), allow_voice: Some(allow_voice) };
417
418        let response: steam_protos::CChatRoomCreateChatRoomResponse = self.send_service_method_and_wait("ChatRoom.CreateChatRoom#1", &msg).await?;
419
420        let room = response.chat_room.ok_or_else(|| SteamError::Other("No chat room returned".into()))?;
421
422        Ok(ChatRoom {
423            chat_id: room.chat_id.unwrap_or(0).to_string(),
424            chat_name: room.chat_name.unwrap_or_default(),
425            voice_allowed: room.voice_allowed.unwrap_or(false),
426            members_in_voice: room.members_in_voice.into_iter().map(SteamID::from_individual_account_id).collect(),
427            time_last_message: room.time_last_message.unwrap_or(0),
428            sort_order: room.sort_order.unwrap_or(0),
429            last_message: room.last_message.unwrap_or_default(),
430            steamid_last_message: room.accountid_last_message.map(SteamID::from_individual_account_id),
431        })
432    }
433
434    /// Rename a chat room in a group.
435    ///
436    /// # Arguments
437    /// * `group_id` - The group ID
438    /// * `chat_id` - The chat room ID
439    /// * `name` - The new name
440    pub async fn rename_chat_room(&mut self, group_id: SteamID, chat_id: u64, name: &str) -> Result<(), SteamError> {
441        if !self.is_logged_in() {
442            return Err(SteamError::NotLoggedOn);
443        }
444
445        let msg = steam_protos::CChatRoomRenameChatRoomRequest { chat_group_id: Some(group_id.steam_id64()), chat_id: Some(chat_id), name: Some(name.to_string()) };
446
447        self.send_service_method("ChatRoom.RenameChatRoom#1", &msg).await
448    }
449
450    /// Delete a chat room from a group.
451    ///
452    /// # Arguments
453    /// * `group_id` - The group ID
454    /// * `chat_id` - The chat room ID
455    pub async fn delete_chat_room(&mut self, group_id: SteamID, chat_id: u64) -> Result<(), SteamError> {
456        if !self.is_logged_in() {
457            return Err(SteamError::NotLoggedOn);
458        }
459
460        let msg = steam_protos::CChatRoomDeleteChatRoomRequest { chat_group_id: Some(group_id.steam_id64()), chat_id: Some(chat_id) };
461
462        self.send_service_method("ChatRoom.DeleteChatRoom#1", &msg).await
463    }
464
465    /// Get message history for a chat room.
466    ///
467    /// # Arguments
468    /// * `group_id` - The group ID
469    /// * `chat_id` - The chat room ID
470    /// * `last_time` - Get messages before this time (0 for most recent)
471    /// * `last_ordinal` - Get messages before this ordinal
472    /// * `max_count` - Maximum messages to return
473    pub async fn get_chat_room_message_history(&mut self, group_id: SteamID, chat_id: u64, last_time: u32, last_ordinal: u32, max_count: u32) -> Result<Vec<ChatRoomMessage>, SteamError> {
474        if !self.is_logged_in() {
475            return Err(SteamError::NotLoggedOn);
476        }
477
478        let msg = steam_protos::CChatRoomGetMessageHistoryRequest {
479            chat_group_id: Some(group_id.steam_id64()),
480            chat_id: Some(chat_id),
481            last_time: Some(last_time),
482            last_ordinal: Some(last_ordinal),
483            max_count: Some(max_count),
484            ..Default::default()
485        };
486
487        self.send_service_method("ChatRoom.GetMessageHistory#1", &msg).await?;
488
489        Ok(Vec::new())
490    }
491
492    /// Acknowledge (mark as read) a chat room message.
493    ///
494    /// # Arguments
495    /// * `group_id` - The group ID
496    /// * `chat_id` - The chat room ID
497    /// * `timestamp` - The timestamp of the newest message acknowledged
498    pub async fn ack_chat_message(&mut self, group_id: SteamID, chat_id: u64, timestamp: u32) -> Result<(), SteamError> {
499        if !self.is_logged_in() {
500            return Err(SteamError::NotLoggedOn);
501        }
502
503        let msg = steam_protos::CChatRoomAckChatMessageNotification { chat_group_id: Some(group_id.steam_id64()), chat_id: Some(chat_id), timestamp: Some(timestamp) };
504
505        self.send_service_method("ChatRoom.AckChatMessage#1", &msg).await
506    }
507
508    /// Delete messages from a chat room.
509    ///
510    /// # Arguments
511    /// * `group_id` - The group ID
512    /// * `chat_id` - The chat room ID
513    /// * `messages` - List of (server_timestamp, ordinal) tuples to delete
514    pub async fn delete_chat_messages(&mut self, group_id: SteamID, chat_id: u64, messages: Vec<(u32, u32)>) -> Result<(), SteamError> {
515        if !self.is_logged_in() {
516            return Err(SteamError::NotLoggedOn);
517        }
518
519        let proto_messages = messages.into_iter().map(|(ts, ord)| steam_protos::CChatRoomMessage { server_timestamp: Some(ts), ordinal: Some(ord) }).collect();
520
521        let msg = steam_protos::CChatRoomDeleteChatMessagesRequest { chat_group_id: Some(group_id.steam_id64()), chat_id: Some(chat_id), messages: proto_messages };
522
523        self.send_service_method("ChatRoom.DeleteChatMessages#1", &msg).await
524    }
525
526    /// Get the chat room group info for a clan (Steam group).
527    ///
528    /// # Arguments
529    /// * `clan_id` - The clan's SteamID
530    pub async fn get_clan_chat_group_info(&mut self, clan_id: SteamID) -> Result<ChatRoomGroup, SteamError> {
531        if !self.is_logged_in() {
532            return Err(SteamError::NotLoggedOn);
533        }
534
535        let msg = steam_protos::CClanChatRoomsGetClanChatRoomInfoRequest { steamid: Some(clan_id.steam_id64()), autocreate: Some(true) };
536
537        let response: steam_protos::CClanChatRoomsGetClanChatRoomInfoResponse = self.send_service_method_and_wait("ClanChatRooms.GetClanChatRoomInfo#1", &msg).await?;
538
539        // Basic conversion, full conversion would require more robust mapping
540        // returning empty group for now as place holder or partial data
541        let summary = response.chat_group_summary.unwrap_or_default();
542
543        Ok(ChatRoomGroup {
544            chat_group_id: summary.chat_group_id.unwrap_or(0).to_string(),
545            chat_group_name: summary.chat_group_name.unwrap_or_default(),
546            steamid_owner: SteamID::from_steam_id64(summary.steamid_owner.unwrap_or(0)),
547            tagline: summary.chat_group_tagline.unwrap_or_default(),
548            avatar_url: None, // Need to convert sha to url
549            appid: summary.appid,
550            default_chat_id: summary.default_chat_id.unwrap_or(0).to_string(),
551            active_member_count: summary.active_member_count.unwrap_or(0),
552            active_voice_member_count: summary.active_voice_member_count.unwrap_or(0),
553            chat_rooms: Vec::new(), // Would need to map summary.chat_rooms
554            members: Vec::new(),
555            roles: Vec::new(),
556        })
557    }
558
559    /// Kick a user from a chat room group.
560    ///
561    /// # Arguments
562    /// * `group_id` - The group ID
563    /// * `steamid` - The user to kick
564    /// * `expiry` - When the kick expires (Unix timestamp, None for permanent)
565    pub async fn kick_chat_room_member(&mut self, group_id: SteamID, steamid: SteamID, expiry: Option<u32>) -> Result<(), SteamError> {
566        if !self.is_logged_in() {
567            return Err(SteamError::NotLoggedOn);
568        }
569
570        let msg = steam_protos::CChatRoomKickUserRequest {
571            chat_group_id: Some(group_id.steam_id64()),
572            steamid: Some(steamid.steam_id64()),
573            expiration: expiry.map(|e| e as i32),
574        };
575
576        self.send_service_method("ChatRoom.KickUser#1", &msg).await
577    }
578
579    /// Ban a user from a chat room group.
580    ///
581    /// # Arguments
582    /// * `group_id` - The group ID
583    /// * `steamid` - The user to ban
584    /// * `expiry` - When the ban expires (Unix timestamp, None for permanent)
585    pub async fn ban_chat_room_member(&mut self, group_id: SteamID, steamid: SteamID, _expiry: Option<u32>) -> Result<(), SteamError> {
586        if !self.is_logged_in() {
587            return Err(SteamError::NotLoggedOn);
588        }
589
590        let msg = steam_protos::CChatRoomSetUserBanStateRequest { chat_group_id: Some(group_id.steam_id64()), steamid: Some(steamid.steam_id64()), ban_state: Some(true) };
591
592        self.send_service_method("ChatRoom.SetUserBanState#1", &msg).await
593    }
594
595    /// Unban a user from a chat room group.
596    ///
597    /// # Arguments
598    /// * `group_id` - The group ID
599    /// * `steamid` - The user to unban
600    pub async fn unban_chat_room_member(&mut self, group_id: SteamID, steamid: SteamID) -> Result<(), SteamError> {
601        if !self.is_logged_in() {
602            return Err(SteamError::NotLoggedOn);
603        }
604
605        let msg = steam_protos::CChatRoomSetUserBanStateRequest { chat_group_id: Some(group_id.steam_id64()), steamid: Some(steamid.steam_id64()), ban_state: Some(false) };
606
607        self.send_service_method("ChatRoom.SetUserBanState#1", &msg).await
608    }
609
610    /// Get the ban list for a chat room group.
611    ///
612    /// # Arguments
613    /// * `group_id` - The group ID
614    pub async fn get_group_ban_list(&mut self, group_id: SteamID) -> Result<Vec<ChatRoomBan>, SteamError> {
615        if !self.is_logged_in() {
616            return Err(SteamError::NotLoggedOn);
617        }
618
619        let msg = steam_protos::CChatRoomGetBanListRequest { chat_group_id: Some(group_id.steam_id64()) };
620
621        let response: steam_protos::CChatRoomGetBanListResponse = self.send_service_method_and_wait("ChatRoom.GetBanList#1", &msg).await?;
622
623        let bans = response
624            .bans
625            .into_iter()
626            .map(|ban| ChatRoomBan {
627                steamid: SteamID::from_steam_id64(ban.steamid.unwrap_or(0)),
628                steamid_actor: SteamID::from_steam_id64(ban.steamid_actor.unwrap_or(0)),
629                time_banned: ban.time_banned.unwrap_or(0),
630                ban_reason: ban.ban_reason.unwrap_or_default(),
631            })
632            .collect();
633
634        Ok(bans)
635    }
636
637    /// Add or remove a role to a group user.
638    ///
639    /// # Arguments
640    /// * `group_id` - The group ID
641    /// * `steamid` - The user SteamID
642    /// * `role_id` - The role ID
643    /// * `role_state` - True to add role, false to remove
644    pub async fn set_group_user_role_state(&mut self, group_id: SteamID, steamid: SteamID, role_id: &str, role_state: bool) -> Result<(), SteamError> {
645        if !self.is_logged_in() {
646            return Err(SteamError::NotLoggedOn);
647        }
648
649        if role_state {
650            let msg = steam_protos::CChatRoomAddRoleToUserRequest {
651                chat_group_id: Some(group_id.steam_id64()),
652                role_id: Some(role_id.parse().unwrap_or(0)),
653                steamid: Some(steamid.steam_id64()),
654            };
655            self.send_service_method("ChatRoom.AddRoleToUser#1", &msg).await
656        } else {
657            let msg = steam_protos::CChatRoomDeleteRoleFromUserRequest {
658                chat_group_id: Some(group_id.steam_id64()),
659                role_id: Some(role_id.parse().unwrap_or(0)),
660                steamid: Some(steamid.steam_id64()),
661            };
662            self.send_service_method("ChatRoom.DeleteRoleFromUser#1", &msg).await
663        }
664    }
665
666    /// Invite a user to a chat room group.
667    ///
668    /// # Arguments
669    /// * `group_id` - The group ID
670    /// * `steamid` - The user to invite
671    pub async fn invite_to_chat_room_group(&mut self, group_id: SteamID, steamid: SteamID) -> Result<(), SteamError> {
672        if !self.is_logged_in() {
673            return Err(SteamError::NotLoggedOn);
674        }
675
676        let msg = steam_protos::CChatRoomInviteFriendToChatRoomGroupRequest { chat_group_id: Some(group_id.steam_id64()), steamid: Some(steamid.steam_id64()), ..Default::default() };
677
678        self.send_service_method("ChatRoom.InviteFriendToChatRoomGroup#1", &msg).await
679    }
680}