Skip to main content

serenity/model/channel/
message.rs

1//! Models relating to Discord channels.
2
3#[cfg(feature = "model")]
4use std::fmt::Display;
5#[cfg(all(feature = "cache", feature = "model"))]
6use std::fmt::Write;
7
8#[cfg(all(feature = "model", feature = "utils"))]
9use crate::builder::{Builder, CreateAllowedMentions, CreateMessage, EditMessage};
10#[cfg(all(feature = "cache", feature = "model"))]
11use crate::cache::{Cache, GuildRef};
12#[cfg(feature = "collector")]
13use crate::collector::{
14    ComponentInteractionCollector,
15    ModalInteractionCollector,
16    ReactionCollector,
17};
18#[cfg(feature = "model")]
19use crate::constants;
20#[cfg(feature = "collector")]
21use crate::gateway::ShardMessenger;
22#[cfg(feature = "model")]
23use crate::http::{CacheHttp, Http};
24use crate::model::prelude::*;
25use crate::model::utils::{deserialize_components, discord_colours, StrOrInt};
26#[cfg(all(feature = "model", feature = "cache"))]
27use crate::utils;
28
29/// A representation of a message over a guild's text channel, a group, or a private channel.
30///
31/// [Discord docs](https://discord.com/developers/docs/resources/channel#message-object) with some
32/// [extra fields](https://discord.com/developers/docs/topics/gateway-events#message-create-message-create-extra-fields).
33#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
34#[derive(Clone, Debug, Default, Deserialize, Serialize)]
35#[non_exhaustive]
36pub struct Message {
37    /// The unique Id of the message. Can be used to calculate the creation date of the message.
38    pub id: MessageId,
39    /// The Id of the [`Channel`] that the message was sent to.
40    pub channel_id: ChannelId,
41    /// The user that sent the message.
42    pub author: User,
43    /// The content of the message.
44    pub content: String,
45    /// Initial message creation timestamp, calculated from its Id.
46    pub timestamp: Timestamp,
47    /// The timestamp of the last time the message was updated, if it was.
48    pub edited_timestamp: Option<Timestamp>,
49    /// Indicator of whether the command is to be played back via text-to-speech.
50    ///
51    /// In the client, this is done via the `/tts` slash command.
52    pub tts: bool,
53    /// Indicator of whether the message mentions everyone.
54    pub mention_everyone: bool,
55    /// Array of users mentioned in the message.
56    pub mentions: Vec<User>,
57    /// Array of [`Role`]s' Ids mentioned in the message.
58    pub mention_roles: Vec<RoleId>,
59    /// Channels specifically mentioned in this message.
60    ///
61    /// **Note**: Not all channel mentions in a message will appear in [`Self::mention_channels`].
62    /// Only textual channels that are visible to everyone in a lurkable guild will ever be
63    /// included.
64    ///
65    /// A lurkable guild is one that allows users to read public channels in a server without
66    /// actually joining the server. It also allows users to look at these channels without being
67    /// logged in to Discord.
68    ///
69    /// Only crossposted messages (via Channel Following) currently include
70    /// [`Self::mention_channels`] at all. If no mentions in the message meet these requirements,
71    /// this field will not be sent.
72    ///
73    /// [Refer to Discord's documentation for more information][discord-docs].
74    ///
75    /// [discord-docs]: https://discord.com/developers/docs/resources/channel#message-object
76    #[serde(default = "Vec::new")]
77    pub mention_channels: Vec<ChannelMention>,
78    /// An vector of the files attached to a message.
79    pub attachments: Vec<Attachment>,
80    /// Array of embeds sent with the message.
81    pub embeds: Vec<Embed>,
82    /// Array of reactions performed on the message.
83    #[serde(default)]
84    pub reactions: Vec<MessageReaction>,
85    /// Non-repeating number used for ensuring message order.
86    #[serde(default)]
87    pub nonce: Option<Nonce>,
88    /// Indicator of whether the message is pinned.
89    pub pinned: bool,
90    /// The Id of the webhook that sent this message, if one did.
91    pub webhook_id: Option<WebhookId>,
92    /// Indicator of the type of message this is, i.e. whether it is a regular message or a system
93    /// message.
94    #[serde(rename = "type")]
95    pub kind: MessageType,
96    /// Sent with Rich Presence-related chat embeds.
97    pub activity: Option<MessageActivity>,
98    /// Sent with Rich Presence-related chat embeds.
99    pub application: Option<MessageApplication>,
100    /// If the message is an Interaction or application-owned webhook, this is the id of the
101    /// application.
102    pub application_id: Option<ApplicationId>,
103    /// Reference data sent with crossposted messages.
104    pub message_reference: Option<MessageReference>,
105    /// Bit flags describing extra features of the message.
106    pub flags: Option<MessageFlags>,
107    /// The message that was replied to using this message.
108    pub referenced_message: Option<Box<Message>>, // Boxed to avoid recursion
109    /// An array of message snapshots, known as forwarded messages.
110    #[serde(default, deserialize_with = "deserialize_snapshots")]
111    pub message_snapshots: Vec<MessageSnapshot>,
112    #[cfg_attr(not(ignore_serenity_deprecated), deprecated = "Use interaction_metadata")]
113    pub interaction: Option<Box<MessageInteraction>>,
114    /// Sent if the message is a response to an [`Interaction`].
115    ///
116    /// [`Interaction`]: crate::model::application::Interaction
117    pub interaction_metadata: Option<Box<MessageInteractionMetadata>>,
118    /// The thread that was started from this message, includes thread member object.
119    pub thread: Option<GuildChannel>,
120    /// The components of this message
121    #[serde(default, deserialize_with = "deserialize_components")]
122    pub components: Vec<ActionRow>,
123    /// Array of message sticker item objects.
124    #[serde(default)]
125    pub sticker_items: Vec<StickerItem>,
126    /// A generally increasing integer (there may be gaps or duplicates) that represents the
127    /// approximate position of the message in a thread, it can be used to estimate the relative
128    /// position of the message in a thread in company with total_message_sent on parent thread.
129    pub position: Option<u64>,
130    /// Data of the role subscription purchase or renewal that prompted this
131    /// [`MessageType::RoleSubscriptionPurchase`] message.
132    pub role_subscription_data: Option<RoleSubscriptionData>,
133    // Field omitted: stickers (it's deprecated by Discord)
134    /// The Id of the [`Guild`] that the message was sent in. This value will only be present if
135    /// this message was received over the gateway, therefore **do not use this to check if message
136    /// is in DMs**, it is not a reliable method.
137    // TODO: maybe introduce an `enum MessageLocation { Dm, Guild(GuildId) }` and store
138    // `Option<MessageLocation` here. Instead of None being ambiguous (is it in DMs? Or do we just
139    // not know because HTTP retrieved Messages don't have guild ID?), we'd set
140    // Some(MessageLocation::Dm) in gateway and None in HTTP.
141    pub guild_id: Option<GuildId>,
142    /// A partial amount of data about the user's member data
143    ///
144    /// Only present in [`MessageCreateEvent`].
145    pub member: Option<Box<PartialMember>>,
146    /// A poll that may be attached to a message.
147    ///
148    /// This is often omitted, so is boxed to improve memory usage.
149    ///
150    /// Only present in [`MessageCreateEvent`].
151    pub poll: Option<Box<Poll>>,
152}
153
154#[cfg(feature = "model")]
155impl Message {
156    /// Crossposts this message.
157    ///
158    /// Requires either to be the message author or to have manage [Manage Messages] permissions on
159    /// this channel.
160    ///
161    /// **Note**: Only available on news channels.
162    ///
163    /// # Errors
164    ///
165    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
166    /// does not have the required permissions.
167    ///
168    /// Returns a [`ModelError::MessageAlreadyCrossposted`] if the message has already been
169    /// crossposted.
170    ///
171    /// Returns a [`ModelError::CannotCrosspostMessage`] if the message cannot be crossposted.
172    ///
173    /// [Manage Messages]: Permissions::MANAGE_MESSAGES
174    pub async fn crosspost(&self, cache_http: impl CacheHttp) -> Result<Message> {
175        #[cfg(feature = "cache")]
176        {
177            if let Some(cache) = cache_http.cache() {
178                if self.author.id != cache.current_user().id && self.guild_id.is_some() {
179                    utils::user_has_perms_cache(
180                        cache,
181                        self.channel_id,
182                        Permissions::MANAGE_MESSAGES,
183                    )?;
184                }
185            }
186        }
187
188        if let Some(flags) = self.flags {
189            if flags.contains(MessageFlags::CROSSPOSTED) {
190                return Err(Error::Model(ModelError::MessageAlreadyCrossposted));
191            } else if flags.contains(MessageFlags::IS_CROSSPOST)
192                || self.kind != MessageType::Regular
193            {
194                return Err(Error::Model(ModelError::CannotCrosspostMessage));
195            }
196        }
197
198        self.channel_id.crosspost(cache_http.http(), self.id).await
199    }
200
201    /// First attempts to find a [`Channel`] by its Id in the cache, upon failure requests it via
202    /// the REST API.
203    ///
204    /// **Note**: If the `cache`-feature is enabled permissions will be checked and upon owning the
205    /// required permissions the HTTP-request will be issued.
206    ///
207    /// # Errors
208    ///
209    /// Can return an error if the HTTP request fails.
210    #[inline]
211    pub async fn channel(&self, cache_http: impl CacheHttp) -> Result<Channel> {
212        self.channel_id.to_channel(cache_http).await
213    }
214
215    /// A util function for determining whether this message was sent by someone else, or the bot.
216    #[cfg(feature = "cache")]
217    #[deprecated = "Check Message::author is equal to Cache::current_user"]
218    pub fn is_own(&self, cache: impl AsRef<Cache>) -> bool {
219        self.author.id == cache.as_ref().current_user().id
220    }
221
222    /// Calculates the permissions of the message author in the current channel.
223    ///
224    /// This handles the [`Permissions::SEND_MESSAGES_IN_THREADS`] permission for threads, setting
225    /// [`Permissions::SEND_MESSAGES`] accordingly if this message was sent in a thread.
226    ///
227    /// This may return `None` if:
228    /// - The [`Cache`] does not have the current [`Guild`]
229    /// - The [`Guild`] does not have the current channel cached (should never happen).
230    /// - This message is not from [`MessageCreateEvent`] and the author's [`Member`] cannot be
231    ///   found in [`Guild#structfield.members`].
232    #[cfg(feature = "cache")]
233    pub fn author_permissions(&self, cache: impl AsRef<Cache>) -> Option<Permissions> {
234        let Some(guild_id) = self.guild_id else {
235            return Some(Permissions::dm_permissions());
236        };
237
238        let guild = cache.as_ref().guild(guild_id)?;
239        let (channel, is_thread) = if let Some(channel) = guild.channels.get(&self.channel_id) {
240            (channel, false)
241        } else if let Some(thread) = guild.threads.iter().find(|th| th.id == self.channel_id) {
242            (thread, true)
243        } else {
244            return None;
245        };
246
247        let mut permissions = if let Some(member) = &self.member {
248            guild.partial_member_permissions_in(channel, self.author.id, member)
249        } else {
250            guild.user_permissions_in(channel, guild.members.get(&self.author.id)?)
251        };
252
253        if is_thread {
254            permissions.set(Permissions::SEND_MESSAGES, permissions.send_messages_in_threads());
255        }
256
257        Some(permissions)
258    }
259
260    /// Deletes the message.
261    ///
262    /// **Note**: The logged in user must either be the author of the message or have the [Manage
263    /// Messages] permission.
264    ///
265    /// # Errors
266    ///
267    /// If the `cache` feature is enabled, then returns a [`ModelError::InvalidPermissions`] if the
268    /// current user does not have the required permissions.
269    ///
270    /// [Manage Messages]: Permissions::MANAGE_MESSAGES
271    pub async fn delete(&self, cache_http: impl CacheHttp) -> Result<()> {
272        #[cfg(feature = "cache")]
273        {
274            if let Some(cache) = cache_http.cache() {
275                if self.author.id != cache.current_user().id {
276                    utils::user_has_perms_cache(
277                        cache,
278                        self.channel_id,
279                        Permissions::MANAGE_MESSAGES,
280                    )?;
281                }
282            }
283        }
284
285        self.channel_id.delete_message(cache_http.http(), self.id).await
286    }
287
288    /// Deletes all of the [`Reaction`]s associated with the message.
289    ///
290    /// **Note**: Requires the [Manage Messages] permission.
291    ///
292    /// # Errors
293    ///
294    /// If the `cache` feature is enabled, then returns a [`ModelError::InvalidPermissions`] if the
295    /// current user does not have the required permissions.
296    ///
297    /// [Manage Messages]: Permissions::MANAGE_MESSAGES
298    pub async fn delete_reactions(&self, cache_http: impl CacheHttp) -> Result<()> {
299        #[cfg(feature = "cache")]
300        {
301            if let Some(cache) = cache_http.cache() {
302                utils::user_has_perms_cache(cache, self.channel_id, Permissions::MANAGE_MESSAGES)?;
303            }
304        }
305
306        self.channel_id.delete_reactions(cache_http.http(), self.id).await
307    }
308
309    /// Deletes the given [`Reaction`] from the message.
310    ///
311    /// **Note**: Requires the [Manage Messages] permission, _if_ the current user did not perform
312    /// the reaction.
313    ///
314    /// # Errors
315    ///
316    /// Returns [`Error::Http`] if the current user did not perform the reaction, or lacks
317    /// permission.
318    ///
319    /// [Manage Messages]: Permissions::MANAGE_MESSAGES
320    #[inline]
321    pub async fn delete_reaction(
322        &self,
323        http: impl AsRef<Http>,
324        user_id: Option<UserId>,
325        reaction_type: impl Into<ReactionType>,
326    ) -> Result<()> {
327        self.channel_id.delete_reaction(http, self.id, user_id, reaction_type).await
328    }
329
330    /// Deletes all of the [`Reaction`]s of a given emoji associated with the message.
331    ///
332    /// **Note**: Requires the [Manage Messages] permission.
333    ///
334    /// # Errors
335    ///
336    /// If the `cache` feature is enabled, then returns a [`ModelError::InvalidPermissions`] if the
337    /// current user does not have the required permissions.
338    ///
339    /// [Manage Messages]: Permissions::MANAGE_MESSAGES
340    pub async fn delete_reaction_emoji(
341        &self,
342        cache_http: impl CacheHttp,
343        reaction_type: impl Into<ReactionType>,
344    ) -> Result<()> {
345        #[cfg(feature = "cache")]
346        {
347            if let Some(cache) = cache_http.cache() {
348                utils::user_has_perms_cache(cache, self.channel_id, Permissions::MANAGE_MESSAGES)?;
349            }
350        }
351
352        cache_http
353            .http()
354            .as_ref()
355            .delete_message_reaction_emoji(self.channel_id, self.id, &reaction_type.into())
356            .await
357    }
358
359    /// Edits this message, replacing the original content with new content.
360    ///
361    /// Message editing preserves all unchanged message data, with some exceptions for embeds and
362    /// attachments.
363    ///
364    /// **Note**: In most cases requires that the current user be the author of the message.
365    ///
366    /// Refer to the documentation for [`EditMessage`] for information regarding content
367    /// restrictions and requirements.
368    ///
369    /// # Examples
370    ///
371    /// Edit a message with new content:
372    ///
373    /// ```rust,no_run
374    /// # use serenity::builder::EditMessage;
375    /// # use serenity::model::channel::Message;
376    /// # use serenity::model::id::ChannelId;
377    /// # use serenity::http::Http;
378    /// #
379    /// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
380    /// # let http: Http = unimplemented!();
381    /// # let mut message: Message = unimplemented!();
382    /// // assuming a `message` has already been bound
383    /// let builder = EditMessage::new().content("new content");
384    /// message.edit(&http, builder).await?;
385    /// # Ok(())
386    /// # }
387    /// ```
388    ///
389    /// # Errors
390    ///
391    /// If the `cache` is enabled, returns a [`ModelError::InvalidUser`] if the current user is not
392    /// the author. Otherwise returns [`Error::Http`] if the user lacks permission, as well as if
393    /// invalid data is given.
394    ///
395    /// Returns a [`ModelError::MessageTooLong`] if the message contents are too long.
396    ///
397    /// [Manage Messages]: Permissions::MANAGE_MESSAGES
398    pub async fn edit(&mut self, cache_http: impl CacheHttp, builder: EditMessage) -> Result<()> {
399        if let Some(flags) = self.flags {
400            if flags.contains(MessageFlags::IS_VOICE_MESSAGE) {
401                return Err(Error::Model(ModelError::CannotEditVoiceMessage));
402            }
403        }
404
405        *self =
406            builder.execute(cache_http, (self.channel_id, self.id, Some(self.author.id))).await?;
407        Ok(())
408    }
409
410    /// Returns message content, but with user and role mentions replaced with
411    /// names and everyone/here mentions cancelled.
412    #[cfg(feature = "cache")]
413    pub fn content_safe(&self, cache: impl AsRef<Cache>) -> String {
414        let mut result = self.content.clone();
415
416        // First replace all user mentions.
417        for u in &self.mentions {
418            let mut at_distinct = String::with_capacity(38);
419            at_distinct.push('@');
420            at_distinct.push_str(&u.name);
421            if let Some(discriminator) = u.discriminator {
422                at_distinct.push('#');
423                write!(at_distinct, "{:04}", discriminator.get())
424                    .expect("writing to a string should never fail");
425            }
426
427            let mut m = u.mention().to_string();
428            // Check whether we're replacing a nickname mention or a normal mention.
429            // `UserId::mention` returns a normal mention. If it isn't present in the message, it's
430            // a nickname mention.
431            if !result.contains(&m) {
432                m.insert(2, '!');
433            }
434
435            result = result.replace(&m, &at_distinct);
436        }
437
438        // Then replace all role mentions.
439        if let Some(guild_id) = self.guild_id {
440            for id in &self.mention_roles {
441                let mention = id.mention().to_string();
442
443                if let Some(guild) = cache.as_ref().guild(guild_id) {
444                    if let Some(role) = guild.roles.get(id) {
445                        result = result.replace(&mention, &format!("@{}", role.name));
446                        continue;
447                    }
448                }
449
450                result = result.replace(&mention, "@deleted-role");
451            }
452        }
453
454        // And finally replace everyone and here mentions.
455        result.replace("@everyone", "@\u{200B}everyone").replace("@here", "@\u{200B}here")
456    }
457
458    /// Gets the list of [`User`]s who have reacted to a [`Message`] with a certain [`Emoji`].
459    ///
460    /// The default `limit` is `50` - specify otherwise to receive a different maximum number of
461    /// users. The maximum that may be retrieve at a time is `100`, if a greater number is provided
462    /// then it is automatically reduced.
463    ///
464    /// The optional `after` attribute is to retrieve the users after a certain user. This is
465    /// useful for pagination.
466    ///
467    /// **Note**: Requires the [Read Message History] permission.
468    ///
469    /// **Note**: If the passed reaction_type is a custom guild emoji, it must contain the name.
470    /// So, [`Emoji`] or [`EmojiIdentifier`] will always work, [`ReactionType`] only if
471    /// [`ReactionType::Custom::name`] is Some, and **[`EmojiId`] will never work**.
472    ///
473    /// # Errors
474    ///
475    /// Returns [`Error::Http`] if the current user lacks permission.
476    ///
477    /// [Read Message History]: Permissions::READ_MESSAGE_HISTORY
478    #[inline]
479    pub async fn reaction_users(
480        &self,
481        http: impl AsRef<Http>,
482        reaction_type: impl Into<ReactionType>,
483        limit: Option<u8>,
484        after: impl Into<Option<UserId>>,
485    ) -> Result<Vec<User>> {
486        self.channel_id.reaction_users(http, self.id, reaction_type, limit, after).await
487    }
488
489    /// Returns the associated [`Guild`] for the message if one is in the cache.
490    ///
491    /// Returns [`None`] if the guild's Id could not be found via [`Self::guild_id`] or if the
492    /// Guild itself is not cached.
493    ///
494    /// Requires the `cache` feature be enabled.
495    #[cfg(feature = "cache")]
496    pub fn guild<'a>(&self, cache: &'a Cache) -> Option<GuildRef<'a>> {
497        cache.guild(self.guild_id?)
498    }
499
500    /// True if message was sent using direct messages.
501    ///
502    /// **Only use this for messages from the gateway (event handler)!** Not for returned Message
503    /// objects from HTTP requests, like [`ChannelId::send_message`], because [`Self::guild_id`] is
504    /// never set for those, which this method relies on.
505    #[inline]
506    #[must_use]
507    #[deprecated = "Check if guild_id is None if the message is received from the gateway."]
508    pub fn is_private(&self) -> bool {
509        self.guild_id.is_none()
510    }
511
512    /// Retrieves a clone of the author's Member instance, if this message was sent in a guild.
513    ///
514    /// If the instance cannot be found in the cache, or the `cache` feature is disabled, a HTTP
515    /// request is performed to retrieve it from Discord's API.
516    ///
517    /// # Errors
518    ///
519    /// [`ModelError::ItemMissing`] is returned if [`Self::guild_id`] is [`None`].
520    pub async fn member(&self, cache_http: impl CacheHttp) -> Result<Member> {
521        match self.guild_id {
522            Some(guild_id) => guild_id.member(cache_http, self.author.id).await,
523            None => Err(Error::Model(ModelError::ItemMissing)),
524        }
525    }
526
527    /// Checks the length of a message to ensure that it is within Discord's maximum length limit.
528    ///
529    /// Returns [`None`] if the message is within the limit, otherwise returns [`Some`] with an
530    /// inner value of how many unicode code points the message is over.
531    #[must_use]
532    pub fn overflow_length(content: &str) -> Option<usize> {
533        crate::builder::check_overflow(content.chars().count(), constants::MESSAGE_CODE_LIMIT).err()
534    }
535
536    /// Pins this message to its channel.
537    ///
538    /// **Note**: Requires the [Manage Messages] permission.
539    ///
540    /// # Errors
541    ///
542    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
543    /// does not have the required permissions.
544    ///
545    /// [Manage Messages]: Permissions::MANAGE_MESSAGES
546    pub async fn pin(&self, cache_http: impl CacheHttp) -> Result<()> {
547        #[cfg(feature = "cache")]
548        {
549            if let Some(cache) = cache_http.cache() {
550                if self.guild_id.is_some() {
551                    utils::user_has_perms_cache(
552                        cache,
553                        self.channel_id,
554                        Permissions::MANAGE_MESSAGES,
555                    )?;
556                }
557            }
558        }
559
560        self.channel_id.pin(cache_http.http(), self.id).await
561    }
562
563    /// React to the message with a custom [`Emoji`] or unicode character.
564    ///
565    /// **Note**: Requires the [Add Reactions] permission.
566    ///
567    /// # Errors
568    ///
569    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
570    /// does not have the required [permissions].
571    ///
572    /// [Add Reactions]: Permissions::ADD_REACTIONS
573    /// [permissions]: crate::model::permissions
574    #[inline]
575    pub async fn react(
576        &self,
577        cache_http: impl CacheHttp,
578        reaction_type: impl Into<ReactionType>,
579    ) -> Result<Reaction> {
580        self.react_(cache_http, reaction_type.into()).await
581    }
582
583    async fn react_(
584        &self,
585        cache_http: impl CacheHttp,
586        reaction_type: ReactionType,
587    ) -> Result<Reaction> {
588        #[cfg_attr(not(feature = "cache"), allow(unused_mut))]
589        let mut user_id = None;
590
591        #[cfg(feature = "cache")]
592        {
593            if let Some(cache) = cache_http.cache() {
594                if self.guild_id.is_some() {
595                    utils::user_has_perms_cache(
596                        cache,
597                        self.channel_id,
598                        Permissions::ADD_REACTIONS,
599                    )?;
600                }
601
602                user_id = Some(cache.current_user().id);
603            }
604        }
605
606        cache_http.http().create_reaction(self.channel_id, self.id, &reaction_type).await?;
607
608        Ok(Reaction {
609            channel_id: self.channel_id,
610            emoji: reaction_type,
611            message_id: self.id,
612            user_id,
613            guild_id: self.guild_id,
614            member: self.member.as_deref().map(|member| member.clone().into()),
615            message_author_id: None,
616            burst: false,
617            burst_colours: None,
618            reaction_type: ReactionTypes::Normal,
619        })
620    }
621
622    /// Uses Discord's inline reply to a user without pinging them.
623    ///
624    /// User mentions are generally around 20 or 21 characters long.
625    ///
626    /// **Note**: Requires the [Send Messages] permission.
627    ///
628    /// **Note**: Message contents must be under 2000 unicode code points.
629    ///
630    /// # Errors
631    ///
632    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
633    /// does not have the required permissions.
634    ///
635    /// Returns a [`ModelError::MessageTooLong`] if the content of the message is over the above
636    /// limit, containing the number of unicode code points over the limit.
637    ///
638    /// [Send Messages]: Permissions::SEND_MESSAGES
639    #[inline]
640    pub async fn reply(
641        &self,
642        cache_http: impl CacheHttp,
643        content: impl Into<String>,
644    ) -> Result<Message> {
645        self.reply_(cache_http, content, Some(false)).await
646    }
647
648    /// Uses Discord's inline reply to a user with a ping.
649    ///
650    /// **Note**: Requires the [Send Messages] permission.
651    ///
652    /// **Note**: Message contents must be under 2000 unicode code points.
653    ///
654    /// # Errors
655    ///
656    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
657    /// does not have the required permissions.
658    ///
659    /// Returns a [`ModelError::MessageTooLong`] if the content of the message is over the above
660    /// limit, containing the number of unicode code points over the limit.
661    ///
662    /// [Send Messages]: Permissions::SEND_MESSAGES
663    #[inline]
664    pub async fn reply_ping(
665        &self,
666        cache_http: impl CacheHttp,
667        content: impl Into<String>,
668    ) -> Result<Message> {
669        self.reply_(cache_http, content, Some(true)).await
670    }
671
672    /// Replies to the user, mentioning them prior to the content in the form of: `@<USER_ID>
673    /// YOUR_CONTENT`.
674    ///
675    /// User mentions are generally around 20 or 21 characters long.
676    ///
677    /// **Note**: Requires the [Send Messages] permission.
678    ///
679    /// **Note**: Message contents must be under 2000 unicode code points.
680    ///
681    /// # Errors
682    ///
683    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
684    /// does not have the required permissions.
685    ///
686    /// Returns a [`ModelError::MessageTooLong`] if the content of the message is over the above
687    /// limit, containing the number of unicode code points over the limit.
688    ///
689    /// [Send Messages]: Permissions::SEND_MESSAGES
690    #[inline]
691    pub async fn reply_mention(
692        &self,
693        cache_http: impl CacheHttp,
694        content: impl Display,
695    ) -> Result<Message> {
696        self.reply_(cache_http, format!("{} {content}", self.author.mention()), None).await
697    }
698
699    /// `inlined` decides whether this reply is inlined and whether it pings.
700    async fn reply_(
701        &self,
702        cache_http: impl CacheHttp,
703        content: impl Into<String>,
704        inlined: Option<bool>,
705    ) -> Result<Message> {
706        #[cfg(feature = "cache")]
707        {
708            if let Some(cache) = cache_http.cache() {
709                if self.guild_id.is_some() {
710                    utils::user_has_perms_cache(
711                        cache,
712                        self.channel_id,
713                        Permissions::SEND_MESSAGES,
714                    )?;
715                }
716            }
717        }
718
719        let mut builder = CreateMessage::new().content(content);
720        if let Some(ping_user) = inlined {
721            let allowed_mentions = CreateAllowedMentions::new()
722                .replied_user(ping_user)
723                // By providing allowed_mentions, Discord disabled _all_ pings by default so we
724                // need to re-enable them
725                .everyone(true)
726                .all_users(true)
727                .all_roles(true);
728            builder = builder.reference_message(self).allowed_mentions(allowed_mentions);
729        }
730        self.channel_id.send_message(cache_http, builder).await
731    }
732
733    /// Checks whether the message mentions passed [`UserId`].
734    #[inline]
735    pub fn mentions_user_id(&self, id: impl Into<UserId>) -> bool {
736        let id = id.into();
737        self.mentions.iter().any(|mentioned_user| mentioned_user.id == id)
738    }
739
740    /// Checks whether the message mentions passed [`User`].
741    #[inline]
742    #[must_use]
743    pub fn mentions_user(&self, user: &User) -> bool {
744        self.mentions_user_id(user.id)
745    }
746
747    /// Checks whether the message mentions the current user.
748    ///
749    /// # Errors
750    ///
751    /// May return [`Error::Http`] if the `cache` feature is not enabled, or if the cache is
752    /// otherwise unavailable.
753    pub async fn mentions_me(&self, cache_http: impl CacheHttp) -> Result<bool> {
754        #[cfg(feature = "cache")]
755        {
756            if let Some(cache) = cache_http.cache() {
757                return Ok(self.mentions_user_id(cache.current_user().id));
758            }
759        }
760
761        let current_user = cache_http.http().get_current_user().await?;
762        Ok(self.mentions_user_id(current_user.id))
763    }
764
765    /// Unpins the message from its channel.
766    ///
767    /// **Note**: Requires the [Manage Messages] permission.
768    ///
769    /// # Errors
770    ///
771    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`] if the current user
772    /// does not have the required permissions.
773    ///
774    /// [Manage Messages]: Permissions::MANAGE_MESSAGES
775    pub async fn unpin(&self, cache_http: impl CacheHttp) -> Result<()> {
776        #[cfg(feature = "cache")]
777        {
778            if let Some(cache) = cache_http.cache() {
779                if self.guild_id.is_some() {
780                    utils::user_has_perms_cache(
781                        cache,
782                        self.channel_id,
783                        Permissions::MANAGE_MESSAGES,
784                    )?;
785                }
786            }
787        }
788
789        cache_http.http().unpin_message(self.channel_id, self.id, None).await
790    }
791
792    /// Ends the [`Poll`] on this message, if there is one.
793    ///
794    /// # Errors
795    ///
796    /// See [`ChannelId::end_poll`] for more information.
797    pub async fn end_poll(&self, http: impl AsRef<Http>) -> Result<Self> {
798        self.channel_id.end_poll(http, self.id).await
799    }
800
801    /// Tries to return author's nickname in the current channel's guild.
802    ///
803    /// Refer to [`User::nick_in()`] inside and [`None`] outside of a guild.
804    #[inline]
805    pub async fn author_nick(&self, cache_http: impl CacheHttp) -> Option<String> {
806        self.author.nick_in(cache_http, self.guild_id?).await
807    }
808
809    /// Returns a link referencing this message. When clicked, users will jump to the message. The
810    /// link will be valid for messages in either private channels or guilds.
811    #[inline]
812    #[must_use]
813    pub fn link(&self) -> String {
814        self.id.link(self.channel_id, self.guild_id)
815    }
816
817    /// Same as [`Self::link`] but tries to find the [`GuildId`] if Discord does not provide it.
818    ///
819    /// [`guild_id`]: Self::guild_id
820    #[inline]
821    #[allow(deprecated)]
822    #[deprecated = "Use Self::link if Message was recieved via an event, otherwise use MessageId::link to provide the guild_id yourself."]
823    pub async fn link_ensured(&self, cache_http: impl CacheHttp) -> String {
824        self.id.link_ensured(cache_http, self.channel_id, self.guild_id).await
825    }
826
827    /// Returns a builder which can be awaited to obtain a reaction or stream of reactions on this
828    /// message.
829    #[cfg(feature = "collector")]
830    pub fn await_reaction(&self, shard_messenger: impl AsRef<ShardMessenger>) -> ReactionCollector {
831        ReactionCollector::new(shard_messenger).message_id(self.id)
832    }
833
834    /// Same as [`Self::await_reaction`].
835    #[cfg(feature = "collector")]
836    pub fn await_reactions(
837        &self,
838        shard_messenger: impl AsRef<ShardMessenger>,
839    ) -> ReactionCollector {
840        self.await_reaction(shard_messenger)
841    }
842
843    /// Returns a builder which can be awaited to obtain a single component interactions or a
844    /// stream of component interactions on this message.
845    #[cfg(feature = "collector")]
846    pub fn await_component_interaction(
847        &self,
848        shard_messenger: impl AsRef<ShardMessenger>,
849    ) -> ComponentInteractionCollector {
850        ComponentInteractionCollector::new(shard_messenger).message_id(self.id)
851    }
852
853    /// Same as [`Self::await_component_interaction`].
854    #[cfg(feature = "collector")]
855    pub fn await_component_interactions(
856        &self,
857        shard_messenger: impl AsRef<ShardMessenger>,
858    ) -> ComponentInteractionCollector {
859        self.await_component_interaction(shard_messenger)
860    }
861
862    /// Returns a builder which can be awaited to obtain a model submit interaction or stream of
863    /// modal submit interactions on this message.
864    #[cfg(feature = "collector")]
865    pub fn await_modal_interaction(
866        &self,
867        shard_messenger: impl AsRef<ShardMessenger>,
868    ) -> ModalInteractionCollector {
869        ModalInteractionCollector::new(shard_messenger).message_id(self.id)
870    }
871
872    /// Same as [`Self::await_modal_interaction`].
873    #[cfg(feature = "collector")]
874    pub fn await_modal_interactions(
875        &self,
876        shard_messenger: impl AsRef<ShardMessenger>,
877    ) -> ModalInteractionCollector {
878        self.await_modal_interaction(shard_messenger)
879    }
880
881    /// Retrieves the message channel's category ID if the channel has one.
882    pub async fn category_id(&self, cache_http: impl CacheHttp) -> Option<ChannelId> {
883        #[cfg(feature = "cache")]
884        if let Some(cache) = cache_http.cache() {
885            if let Some(guild) = cache.guild(self.guild_id?) {
886                let channel = guild.channels.get(&self.channel_id)?;
887                return if channel.thread_metadata.is_some() {
888                    let thread_parent = guild.channels.get(&channel.parent_id?)?;
889                    thread_parent.parent_id
890                } else {
891                    channel.parent_id
892                };
893            }
894        }
895
896        let channel = self.channel_id.to_channel(&cache_http).await.ok()?.guild()?;
897        if channel.thread_metadata.is_some() {
898            let thread_parent = channel.parent_id?.to_channel(cache_http).await.ok()?.guild()?;
899            thread_parent.parent_id
900        } else {
901            channel.parent_id
902        }
903    }
904}
905
906impl AsRef<MessageId> for Message {
907    fn as_ref(&self) -> &MessageId {
908        &self.id
909    }
910}
911
912impl From<Message> for MessageId {
913    /// Gets the Id of a [`Message`].
914    fn from(message: Message) -> MessageId {
915        message.id
916    }
917}
918
919impl From<&Message> for MessageId {
920    /// Gets the Id of a [`Message`].
921    fn from(message: &Message) -> MessageId {
922        message.id
923    }
924}
925
926/// A representation of a reaction to a message.
927///
928/// Multiple of the same [reaction type] are sent into one [`MessageReaction`], with an associated
929/// [`Self::count`].
930///
931/// [Discord docs](https://discord.com/developers/docs/resources/channel#reaction-object).
932///
933/// [reaction type]: ReactionType
934#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
935#[derive(Clone, Debug, Deserialize, Serialize)]
936#[non_exhaustive]
937pub struct MessageReaction {
938    /// The amount of the type of reaction that have been sent for the associated message
939    /// including super reactions.
940    pub count: u64,
941    /// A breakdown of what reactions were from regular reactions and super reactions.
942    pub count_details: CountDetails,
943    /// Indicator of whether the current user has sent this type of reaction.
944    pub me: bool,
945    /// Indicator of whether the current user has sent the type of super-reaction.
946    pub me_burst: bool,
947    /// The type of reaction.
948    #[serde(rename = "emoji")]
949    pub reaction_type: ReactionType,
950    // The colours used for super reactions.
951    #[serde(rename = "burst_colors", deserialize_with = "discord_colours")]
952    pub burst_colours: Vec<Colour>,
953}
954
955/// A representation of reaction count details.
956///
957/// [Discord docs](https://discord.com/developers/docs/resources/channel#reaction-count-details-object).
958#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
959#[derive(Clone, Debug, Deserialize, Serialize)]
960#[non_exhaustive]
961pub struct CountDetails {
962    pub burst: u64,
963    pub normal: u64,
964}
965
966enum_number! {
967    /// Differentiates between regular and different types of system messages.
968    ///
969    /// [Discord docs](https://discord.com/developers/docs/resources/channel#message-object-message-types).
970    #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
971    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
972    #[serde(from = "u8", into = "u8")]
973    #[non_exhaustive]
974    pub enum MessageType {
975        /// A regular message.
976        #[default]
977        Regular = 0,
978        /// An indicator that a recipient was added by the author.
979        GroupRecipientAddition = 1,
980        /// An indicator that a recipient was removed by the author.
981        GroupRecipientRemoval = 2,
982        /// An indicator that a call was started by the author.
983        GroupCallCreation = 3,
984        /// An indicator that the group name was modified by the author.
985        GroupNameUpdate = 4,
986        /// An indicator that the group icon was modified by the author.
987        GroupIconUpdate = 5,
988        /// An indicator that a message was pinned by the author.
989        PinsAdd = 6,
990        /// An indicator that a member joined the guild.
991        MemberJoin = 7,
992        /// An indicator that someone has boosted the guild.
993        NitroBoost = 8,
994        /// An indicator that the guild has reached nitro tier 1
995        NitroTier1 = 9,
996        /// An indicator that the guild has reached nitro tier 2
997        NitroTier2 = 10,
998        /// An indicator that the guild has reached nitro tier 3
999        NitroTier3 = 11,
1000        /// An indicator that the channel is now following a news channel
1001        ChannelFollowAdd = 12,
1002        /// An indicator that the guild is disqualified for Discovery Feature
1003        GuildDiscoveryDisqualified = 14,
1004        /// An indicator that the guild is requalified for Discovery Feature
1005        GuildDiscoveryRequalified = 15,
1006        /// The first warning before guild discovery removal.
1007        GuildDiscoveryGracePeriodInitialWarning = 16,
1008        /// The last warning before guild discovery removal.
1009        GuildDiscoveryGracePeriodFinalWarning = 17,
1010        /// Message sent to inform users that a thread was created.
1011        ThreadCreated = 18,
1012        /// A message reply.
1013        InlineReply = 19,
1014        /// A slash command.
1015        ChatInputCommand = 20,
1016        /// A thread start message.
1017        ThreadStarterMessage = 21,
1018        /// Server setup tips.
1019        GuildInviteReminder = 22,
1020        /// A context menu command.
1021        ContextMenuCommand = 23,
1022        /// A message from an auto moderation action.
1023        AutoModAction = 24,
1024        RoleSubscriptionPurchase = 25,
1025        InteractionPremiumUpsell = 26,
1026        StageStart = 27,
1027        StageEnd = 28,
1028        StageSpeaker = 29,
1029        StageTopic = 31,
1030        GuildApplicationPremiumSubscription = 32,
1031        GuildIncidentAlertModeEnabled = 36,
1032        GuildIncidentAlertModeDisabled = 37,
1033        GuildIncidentReportRaid = 38,
1034        GuildIncidentReportFalseAlarm = 39,
1035        PurchaseNotification = 44,
1036        _ => Unknown(u8),
1037    }
1038}
1039
1040enum_number! {
1041    /// [Discord docs](https://discord.com/developers/docs/resources/channel#message-object-message-activity-types).
1042    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
1043    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1044    #[serde(from = "u8", into = "u8")]
1045    #[non_exhaustive]
1046    pub enum MessageActivityKind {
1047        Join = 1,
1048        Spectate = 2,
1049        Listen = 3,
1050        JoinRequest = 5,
1051        _ => Unknown(u8),
1052    }
1053}
1054
1055/// Rich Presence application information.
1056///
1057/// [Discord docs](https://discord.com/developers/docs/resources/application#application-object),
1058/// [subset undocumented](https://discord.com/developers/docs/resources/channel#message-object-message-structure).
1059#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1060#[derive(Clone, Debug, Deserialize, Serialize)]
1061#[non_exhaustive]
1062pub struct MessageApplication {
1063    /// ID of the application.
1064    pub id: ApplicationId,
1065    /// ID of the embed's image asset.
1066    pub cover_image: Option<ImageHash>,
1067    /// Application's description.
1068    pub description: String,
1069    /// ID of the application's icon.
1070    pub icon: Option<ImageHash>,
1071    /// Name of the application.
1072    pub name: String,
1073}
1074
1075/// Rich Presence activity information.
1076///
1077/// [Discord docs](https://discord.com/developers/docs/resources/channel#message-object-message-activity-structure).
1078#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1079#[derive(Clone, Debug, Deserialize, Serialize)]
1080#[non_exhaustive]
1081pub struct MessageActivity {
1082    /// Kind of message activity.
1083    #[serde(rename = "type")]
1084    pub kind: MessageActivityKind,
1085    /// `party_id` from a Rich Presence event.
1086    pub party_id: Option<String>,
1087}
1088
1089enum_number! {
1090    /// Message Reference Type information
1091    ///
1092    /// [Discord docs](https://discord.com/developers/docs/resources/message#message-reference-types)
1093    #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
1094    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1095    #[serde(from = "u8", into = "u8")]
1096    #[non_exhaustive]
1097    pub enum MessageReferenceKind {
1098        #[default]
1099        Default = 0,
1100        Forward = 1,
1101        _ => Unknown(u8),
1102    }
1103}
1104
1105/// Reference data sent with crossposted messages.
1106///
1107/// [Discord docs](https://discord.com/developers/docs/resources/channel#message-reference-object-message-reference-structure).
1108#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1109#[derive(Clone, Debug, Deserialize, Serialize)]
1110#[non_exhaustive]
1111pub struct MessageReference {
1112    /// The Type of Message Reference
1113    #[serde(rename = "type", default = "MessageReferenceKind::default")]
1114    pub kind: MessageReferenceKind,
1115    /// ID of the originating message.
1116    pub message_id: Option<MessageId>,
1117    /// ID of the originating message's channel.
1118    pub channel_id: ChannelId,
1119    /// ID of the originating message's guild.
1120    pub guild_id: Option<GuildId>,
1121    /// When sending, whether to error if the referenced message doesn't exist instead of sending
1122    /// as a normal (non-reply) message, default true.
1123    pub fail_if_not_exists: Option<bool>,
1124}
1125
1126impl MessageReference {
1127    #[must_use]
1128    pub fn new(kind: MessageReferenceKind, channel_id: ChannelId) -> Self {
1129        Self {
1130            kind,
1131            channel_id,
1132            message_id: None,
1133            guild_id: None,
1134            fail_if_not_exists: None,
1135        }
1136    }
1137
1138    #[must_use]
1139    pub fn message_id(mut self, message_id: MessageId) -> Self {
1140        self.message_id = Some(message_id);
1141        self
1142    }
1143
1144    #[must_use]
1145    pub fn guild_id(mut self, guild_id: GuildId) -> Self {
1146        self.guild_id = Some(guild_id);
1147        self
1148    }
1149
1150    #[must_use]
1151    pub fn fail_if_not_exists(mut self, fail_if_not_exists: bool) -> Self {
1152        self.fail_if_not_exists = Some(fail_if_not_exists);
1153        self
1154    }
1155}
1156
1157impl From<&Message> for MessageReference {
1158    fn from(m: &Message) -> Self {
1159        Self {
1160            kind: MessageReferenceKind::default(),
1161            message_id: Some(m.id),
1162            channel_id: m.channel_id,
1163            guild_id: m.guild_id,
1164            fail_if_not_exists: None,
1165        }
1166    }
1167}
1168
1169impl From<(ChannelId, MessageId)> for MessageReference {
1170    // TODO(next): Remove this
1171    fn from(pair: (ChannelId, MessageId)) -> Self {
1172        Self {
1173            kind: MessageReferenceKind::default(),
1174            message_id: Some(pair.1),
1175            channel_id: pair.0,
1176            guild_id: None,
1177            fail_if_not_exists: None,
1178        }
1179    }
1180}
1181
1182/// [Discord docs](https://discord.com/developers/docs/resources/channel#channel-mention-object).
1183#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1184#[derive(Clone, Debug, Deserialize, Serialize)]
1185#[non_exhaustive]
1186pub struct ChannelMention {
1187    /// ID of the channel.
1188    pub id: ChannelId,
1189    /// ID of the guild containing the channel.
1190    pub guild_id: GuildId,
1191    /// The kind of channel
1192    #[serde(rename = "type")]
1193    pub kind: ChannelType,
1194    /// The name of the channel
1195    pub name: String,
1196}
1197
1198/// [Discord docs](https://discord.com/developers/docs/resources/message#message-snapshot-structure)
1199///
1200/// For field documentation, see [`Message`].
1201#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1202#[derive(Clone, Debug, Serialize, Deserialize)]
1203#[non_exhaustive]
1204pub struct MessageSnapshot {
1205    pub content: String,
1206    pub timestamp: Timestamp,
1207    pub edited_timestamp: Option<Timestamp>,
1208    pub mentions: Vec<User>,
1209    #[serde(default)]
1210    pub mention_roles: Vec<RoleId>,
1211    pub attachments: Vec<Attachment>,
1212    pub embeds: Vec<Embed>,
1213    #[serde(rename = "type")]
1214    pub kind: MessageType,
1215    pub flags: Option<MessageFlags>,
1216    #[serde(default, deserialize_with = "deserialize_components")]
1217    pub components: Vec<ActionRow>,
1218    #[serde(default)]
1219    pub sticker_items: Vec<StickerItem>,
1220}
1221
1222/// Custom deserialization function to handle the nested "message" field
1223fn deserialize_snapshots<'de, D>(deserializer: D) -> Result<Vec<MessageSnapshot>, D::Error>
1224where
1225    D: Deserializer<'de>,
1226{
1227    #[derive(Deserialize)]
1228    struct MessageSnapshotWrapper {
1229        pub message: MessageSnapshot,
1230    }
1231
1232    let snapshots: Vec<MessageSnapshotWrapper> = Deserialize::deserialize(deserializer)?;
1233
1234    let result = snapshots.into_iter().map(|wrapper| wrapper.message).collect();
1235
1236    Ok(result)
1237}
1238
1239bitflags! {
1240    /// Describes extra features of the message.
1241    ///
1242    /// [Discord docs](https://discord.com/developers/docs/resources/channel#message-object-message-flags).
1243    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1244    #[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)]
1245    pub struct MessageFlags: u64 {
1246        /// This message has been published to subscribed channels (via Channel Following).
1247        const CROSSPOSTED = 1 << 0;
1248        /// This message originated from a message in another channel (via Channel Following).
1249        const IS_CROSSPOST = 1 << 1;
1250        /// Do not include any embeds when serializing this message.
1251        const SUPPRESS_EMBEDS = 1 << 2;
1252        /// The source message for this crosspost has been deleted (via Channel Following).
1253        const SOURCE_MESSAGE_DELETED = 1 << 3;
1254        /// This message came from the urgent message system.
1255        const URGENT = 1 << 4;
1256        /// This message has an associated thread, with the same id as the message.
1257        const HAS_THREAD = 1 << 5;
1258        /// This message is only visible to the user who invoked the Interaction.
1259        const EPHEMERAL = 1 << 6;
1260        /// This message is an Interaction Response and the bot is "thinking".
1261        const LOADING = 1 << 7;
1262        /// This message failed to mention some roles and add their members to the thread.
1263        const FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8;
1264        /// This message will not trigger push and desktop notifications.
1265        const SUPPRESS_NOTIFICATIONS = 1 << 12;
1266        /// This message is a voice message.
1267        ///
1268        /// Voice messages have the following properties:
1269        /// - They cannot be edited.
1270        /// - Only a single audio attachment is allowed. No content, stickers, etc...
1271        /// - The [`Attachment`] has additional fields: `duration_secs` and `waveform`.
1272        ///
1273        /// As of 2023-04-14, clients upload a 1 channel, 48000 Hz, 32kbps Opus stream in an OGG container.
1274        /// The encoding is a Discord implementation detail and may change without warning or documentation.
1275        ///
1276        /// As of 2023-04-20, bots are currently not able to send voice messages
1277        /// ([source](https://github.com/discord/discord-api-docs/pull/6082)).
1278        const IS_VOICE_MESSAGE = 1 << 13;
1279    }
1280}
1281
1282#[cfg(feature = "model")]
1283impl MessageId {
1284    /// Returns a link referencing this message. When clicked, users will jump to the message. The
1285    /// link will be valid for messages in either private channels or guilds.
1286    #[must_use]
1287    pub fn link(&self, channel_id: ChannelId, guild_id: Option<GuildId>) -> String {
1288        if let Some(guild_id) = guild_id {
1289            format!("https://discord.com/channels/{guild_id}/{channel_id}/{self}")
1290        } else {
1291            format!("https://discord.com/channels/@me/{channel_id}/{self}")
1292        }
1293    }
1294
1295    /// Same as [`Self::link`] but tries to find the [`GuildId`] if it is not provided.
1296    #[deprecated = "Use GuildChannel::guild_id if you have no GuildId"]
1297    pub async fn link_ensured(
1298        &self,
1299        cache_http: impl CacheHttp,
1300        channel_id: ChannelId,
1301        mut guild_id: Option<GuildId>,
1302    ) -> String {
1303        if guild_id.is_none() {
1304            let found_channel = channel_id.to_channel(cache_http).await;
1305
1306            if let Ok(channel) = found_channel {
1307                if let Some(c) = channel.guild() {
1308                    guild_id = Some(c.guild_id);
1309                }
1310            }
1311        }
1312
1313        self.link(channel_id, guild_id)
1314    }
1315}
1316
1317#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1318#[derive(Clone, Debug, Serialize)]
1319#[serde(untagged)]
1320pub enum Nonce {
1321    String(String),
1322    Number(u64),
1323}
1324
1325impl<'de> serde::Deserialize<'de> for Nonce {
1326    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
1327        Ok(StrOrInt::deserialize(deserializer)?.into_enum(Self::String, Self::Number))
1328    }
1329}
1330
1331/// [Discord docs](https://discord.com/developers/docs/resources/channel#role-subscription-data-object)
1332#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1333#[derive(Clone, Debug, Deserialize, Serialize)]
1334pub struct RoleSubscriptionData {
1335    /// The id of the sku and listing that the user is subscribed to.
1336    pub role_subscription_listing_id: SkuId,
1337    /// The name of the tier that the user is subscribed to.
1338    pub tier_name: String,
1339    /// The cumulative number of months that the user has been subscribed for.
1340    pub total_months_subscribed: u16,
1341    /// Whether this notification is for a renewal rather than a new purchase.
1342    pub is_renewal: bool,
1343}
1344
1345/// A poll that has been attached to a [`Message`].
1346///
1347/// [Discord docs](https://discord.com/developers/docs/resources/poll#poll-object)
1348#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1349#[derive(Clone, Debug, Deserialize, Serialize)]
1350#[non_exhaustive]
1351pub struct Poll {
1352    pub question: PollMedia,
1353    pub answers: Vec<PollAnswer>,
1354    pub expiry: Option<Timestamp>,
1355    pub allow_multiselect: bool,
1356    pub layout_type: PollLayoutType,
1357    /// The results of the Poll.
1358    ///
1359    /// None does **not** mean that there are no results, simply that Discord has not provide them.
1360    /// See the discord docs for a more detailed explaination.
1361    pub results: Option<PollResults>,
1362}
1363
1364/// A piece of data used in mutliple parts of the [`Poll`] structure.
1365///
1366/// Currently holds text and an optional emoji, but this is expected to change in future
1367///
1368/// [Discord docs](https://discord.com/developers/docs/resources/poll#poll-media-object)
1369#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1370#[derive(Clone, Debug, Default, Deserialize, Serialize)]
1371#[non_exhaustive]
1372pub struct PollMedia {
1373    pub text: Option<String>,
1374    pub emoji: Option<PollMediaEmoji>,
1375}
1376
1377/// The "Partial Emoji" attached to a [`PollMedia`] model.
1378///
1379/// [Discord docs](https://discord.com/developers/docs/resources/poll#poll-media-object)
1380#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1381#[derive(Clone, Debug, Serialize)]
1382#[serde(rename_all = "lowercase")]
1383pub enum PollMediaEmoji {
1384    Name(String),
1385    Id(EmojiId),
1386}
1387
1388impl<'de> serde::Deserialize<'de> for PollMediaEmoji {
1389    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
1390        #[derive(serde::Deserialize)]
1391        struct RawPollMediaEmoji {
1392            name: Option<String>,
1393            id: Option<EmojiId>,
1394        }
1395
1396        let raw = RawPollMediaEmoji::deserialize(deserializer)?;
1397        if let Some(name) = raw.name {
1398            Ok(PollMediaEmoji::Name(name))
1399        } else if let Some(id) = raw.id {
1400            Ok(PollMediaEmoji::Id(id))
1401        } else {
1402            Err(serde::de::Error::duplicate_field("emoji"))
1403        }
1404    }
1405}
1406
1407impl From<String> for PollMediaEmoji {
1408    fn from(value: String) -> Self {
1409        Self::Name(value)
1410    }
1411}
1412
1413impl From<EmojiId> for PollMediaEmoji {
1414    fn from(value: EmojiId) -> Self {
1415        Self::Id(value)
1416    }
1417}
1418
1419/// A possible answer for a [`Poll`].
1420///
1421/// [Discord docs](https://discord.com/developers/docs/resources/poll#poll-answer-object)
1422#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1423#[derive(Clone, Debug, Deserialize, Serialize)]
1424#[non_exhaustive]
1425pub struct PollAnswer {
1426    pub answer_id: AnswerId,
1427    pub poll_media: PollMedia,
1428}
1429
1430enum_number! {
1431    /// Represents the different layouts that a [`Poll`] may have.
1432    ///
1433    /// Currently, there is only the one option.
1434    ///
1435    /// [Discord docs](https://discord.com/developers/docs/resources/poll#layout-type)
1436    #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
1437    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1438    #[serde(from = "u8", into = "u8")]
1439    #[non_exhaustive]
1440    pub enum PollLayoutType {
1441        #[default]
1442        Default = 1,
1443        _ => Unknown(u8),
1444    }
1445}
1446
1447/// The model for the results of a [`Poll`].
1448///
1449/// If `is_finalized` is `false`, `answer_counts` will be inaccurate due to Discord's scale.
1450///
1451/// [Discord docs](https://discord.com/developers/docs/resources/poll#poll-results-object-poll-results-object-structure)
1452#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1453#[derive(Clone, Debug, Deserialize, Serialize)]
1454#[non_exhaustive]
1455pub struct PollResults {
1456    pub is_finalized: bool,
1457    pub answer_counts: Vec<PollAnswerCount>,
1458}
1459
1460/// The count of a single [`PollAnswer`]'s results.
1461///
1462/// [Discord docs](https://discord.com/developers/docs/resources/poll#poll-results-object-poll-answer-count-object-structure)
1463#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
1464#[derive(Clone, Debug, Deserialize, Serialize)]
1465#[non_exhaustive]
1466pub struct PollAnswerCount {
1467    pub id: AnswerId,
1468    pub count: u64,
1469    pub me_voted: bool,
1470}
1471
1472// all tests here require cache, move if non-cache test is added
1473#[cfg(all(test, feature = "cache"))]
1474mod tests {
1475    use std::collections::HashMap;
1476
1477    use dashmap::DashMap;
1478
1479    use super::{
1480        Guild,
1481        GuildChannel,
1482        Member,
1483        Message,
1484        PermissionOverwrite,
1485        PermissionOverwriteType,
1486        Permissions,
1487        User,
1488        UserId,
1489    };
1490    use crate::cache::wrappers::MaybeMap;
1491    use crate::cache::Cache;
1492
1493    /// Test that author_permissions checks the permissions in a channel, not just the guild.
1494    #[test]
1495    fn author_permissions_respects_overwrites() {
1496        // Author of the message, with a random ID that won't collide with defaults.
1497        let author = User {
1498            id: UserId::new(50778944701071),
1499            ..Default::default()
1500        };
1501
1502        // Channel with the message, with SEND_MESSAGES on.
1503        let channel = GuildChannel {
1504            permission_overwrites: vec![PermissionOverwrite {
1505                allow: Permissions::SEND_MESSAGES,
1506                deny: Permissions::default(),
1507                kind: PermissionOverwriteType::Member(author.id),
1508            }],
1509            ..Default::default()
1510        };
1511        let channel_id = channel.id;
1512
1513        // Guild with the author and channel cached, default (empty) permissions.
1514        let guild = Guild {
1515            channels: HashMap::from([(channel.id, channel)]),
1516            members: HashMap::from([(author.id, Member {
1517                user: author.clone(),
1518                ..Default::default()
1519            })]),
1520            ..Default::default()
1521        };
1522
1523        // Message, tied to the guild and the channel.
1524        let message = Message {
1525            author,
1526            channel_id,
1527            guild_id: Some(guild.id),
1528            ..Default::default()
1529        };
1530
1531        // Cache, with the guild setup.
1532        let mut cache = Cache::new();
1533        cache.guilds = MaybeMap(Some({
1534            let guilds = DashMap::default();
1535            guilds.insert(guild.id, guild);
1536            guilds
1537        }));
1538
1539        // The author should only have the one permission, SEND_MESSAGES.
1540        assert_eq!(message.author_permissions(&cache), Some(Permissions::SEND_MESSAGES));
1541    }
1542}