mutiny_rs/model/
channel.rs

1use crate::builders::create_message::CreateMessage;
2use crate::builders::edit_channel::EditChannel;
3use crate::builders::fetch_messages::FetchMessagesBuilder;
4use crate::context::Context;
5use crate::http::HttpError;
6use crate::model::message::Message;
7use serde::{Deserialize, Serialize};
8use std::fmt;
9use crate::error::Error;
10use crate::model::invite::Invite;
11
12/// A lightweight wrapper around a Channel ID string.
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
14#[serde(transparent)]
15pub struct ChannelId(pub String);
16
17impl ChannelId {
18    /// Creates a builder to send a message.
19    pub async fn send_message(&self, ctx: &Context, builder: CreateMessage) -> Result<Message, HttpError> {
20        builder.execute(&ctx.http, self).await
21    }
22
23    /// Creates a builder to fetch messages.
24    pub fn fetch_messages<'a>(&self, ctx: &'a Context) -> FetchMessagesBuilder<'a> {
25        FetchMessagesBuilder {
26            channel_id: self.0.clone(),
27            limit: None,
28            before: None,
29            after: None,
30            sort: None,
31            nearby: None,
32            include_users: None,
33            ctx,
34        }
35    }
36    /// Use this when you just want to check the name or type from RAM.
37    /// Returns Option because it might not be cached yet.
38    pub async fn get(&self, ctx: &Context) -> Option<Channel> {
39        ctx.cache.channels.get(&self.0).await
40    }
41    /// Use this when you need fresh data or the cache returned None.
42    /// Returns Result because the network might fail.
43    pub async fn fetch(&self, ctx: &Context, force: Option<bool>) -> Result<Channel, HttpError> {
44        if !force.unwrap_or(false) {
45            if let Some(channel) = ctx.cache.channels.get(&self.0).await {
46                return Ok(channel);
47            }
48        }
49        let url = format!("/channels/{}", self.0);
50        let channel = ctx.http.get::<Channel>(&url).await?;
51
52        ctx.cache.channels.insert(self.0.clone(), channel.clone()).await;
53
54        Ok(channel)
55    }
56    /// Depending on [Channel] this function does 3 things
57    ///
58    /// [TextChannel] tries to delete the channel,
59    /// [Group] leaves or closes a group
60    pub async fn delete(&self, ctx: &Context, leave_silent: Option<bool>) -> Result<(), HttpError> {
61        #[derive(Serialize)]
62        struct CloseQuery {
63            leave_silently: bool,
64        }
65        let url = format!("/channels/{}", self.0);
66        let query = CloseQuery {
67            leave_silently: leave_silent.unwrap_or(false),
68        };
69        ctx.http.delete_with_query(&url, Some(&query)).await
70    }
71    pub async fn edit(&self, ctx: &Context, builder: EditChannel) -> Result<Channel, HttpError> {
72        builder.execute(&ctx.http, self).await
73    }
74    /// Attempt to get the [Channel] using cache.
75    /// Returns [None] if channel is not cached, use [Self::fetch()]
76    /// to get a [Channel] object
77    pub async fn to_channel(&self, ctx: &Context) -> Option<Channel> {
78        ctx.cache.channels.get(&self.0).await
79    }
80    pub async fn create_invite(&self, ctx: &Context) -> Result<Invite, HttpError> {
81        let url = format!("/channels/{}/invites", self.0);
82        ctx.http.post(&url, &()).await
83    }
84}
85
86impl fmt::Display for ChannelId {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        write!(f, "{}", self.0)
89    }
90}
91
92impl From<&str> for ChannelId {
93    fn from(s: &str) -> Self {
94        Self(s.to_string())
95    }
96}
97impl From<String> for ChannelId {
98    fn from(s: String) -> Self {
99        Self(s)
100    }
101}
102
103#[derive(Debug, Clone, Deserialize, Serialize)]
104#[serde(tag = "channel_type")]
105pub enum ChannelKind {
106    SavedMessages(SavedMessages),
107    DirectMessage(DirectMessage),
108    Group(Group),
109    TextChannel(TextChannel),
110    VoiceChannel(VoiceChannel),
111
112    #[serde(other)]
113    Unknown,
114}
115#[derive(Debug, Clone, Deserialize, Serialize)]
116pub struct Channel {
117    #[serde(rename = "_id")]
118    pub id: ChannelId, // <--- ID IS NOW ALWAYS AVAILABLE
119
120    #[serde(flatten)]
121    pub kind: ChannelKind, // <--- The specific data lives here
122}
123impl Channel {
124    pub async fn send_message(&self, ctx: &Context, builder: CreateMessage) -> Result<Message, HttpError> {
125        self.id.send_message(ctx, builder).await
126    }
127    pub fn server_id(&self) -> Option<&str> {
128        match &self.kind {
129            ChannelKind::TextChannel(c) => Some(&c.server),
130
131            ChannelKind::VoiceChannel(c) => Some(&c.server),
132
133            _ => None,
134        }
135    }
136
137    pub fn name(&self) -> Option<&str> {
138        match &self.kind {
139            ChannelKind::TextChannel(c) => Some(&c.name),
140
141            ChannelKind::VoiceChannel(c) => Some(&c.name),
142
143            ChannelKind::Group(c) => Some(&c.name),
144
145            _ => None,
146        }
147    }
148    /// Get the channel as text
149    /// # Example
150    /// ```rust
151    /// if let Some(channel) = ctx.cache.channels.get(&message.channel.0).await {
152    ///     if let Some(text_channel) = channel.as_text() {
153    ///         println!("--- Text Channel: {} ---", text_channel.name);
154    ///     } else {
155    ///         println!("--- Not a Text Channel ---");
156    ///     }
157    /// }
158    /// ```
159    pub fn as_text(&self) -> Option<&TextChannel> {
160        match &self.kind {
161            ChannelKind::TextChannel(c) => Some(c), // 'c' is already a reference here
162            _ => None,
163        }
164    }
165
166    pub fn as_voice(&self) -> Option<&VoiceChannel> {
167        match &self.kind {
168            ChannelKind::VoiceChannel(c) => Some(c),
169            _ => None,
170        }
171    }
172
173    pub fn as_group(&self) -> Option<&Group> {
174        match &self.kind {
175            ChannelKind::Group(c) => Some(c),
176            _ => None,
177        }
178    }
179
180    pub fn as_dm(&self) -> Option<&DirectMessage> {
181        match &self.kind {
182            ChannelKind::DirectMessage(c) => Some(c),
183            _ => None,
184        }
185    }
186    pub async fn create_invite(&self, ctx: &Context) -> Result<Invite, Error> {
187        // Allow TextChannel OR Group
188        let is_allowed = matches!(
189            self.kind,
190            ChannelKind::TextChannel(_) | ChannelKind::Group(_)
191        );
192
193        if !is_allowed {
194            return Err(Error::InvalidChannelType(
195                "Invites can only be created for Text Channels or Groups".into()
196            ));
197        }
198
199        // Delegate to the ID logic (which sends the HTTP request)
200        self.id.create_invite(ctx).await.map_err(Error::from)
201    }
202}
203#[derive(Debug, Clone, Deserialize, Serialize)]
204pub struct SavedMessages {
205    pub user: String,
206}
207
208#[derive(Debug, Clone, Deserialize, Serialize)]
209pub struct DirectMessage {
210    pub active: bool,
211    pub recipients: Vec<String>,
212    pub last_message: Option<Message>,
213}
214
215#[derive(Debug, Clone, Deserialize, Serialize)]
216pub struct Group {
217    pub name: String,
218    pub owner: String,
219    pub recipients: Vec<String>,
220    pub permissions: Option<u64>,
221    pub nsfw: Option<bool>,
222}
223
224#[derive(Debug, Clone, Deserialize, Serialize)]
225pub struct TextChannel {
226    pub server: String,
227    pub name: String,
228    pub description: Option<String>,
229    pub last_message_id: Option<String>,
230    #[serde(default)]
231    pub nsfw: bool,
232}
233
234#[derive(Debug, Clone, Deserialize, Serialize)]
235pub struct VoiceChannel {
236    pub server: String,
237    pub name: String,
238    pub description: Option<String>,
239}