1use async_trait::async_trait;
2use bytes::Bytes;
3use stoat_models::v0::{
4 DataEditUser, FlagResponse, MutualResponse, User, UserProfile, UserVoiceState,
5};
6
7use crate::{FileExt, GlobalCache, HttpClient, Identifiable, Result, builders::SendMessageBuilder};
8
9#[async_trait]
10pub trait UserExt: Identifiable {
11 fn mention(&self) -> String;
12 fn name(&self) -> &str;
13
14 fn voice(&self, cache: impl AsRef<GlobalCache>) -> Vec<(String, UserVoiceState)>;
15
16 async fn send(&self, http: impl AsRef<HttpClient> + Send) -> Result<SendMessageBuilder>;
17 async fn edit(
18 &mut self,
19 http: impl AsRef<HttpClient> + Send,
20 data: &DataEditUser,
21 ) -> Result<()>;
22 async fn fetch_profile(&self, http: impl AsRef<HttpClient> + Send) -> Result<UserProfile>;
23 async fn fetch_flags(&self, http: impl AsRef<HttpClient> + Send) -> Result<FlagResponse>;
24 async fn fetch_mutuals(&self, http: impl AsRef<HttpClient> + Send) -> Result<MutualResponse>;
25 async fn fetch_default_avatar(&self, http: impl AsRef<HttpClient> + Send) -> Result<Bytes>;
26
27 fn avatar_url(&self, http: impl AsRef<HttpClient> + Send) -> String;
28 fn default_avatar_url(&self, http: impl AsRef<HttpClient> + Send) -> String;
29}
30
31#[async_trait]
32impl UserExt for User {
33 fn mention(&self) -> String {
34 format!("<@{}>", &self.id)
35 }
36
37 fn name(&self) -> &str {
38 self.display_name.as_deref().unwrap_or(&self.username)
39 }
40
41 fn voice(&self, cache: impl AsRef<GlobalCache>) -> Vec<(String, UserVoiceState)> {
42 let mut states = Vec::new();
43
44 cache.as_ref().servers.iter_sync(|_, server| {
45 for channel in &server.channels {
46 if let Some(channel_voice_state) = cache.as_ref().voice_states.get_sync(channel) {
47 if let Some(user_voice_state) = channel_voice_state
48 .participants
49 .iter()
50 .find(|s| &s.id == &self.id)
51 {
52 states.push((channel.clone(), user_voice_state.clone()));
53 }
54 }
55 }
56
57 true
58 });
59
60 states
61 }
62
63 async fn send(&self, http: impl AsRef<HttpClient> + Send) -> Result<SendMessageBuilder> {
64 let dm_channel = http.as_ref().open_dm(&self.id).await?;
65
66 Ok(SendMessageBuilder::new(
67 http.as_ref().clone(),
68 dm_channel.id().to_string(),
69 ))
70 }
71
72 async fn edit(
73 &mut self,
74 http: impl AsRef<HttpClient> + Send,
75 data: &DataEditUser,
76 ) -> Result<()> {
77 let user = http.as_ref().edit_user(&self.id, data).await?;
78
79 *self = user;
80
81 Ok(())
82 }
83
84 async fn fetch_profile(&self, http: impl AsRef<HttpClient> + Send) -> Result<UserProfile> {
85 http.as_ref().fetch_user_profile(&self.id).await
86 }
87
88 async fn fetch_flags(&self, http: impl AsRef<HttpClient> + Send) -> Result<FlagResponse> {
89 http.as_ref().fetch_user_flags(&self.id).await
90 }
91
92 async fn fetch_mutuals(&self, http: impl AsRef<HttpClient> + Send) -> Result<MutualResponse> {
93 http.as_ref().fetch_user_mutuals(&self.id).await
94 }
95
96 async fn fetch_default_avatar(&self, http: impl AsRef<HttpClient> + Send) -> Result<Bytes> {
97 http.as_ref().fetch_default_avatar(&self.id).await
98 }
99
100 fn avatar_url(&self, http: impl AsRef<HttpClient> + Send) -> String {
101 self.avatar
102 .as_ref()
103 .map(|file| file.url(http.as_ref(), false))
104 .unwrap_or_else(|| self.default_avatar_url(http.as_ref()))
105 }
106
107 fn default_avatar_url(&self, http: impl AsRef<HttpClient> + Send) -> String {
108 format!("{}/users/{}/default_avatar", &http.as_ref().base, &self.id)
109 }
110}
111
112impl Identifiable for User {
113 fn id(&self) -> &str {
114 &self.id
115 }
116}