serenity/model/application/
mod.rs

1//! Models about OAuth2 applications.
2
3use std::collections::HashMap;
4
5mod command;
6pub use command::*;
7mod command_interaction;
8pub use command_interaction::*;
9mod component;
10pub use component::*;
11mod component_interaction;
12pub use component_interaction::*;
13mod interaction;
14pub use interaction::*;
15mod modal_interaction;
16pub use modal_interaction::*;
17mod oauth;
18pub use oauth::*;
19mod ping_interaction;
20pub use ping_interaction::*;
21
22use super::guild::PartialGuild;
23use super::id::{ApplicationId, GenericId, GuildId, SkuId, UserId};
24use super::misc::ImageHash;
25use super::user::User;
26use super::Permissions;
27
28/// Partial information about the given application.
29///
30/// Discord docs: [application field of Ready](https://discord.com/developers/docs/topics/gateway-events#ready-ready-event-fields)
31#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
32#[derive(Clone, Debug, Deserialize, Serialize)]
33#[non_exhaustive]
34pub struct PartialCurrentApplicationInfo {
35    /// The unique Id of the user.
36    pub id: ApplicationId,
37    /// The flags associated with the application.
38    pub flags: ApplicationFlags,
39}
40
41/// Information about the current application and its owner.
42///
43/// [Discord docs](https://discord.com/developers/docs/resources/application#application-object-application-structure).
44#[derive(Clone, Debug, Deserialize, Serialize)]
45#[non_exhaustive]
46pub struct CurrentApplicationInfo {
47    pub id: ApplicationId,
48    pub name: String,
49    pub icon: Option<ImageHash>,
50    pub description: String,
51    #[serde(default)]
52    pub rpc_origins: Vec<String>,
53    pub bot_public: bool,
54    pub bot_require_code_grant: bool,
55    #[serde(default)]
56    pub terms_of_service_url: Option<String>,
57    #[serde(default)]
58    pub privacy_policy_url: Option<String>,
59    pub owner: Option<User>,
60    // omitted `summary` because it deprecated
61    pub verify_key: String,
62    pub team: Option<Team>,
63    #[serde(default)]
64    pub guild_id: Option<GuildId>,
65    #[serde(default)]
66    pub primary_sku_id: Option<SkuId>,
67    #[serde(default)]
68    pub slug: Option<String>,
69    #[serde(default)]
70    pub cover_image: Option<String>,
71    #[serde(default)]
72    pub flags: Option<ApplicationFlags>,
73    #[serde(default)]
74    pub tags: Option<Vec<String>>,
75    #[serde(default)]
76    pub install_params: Option<InstallParams>,
77    #[serde(default)]
78    pub custom_install_url: Option<String>,
79    /// The application's role connection verification entry point, which when configured will
80    /// render the app as a verification method in the guild role verification configuration.
81    pub role_connections_verification_url: Option<String>,
82    #[serde(default)]
83    pub integration_types_config: HashMap<InstallationContext, InstallationContextConfig>,
84    pub approximate_guild_count: Option<u32>,
85    pub approximate_user_install_count: Option<u32>,
86    pub guild: Option<PartialGuild>,
87    pub redirect_uris: Option<Vec<String>>,
88    pub interactions_endpoint_url: Option<String>,
89}
90
91impl CurrentApplicationInfo {
92    /// Returns the store url for the application. If included in a message, will render as a rich
93    /// embed. See the [Discord docs] for details.
94    ///
95    /// [Discord docs]: https://discord.com/developers/docs/monetization/managing-your-store#linking-to-your-store
96    #[must_use]
97    pub fn store_url(&self) -> String {
98        format!("https://discord.com/application-directory/{}/store", self.id)
99    }
100}
101
102enum_number! {
103    /// An enum representing the [installation contexts].
104    ///
105    /// [interaction contexts](https://discord.com/developers/docs/resources/application#application-object-application-integration-types).
106    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
107    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
108    #[serde(from = "u8", into = "u8")]
109    #[non_exhaustive]
110    pub enum InstallationContext {
111        Guild = 0,
112        User = 1,
113        _ => Unknown(u8),
114    }
115}
116
117enum_number! {
118    /// An enum representing the different [interaction contexts].
119    ///
120    /// [interaction contexts](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-object-interaction-context-types).
121    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
122    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
123    #[serde(from = "u8", into = "u8")]
124    #[non_exhaustive]
125    pub enum InteractionContext {
126        /// Interaction can be used within servers
127        Guild = 0,
128        /// Interaction can be used within DMs with the app's bot user
129        BotDm = 1,
130        /// Interaction can be used within Group DMs and DMs other than the app's bot user
131        PrivateChannel = 2,
132        _ => Unknown(u8),
133    }
134}
135
136/// Information about how the [`CurrentApplicationInfo`] is installed.
137///
138/// [Discord docs](https://discord.com/developers/docs/resources/application#application-object-application-integration-types).
139#[derive(Clone, Debug, Deserialize, Serialize)]
140pub struct InstallationContextConfig {
141    pub oauth2_install_params: Option<InstallParams>,
142}
143
144/// Information about the Team group of the application.
145///
146/// [Discord docs](https://discord.com/developers/docs/topics/teams#data-models-team-object).
147#[derive(Clone, Debug, Deserialize, Serialize)]
148#[non_exhaustive]
149pub struct Team {
150    /// The icon of the team.
151    pub icon: Option<ImageHash>,
152    /// The snowflake ID of the team.
153    pub id: GenericId,
154    /// The name of the team.
155    pub name: String,
156    /// The members of the team
157    pub members: Vec<TeamMember>,
158    /// The user id of the team owner.
159    pub owner_user_id: UserId,
160}
161
162/// Information about a Member on a Team.
163///
164/// [Discord docs](https://discord.com/developers/docs/topics/teams#data-models-team-member-object).
165#[derive(Clone, Debug, Deserialize, Serialize)]
166#[non_exhaustive]
167pub struct TeamMember {
168    /// The member's membership state.
169    pub membership_state: MembershipState,
170    /// The list of permissions of the member on the team.
171    ///
172    /// NOTE: Will always be "*" for now.
173    #[deprecated = "This field is not sent by the API anymore"]
174    pub permissions: Vec<String>,
175    /// The ID of the team they are a member of.
176    pub team_id: GenericId,
177    /// The user type of the team member.
178    pub user: User,
179    /// The [`TeamMemberRole`] of the team member.
180    pub role: TeamMemberRole,
181}
182
183enum_number! {
184    /// [Discord docs](https://discord.com/developers/docs/topics/teams#data-models-membership-state-enum).
185    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
186    #[serde(from = "u8", into = "u8")]
187    #[non_exhaustive]
188    pub enum MembershipState {
189        Invited = 1,
190        Accepted = 2,
191        _ => Unknown(u8),
192    }
193}
194
195#[derive(Clone, Debug, Deserialize, Serialize)]
196#[serde(rename_all = "snake_case")]
197#[non_exhaustive]
198pub enum TeamMemberRole {
199    Admin,
200    Developer,
201    ReadOnly,
202    #[serde(untagged)]
203    Other(String),
204}
205
206impl TeamMemberRole {
207    fn discriminant(&self) -> u8 {
208        match self {
209            Self::Admin => 3,
210            Self::Developer => 2,
211            Self::ReadOnly => 1,
212            Self::Other(_) => 0,
213        }
214    }
215}
216
217impl PartialEq for TeamMemberRole {
218    fn eq(&self, other: &Self) -> bool {
219        self.discriminant() == other.discriminant()
220    }
221}
222
223impl Eq for TeamMemberRole {}
224
225impl PartialOrd for TeamMemberRole {
226    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
227        Some(self.cmp(other))
228    }
229}
230
231impl Ord for TeamMemberRole {
232    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
233        self.discriminant().cmp(&other.discriminant())
234    }
235}
236
237bitflags! {
238    /// The flags of the application.
239    ///
240    /// [Discord docs](https://discord.com/developers/docs/resources/application#application-object-application-flags).
241    #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
242    #[derive(Copy, Clone, Default, Debug, Eq, Hash, PartialEq)]
243    pub struct ApplicationFlags: u64 {
244        /// Indicates if an app uses the Auto Moderation API
245        const APPLICATION_AUTO_MODERATION_RULE_CREATE_BADGE = 1 << 6;
246        /// Intent required for bots in 100 or more servers to receive presence_update events
247        const GATEWAY_PRESENCE = 1 << 12;
248        /// Intent required for bots in under 100 servers to receive presence_update events, found
249        /// on the Bot page in your app's settings
250        const GATEWAY_PRESENCE_LIMITED = 1 << 13;
251        /// Intent required for bots in 100 or more servers to receive member-related events like
252        /// guild_member_add. See the list of member-related events under [GUILD_MEMBERS](https://discord.com/developers/docs/topics/gateway#list-of-intents)
253        const GATEWAY_GUILD_MEMBERS = 1 << 14;
254        /// Intent required for bots in under 100 servers to receive member-related events like
255        /// guild_member_add, found on the Bot page in your app's settings. See the list of
256        /// member-related events under [GUILD_MEMBERS](https://discord.com/developers/docs/topics/gateway#list-of-intents)
257        const GATEWAY_GUILD_MEMBERS_LIMITED = 1 << 15;
258        /// Indicates unusual growth of an app that prevents verification
259        const VERIFICATION_PENDING_GUILD_LIMIT = 1 << 16;
260        /// Indicates if an app is embedded within the Discord client (currently unavailable
261        /// publicly)
262        const EMBEDDED = 1 << 17;
263        /// Intent required for bots in 100 or more servers to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055).
264        const GATEWAY_MESSAGE_CONTENT = 1 << 18;
265        /// Intent required for bots in under 100 servers to receive [message content](https://support-dev.discord.com/hc/en-us/articles/4404772028055),
266        /// found on the Bot page in your app's settings
267        const GATEWAY_MESSAGE_CONTENT_LIMITED = 1 << 19;
268        /// Indicates if an app has registered global application commands
269        const APPLICATION_COMMAND_BADGE = 1 << 19;
270    }
271}
272
273/// Settings for the application's default in-app authorization link
274///
275/// [Discord docs](https://discord.com/developers/docs/resources/application#install-params-object-install-params-structure).
276#[derive(Debug, Clone, Serialize, Deserialize)]
277#[non_exhaustive]
278pub struct InstallParams {
279    pub scopes: Vec<Scope>,
280    pub permissions: Permissions,
281}
282
283#[cfg(test)]
284mod team_role_ordering {
285    use super::TeamMemberRole;
286
287    fn other(val: &str) -> TeamMemberRole {
288        TeamMemberRole::Other(String::from(val))
289    }
290
291    #[test]
292    fn test_normal_ordering() {
293        let mut roles = [
294            TeamMemberRole::Developer,
295            TeamMemberRole::Admin,
296            other(""),
297            TeamMemberRole::ReadOnly,
298            other("test"),
299        ];
300
301        roles.sort();
302
303        assert_eq!(roles, [
304            other(""),
305            other("test"),
306            TeamMemberRole::ReadOnly,
307            TeamMemberRole::Developer,
308            TeamMemberRole::Admin,
309        ]);
310    }
311
312    #[test]
313    fn test_other_eq() {
314        assert_eq!(other("").cmp(&other("")), std::cmp::Ordering::Equal);
315    }
316}