Skip to content

Commit

Permalink
feat(model): Implement user applications (#2323)
Browse files Browse the repository at this point in the history
  • Loading branch information
Erk- authored Oct 24, 2024
1 parent 6e0e2a5 commit 4fae289
Show file tree
Hide file tree
Showing 21 changed files with 357 additions and 50 deletions.
11 changes: 9 additions & 2 deletions twilight-cache-inmemory/src/event/interaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ mod tests {
gateway::payload::incoming::InteractionCreate,
guild::{MemberFlags, PartialMember, Permissions, Role, RoleFlags},
id::Id,
oauth::ApplicationIntegrationMap,
user::User,
util::{image_hash::ImageHashParseError, ImageHash, Timestamp},
};
Expand All @@ -97,6 +98,10 @@ mod tests {
cache.update(&InteractionCreate(Interaction {
app_permissions: Some(Permissions::SEND_MESSAGES),
application_id: Id::new(1),
authorizing_integration_owners: ApplicationIntegrationMap {
guild: None,
user: None,
},
channel: Some(Channel {
bitrate: None,
guild_id: None,
Expand Down Expand Up @@ -135,6 +140,7 @@ mod tests {
video_quality_mode: None,
}),
channel_id: Some(Id::new(2)),
context: None,
data: Some(InteractionData::ApplicationCommand(Box::new(CommandData {
guild_id: None,
id: Id::new(5),
Expand Down Expand Up @@ -195,6 +201,7 @@ mod tests {
guild_id: Some(Id::new(1)),
id: Id::new(4),
interaction: None,
interaction_metadata: None,
kind: MessageType::Regular,
member: Some(PartialMember {
avatar: None,
Expand All @@ -218,15 +225,15 @@ mod tests {
poll: None,
reactions: Vec::new(),
reference: None,
referenced_message: None,
role_subscription_data: None,
sticker_items: vec![MessageSticker {
format_type: StickerFormatType::Png,
id: Id::new(1),
name: "sticker name".to_owned(),
}],
referenced_message: None,
thread: None,
timestamp,
thread: None,
tts: false,
webhook_id: None,
},
Expand Down
6 changes: 4 additions & 2 deletions twilight-cache-inmemory/src/event/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ mod tests {
util::{image_hash::ImageHashParseError, ImageHash, Timestamp},
};

#[allow(deprecated)]
#[test]
fn message_create() -> Result<(), ImageHashParseError> {
let joined_at = Some(Timestamp::from_secs(1_632_072_645).expect("non zero"));
Expand Down Expand Up @@ -147,6 +148,7 @@ mod tests {
guild_id: Some(Id::new(1)),
id: Id::new(4),
interaction: None,
interaction_metadata: None,
kind: MessageType::Regular,
member: Some(PartialMember {
avatar: None,
Expand All @@ -170,11 +172,11 @@ mod tests {
poll: None,
reactions: Vec::new(),
reference: None,
referenced_message: None,
role_subscription_data: None,
sticker_items: Vec::new(),
thread: None,
referenced_message: None,
timestamp: Timestamp::from_secs(1_632_072_645).expect("non zero"),
thread: None,
tts: false,
webhook_id: None,
};
Expand Down
3 changes: 3 additions & 0 deletions twilight-cache-inmemory/src/model/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ impl CachedMessage {
}

impl From<Message> for CachedMessage {
#[allow(deprecated)]
fn from(message: Message) -> Self {
let Message {
activity,
Expand All @@ -318,6 +319,7 @@ impl From<Message> for CachedMessage {
guild_id,
id,
interaction,
interaction_metadata: _,
kind,
member,
mention_channels,
Expand Down Expand Up @@ -376,6 +378,7 @@ impl From<Message> for CachedMessage {
}

impl PartialEq<Message> for CachedMessage {
#[allow(deprecated)]
fn eq(&self, other: &Message) -> bool {
self.id == other.id
&& self.activity == other.activity
Expand Down
7 changes: 4 additions & 3 deletions twilight-cache-inmemory/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub fn cache() -> DefaultInMemoryCache {
DefaultInMemoryCache::new()
}

#[allow(clippy::too_many_lines)]
#[allow(clippy::too_many_lines, deprecated)]
pub fn cache_with_message_and_reactions() -> DefaultInMemoryCache {
let joined_at = Some(Timestamp::from_secs(1_632_072_645).expect("non zero"));
let cache = DefaultInMemoryCache::new();
Expand Down Expand Up @@ -75,6 +75,7 @@ pub fn cache_with_message_and_reactions() -> DefaultInMemoryCache {
guild_id: Some(Id::new(1)),
id: Id::new(4),
interaction: None,
interaction_metadata: None,
kind: MessageType::Regular,
member: Some(PartialMember {
avatar: None,
Expand All @@ -98,11 +99,11 @@ pub fn cache_with_message_and_reactions() -> DefaultInMemoryCache {
poll: None,
reactions: Vec::new(),
reference: None,
referenced_message: None,
role_subscription_data: None,
sticker_items: Vec::new(),
thread: None,
referenced_message: None,
timestamp: Timestamp::from_secs(1_632_072_645).expect("non zero"),
thread: None,
tts: false,
webhook_id: None,
};
Expand Down
3 changes: 3 additions & 0 deletions twilight-http/src/request/application/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,11 @@ mod tests {
/// `Command` or a type is changed then the destructure of it and creation
/// of `CommandBorrowed` will fail.
#[test]
#[allow(deprecated)]
fn command_borrowed_from_command() {
let command = Command {
application_id: Some(Id::new(1)),
contexts: None,
default_member_permissions: Some(Permissions::ADMINISTRATOR),
dm_permission: Some(true),
description: "command description".to_owned(),
Expand All @@ -88,6 +90,7 @@ mod tests {
)])),
guild_id: Some(Id::new(2)),
id: Some(Id::new(3)),
integration_types: None,
kind: CommandType::ChatInput,
name: "command name".to_owned(),
name_localizations: Some(HashMap::from([(
Expand Down
26 changes: 25 additions & 1 deletion twilight-http/src/request/update_user_application.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::future::IntoFuture;

use serde::Serialize;
use twilight_model::oauth::{Application, ApplicationFlags, InstallParams};
use twilight_model::oauth::{
Application, ApplicationFlags, ApplicationIntegrationMap, ApplicationIntegrationTypeConfig,
InstallParams,
};

use crate::{
client::Client,
Expand All @@ -26,6 +29,8 @@ struct UpdateCurrentUserApplicationFields<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
install_params: Option<InstallParams>,
#[serde(skip_serializing_if = "Option::is_none")]
integration_types_config: Option<ApplicationIntegrationMap<ApplicationIntegrationTypeConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
interactions_endpoint_url: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
role_connections_verification_url: Option<&'a str>,
Expand Down Expand Up @@ -77,6 +82,7 @@ impl<'a> UpdateCurrentUserApplication<'a> {
flags: None,
icon: None,
install_params: None,
integration_types_config: None,
interactions_endpoint_url: None,
role_connections_verification_url: None,
tags: None,
Expand Down Expand Up @@ -129,6 +135,24 @@ impl<'a> UpdateCurrentUserApplication<'a> {
self
}

pub fn integrations_types_config(
mut self,
guild: Option<InstallParams>,
user: Option<InstallParams>,
) -> Self {
let guild = guild.map(|g| ApplicationIntegrationTypeConfig {
oauth2_install_params: Some(g),
});

let user = user.map(|u| ApplicationIntegrationTypeConfig {
oauth2_install_params: Some(u),
});

self.fields.integration_types_config = Some(ApplicationIntegrationMap { guild, user });

self
}

/// Sets the interactions endpoint URL of the application.
pub const fn interactions_endpoint_url(mut self, interactions_endpoint_url: &'a str) -> Self {
self.fields.interactions_endpoint_url = Some(interactions_endpoint_url);
Expand Down
6 changes: 6 additions & 0 deletions twilight-model/benches/deserialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ fn member_chunk() {
"members": [{
"deaf": false,
"hoisted_role": "6",
"flags": 0,
"joined_at": "2020-04-04T04:04:04.000000+00:00",
"mute": false,
"nick": "chunk",
Expand All @@ -48,6 +49,7 @@ fn member_chunk() {
}, {
"deaf": false,
"hoisted_role": "6",
"flags": 0,
"joined_at": "2020-04-04T04:04:04.000000+00:00",
"mute": false,
"nick": "chunk",
Expand All @@ -61,6 +63,7 @@ fn member_chunk() {
}, {
"deaf": false,
"hoisted_role": "6",
"flags": 0,
"joined_at": "2020-04-04T04:04:04.000000+00:00",
"mute": false,
"nick": "chunk",
Expand All @@ -75,6 +78,7 @@ fn member_chunk() {
}, {
"deaf": false,
"hoisted_role": "6",
"flags": 0,
"joined_at": "2020-04-04T04:04:04.000000+00:00",
"mute": false,
"nick": "chunk",
Expand Down Expand Up @@ -134,6 +138,7 @@ fn reaction() {
"member": {
"deaf": false,
"hoisted_role": "5",
"flags": 0,
"joined_at": "2020-01-01T00:00:00.000000+00:00",
"mute": false,
"nick": "typing",
Expand All @@ -159,6 +164,7 @@ fn typing_start() {
"member": {
"deaf": false,
"hoisted_role": "4",
"flags": 0,
"joined_at": "2020-01-01T00:00:00.000000+00:00",
"mute": false,
"nick": "typing",
Expand Down
12 changes: 11 additions & 1 deletion twilight-model/src/application/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ use crate::{
marker::{ApplicationMarker, CommandMarker, CommandVersionMarker, GuildMarker},
Id,
},
oauth::ApplicationIntegrationType,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use super::interaction::InteractionContextType;

/// Data sent to Discord to create a command.
///
/// [`CommandOption`]s that are required must be listed before optional ones.
Expand All @@ -41,6 +44,8 @@ use std::collections::HashMap;
pub struct Command {
#[serde(skip_serializing_if = "Option::is_none")]
pub application_id: Option<Id<ApplicationMarker>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub contexts: Option<Vec<InteractionContextType>>,
/// Default permissions required for a member to run the command.
///
/// Setting this [`Permissions::empty()`] will prohibit anyone from running
Expand All @@ -50,6 +55,7 @@ pub struct Command {
///
/// This is only relevant for globally-scoped commands. By default, commands
/// are visible in DMs.
#[deprecated(note = "use contexts instead")]
#[serde(skip_serializing_if = "Option::is_none")]
pub dm_permission: Option<bool>,
/// Description of the command.
Expand All @@ -71,6 +77,8 @@ pub struct Command {
pub guild_id: Option<Id<GuildMarker>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<Id<CommandMarker>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub integration_types: Option<Vec<ApplicationIntegrationType>>,
#[serde(rename = "type")]
pub kind: CommandType,
pub name: String,
Expand Down Expand Up @@ -105,10 +113,11 @@ mod tests {
use std::collections::HashMap;

#[test]
#[allow(clippy::too_many_lines)]
#[allow(clippy::too_many_lines, deprecated)]
fn command_option_full() {
let value = Command {
application_id: Some(Id::new(100)),
contexts: None,
default_member_permissions: Some(Permissions::ADMINISTRATOR),
dm_permission: Some(false),
description: "this command is a test".into(),
Expand All @@ -118,6 +127,7 @@ mod tests {
)])),
guild_id: Some(Id::new(300)),
id: Some(Id::new(200)),
integration_types: None,
kind: CommandType::ChatInput,
name: "test command".into(),
name_localizations: Some(HashMap::from([("en-US".into(), "test command".into())])),
Expand Down
49 changes: 49 additions & 0 deletions twilight-model/src/application/interaction/context_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[non_exhaustive]
#[serde(from = "u8", into = "u8")]
pub enum InteractionContextType {
/// Interaction can be used within servers.
Guild,
/// Interaction can be used within DMs with the app's bot user.
BotDm,
/// Interaction can be used within Group DMs and DMs other than
/// the app's bot user.
PrivateChannel,
/// Variant value is unknown to the library.
Unknown(u8),
}

impl InteractionContextType {
pub const fn kind(self) -> &'static str {
match self {
Self::Guild => "GUILD",
Self::BotDm => "BOT_DM",
Self::PrivateChannel => "PRIVATE_CHANNEL",
Self::Unknown(_) => "Unknown",
}
}
}

impl From<u8> for InteractionContextType {
fn from(value: u8) -> Self {
match value {
0 => Self::Guild,
1 => Self::BotDm,
2 => Self::PrivateChannel,
unknown => Self::Unknown(unknown),
}
}
}

impl From<InteractionContextType> for u8 {
fn from(value: InteractionContextType) -> Self {
match value {
InteractionContextType::Guild => 0,
InteractionContextType::BotDm => 1,
InteractionContextType::PrivateChannel => 2,
InteractionContextType::Unknown(unknown) => unknown,
}
}
}
Loading

0 comments on commit 4fae289

Please sign in to comment.