1use std::collections::HashMap;
7
8use steamid::SteamID;
9
10use crate::{error::SteamError, SteamClient};
11
12#[derive(Debug, Clone)]
14pub struct ChatRoomGroup {
15 pub chat_group_id: String,
17 pub chat_group_name: String,
19 pub steamid_owner: SteamID,
21 pub tagline: String,
23 pub avatar_url: Option<String>,
25 pub appid: Option<u32>,
27 pub default_chat_id: String,
29 pub active_member_count: u32,
31 pub active_voice_member_count: u32,
33 pub chat_rooms: Vec<ChatRoom>,
35 pub members: Vec<ChatRoomMember>,
37 pub roles: Vec<ChatRole>,
39}
40
41#[derive(Debug, Clone)]
43pub struct ChatRoom {
44 pub chat_id: String,
46 pub chat_name: String,
48 pub voice_allowed: bool,
50 pub members_in_voice: Vec<SteamID>,
52 pub time_last_message: u32,
54 pub sort_order: u32,
56 pub last_message: String,
58 pub steamid_last_message: Option<SteamID>,
60}
61
62#[derive(Debug, Clone)]
64pub struct ChatRoomMember {
65 pub steamid: SteamID,
67 pub state: u32,
69 pub rank: u32,
71 pub time_kick_expire: Option<u32>,
73 pub role_ids: Vec<String>,
75}
76
77#[derive(Debug, Clone)]
79pub struct ChatRole {
80 pub role_id: String,
82 pub name: String,
84 pub ordinal: u32,
86}
87
88#[derive(Debug, Clone, Default)]
90pub struct RolePermissions {
91 pub can_create_rename_delete_channel: bool,
93 pub can_kick: bool,
95 pub can_ban: bool,
97 pub can_invite: bool,
99 pub can_change_tagline_avatar_name: bool,
101 pub can_chat: bool,
103 pub can_view_history: bool,
105 pub can_change_group_roles: bool,
107 pub can_change_user_roles: bool,
109 pub can_mention_all: bool,
111 pub can_set_watching_broadcast: bool,
113}
114
115#[derive(Debug, Clone)]
117pub struct ChatRoomMessage {
118 pub sender: SteamID,
120 pub message: String,
122 pub server_timestamp: u32,
124 pub ordinal: u32,
126 pub deleted: bool,
128}
129
130#[derive(Debug, Clone)]
132pub struct ChatRoomBan {
133 pub steamid: SteamID,
135 pub steamid_actor: SteamID,
137 pub time_banned: u32,
139 pub ban_reason: String,
141}
142
143#[derive(Debug, Clone)]
145pub struct InviteLinkInfo {
146 pub invite_code: String,
148 pub steamid_sender: SteamID,
150 pub time_expires: Option<u32>,
152 pub group_summary: Option<ChatRoomGroup>,
154 pub banned: bool,
156 pub time_kick_expire: Option<u32>,
158 pub chat_id: Option<String>,
160}
161
162impl SteamClient {
163 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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, 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(), members: Vec::new(),
555 roles: Vec::new(),
556 })
557 }
558
559 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 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 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 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 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 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}