Skip to main content

stoat/ext/
member.rs

1use async_trait::async_trait;
2use stoat_models::v0::{Channel, DataBanCreate, DataMemberEdit, Member, Role, ServerBan, UserVoiceState};
3
4use crate::{GlobalCache, HttpClient, Identifiable, Result, builders::EditMemberBuilder};
5
6#[async_trait]
7pub trait MemberExt {
8    /// Bans a member from its server.
9    async fn ban(
10        &self,
11        http: impl AsRef<HttpClient> + Send,
12        reason: Option<String>,
13    ) -> Result<ServerBan>;
14
15    /// Edits a member.
16    fn edit(&self, http: impl AsRef<HttpClient>) -> EditMemberBuilder;
17
18    /// Kicks a member from its server.
19    async fn kick(&self, http: impl AsRef<HttpClient> + Send) -> Result<()>;
20
21    /// Bans a member from its server.
22    async fn add_roles(
23        &self,
24        http: impl AsRef<HttpClient> + Send,
25        roles: &[Role],
26    ) -> Result<Member>;
27
28    /// Gets the current voice channel and user voice state for a member if they are connected to a voice channel in this server
29    fn voice(&self, cache: impl AsRef<GlobalCache>) -> Option<(Channel, UserVoiceState)>;
30
31    /// Formats a user mention for the member.
32    ///
33    fn mention(&self) -> String;
34}
35
36#[async_trait]
37impl MemberExt for Member {
38    async fn ban(
39        &self,
40        http: impl AsRef<HttpClient> + Send,
41        reason: Option<String>,
42    ) -> Result<ServerBan> {
43        http.as_ref()
44            .ban_member(&self.id.server, &self.id.user, &DataBanCreate { reason })
45            .await
46    }
47
48    fn edit(&self, http: impl AsRef<HttpClient>) -> EditMemberBuilder {
49        EditMemberBuilder::new(
50            http.as_ref().clone(),
51            self.id.server.clone(),
52            self.id.user.clone(),
53        )
54    }
55
56    async fn kick(&self, http: impl AsRef<HttpClient> + Send) -> Result<()> {
57        http.as_ref()
58            .kick_member(&self.id.server, &self.id.user)
59            .await
60    }
61
62    async fn add_roles(
63        &self,
64        http: impl AsRef<HttpClient> + Send,
65        roles: &[Role],
66    ) -> Result<Member> {
67        let mut new_roles = self.roles.clone();
68        new_roles.extend(roles.iter().map(|r| r.id.clone()));
69
70        http.as_ref()
71            .edit_member(
72                &self.id.server,
73                &self.id.user,
74                &DataMemberEdit {
75                    nickname: None,
76                    avatar: None,
77                    roles: Some(new_roles),
78                    timeout: None,
79                    can_publish: None,
80                    can_receive: None,
81                    voice_channel: None,
82                    remove: Vec::new(),
83                },
84            )
85            .await
86    }
87
88    fn voice(&self, cache: impl AsRef<GlobalCache>) -> Option<(Channel, UserVoiceState)> {
89        let server_channels = cache
90            .as_ref()
91            .servers
92            .get_sync(&self.id.server)
93            .map(|s| s.channels.clone())?;
94
95        for channel_id in server_channels {
96            if let Some(channel_voice_state) = cache.as_ref().voice_states.get_sync(&channel_id) {
97                if let Some(user_voice_state) = channel_voice_state
98                    .participants
99                    .iter()
100                    .find(|s| &s.id == &self.id.user)
101                {
102                    if let Some(channel) = cache.as_ref().get_channel(&channel_id) {
103                        return Some((channel, user_voice_state.clone()));
104                    }
105                }
106            }
107        }
108
109        None
110    }
111
112    fn mention(&self) -> String {
113        format!("<@{}>", &self.id.user)
114    }
115}
116
117impl Identifiable for Member {
118    fn id(&self) -> &str {
119        &self.id.user
120    }
121}