serenity/model/
gateway.rs

1//! Models pertaining to the gateway.
2
3use std::num::NonZeroU16;
4
5use serde::ser::SerializeSeq;
6use url::Url;
7
8use super::prelude::*;
9use super::utils::*;
10
11/// A representation of the data retrieved from the bot gateway endpoint.
12///
13/// This is different from the [`Gateway`], as this includes the number of shards that Discord
14/// recommends to use for a bot user.
15///
16/// This is only applicable to bot users.
17///
18/// [Discord docs](https://discord.com/developers/docs/topics/gateway#get-gateway-bot-json-response).
19#[derive(Clone, Debug, Deserialize, Serialize)]
20#[non_exhaustive]
21pub struct BotGateway {
22    /// The gateway to connect to.
23    pub url: String,
24    /// The number of shards that is recommended to be used by the current bot user.
25    pub shards: u32,
26    /// Information describing how many gateway sessions you can initiate within a ratelimit
27    /// period.
28    pub session_start_limit: SessionStartLimit,
29}
30
31/// Representation of an activity that a [`User`] is performing.
32///
33/// [Discord docs](https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-structure).
34#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
35#[derive(Clone, Debug, Deserialize, Serialize)]
36#[non_exhaustive]
37pub struct Activity {
38    /// The ID of the application for the activity.
39    #[serde(deserialize_with = "deserialize_buggy_id")]
40    #[serde(default)]
41    pub application_id: Option<ApplicationId>,
42    /// Images for the presence and their texts.
43    pub assets: Option<ActivityAssets>,
44    /// What the user is doing.
45    pub details: Option<String>,
46    /// Activity flags describing what the payload includes.
47    pub flags: Option<ActivityFlags>,
48    /// Whether or not the activity is an instanced game session.
49    pub instance: Option<bool>,
50    /// The type of activity being performed
51    #[serde(rename = "type")]
52    pub kind: ActivityType,
53    /// The name of the activity.
54    pub name: String,
55    /// Information about the user's current party.
56    pub party: Option<ActivityParty>,
57    /// Secrets for Rich Presence joining and spectating.
58    pub secrets: Option<ActivitySecrets>,
59    /// The user's current party status.
60    pub state: Option<String>,
61    /// Emoji currently used in custom status
62    pub emoji: Option<ActivityEmoji>,
63    /// Unix timestamps for the start and/or end times of the activity.
64    pub timestamps: Option<ActivityTimestamps>,
65    /// The sync ID of the activity. Mainly used by the Spotify activity type which uses this
66    /// parameter to store the track ID.
67    #[cfg(feature = "unstable_discord_api")]
68    pub sync_id: Option<String>,
69    /// The session ID of the activity. Reserved for specific activity types, such as the Activity
70    /// that is transmitted when a user is listening to Spotify.
71    #[cfg(feature = "unstable_discord_api")]
72    pub session_id: Option<String>,
73    /// The Stream URL if [`Self::kind`] is [`ActivityType::Streaming`].
74    pub url: Option<Url>,
75    /// The buttons of this activity.
76    ///
77    /// **Note**: There can only be up to 2 buttons.
78    #[serde(default, deserialize_with = "deserialize_buttons")]
79    pub buttons: Vec<ActivityButton>,
80    /// Unix timestamp (in milliseconds) of when the activity was added to the user's session
81    pub created_at: u64,
82}
83
84/// [Discord docs](https://discord.com/developers/docs/topics/gateway#activity-object-activity-buttons).
85#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
86#[derive(Clone, Debug, Serialize, Deserialize)]
87#[non_exhaustive]
88pub struct ActivityButton {
89    /// The text shown on the button.
90    pub label: String,
91    /// The url opened when clicking the button.
92    ///
93    /// **Note**: Bots cannot access activity button URL.
94    #[serde(default)]
95    pub url: String,
96}
97
98/// The assets for an activity.
99///
100/// [Discord docs](https://discord.com/developers/docs/topics/gateway#activity-object-activity-assets).
101#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
102#[derive(Clone, Debug, Deserialize, Serialize)]
103#[non_exhaustive]
104pub struct ActivityAssets {
105    /// The ID for a large asset of the activity, usually a snowflake.
106    pub large_image: Option<String>,
107    /// Text displayed when hovering over the large image of the activity.
108    pub large_text: Option<String>,
109    /// The ID for a small asset of the activity, usually a snowflake.
110    pub small_image: Option<String>,
111    /// Text displayed when hovering over the small image of the activity.
112    pub small_text: Option<String>,
113}
114
115bitflags! {
116    /// A set of flags defining what is in an activity's payload.
117    ///
118    /// [Discord docs](https://discord.com/developers/docs/topics/gateway#activity-object-activity-flags).
119    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
120    #[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)]
121    pub struct ActivityFlags: u64 {
122        /// Whether the activity is an instance activity.
123        const INSTANCE = 1 << 0;
124        /// Whether the activity is joinable.
125        const JOIN = 1 << 1;
126        /// Whether the activity can be spectated.
127        const SPECTATE = 1 << 2;
128        /// Whether a request can be sent to join the user's party.
129        const JOIN_REQUEST = 1 << 3;
130        /// Whether the activity can be synced.
131        const SYNC = 1 << 4;
132        /// Whether the activity can be played.
133        const PLAY = 1 << 5;
134        /// Whether the activity party is friend only.
135        const PARTY_PRIVACY_FRIENDS = 1 << 6;
136        /// Whether the activity party is in a voice channel.
137        const PARTY_PRIVACY_VOICE_CHANNEL = 1 << 7;
138        /// Whether the activity can be embedded.
139        const EMBEDDED = 1 << 8;
140    }
141}
142
143/// Information about an activity's party.
144///
145/// [Discord docs](https://discord.com/developers/docs/game-sdk/activities#data-models-activityparty-struct).
146#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
147#[derive(Clone, Debug, Deserialize, Serialize)]
148#[non_exhaustive]
149pub struct ActivityParty {
150    /// The ID of the party.
151    pub id: Option<String>,
152    /// Used to show the party's current and maximum size.
153    pub size: Option<[u32; 2]>,
154}
155
156/// Secrets for an activity.
157///
158/// [Discord docs](https://discord.com/developers/docs/topics/gateway#activity-object-activity-secrets).
159#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
160#[derive(Clone, Debug, Deserialize, Serialize)]
161#[non_exhaustive]
162pub struct ActivitySecrets {
163    /// The secret for joining a party.
164    pub join: Option<String>,
165    /// The secret for a specific instanced match.
166    #[serde(rename = "match")]
167    pub match_: Option<String>,
168    /// The secret for spectating an activity.
169    pub spectate: Option<String>,
170}
171
172/// Representation of an emoji used in a custom status
173///
174/// [Discord docs](https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-emoji).
175#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
176#[derive(Clone, Debug, Deserialize, Serialize)]
177#[non_exhaustive]
178pub struct ActivityEmoji {
179    /// The name of the emoji.
180    pub name: String,
181    /// The id of the emoji.
182    pub id: Option<EmojiId>,
183    /// Whether this emoji is animated.
184    pub animated: Option<bool>,
185}
186
187enum_number! {
188    /// [Discord docs](https://discord.com/developers/docs/topics/gateway-events#activity-object-activity-types).
189    #[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
190    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
191    #[serde(from = "u8", into = "u8")]
192    #[non_exhaustive]
193    pub enum ActivityType {
194        /// An indicator that the user is playing a game.
195        #[default]
196        Playing = 0,
197        /// An indicator that the user is streaming to a service.
198        Streaming = 1,
199        /// An indicator that the user is listening to something.
200        Listening = 2,
201        /// An indicator that the user is watching something.
202        Watching = 3,
203        /// An indicator that the user uses custom statuses
204        Custom = 4,
205        /// An indicator that the user is competing somewhere.
206        Competing = 5,
207        _ => Unknown(u8),
208    }
209}
210
211/// A representation of the data retrieved from the gateway endpoint.
212///
213/// For the bot-specific gateway, refer to [`BotGateway`].
214///
215/// [Discord docs](https://discord.com/developers/docs/topics/gateway#get-gateway-example-response).
216#[derive(Clone, Debug, Deserialize, Serialize)]
217#[non_exhaustive]
218pub struct Gateway {
219    /// The gateway to connect to.
220    pub url: String,
221}
222
223/// Information detailing the current active status of a [`User`].
224///
225/// [Discord docs](https://discord.com/developers/docs/topics/gateway#client-status-object).
226#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
227#[derive(Clone, Debug, Deserialize, Serialize)]
228#[non_exhaustive]
229pub struct ClientStatus {
230    pub desktop: Option<OnlineStatus>,
231    pub mobile: Option<OnlineStatus>,
232    pub web: Option<OnlineStatus>,
233}
234
235/// Information about the user of a [`Presence`] event.
236///
237/// Fields should be identical to those of [`User`], except that every field but `id` is
238/// optional. This is currently not implemented fully.
239///
240/// [Discord docs](https://discord.com/developers/docs/resources/user#user-object),
241/// [modification description](https://discord.com/developers/docs/topics/gateway-events#presence-update).
242#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
243#[derive(Clone, Debug, Default, Deserialize, Serialize)]
244#[non_exhaustive]
245pub struct PresenceUser {
246    pub id: UserId,
247    pub avatar: Option<ImageHash>,
248    pub bot: Option<bool>,
249    #[serde(default, skip_serializing_if = "Option::is_none", with = "discriminator")]
250    pub discriminator: Option<NonZeroU16>,
251    pub email: Option<String>,
252    pub mfa_enabled: Option<bool>,
253    #[serde(rename = "username")]
254    pub name: Option<String>,
255    pub verified: Option<bool>,
256    pub public_flags: Option<UserPublicFlags>,
257}
258
259impl PresenceUser {
260    /// Attempts to convert this [`PresenceUser`] instance into a [`User`].
261    ///
262    /// If one of [`User`]'s required fields is None in `self`, None is returned.
263    #[must_use]
264    pub fn into_user(self) -> Option<User> {
265        Some(User {
266            avatar: self.avatar,
267            bot: self.bot?,
268            discriminator: self.discriminator,
269            global_name: None,
270            id: self.id,
271            name: self.name?,
272            public_flags: self.public_flags,
273            banner: None,
274            accent_colour: None,
275            member: None,
276            system: false,
277            mfa_enabled: self.mfa_enabled.unwrap_or_default(),
278            locale: None,
279            verified: self.verified,
280            email: self.email,
281            flags: self.public_flags.unwrap_or_default(),
282            premium_type: PremiumType::None,
283        })
284    }
285
286    /// Attempts to convert this [`PresenceUser`] instance into a [`User`].
287    ///
288    /// Will clone individual fields if needed.
289    ///
290    /// If one of [`User`]'s required fields is None in `self`, None is returned.
291    #[must_use]
292    pub fn to_user(&self) -> Option<User> {
293        self.clone().into_user()
294    }
295
296    #[cfg(feature = "cache")] // method is only used with the cache feature enabled
297    pub(crate) fn update_with_user(&mut self, user: &User) {
298        self.id = user.id;
299        if let Some(avatar) = user.avatar {
300            self.avatar = Some(avatar);
301        }
302        self.bot = Some(user.bot);
303        self.discriminator = user.discriminator;
304        self.name = Some(user.name.clone());
305        if let Some(public_flags) = user.public_flags {
306            self.public_flags = Some(public_flags);
307        }
308    }
309}
310
311/// Information detailing the current online status of a [`User`].
312///
313/// [Discord docs](https://discord.com/developers/docs/topics/gateway#presence-update-presence-update-event-fields).
314#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
315#[derive(Clone, Debug, Deserialize, Serialize)]
316#[non_exhaustive]
317pub struct Presence {
318    /// Data about the associated user.
319    pub user: PresenceUser,
320    /// The `GuildId` the presence update is coming from.
321    pub guild_id: Option<GuildId>,
322    /// The user's online status.
323    pub status: OnlineStatus,
324    /// [`User`]'s current activities.
325    #[serde(default)]
326    pub activities: Vec<Activity>,
327    /// The devices a user are currently active on, if available.
328    pub client_status: Option<ClientStatus>,
329}
330
331/// An initial set of information given after IDENTIFYing to the gateway.
332///
333/// [Discord docs](https://discord.com/developers/docs/topics/gateway#ready-ready-event-fields).
334#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
335#[derive(Clone, Debug, Deserialize, Serialize)]
336#[non_exhaustive]
337pub struct Ready {
338    /// API version
339    #[serde(rename = "v")]
340    pub version: u8,
341    /// Information about the user including email
342    pub user: CurrentUser,
343    /// Guilds the user is in
344    pub guilds: Vec<UnavailableGuild>,
345    /// Used for resuming connections
346    pub session_id: String,
347    /// Gateway URL for resuming connections
348    pub resume_gateway_url: String,
349    /// Shard information associated with this session, if sent when identifying
350    pub shard: Option<ShardInfo>,
351    /// Contains id and flags
352    pub application: PartialCurrentApplicationInfo,
353}
354
355/// Information describing how many gateway sessions you can initiate within a ratelimit period.
356///
357/// [Discord docs](https://discord.com/developers/docs/topics/gateway#session-start-limit-object-session-start-limit-structure).
358#[derive(Clone, Debug, Deserialize, Serialize)]
359#[non_exhaustive]
360pub struct SessionStartLimit {
361    /// The number of sessions that you can still initiate within the current ratelimit period.
362    pub remaining: u64,
363    /// The number of milliseconds until the ratelimit period resets.
364    pub reset_after: u64,
365    /// The total number of session starts within the ratelimit period allowed.
366    pub total: u64,
367    /// The number of identify requests allowed per 5 seconds.
368    pub max_concurrency: u64,
369}
370
371#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
372#[derive(Clone, Copy, Debug)]
373pub struct ShardInfo {
374    pub id: ShardId,
375    pub total: u32,
376}
377
378impl ShardInfo {
379    #[must_use]
380    pub(crate) fn new(id: ShardId, total: u32) -> Self {
381        Self {
382            id,
383            total,
384        }
385    }
386}
387
388impl<'de> serde::Deserialize<'de> for ShardInfo {
389    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> StdResult<Self, D::Error> {
390        <(u32, u32)>::deserialize(deserializer).map(|(id, total)| ShardInfo {
391            id: ShardId(id),
392            total,
393        })
394    }
395}
396
397impl serde::Serialize for ShardInfo {
398    fn serialize<S: serde::Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
399        let mut seq = serializer.serialize_seq(Some(2))?;
400        seq.serialize_element(&self.id.0)?;
401        seq.serialize_element(&self.total)?;
402        seq.end()
403    }
404}
405
406/// Timestamps of when a user started and/or is ending their activity.
407///
408/// [Discord docs](https://discord.com/developers/docs/game-sdk/activities#data-models-activitytimestamps-struct).
409#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
410#[derive(Clone, Debug, Deserialize, Serialize)]
411#[non_exhaustive]
412pub struct ActivityTimestamps {
413    pub end: Option<u64>,
414    pub start: Option<u64>,
415}
416
417bitflags! {
418    /// [Gateway Intents] will limit the events your bot will receive via the gateway. By default,
419    /// all intents except [Privileged Intents] are selected.
420    ///
421    /// # What are Intents
422    ///
423    /// A [gateway intent] sets the types of gateway events (e.g. member joins, guild integrations,
424    /// guild emoji updates, ...) the bot shall receive. Carefully picking the needed intents
425    /// greatly helps the bot to scale, as less intents will result in less events to be received
426    /// via the network from Discord and less processing needed for handling the data.
427    ///
428    /// # Privileged Intents
429    ///
430    /// The intents [`GatewayIntents::GUILD_PRESENCES`], [`GatewayIntents::GUILD_MEMBERS`] and
431    /// [`GatewayIntents::MESSAGE_CONTENT`] are [Privileged Intents]. They need to be enabled in
432    /// the *developer portal*.
433    ///
434    /// **Note**: Once the bot is in 100 guilds or more, [the bot must be verified] in order to use
435    /// privileged intents.
436    ///
437    /// [Discord docs](https://discord.com/developers/docs/topics/gateway#list-of-intents).
438    ///
439    /// [Gateway Intents]: https://discord.com/developers/docs/topics/gateway#gateway-intents
440    /// [Privileged Intents]: https://discord.com/developers/docs/topics/gateway#privileged-intents
441    /// [the bot must be verified]: https://support.discord.com/hc/en-us/articles/360040720412-Bot-Verification-and-Data-Whitelisting
442    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
443    #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
444    pub struct GatewayIntents: u64 {
445        /// Enables the following gateway events:
446        ///  - GUILD_CREATE
447        ///  - GUILD_UPDATE
448        ///  - GUILD_DELETE
449        ///  - GUILD_ROLE_CREATE
450        ///  - GUILD_ROLE_UPDATE
451        ///  - GUILD_ROLE_DELETE
452        ///  - CHANNEL_CREATE
453        ///  - CHANNEL_UPDATE
454        ///  - CHANNEL_DELETE
455        ///  - CHANNEL_PINS_UPDATE
456        ///  - THREAD_CREATE
457        ///  - THREAD_UPDATE
458        ///  - THREAD_DELETE
459        ///  - THREAD_LIST_SYNC
460        ///  - THREAD_MEMBER_UPDATE
461        ///  - THREAD_MEMBERS_UPDATE
462        ///  - STAGE_INSTANCE_CREATE
463        ///  - STAGE_INSTANCE_UPDATE
464        ///  - STAGE_INSTANCE_DELETE
465        ///
466        /// **Info:** The THREAD_MEMBERS_UPDATE event contains different data depending on which
467        /// intents are used. See [Discord's Docs](https://discord.com/developers/docs/topics/gateway-events#thread-members-update)
468        /// for more information.
469        const GUILDS = 1;
470        /// Enables the following gateway events:
471        /// - GUILD_MEMBER_ADD
472        /// - GUILD_MEMBER_UPDATE
473        /// - GUILD_MEMBER_REMOVE
474        /// - THREAD_MEMBERS_UPDATE
475        ///
476        /// **Info**: This intent is *privileged*. In order to use it, you must head to your
477        /// application in the Developer Portal and enable the toggle for *Privileged Intents*, as
478        /// well as enabling it in your code.
479        ///
480        /// **Info:** The THREAD_MEMBERS_UPDATE event contains different data depending on which
481        /// intents are used. See [Discord's Docs](https://discord.com/developers/docs/topics/gateway-events#thread-members-update)
482        /// for more information.
483        const GUILD_MEMBERS = 1 << 1;
484
485        /// Enables the following gateway events:
486        /// - GUILD_AUDIT_LOG_ENTRY_CREATE
487        /// - GUILD_BAN_ADD
488        /// - GUILD_BAN_REMOVE
489        const GUILD_MODERATION = 1 << 2;
490        /// Backwards compatibility with old gateway event name. Same as GUILD_MODERATION
491        #[deprecated = "Use [`Self::GUILD_MODERATION`] instead"]
492        const GUILD_BANS = 1 << 2;
493
494        /// Enables the following gateway events:
495        /// - GUILD_EMOJIS_UPDATE
496        /// - GUILD_STICKERS_UPDATE
497        const GUILD_EMOJIS_AND_STICKERS = 1 << 3;
498        /// Enables the following gateway events:
499        /// - GUILD_INTEGRATIONS_UPDATE
500        /// - INTEGRATION_CREATE
501        /// - INTEGRATION_UPDATE
502        /// - INTEGRATION_DELETE
503        const GUILD_INTEGRATIONS = 1 << 4;
504        /// Enables the following gateway event:
505        /// - WEBHOOKS_UPDATE
506        const GUILD_WEBHOOKS = 1 << 5;
507        /// Enables the following gateway events:
508        /// - INVITE_CREATE
509        /// - INVITE_DELETE
510        const GUILD_INVITES = 1 << 6;
511        /// Enables the following gateway event:
512        /// - VOICE_STATE_UPDATE
513        ///
514        /// **Note**: this intent is mandatory for `songbird` to function properly.
515        const GUILD_VOICE_STATES = 1 << 7;
516        /// Enables the following gateway event:
517        /// - PRESENCE_UPDATE
518        ///
519        /// **Info**: This intent is *privileged*. In order to use it, you must head to your
520        /// application in the Developer Portal and enable the toggle for *Privileged Intents*,
521        /// as well as enabling it in your code.
522        const GUILD_PRESENCES = 1 << 8;
523        /// Enables the following gateway events in guilds:
524        /// - MESSAGE_CREATE
525        /// - MESSAGE_UPDATE
526        /// - MESSAGE_DELETE
527        /// - MESSAGE_DELETE_BULK
528        const GUILD_MESSAGES = 1 << 9;
529        /// Enables the following gateway events in guilds:
530        /// - MESSAGE_REACTION_ADD
531        /// - MESSAGE_REACTION_REMOVE
532        /// - MESSAGE_REACTION_REMOVE_ALL
533        /// - MESSAGE_REACTION_REMOVE_EMOJI
534        const GUILD_MESSAGE_REACTIONS = 1 << 10;
535        /// Enable following gateway event:
536        /// - TYPING_START
537        const GUILD_MESSAGE_TYPING = 1 << 11;
538
539        /// Enables the following gateway events for direct messages:
540        /// - MESSAGE_CREATE
541        /// - MESSAGE_UPDATE
542        /// - MESSAGE_DELETE
543        /// - CHANNEL_PINS_UPDATE
544        const DIRECT_MESSAGES = 1 << 12;
545        /// Enable following gateway events for direct messages:
546        /// - MESSAGE_REACTION_ADD
547        /// - MESSAGE_REACTION_REMOVE
548        /// - MESSAGE_REACTION_REMOVE_ALL
549        /// - MESSAGE_REACTION_REMOVE_EMOJI
550        const DIRECT_MESSAGE_REACTIONS = 1 << 13;
551        /// Enables the following gateway events for direct messages:
552        /// - TYPING_START
553        const DIRECT_MESSAGE_TYPING = 1 << 14;
554
555        /// Enables receiving message content in gateway events
556        ///
557        /// See [Discord's Docs](https://discord.com/developers/docs/topics/gateway#message-content-intent) for more information
558        ///
559        /// **Info**: This intent is *privileged*. In order to use it, you must head to your
560        /// application in the Developer Portal and enable the toggle for *Privileged Intents*,
561        /// as well as enabling it in your code.
562        const MESSAGE_CONTENT = 1 << 15;
563
564        /// Enables the following gateway events:
565        /// - GUILD_SCHEDULED_EVENT_CREATE
566        /// - GUILD_SCHEDULED_EVENT_UPDATE
567        /// - GUILD_SCHEDULED_EVENT_DELETE
568        /// - GUILD_SCHEDULED_EVENT_USER_ADD
569        /// - GUILD_SCHEDULED_EVENT_USER_REMOVE
570        const GUILD_SCHEDULED_EVENTS = 1 << 16;
571        /// Enables the following gateway events:
572        /// - AUTO_MODERATION_RULE_CREATE
573        /// - AUTO_MODERATION_RULE_UPDATE
574        /// - AUTO_MODERATION_RULE_DELETE
575        const AUTO_MODERATION_CONFIGURATION = 1 << 20;
576        /// Enables the following gateway events:
577        /// - AUTO_MODERATION_ACTION_EXECUTION
578        const AUTO_MODERATION_EXECUTION = 1 << 21;
579
580        /// Enables the following gateway events for guilds:
581        /// - MESSAGE_POLL_VOTE_ADD
582        /// - MESSAGE_POLL_VOTE_REMOVE
583        const GUILD_MESSAGE_POLLS = 1 << 24;
584        /// Enables the following gateway events for direct messages:
585        /// - MESSAGE_POLL_VOTE_ADD
586        /// - MESSAGE_POLL_VOTE_REMOVE
587        const DIRECT_MESSAGE_POLLS = 1 << 25;
588    }
589}
590
591impl GatewayIntents {
592    /// Gets all of the intents that aren't considered privileged by Discord.
593    #[must_use]
594    pub const fn non_privileged() -> GatewayIntents {
595        // bitflags don't support const evaluation. Workaround.
596        // See: https://github.com/bitflags/bitflags/issues/180
597        Self::privileged().complement()
598    }
599
600    /// Gets all of the intents that are considered privileged by Discord.
601    /// Use of these intents will require explicitly whitelisting the bot.
602    #[must_use]
603    pub const fn privileged() -> GatewayIntents {
604        // bitflags don't support const evaluation. Workaround.
605        // See: https://github.com/bitflags/bitflags/issues/180
606        Self::GUILD_MEMBERS.union(Self::GUILD_PRESENCES).union(Self::MESSAGE_CONTENT)
607    }
608}
609
610#[cfg(feature = "model")]
611impl GatewayIntents {
612    /// Checks if any of the included intents are privileged.
613    #[must_use]
614    pub const fn is_privileged(self) -> bool {
615        self.intersects(Self::privileged())
616    }
617
618    /// Shorthand for checking that the set of intents contains the [GUILDS] intent.
619    ///
620    /// [GUILDS]: Self::GUILDS
621    #[must_use]
622    pub const fn guilds(self) -> bool {
623        self.contains(Self::GUILDS)
624    }
625
626    /// Shorthand for checking that the set of intents contains the [GUILD_MEMBERS] intent.
627    ///
628    /// [GUILD_MEMBERS]: Self::GUILD_MEMBERS
629    #[must_use]
630    pub const fn guild_members(self) -> bool {
631        self.contains(Self::GUILD_MEMBERS)
632    }
633
634    /// Shorthand for checking that the set of intents contains the [GUILD_BANS] intent.
635    ///
636    /// [GUILD_BANS]: Self::GUILD_BANS
637    ///
638    /// This is the same as calling guild_moderation since Discord changed the name
639    #[must_use]
640    #[deprecated = "Use [`Self::guild_moderation`] instead"]
641    pub const fn guild_bans(self) -> bool {
642        #[allow(deprecated)] // this is a deprecated method itself
643        self.contains(Self::GUILD_BANS)
644    }
645
646    /// Shorthand for checking that the set of intents contains the [GUILD_MODERATION] intent.
647    ///
648    /// [GUILD_MODERATION]: Self::GUILD_MODERATION
649    #[must_use]
650    pub const fn guild_moderation(self) -> bool {
651        self.contains(Self::GUILD_MODERATION)
652    }
653
654    /// Shorthand for checking that the set of intents contains the [GUILD_EMOJIS_AND_STICKERS]
655    /// intent.
656    ///
657    /// [GUILD_EMOJIS_AND_STICKERS]: Self::GUILD_EMOJIS_AND_STICKERS
658    #[must_use]
659    pub const fn guild_emojis_and_stickers(self) -> bool {
660        self.contains(Self::GUILD_EMOJIS_AND_STICKERS)
661    }
662
663    /// Shorthand for checking that the set of intents contains the [GUILD_INTEGRATIONS] intent.
664    ///
665    /// [GUILD_INTEGRATIONS]: Self::GUILD_INTEGRATIONS
666    #[must_use]
667    pub const fn guild_integrations(self) -> bool {
668        self.contains(Self::GUILD_INTEGRATIONS)
669    }
670
671    /// Shorthand for checking that the set of intents contains the [GUILD_WEBHOOKS] intent.
672    ///
673    /// [GUILD_WEBHOOKS]: Self::GUILD_WEBHOOKS
674    #[must_use]
675    pub const fn guild_webhooks(self) -> bool {
676        self.contains(Self::GUILD_WEBHOOKS)
677    }
678
679    /// Shorthand for checking that the set of intents contains the [GUILD_INVITES] intent.
680    ///
681    /// [GUILD_INVITES]: Self::GUILD_INVITES
682    #[must_use]
683    pub const fn guild_invites(self) -> bool {
684        self.contains(Self::GUILD_INVITES)
685    }
686
687    /// Shorthand for checking that the set of intents contains the [GUILD_VOICE_STATES] intent.
688    ///
689    /// [GUILD_VOICE_STATES]: Self::GUILD_VOICE_STATES
690    #[must_use]
691    pub const fn guild_voice_states(self) -> bool {
692        self.contains(Self::GUILD_VOICE_STATES)
693    }
694
695    /// Shorthand for checking that the set of intents contains the [GUILD_PRESENCES] intent.
696    ///
697    /// [GUILD_PRESENCES]: Self::GUILD_PRESENCES
698    #[must_use]
699    pub const fn guild_presences(self) -> bool {
700        self.contains(Self::GUILD_PRESENCES)
701    }
702
703    /// Shorthand for checking that the set of intents contains the [GUILD_MESSAGE_REACTIONS]
704    /// intent.
705    ///
706    /// [GUILD_MESSAGE_REACTIONS]: Self::GUILD_MESSAGE_REACTIONS
707    #[must_use]
708    pub const fn guild_message_reactions(self) -> bool {
709        self.contains(Self::GUILD_MESSAGE_REACTIONS)
710    }
711
712    /// Shorthand for checking that the set of intents contains the [GUILD_MESSAGE_TYPING] intent.
713    ///
714    /// [GUILD_MESSAGE_TYPING]: Self::GUILD_MESSAGE_TYPING
715    #[must_use]
716    pub const fn guild_message_typing(self) -> bool {
717        self.contains(Self::GUILD_MESSAGE_TYPING)
718    }
719
720    /// Shorthand for checking that the set of intents contains the [DIRECT_MESSAGES] intent.
721    ///
722    /// [DIRECT_MESSAGES]: Self::DIRECT_MESSAGES
723    #[must_use]
724    pub const fn direct_messages(self) -> bool {
725        self.contains(Self::DIRECT_MESSAGES)
726    }
727
728    /// Shorthand for checking that the set of intents contains the [DIRECT_MESSAGE_REACTIONS]
729    /// intent.
730    ///
731    /// [DIRECT_MESSAGE_REACTIONS]: Self::DIRECT_MESSAGE_REACTIONS
732    #[must_use]
733    pub const fn direct_message_reactions(self) -> bool {
734        self.contains(Self::DIRECT_MESSAGE_REACTIONS)
735    }
736
737    /// Shorthand for checking that the set of intents contains the [DIRECT_MESSAGE_TYPING] intent.
738    ///
739    /// [DIRECT_MESSAGE_TYPING]: Self::DIRECT_MESSAGE_TYPING
740    #[must_use]
741    pub const fn direct_message_typing(self) -> bool {
742        self.contains(Self::DIRECT_MESSAGE_TYPING)
743    }
744
745    /// Shorthand for checking that the set of intents contains the [MESSAGE_CONTENT] intent.
746    ///
747    /// [MESSAGE_CONTENT]: Self::MESSAGE_CONTENT
748    #[must_use]
749    pub const fn message_content(self) -> bool {
750        self.contains(Self::MESSAGE_CONTENT)
751    }
752
753    /// Shorthand for checking that the set of intents contains the [GUILD_SCHEDULED_EVENTS]
754    /// intent.
755    ///
756    /// [GUILD_SCHEDULED_EVENTS]: Self::GUILD_SCHEDULED_EVENTS
757    #[must_use]
758    pub const fn guild_scheduled_events(self) -> bool {
759        self.contains(Self::GUILD_SCHEDULED_EVENTS)
760    }
761
762    /// Shorthand for checking that the set of intents contains the [AUTO_MODERATION_CONFIGURATION]
763    /// intent.
764    ///
765    /// [AUTO_MODERATION_CONFIGURATION]: Self::AUTO_MODERATION_CONFIGURATION
766    #[must_use]
767    pub const fn auto_moderation_configuration(self) -> bool {
768        self.contains(Self::AUTO_MODERATION_CONFIGURATION)
769    }
770
771    /// Shorthand for checking that the set of intents contains the [AUTO_MODERATION_EXECUTION]
772    /// intent.
773    ///
774    /// [AUTO_MODERATION_EXECUTION]: Self::AUTO_MODERATION_EXECUTION
775    #[must_use]
776    pub const fn auto_moderation_execution(self) -> bool {
777        self.contains(Self::AUTO_MODERATION_EXECUTION)
778    }
779}
780
781impl Default for GatewayIntents {
782    fn default() -> Self {
783        Self::non_privileged()
784    }
785}