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