Skip to main content

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