Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(model): Implement user applications #2323

Merged
merged 9 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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