From 3231e45c20aaf1a7ed04eccbf2fd5c266cc09198 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Tue, 8 Jul 2025 10:55:43 +0800 Subject: [PATCH 01/27] feat(gateway): add REQUEST_SOUNDBOARD_SOUNDS --- twilight-gateway/src/command.rs | 8 ++++---- twilight-model/src/gateway/opcode.rs | 7 +++++++ .../src/gateway/payload/outgoing/mod.rs | 4 +++- .../outgoing/request_soundboard_sounds.rs | 17 +++++++++++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 twilight-model/src/gateway/payload/outgoing/request_soundboard_sounds.rs diff --git a/twilight-gateway/src/command.rs b/twilight-gateway/src/command.rs index 61e4da0205..dc268949cc 100644 --- a/twilight-gateway/src/command.rs +++ b/twilight-gateway/src/command.rs @@ -2,9 +2,7 @@ //! //! [`Shard::command`]: crate::Shard::command -use twilight_model::gateway::payload::outgoing::{ - RequestGuildMembers, UpdatePresence, UpdateVoiceState, -}; +use twilight_model::gateway::payload::outgoing::{RequestGuildMembers, RequestSoundboardSounds, UpdatePresence, UpdateVoiceState}; mod private { //! Private module to provide a sealed trait depended on by [`Command`], @@ -14,13 +12,14 @@ mod private { use serde::Serialize; use twilight_model::gateway::payload::outgoing::{ - RequestGuildMembers, UpdatePresence, UpdateVoiceState, + RequestGuildMembers, RequestSoundboardSounds, UpdatePresence, UpdateVoiceState, }; /// Sealed trait to prevent users from implementing the Command trait. pub trait Sealed: Serialize {} impl Sealed for RequestGuildMembers {} + impl Sealed for RequestSoundboardSounds {} impl Sealed for UpdatePresence {} impl Sealed for UpdateVoiceState {} } @@ -44,6 +43,7 @@ mod private { pub trait Command: private::Sealed {} impl Command for RequestGuildMembers {} +impl Command for RequestSoundboardSounds {} impl Command for UpdatePresence {} impl Command for UpdateVoiceState {} diff --git a/twilight-model/src/gateway/opcode.rs b/twilight-model/src/gateway/opcode.rs index 31ddde99fa..d8abf7fec2 100644 --- a/twilight-model/src/gateway/opcode.rs +++ b/twilight-model/src/gateway/opcode.rs @@ -37,6 +37,8 @@ pub enum OpCode { /// /// [`Heartbeat`]: Self::Heartbeat HeartbeatAck = 11, + /// Request a list of soundboard sounds for a list of guilds. + RequestSoundboardSounds = 31, } impl OpCode { @@ -55,6 +57,7 @@ impl OpCode { 9 => Self::InvalidSession, 10 => Self::Hello, 11 => Self::HeartbeatAck, + 31 => Self::RequestSoundboardSounds, _ => return None, }) } @@ -97,6 +100,7 @@ impl OpCode { /// - [`PresenceUpdate`] /// - [`Resume`] /// - [`RequestGuildMembers`] + /// - [`RequestSoundboardSounds`] /// - [`VoiceStateUpdate`] /// /// [`Heartbeat`]: Self::Heartbeat @@ -104,6 +108,7 @@ impl OpCode { /// [`PresenceUpdate`]: Self::PresenceUpdate /// [`Resume`]: Self::Resume /// [`RequestGuildMembers`]: Self::RequestGuildMembers + /// [`RequestSoundboardSounds`]: Self::RequestSoundboardSounds /// [`VoiceStateUpdate`]: Self::VoiceStateUpdate pub const fn is_sent(self) -> bool { matches!( @@ -113,6 +118,7 @@ impl OpCode { | Self::PresenceUpdate | Self::Resume | Self::RequestGuildMembers + | Self::RequestSoundboardSounds | Self::VoiceStateUpdate ) } @@ -150,6 +156,7 @@ mod tests { (OpCode::InvalidSession, 9, true, false), (OpCode::Hello, 10, true, false), (OpCode::HeartbeatAck, 11, true, false), + (OpCode::RequestSoundboardSounds, 31, false, true), ]; #[test] diff --git a/twilight-model/src/gateway/payload/outgoing/mod.rs b/twilight-model/src/gateway/payload/outgoing/mod.rs index eaa7bb0fe3..774c125f84 100644 --- a/twilight-model/src/gateway/payload/outgoing/mod.rs +++ b/twilight-model/src/gateway/payload/outgoing/mod.rs @@ -12,6 +12,7 @@ pub mod identify; pub mod request_guild_members; +pub mod request_soundboard_sounds; pub mod resume; pub mod update_presence; pub mod update_voice_state; @@ -20,5 +21,6 @@ mod heartbeat; pub use self::{ heartbeat::Heartbeat, identify::Identify, request_guild_members::RequestGuildMembers, - resume::Resume, update_presence::UpdatePresence, update_voice_state::UpdateVoiceState, + request_soundboard_sounds::RequestSoundboardSounds, resume::Resume, + update_presence::UpdatePresence, update_voice_state::UpdateVoiceState, }; diff --git a/twilight-model/src/gateway/payload/outgoing/request_soundboard_sounds.rs b/twilight-model/src/gateway/payload/outgoing/request_soundboard_sounds.rs new file mode 100644 index 0000000000..bf50e9a58f --- /dev/null +++ b/twilight-model/src/gateway/payload/outgoing/request_soundboard_sounds.rs @@ -0,0 +1,17 @@ +use crate::{ + gateway::OpCode, + id::{marker::GuildMarker, Id}, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct RequestSoundboardSounds { + pub d: RequestSoundboardSoundsInfo, + pub op: OpCode, +} + +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct RequestSoundboardSoundsInfo { + /// Guild IDs to request soundboard sounds for. + pub guild_ids: Vec>, +} From 4ee0e511759959fc559bc695b44c9a0b68197415 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Tue, 8 Jul 2025 11:20:47 +0800 Subject: [PATCH 02/27] feat: update event type flags and gateway intents --- twilight-gateway/src/command.rs | 4 ++- twilight-gateway/src/event.rs | 36 ++++++++++++++++++++++-- twilight-model/src/gateway/event/kind.rs | 31 ++++++++++++++++++++ twilight-model/src/gateway/intents.rs | 28 +++++++++++++++++- 4 files changed, 94 insertions(+), 5 deletions(-) diff --git a/twilight-gateway/src/command.rs b/twilight-gateway/src/command.rs index dc268949cc..dfc570224a 100644 --- a/twilight-gateway/src/command.rs +++ b/twilight-gateway/src/command.rs @@ -2,7 +2,9 @@ //! //! [`Shard::command`]: crate::Shard::command -use twilight_model::gateway::payload::outgoing::{RequestGuildMembers, RequestSoundboardSounds, UpdatePresence, UpdateVoiceState}; +use twilight_model::gateway::payload::outgoing::{ + RequestGuildMembers, RequestSoundboardSounds, UpdatePresence, UpdateVoiceState, +}; mod private { //! Private module to provide a sealed trait depended on by [`Command`], diff --git a/twilight-gateway/src/event.rs b/twilight-gateway/src/event.rs index 86e1ce1eb7..d09398e3ae 100644 --- a/twilight-gateway/src/event.rs +++ b/twilight-gateway/src/event.rs @@ -105,6 +105,14 @@ bitflags! { const GUILD_SCHEDULED_EVENT_USER_ADD = 1 << 67; /// A guild's integrations have been updated. const GUILD_SCHEDULED_EVENT_USER_REMOVE = 1 << 68; + /// A guild's soundboard sound have been updated + const GUILD_SOUNDBOARD_SOUND_CREATE = 1 << 79; + /// A guild's soundboard sound have been updated + const GUILD_SOUNDBOARD_SOUND_DELETE = 1 << 80; + /// A guild's soundboard sound have been updated + const GUILD_SOUNDBOARD_SOUND_UPDATE = 1 << 81; + /// A guild's soundboard sounds have been updated + const GUILD_SOUNDBOARD_SOUNDS_UPDATE = 1 << 82; /// A guild's stickers have been updated. const GUILD_STICKERS_UPDATE = 1 << 63; /// A guild has been updated. @@ -189,6 +197,9 @@ bitflags! { const UNAVAILABLE_GUILD = 1 << 40; /// Current user's profile has been updated. const USER_UPDATE = 1 << 41; + /// Someone sends an effect (emoji or soundboard sound) in a voice + /// channel the current user is connected to. + const VOICE_CHANNEL_EFFECT_SEND = 1 << 45; /// Voice server has provided an update with voice session details. const VOICE_SERVER_UPDATE = 1 << 42; /// User's state in a voice channel has been updated. @@ -253,8 +264,22 @@ bitflags! { /// All [`EventTypeFlags`] in [`Intents::GUILD_EMOJIS_AND_STICKERS`]. /// /// [`Intents::GUILD_EMOJIS_AND_STICKERS`]: crate::Intents::GUILD_EMOJIS_AND_STICKERS + #[deprecated(since = "0.17.0", note = "use `GUILD_EXPRESSIONS` instead")] const GUILD_EMOJIS_AND_STICKERS = Self::GUILD_EMOJIS_UPDATE.bits() - | Self::GUILD_STICKERS_UPDATE.bits(); + | Self::GUILD_STICKERS_UPDATE.bits() + | Self::GUILD_SOUNDBOARD_SOUND_CREATE.bits() + | Self::GUILD_SOUNDBOARD_SOUND_DELETE.bits() + | Self::GUILD_SOUNDBOARD_SOUND_UPDATE.bits() + | Self::GUILD_SOUNDBOARD_SOUNDS_UPDATE.bits(); + /// All [`EventTypeFlags`] in [`Intents::GUILD_EXPRESSIONS`]. + /// + /// [`Intents::GUILD_EXPRESSIONS`]: crate::Intents::GUILD_EXPRESSIONS + const GUILD_EXPRESSIONS = Self::GUILD_EMOJIS_UPDATE.bits() + | Self::GUILD_STICKERS_UPDATE.bits() + | Self::GUILD_SOUNDBOARD_SOUND_CREATE.bits() + | Self::GUILD_SOUNDBOARD_SOUND_DELETE.bits() + | Self::GUILD_SOUNDBOARD_SOUND_UPDATE.bits() + | Self::GUILD_SOUNDBOARD_SOUNDS_UPDATE.bits(); /// All [`EventTypeFlags`] in [`Intents::GUILD_INTEGRATIONS`]. /// @@ -322,13 +347,13 @@ bitflags! { /// All [`EventTypeFlags`] in [`Intents::GUILD_VOICE_STATES`]. /// /// [`Intents::GUILD_VOICE_STATES`]: crate::Intents::GUILD_VOICE_STATES - const GUILD_VOICE_STATES = Self::VOICE_STATE_UPDATE.bits(); + const GUILD_VOICE_STATES = Self::VOICE_STATE_UPDATE.bits() + | Self::VOICE_CHANNEL_EFFECT_SEND.bits(); /// All [`EventTypeFlags`] in [`Intents::GUILD_WEBHOOKS`]. /// /// [`Intents::GUILD_WEBHOOKS`]: crate::Intents::GUILD_WEBHOOKS const GUILD_WEBHOOKS = Self::WEBHOOKS_UPDATE.bits(); - } } @@ -365,6 +390,10 @@ impl From for EventTypeFlags { EventType::GuildScheduledEventUpdate => Self::GUILD_SCHEDULED_EVENT_UPDATE, EventType::GuildScheduledEventUserAdd => Self::GUILD_SCHEDULED_EVENT_USER_ADD, EventType::GuildScheduledEventUserRemove => Self::GUILD_SCHEDULED_EVENT_USER_REMOVE, + EventType::GuildSoundboardSoundCreate => Self::GUILD_SOUNDBOARD_SOUND_CREATE, + EventType::GuildSoundboardSoundDelete => Self::GUILD_SOUNDBOARD_SOUND_DELETE, + EventType::GuildSoundboardSoundUpdate => Self::GUILD_SOUNDBOARD_SOUND_UPDATE, + EventType::GuildSoundboardSoundsUpdate => Self::GUILD_SOUNDBOARD_SOUNDS_UPDATE, EventType::GuildStickersUpdate => Self::GUILD_STICKERS_UPDATE, EventType::GuildUpdate => Self::GUILD_UPDATE, EventType::IntegrationCreate => Self::INTEGRATION_CREATE, @@ -405,6 +434,7 @@ impl From for EventTypeFlags { EventType::TypingStart => Self::TYPING_START, EventType::UnavailableGuild => Self::UNAVAILABLE_GUILD, EventType::UserUpdate => Self::USER_UPDATE, + EventType::VoiceChannelEffectSend => Self::VOICE_CHANNEL_EFFECT_SEND, EventType::VoiceServerUpdate => Self::VOICE_SERVER_UPDATE, EventType::VoiceStateUpdate => Self::VOICE_STATE_UPDATE, EventType::WebhooksUpdate => Self::WEBHOOKS_UPDATE, diff --git a/twilight-model/src/gateway/event/kind.rs b/twilight-model/src/gateway/event/kind.rs index 5dfeeb77f1..a08e54fc1c 100644 --- a/twilight-model/src/gateway/event/kind.rs +++ b/twilight-model/src/gateway/event/kind.rs @@ -38,6 +38,10 @@ pub enum EventType { GuildScheduledEventUserAdd, GuildScheduledEventUserRemove, GuildStickersUpdate, + GuildSoundboardSoundCreate, + GuildSoundboardSoundDelete, + GuildSoundboardSoundUpdate, + GuildSoundboardSoundsUpdate, GuildUpdate, IntegrationCreate, IntegrationDelete, @@ -88,6 +92,7 @@ pub enum EventType { TypingStart, UnavailableGuild, UserUpdate, + VoiceChannelEffectSend, VoiceServerUpdate, VoiceStateUpdate, WebhooksUpdate, @@ -121,6 +126,10 @@ impl EventType { Self::GuildScheduledEventUserAdd => Some("GUILD_SCHEDULED_EVENT_USER_ADD"), Self::GuildScheduledEventUserRemove => Some("GUILD_SCHEDULED_EVENT_USER_REMOVE"), Self::GuildStickersUpdate => Some("GUILD_STICKERS_UPDATE"), + Self::GuildSoundboardSoundCreate => Some("GUILD_SOUNDBOARD_SOUND_CREATE"), + Self::GuildSoundboardSoundDelete => Some("GUILD_SOUNDBOARD_SOUND_DELETE"), + Self::GuildSoundboardSoundUpdate => Some("GUILD_SOUNDBOARD_SOUND_UPDATE"), + Self::GuildSoundboardSoundsUpdate => Some("GUILD_SOUNDBOARD_SOUNDS_UPDATE"), Self::GuildUpdate => Some("GUILD_UPDATE"), Self::IntegrationCreate => Some("INTEGRATION_CREATE"), Self::IntegrationDelete => Some("INTEGRATION_DELETE"), @@ -160,6 +169,7 @@ impl EventType { Self::TypingStart => Some("TYPING_START"), Self::UnavailableGuild => Some("UNAVAILABLE_GUILD"), Self::UserUpdate => Some("USER_UPDATE"), + Self::VoiceChannelEffectSend => Some("VOICE_CHANNEL_EFFECT_SEND"), Self::VoiceServerUpdate => Some("VOICE_SERVER_UPDATE"), Self::VoiceStateUpdate => Some("VOICE_STATE_UPDATE"), Self::WebhooksUpdate => Some("WEBHOOKS_UPDATE"), @@ -200,6 +210,10 @@ impl<'a> TryFrom<&'a str> for EventType { "GUILD_SCHEDULED_EVENT_UPDATE" => Ok(Self::GuildScheduledEventUpdate), "GUILD_SCHEDULED_EVENT_USER_ADD" => Ok(Self::GuildScheduledEventUserAdd), "GUILD_SCHEDULED_EVENT_USER_REMOVE" => Ok(Self::GuildScheduledEventUserRemove), + "GUILD_SOUNDBOARD_SOUND_CREATE" => Ok(Self::GuildSoundboardSoundCreate), + "GUILD_SOUNDBOARD_SOUND_DELETE" => Ok(Self::GuildSoundboardSoundDelete), + "GUILD_SOUNDBOARD_SOUND_UPDATE" => Ok(Self::GuildSoundboardSoundUpdate), + "GUILD_SOUNDBOARD_SOUNDS_UPDATE" => Ok(Self::GuildSoundboardSoundsUpdate), "GUILD_UPDATE" => Ok(Self::GuildUpdate), "INTEGRATION_CREATE" => Ok(Self::IntegrationCreate), "INTEGRATION_DELETE" => Ok(Self::IntegrationDelete), @@ -239,6 +253,7 @@ impl<'a> TryFrom<&'a str> for EventType { "TYPING_START" => Ok(Self::TypingStart), "UNAVAILABLE_GUILD" => Ok(Self::UnavailableGuild), "USER_UPDATE" => Ok(Self::UserUpdate), + "VOICE_CHANNEL_EFFECT_SEND" => Ok(Self::VoiceChannelEffectSend), "VOICE_SERVER_UPDATE" => Ok(Self::VoiceServerUpdate), "VOICE_STATE_UPDATE" => Ok(Self::VoiceStateUpdate), "WEBHOOKS_UPDATE" => Ok(Self::WebhooksUpdate), @@ -331,6 +346,22 @@ mod tests { EventType::GuildScheduledEventUserRemove, "GUILD_SCHEDULED_EVENT_USER_REMOVE", ); + assert_variant( + EventType::GuildSoundboardSoundCreate, + "GUILD_SOUNDBOARD_SOUND_CREATE", + ); + assert_variant( + EventType::GuildSoundboardSoundDelete, + "GUILD_SOUNDBOARD_SOUND_DELETE", + ); + assert_variant( + EventType::GuildSoundboardSoundUpdate, + "GUILD_SOUNDBOARD_SOUND_UPDATE", + ); + assert_variant( + EventType::GuildSoundboardSoundsUpdate, + "GUILD_SOUNDBOARD_SOUNDS_UPDATE", + ); assert_variant(EventType::GuildUpdate, "GUILD_UPDATE"); assert_variant(EventType::IntegrationCreate, "INTEGRATION_CREATE"); assert_variant(EventType::IntegrationDelete, "INTEGRATION_DELETE"); diff --git a/twilight-model/src/gateway/intents.rs b/twilight-model/src/gateway/intents.rs index 640d3e19d1..aed768cffc 100644 --- a/twilight-model/src/gateway/intents.rs +++ b/twilight-model/src/gateway/intents.rs @@ -81,7 +81,7 @@ bitflags! { /// - [`GUILD_BAN_ADD`] /// - [`GUILD_BAN_REMOVE`] /// - /// [`GUILD_AUDIT_LOG_ENTRY_CREATE`]: super::event::Event:: + /// [`GUILD_AUDIT_LOG_ENTRY_CREATE`]: super::event::Event::GuildAuditLogEntryCreate /// [`GUILD_BAN_ADD`]: super::event::Event::BanAdd /// [`GUILD_BAN_REMOVE`]: super::event::Event::BanRemove const GUILD_MODERATION = 1 << 2; @@ -90,10 +90,36 @@ bitflags! { /// Event(s) received: /// - [`GUILD_EMOJIS_UPDATE`] /// - [`GUILD_STICKERS_UPDATE`] + /// - [`GUILD_SOUNDBOARD_SOUND_CREATE`] + /// - [`GUILD_SOUNDBOARD_SOUND_DELETE`] + /// - [`GUILD_SOUNDBOARD_SOUND_UPDATE`] + /// - [`GUILD_SOUNDBOARD_SOUNDS_UPDATE`] /// /// [`GUILD_EMOJIS_UPDATE`]: super::event::Event::GuildEmojisUpdate /// [`GUILD_STICKERS_UPDATE`]: super::event::Event::GuildStickersUpdate + /// [`GUILD_SOUNDBOARD_SOUND_CREATE`]: + /// [`GUILD_SOUNDBOARD_SOUND_DELETE`]: + /// [`GUILD_SOUNDBOARD_SOUND_UPDATE`]: + /// [`GUILD_SOUNDBOARD_SOUNDS_UPDATE`]: + #[deprecated(since = "0.17.0", note = "use `GUILD_EXPRESSIONS` instead")] const GUILD_EMOJIS_AND_STICKERS = 1 << 3; + /// Guild expressions intent. + /// + /// Event(s) received: + /// - [`GUILD_EMOJIS_UPDATE`] + /// - [`GUILD_STICKERS_UPDATE`] + /// - [`GUILD_SOUNDBOARD_SOUND_CREATE`] + /// - [`GUILD_SOUNDBOARD_SOUND_DELETE`] + /// - [`GUILD_SOUNDBOARD_SOUND_UPDATE`] + /// - [`GUILD_SOUNDBOARD_SOUNDS_UPDATE`] + /// + /// [`GUILD_EMOJIS_UPDATE`]: super::event::Event::GuildEmojisUpdate + /// [`GUILD_STICKERS_UPDATE`]: super::event::Event::GuildStickersUpdate + /// [`GUILD_SOUNDBOARD_SOUND_CREATE`]: + /// [`GUILD_SOUNDBOARD_SOUND_DELETE`]: + /// [`GUILD_SOUNDBOARD_SOUND_UPDATE`]: + /// [`GUILD_SOUNDBOARD_SOUNDS_UPDATE`]: + const GUILD_EXPRESSIONS = 1 << 3; /// Guild integrations intent. /// /// Event(s) received: From 5e4b6be5497eb83abc87e7cf9f2d82d7c4653335 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Tue, 8 Jul 2025 11:24:41 +0800 Subject: [PATCH 03/27] fix: request soundboard sounds is not an event from the gatway --- twilight-model/src/gateway/event/gateway.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/twilight-model/src/gateway/event/gateway.rs b/twilight-model/src/gateway/event/gateway.rs index 3c12fbad87..a8c6db273d 100644 --- a/twilight-model/src/gateway/event/gateway.rs +++ b/twilight-model/src/gateway/event/gateway.rs @@ -332,6 +332,12 @@ impl<'de> Visitor<'de> for GatewayEventVisitor<'_> { VALID_OPCODES, )) } + OpCode::RequestSoundboardSounds => { + return Err(DeError::unknown_variant( + "RequestSoundboardSounds", + VALID_OPCODES, + )) + } OpCode::Resume => return Err(DeError::unknown_variant("Resume", VALID_OPCODES)), OpCode::PresenceUpdate => { return Err(DeError::unknown_variant("PresenceUpdate", VALID_OPCODES)) From 255ad44a9cbc558df09501abdcc204b058138c55 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Tue, 8 Jul 2025 23:54:51 +0800 Subject: [PATCH 04/27] model: add soundboard sound and incoming payloads --- .../incoming/guild_soundboard_sound_create.rs | 6 +++++ .../incoming/guild_soundboard_sound_delete.rs | 12 +++++++++ .../incoming/guild_soundboard_sound_update.rs | 6 +++++ .../guild_soundboard_sounds_update.rs | 12 +++++++++ .../src/gateway/payload/incoming/mod.rs | 9 +++++++ .../incoming/voice_channel_effect_send.rs | 1 + twilight-model/src/guild/mod.rs | 3 ++- twilight-model/src/guild/soundboard.rs | 26 +++++++++++++++++++ twilight-model/src/id/marker.rs | 9 +++++++ 9 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_create.rs create mode 100644 twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_delete.rs create mode 100644 twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_update.rs create mode 100644 twilight-model/src/gateway/payload/incoming/guild_soundboard_sounds_update.rs create mode 100644 twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs create mode 100644 twilight-model/src/guild/soundboard.rs diff --git a/twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_create.rs b/twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_create.rs new file mode 100644 index 0000000000..581ecf36d2 --- /dev/null +++ b/twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_create.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +use crate::guild::SoundboardSound; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct GuildSoundboardSoundCreate(pub SoundboardSound); diff --git a/twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_delete.rs b/twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_delete.rs new file mode 100644 index 0000000000..bb4c727a4c --- /dev/null +++ b/twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_delete.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +use crate::id::{ + marker::{GuildMarker, SoundboardSoundMarker}, + Id, +}; + +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct GuildSoundboardSoundDelete { + pub guild_id: Id, + pub sound_id: Id, +} diff --git a/twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_update.rs b/twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_update.rs new file mode 100644 index 0000000000..b22c6b9366 --- /dev/null +++ b/twilight-model/src/gateway/payload/incoming/guild_soundboard_sound_update.rs @@ -0,0 +1,6 @@ +use serde::{Deserialize, Serialize}; + +use crate::guild::SoundboardSound; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct GuildSoundboardSoundUpdate(pub SoundboardSound); diff --git a/twilight-model/src/gateway/payload/incoming/guild_soundboard_sounds_update.rs b/twilight-model/src/gateway/payload/incoming/guild_soundboard_sounds_update.rs new file mode 100644 index 0000000000..b6db690f36 --- /dev/null +++ b/twilight-model/src/gateway/payload/incoming/guild_soundboard_sounds_update.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + guild::SoundboardSound, + id::{marker::GuildMarker, Id}, +}; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct GuildSoundboardSoundsUpdate { + pub guild_id: Id, + pub soundboard_sounds: Vec, +} diff --git a/twilight-model/src/gateway/payload/incoming/mod.rs b/twilight-model/src/gateway/payload/incoming/mod.rs index d2dfac3274..fd87e4fa1b 100644 --- a/twilight-model/src/gateway/payload/incoming/mod.rs +++ b/twilight-model/src/gateway/payload/incoming/mod.rs @@ -38,6 +38,10 @@ mod guild_scheduled_event_delete; mod guild_scheduled_event_update; mod guild_scheduled_event_user_add; mod guild_scheduled_event_user_remove; +mod guild_soundboard_sound_create; +mod guild_soundboard_sound_delete; +mod guild_soundboard_sound_update; +mod guild_soundboard_sounds_update; mod guild_stickers_update; mod guild_update; mod hello; @@ -76,6 +80,7 @@ mod thread_update; mod typing_start; mod unavailable_guild; mod user_update; +mod voice_channel_effect_send; mod voice_server_update; mod voice_state_update; mod webhooks_update; @@ -97,6 +102,10 @@ pub use self::{ guild_scheduled_event_update::GuildScheduledEventUpdate, guild_scheduled_event_user_add::GuildScheduledEventUserAdd, guild_scheduled_event_user_remove::GuildScheduledEventUserRemove, + guild_soundboard_sound_create::GuildSoundboardSoundCreate, + guild_soundboard_sound_delete::GuildSoundboardSoundDelete, + guild_soundboard_sound_update::GuildSoundboardSoundUpdate, + guild_soundboard_sounds_update::GuildSoundboardSoundsUpdate, guild_stickers_update::GuildStickersUpdate, guild_update::GuildUpdate, hello::Hello, integration_create::IntegrationCreate, integration_delete::IntegrationDelete, integration_update::IntegrationUpdate, interaction_create::InteractionCreate, diff --git a/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs b/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs @@ -0,0 +1 @@ + diff --git a/twilight-model/src/guild/mod.rs b/twilight-model/src/guild/mod.rs index a3eee9f57b..035056d83b 100644 --- a/twilight-model/src/guild/mod.rs +++ b/twilight-model/src/guild/mod.rs @@ -39,6 +39,7 @@ mod role; mod role_flags; mod role_position; mod role_tags; +mod soundboard; mod system_channel_flags; mod unavailable_guild; mod vanity_url; @@ -56,7 +57,7 @@ pub use self::{ member::Member, member_flags::MemberFlags, mfa_level::MfaLevel, partial_guild::PartialGuild, partial_member::PartialMember, premium_tier::PremiumTier, preview::GuildPreview, prune::GuildPrune, role::Role, role_flags::RoleFlags, role_position::RolePosition, - role_tags::RoleTags, system_channel_flags::SystemChannelFlags, + role_tags::RoleTags, soundboard::SoundboardSound, system_channel_flags::SystemChannelFlags, unavailable_guild::UnavailableGuild, vanity_url::VanityUrl, verification_level::VerificationLevel, widget::GuildWidget, }; diff --git a/twilight-model/src/guild/soundboard.rs b/twilight-model/src/guild/soundboard.rs new file mode 100644 index 0000000000..694a8413ce --- /dev/null +++ b/twilight-model/src/guild/soundboard.rs @@ -0,0 +1,26 @@ +use serde::{Deserialize, Serialize}; + +use crate::{ + id::{ + marker::{EmojiMarker, GuildMarker, SoundboardSoundMarker}, + Id, + }, + user::User, +}; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct SoundboardSound { + pub available: bool, + pub emoji_id: Option>, + pub emoji_name: Option, + pub guild_id: Option>, + pub name: String, + pub sound_id: Id, + pub user: Option, + pub volume: f64, +} + +#[cfg(test)] +mod tests { + // TODO: stub +} diff --git a/twilight-model/src/id/marker.rs b/twilight-model/src/id/marker.rs index d99741d350..4602a25925 100644 --- a/twilight-model/src/id/marker.rs +++ b/twilight-model/src/id/marker.rs @@ -220,6 +220,15 @@ pub struct ScheduledEventMarker; #[non_exhaustive] pub struct ScheduledEventEntityMarker; +/// Marker for guild soundboard sound IDs. +/// +/// Types such as [`SoundboardSound`] use this ID marker. +/// +/// [`SoundboardSound`]: crate::guild::SoundboardSound +#[derive(Debug)] +#[non_exhaustive] +pub struct SoundboardSoundMarker; + /// Marker for stage IDs. /// /// Types such as [`StageInstance`] use this ID marker. From c30d51e57cf2663a820122282e01368d13061f87 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 00:07:29 +0800 Subject: [PATCH 05/27] model: voice channel effect send event --- .../incoming/voice_channel_effect_send.rs | 26 +++++++++++++++++++ twilight-model/src/guild/soundboard.rs | 4 +++ twilight-model/src/id/marker.rs | 5 ++++ 3 files changed, 35 insertions(+) diff --git a/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs b/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs index 8b13789179..71b4a5f209 100644 --- a/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs +++ b/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs @@ -1 +1,27 @@ +use serde::{Deserialize, Serialize}; +use crate::{guild::Emoji, id::{marker::{AnimationMarker, ChannelMarker, GuildMarker, SoundboardSoundMarker, UserMarker}, Id}}; + + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct VoiceChannelEffectSend { + #[serde(skip_serializing_if = "Option::is_none")] + pub animation_id: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub animation_type: Option, + pub channel_id: Id, + #[serde(skip_serializing_if = "Option::is_none")] + pub emoji: Option, + pub guild_id: Id, + #[serde(skip_serializing_if = "Option::is_none")] + pub sound_id: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub sound_volume: Option, + pub user_id: Id, +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub enum VoiceChannelEffectAnimationType { + Premium, + Basic, +} diff --git a/twilight-model/src/guild/soundboard.rs b/twilight-model/src/guild/soundboard.rs index 694a8413ce..0fa876e3b0 100644 --- a/twilight-model/src/guild/soundboard.rs +++ b/twilight-model/src/guild/soundboard.rs @@ -11,11 +11,15 @@ use crate::{ #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct SoundboardSound { pub available: bool, + #[serde(skip_serializing_if = "Option::is_none")] pub emoji_id: Option>, + #[serde(skip_serializing_if = "Option::is_none")] pub emoji_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub guild_id: Option>, pub name: String, pub sound_id: Id, + #[serde(skip_serializing_if = "Option::is_none")] pub user: Option, pub volume: f64, } diff --git a/twilight-model/src/id/marker.rs b/twilight-model/src/id/marker.rs index 4602a25925..75cb1d3fcd 100644 --- a/twilight-model/src/id/marker.rs +++ b/twilight-model/src/id/marker.rs @@ -9,6 +9,11 @@ // DEVELOPMENT: When adding a new marker, be sure to add its implementation to // `util/snowflake`. +/// Marker for animation IDs. +#[derive(Debug)] +#[non_exhaustive] +pub struct AnimationMarker; + /// Marker for application IDs. /// /// Types such as [`Message::application_id`] or [`Guild::application_id`] From bac701bf9e343171b57346a55e3081c3f5b272fd Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 00:07:42 +0800 Subject: [PATCH 06/27] fmt --- .../payload/incoming/voice_channel_effect_send.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs b/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs index 71b4a5f209..ada2074cbc 100644 --- a/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs +++ b/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs @@ -1,7 +1,12 @@ use serde::{Deserialize, Serialize}; -use crate::{guild::Emoji, id::{marker::{AnimationMarker, ChannelMarker, GuildMarker, SoundboardSoundMarker, UserMarker}, Id}}; - +use crate::{ + guild::Emoji, + id::{ + marker::{AnimationMarker, ChannelMarker, GuildMarker, SoundboardSoundMarker, UserMarker}, + Id, + }, +}; #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] pub struct VoiceChannelEffectSend { From 288a4c4d93c681fde634f4da8c4b15a48367f023 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 00:12:39 +0800 Subject: [PATCH 07/27] model: static tests for soundboard --- twilight-model/src/guild/soundboard.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/twilight-model/src/guild/soundboard.rs b/twilight-model/src/guild/soundboard.rs index 0fa876e3b0..bfede30ba6 100644 --- a/twilight-model/src/guild/soundboard.rs +++ b/twilight-model/src/guild/soundboard.rs @@ -26,5 +26,28 @@ pub struct SoundboardSound { #[cfg(test)] mod tests { - // TODO: stub + use super::SoundboardSound; + use std::fmt::Debug; + use serde::{Deserialize, Serialize}; + use static_assertions::{assert_fields, assert_impl_all}; + + assert_fields!( + SoundboardSound: available, + emoji_id, + emoji_name, + guild_id, + name, + sound_id, + user, + volume + ); + + assert_impl_all!( + SoundboardSound: Clone, Debug, Deserialize<'static>, PartialEq, Serialize + ); + + #[test] + fn soundboard_sound() { + // TODO: stub + } } From 4f3922b3780f7c06ebed65dcda954f08103f993a Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 09:19:24 +0800 Subject: [PATCH 08/27] gateway, model: add incoming events for guild soundboard sound CRUD and voice channel effect --- twilight-cache-inmemory/src/lib.rs | 6 ++++ twilight-gateway/src/event.rs | 6 ++-- twilight-model/src/gateway/event/dispatch.rs | 25 +++++++++++++++ twilight-model/src/gateway/event/mod.rs | 30 ++++++++++++++++++ .../src/gateway/payload/incoming/mod.rs | 4 +-- twilight-model/src/guild/soundboard.rs | 31 +++++++++++++++++-- 6 files changed, 95 insertions(+), 7 deletions(-) diff --git a/twilight-cache-inmemory/src/lib.rs b/twilight-cache-inmemory/src/lib.rs index 0a4c164b52..7336a34d8a 100644 --- a/twilight-cache-inmemory/src/lib.rs +++ b/twilight-cache-inmemory/src/lib.rs @@ -1034,6 +1034,11 @@ impl UpdateCache for Event { | Event::GatewayReconnect | Event::GuildAuditLogEntryCreate(_) | Event::GuildIntegrationsUpdate(_) + // TODO: maybe soundboard sounds will be cached after all + | Event::GuildSoundboardSoundCreate(_) + | Event::GuildSoundboardSoundDelete(_) + | Event::GuildSoundboardSoundUpdate(_) + | Event::GuildSoundboardSoundsUpdate(_) | Event::InviteCreate(_) | Event::InviteDelete(_) | Event::MessagePollVoteAdd(_) @@ -1042,6 +1047,7 @@ impl UpdateCache for Event { | Event::ThreadMembersUpdate(_) | Event::ThreadMemberUpdate(_) | Event::TypingStart(_) + | Event::VoiceChannelEffectSend(_) | Event::VoiceServerUpdate(_) | Event::WebhooksUpdate(_) => {} } diff --git a/twilight-gateway/src/event.rs b/twilight-gateway/src/event.rs index d09398e3ae..02c5fc4647 100644 --- a/twilight-gateway/src/event.rs +++ b/twilight-gateway/src/event.rs @@ -105,11 +105,11 @@ bitflags! { const GUILD_SCHEDULED_EVENT_USER_ADD = 1 << 67; /// A guild's integrations have been updated. const GUILD_SCHEDULED_EVENT_USER_REMOVE = 1 << 68; - /// A guild's soundboard sound have been updated + /// A guild soundboard sound have been created const GUILD_SOUNDBOARD_SOUND_CREATE = 1 << 79; - /// A guild's soundboard sound have been updated + /// A guild soundboard sound have been deleted const GUILD_SOUNDBOARD_SOUND_DELETE = 1 << 80; - /// A guild's soundboard sound have been updated + /// A guild soundboard sound have been updated const GUILD_SOUNDBOARD_SOUND_UPDATE = 1 << 81; /// A guild's soundboard sounds have been updated const GUILD_SOUNDBOARD_SOUNDS_UPDATE = 1 << 82; diff --git a/twilight-model/src/gateway/event/dispatch.rs b/twilight-model/src/gateway/event/dispatch.rs index 6ced713cca..b6b5a5187c 100644 --- a/twilight-model/src/gateway/event/dispatch.rs +++ b/twilight-model/src/gateway/event/dispatch.rs @@ -38,6 +38,10 @@ pub enum DispatchEvent { GuildScheduledEventUpdate(Box), GuildScheduledEventUserAdd(GuildScheduledEventUserAdd), GuildScheduledEventUserRemove(GuildScheduledEventUserRemove), + GuildSoundboardSoundCreate(Box), + GuildSoundboardSoundDelete(GuildSoundboardSoundDelete), + GuildSoundboardSoundUpdate(Box), + GuildSoundboardSoundsUpdate(GuildSoundboardSoundsUpdate), GuildStickersUpdate(GuildStickersUpdate), GuildUpdate(Box), IntegrationCreate(Box), @@ -78,6 +82,7 @@ pub enum DispatchEvent { TypingStart(Box), UnavailableGuild(UnavailableGuild), UserUpdate(UserUpdate), + VoiceChannelEffectSend(Box), VoiceServerUpdate(VoiceServerUpdate), VoiceStateUpdate(Box), WebhooksUpdate(WebhooksUpdate), @@ -111,6 +116,10 @@ impl DispatchEvent { Self::GuildScheduledEventUpdate(_) => EventType::GuildScheduledEventUpdate, Self::GuildScheduledEventUserAdd(_) => EventType::GuildScheduledEventUserAdd, Self::GuildScheduledEventUserRemove(_) => EventType::GuildScheduledEventUserRemove, + Self::GuildSoundboardSoundCreate(_) => EventType::GuildSoundboardSoundCreate, + Self::GuildSoundboardSoundDelete(_) => EventType::GuildSoundboardSoundDelete, + Self::GuildSoundboardSoundUpdate(_) => EventType::GuildSoundboardSoundUpdate, + Self::GuildSoundboardSoundsUpdate(_) => EventType::GuildSoundboardSoundsUpdate, Self::GuildStickersUpdate(_) => EventType::GuildStickersUpdate, Self::GuildUpdate(_) => EventType::GuildUpdate, Self::IntegrationCreate(_) => EventType::IntegrationCreate, @@ -151,6 +160,7 @@ impl DispatchEvent { Self::TypingStart(_) => EventType::TypingStart, Self::UnavailableGuild(_) => EventType::UnavailableGuild, Self::UserUpdate(_) => EventType::UserUpdate, + Self::VoiceChannelEffectSend(_) => EventType::VoiceChannelEffectSend, Self::VoiceServerUpdate(_) => EventType::VoiceServerUpdate, Self::VoiceStateUpdate(_) => EventType::VoiceStateUpdate, Self::WebhooksUpdate(_) => EventType::WebhooksUpdate, @@ -332,6 +342,18 @@ impl<'de> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'_> { "GUILD_ROLE_UPDATE" => { DispatchEvent::RoleUpdate(RoleUpdate::deserialize(deserializer)?) } + "GUILD_SOUNDBOARD_SOUND_CREATE" => { + DispatchEvent::GuildSoundboardSoundCreate(Box::new(GuildSoundboardSoundCreate::deserialize(deserializer)?)) + } + "GUILD_SOUNDBOARD_SOUND_DELETE" => { + DispatchEvent::GuildSoundboardSoundDelete(GuildSoundboardSoundDelete::deserialize(deserializer)?) + } + "GUILD_SOUNDBOARD_SOUND_UPDATE" => { + DispatchEvent::GuildSoundboardSoundUpdate(Box::new(GuildSoundboardSoundUpdate::deserialize(deserializer)?)) + } + "GUILD_SOUNDBOARD_SOUNDS_UPDATE" => { + DispatchEvent::GuildSoundboardSoundsUpdate(GuildSoundboardSoundsUpdate::deserialize(deserializer)?) + } "GUILD_STICKERS_UPDATE" => { DispatchEvent::GuildStickersUpdate(GuildStickersUpdate::deserialize(deserializer)?) } @@ -426,6 +448,9 @@ impl<'de> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'_> { DispatchEvent::TypingStart(Box::new(TypingStart::deserialize(deserializer)?)) } "USER_UPDATE" => DispatchEvent::UserUpdate(UserUpdate::deserialize(deserializer)?), + "VOICE_CHANNEL_EFFECT_SEND" => { + DispatchEvent::VoiceChannelEffectSend(Box::new(VoiceChannelEffectSend::deserialize(deserializer)?)) + } "VOICE_SERVER_UPDATE" => { DispatchEvent::VoiceServerUpdate(VoiceServerUpdate::deserialize(deserializer)?) } diff --git a/twilight-model/src/gateway/event/mod.rs b/twilight-model/src/gateway/event/mod.rs index efdbe0e86c..2c04d848d3 100644 --- a/twilight-model/src/gateway/event/mod.rs +++ b/twilight-model/src/gateway/event/mod.rs @@ -91,6 +91,14 @@ pub enum Event { GuildScheduledEventUserAdd(GuildScheduledEventUserAdd), /// A user was removed from a guild scheduled event. GuildScheduledEventUserRemove(GuildScheduledEventUserRemove), + /// A guild soundboard sound has been created. + GuildSoundboardSoundCreate(Box), + /// A guild soundboard sound has been deleted. + GuildSoundboardSoundDelete(GuildSoundboardSoundDelete), + /// A guild soundboard sound has been updated. + GuildSoundboardSoundUpdate(Box), + /// A guild's soundboard sounds has been updated. + GuildSoundboardSoundsUpdate(GuildSoundboardSoundsUpdate), /// A guild's stickers were updated. GuildStickersUpdate(GuildStickersUpdate), /// A guild was updated. @@ -173,6 +181,8 @@ pub enum Event { UnavailableGuild(UnavailableGuild), /// The current user was updated. UserUpdate(UserUpdate), + /// An effect was sent in a voice channel the current user is in. + VoiceChannelEffectSend(Box), /// A voice server update was sent. VoiceServerUpdate(VoiceServerUpdate), /// A voice state in a voice channel was updated. @@ -206,6 +216,10 @@ impl Event { Event::GuildScheduledEventUpdate(e) => Some(e.0.guild_id), Event::GuildScheduledEventUserAdd(e) => Some(e.guild_id), Event::GuildScheduledEventUserRemove(e) => Some(e.guild_id), + Event::GuildSoundboardSoundCreate(e) => e.0.guild_id, + Event::GuildSoundboardSoundDelete(e) => Some(e.guild_id), + Event::GuildSoundboardSoundUpdate(e) => e.0.guild_id, + Event::GuildSoundboardSoundsUpdate(e) => Some(e.guild_id), Event::GuildStickersUpdate(e) => Some(e.guild_id), Event::GuildUpdate(e) => Some(e.0.id), Event::IntegrationCreate(e) => e.0.guild_id, @@ -243,6 +257,7 @@ impl Event { Event::ThreadUpdate(e) => e.0.guild_id, Event::TypingStart(e) => e.guild_id, Event::UnavailableGuild(e) => Some(e.id), + Event::VoiceChannelEffectSend(e) => Some(e.guild_id), Event::VoiceServerUpdate(e) => Some(e.guild_id), Event::VoiceStateUpdate(e) => e.0.guild_id, Event::WebhooksUpdate(e) => Some(e.guild_id), @@ -293,6 +308,10 @@ impl Event { Self::GuildScheduledEventUpdate(_) => EventType::GuildScheduledEventUpdate, Self::GuildScheduledEventUserAdd(_) => EventType::GuildScheduledEventUserAdd, Self::GuildScheduledEventUserRemove(_) => EventType::GuildScheduledEventUserRemove, + Self::GuildSoundboardSoundCreate(_) => EventType::GuildSoundboardSoundCreate, + Self::GuildSoundboardSoundDelete(_) => EventType::GuildSoundboardSoundDelete, + Self::GuildSoundboardSoundUpdate(_) => EventType::GuildSoundboardSoundUpdate, + Self::GuildSoundboardSoundsUpdate(_) => EventType::GuildSoundboardSoundsUpdate, Self::GuildStickersUpdate(_) => EventType::GuildStickersUpdate, Self::GuildUpdate(_) => EventType::GuildUpdate, Self::IntegrationCreate(_) => EventType::IntegrationCreate, @@ -333,6 +352,7 @@ impl Event { Self::TypingStart(_) => EventType::TypingStart, Self::UnavailableGuild(_) => EventType::UnavailableGuild, Self::UserUpdate(_) => EventType::UserUpdate, + Self::VoiceChannelEffectSend(_) => EventType::VoiceChannelEffectSend, Self::VoiceServerUpdate(_) => EventType::VoiceServerUpdate, Self::VoiceStateUpdate(_) => EventType::VoiceStateUpdate, Self::WebhooksUpdate(_) => EventType::WebhooksUpdate, @@ -371,6 +391,10 @@ impl From for Event { DispatchEvent::GuildScheduledEventUserRemove(v) => { Self::GuildScheduledEventUserRemove(v) } + DispatchEvent::GuildSoundboardSoundCreate(v) => Self::GuildSoundboardSoundCreate(v), + DispatchEvent::GuildSoundboardSoundDelete(v) => Self::GuildSoundboardSoundDelete(v), + DispatchEvent::GuildSoundboardSoundUpdate(v) => Self::GuildSoundboardSoundUpdate(v), + DispatchEvent::GuildSoundboardSoundsUpdate(v) => Self::GuildSoundboardSoundsUpdate(v), DispatchEvent::GuildStickersUpdate(v) => Self::GuildStickersUpdate(v), DispatchEvent::GuildUpdate(v) => Self::GuildUpdate(v), DispatchEvent::IntegrationCreate(v) => Self::IntegrationCreate(v), @@ -411,6 +435,7 @@ impl From for Event { DispatchEvent::TypingStart(v) => Self::TypingStart(v), DispatchEvent::UnavailableGuild(v) => Self::UnavailableGuild(v), DispatchEvent::UserUpdate(v) => Self::UserUpdate(v), + DispatchEvent::VoiceChannelEffectSend(v) => Self::VoiceChannelEffectSend(v), DispatchEvent::VoiceServerUpdate(v) => Self::VoiceServerUpdate(v), DispatchEvent::VoiceStateUpdate(v) => Self::VoiceStateUpdate(v), DispatchEvent::WebhooksUpdate(v) => Self::WebhooksUpdate(v), @@ -497,6 +522,8 @@ mod tests { const_assert!(mem::size_of::() > EVENT_THRESHOLD); const_assert!(mem::size_of::() > EVENT_THRESHOLD); const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); const_assert!(mem::size_of::() > EVENT_THRESHOLD); const_assert!(mem::size_of::() > EVENT_THRESHOLD); const_assert!(mem::size_of::() > EVENT_THRESHOLD); @@ -514,6 +541,7 @@ mod tests { const_assert!(mem::size_of::() > EVENT_THRESHOLD); const_assert!(mem::size_of::() > EVENT_THRESHOLD); const_assert!(mem::size_of::() > EVENT_THRESHOLD); + const_assert!(mem::size_of::() > EVENT_THRESHOLD); const_assert!(mem::size_of::() > EVENT_THRESHOLD); // Unboxed. @@ -530,6 +558,8 @@ mod tests { const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); diff --git a/twilight-model/src/gateway/payload/incoming/mod.rs b/twilight-model/src/gateway/payload/incoming/mod.rs index fd87e4fa1b..35edcbcfdb 100644 --- a/twilight-model/src/gateway/payload/incoming/mod.rs +++ b/twilight-model/src/gateway/payload/incoming/mod.rs @@ -13,6 +13,7 @@ pub mod invite_create; pub mod reaction_remove_emoji; +pub mod voice_channel_effect_send; mod auto_moderation_action_execution; mod auto_moderation_rule_create; @@ -80,7 +81,6 @@ mod thread_update; mod typing_start; mod unavailable_guild; mod user_update; -mod voice_channel_effect_send; mod voice_server_update; mod voice_state_update; mod webhooks_update; @@ -122,6 +122,6 @@ pub use self::{ thread_delete::ThreadDelete, thread_list_sync::ThreadListSync, thread_member_update::ThreadMemberUpdate, thread_members_update::ThreadMembersUpdate, thread_update::ThreadUpdate, typing_start::TypingStart, unavailable_guild::UnavailableGuild, - user_update::UserUpdate, voice_server_update::VoiceServerUpdate, + user_update::UserUpdate, voice_channel_effect_send::VoiceChannelEffectSend, voice_server_update::VoiceServerUpdate, voice_state_update::VoiceStateUpdate, webhooks_update::WebhooksUpdate, }; diff --git a/twilight-model/src/guild/soundboard.rs b/twilight-model/src/guild/soundboard.rs index bfede30ba6..d41e4020d3 100644 --- a/twilight-model/src/guild/soundboard.rs +++ b/twilight-model/src/guild/soundboard.rs @@ -26,10 +26,13 @@ pub struct SoundboardSound { #[cfg(test)] mod tests { + use crate::id::Id; + use super::SoundboardSound; - use std::fmt::Debug; use serde::{Deserialize, Serialize}; + use serde_test::Token; use static_assertions::{assert_fields, assert_impl_all}; + use std::fmt::Debug; assert_fields!( SoundboardSound: available, @@ -48,6 +51,30 @@ mod tests { #[test] fn soundboard_sound() { - // TODO: stub + let sound = SoundboardSound { + available: true, + emoji_id: None, + emoji_name: None, + guild_id: None, + name: "test".to_owned(), + sound_id: Id::new(123), + user: None, + volume: 50.0, + }; + + serde_test::assert_tokens( + &sound, + &[ + Token::Struct { name: "SoundboardSound", len: 4 }, + Token::Str("available"), + Token::Bool(true), + Token::Str("name"), + Token::Str("test"), + Token::Str("sound_id"), + Token::Str("123"), + Token::Str("volume"), + Token::F64(50.0), + ] + ); } } From 3f81bbc088e3410966b80beec6575685d89739b8 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 09:57:15 +0800 Subject: [PATCH 09/27] gateway, model: add SOUNDBOARD_SOUNDS event --- twilight-gateway/src/event.rs | 3 ++ twilight-model/src/gateway/event/dispatch.rs | 36 +++++++++++-------- twilight-model/src/gateway/event/kind.rs | 4 +++ twilight-model/src/gateway/event/mod.rs | 6 ++++ .../src/gateway/payload/incoming/mod.rs | 12 ++++--- .../payload/incoming/soundboard_sounds.rs | 9 +++++ twilight-model/src/guild/soundboard.rs | 9 +++-- 7 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 twilight-model/src/gateway/payload/incoming/soundboard_sounds.rs diff --git a/twilight-gateway/src/event.rs b/twilight-gateway/src/event.rs index 02c5fc4647..44e78d6d85 100644 --- a/twilight-gateway/src/event.rs +++ b/twilight-gateway/src/event.rs @@ -172,6 +172,8 @@ bitflags! { const ROLE_DELETE = 1 << 31; /// Role has been updated in a guild. const ROLE_UPDATE = 1 << 32; + /// Soundboard sounds from a guild. + const SOUNDBOARD_SOUNDS = 1 << 49; /// Stage instance was created in a stage channel. const STAGE_INSTANCE_CREATE = 1 << 57; /// Stage instance was deleted in a stage channel. @@ -422,6 +424,7 @@ impl From for EventTypeFlags { EventType::RoleCreate => Self::ROLE_CREATE, EventType::RoleDelete => Self::ROLE_DELETE, EventType::RoleUpdate => Self::ROLE_UPDATE, + EventType::SoundboardSounds => Self::SOUNDBOARD_SOUNDS, EventType::StageInstanceCreate => Self::STAGE_INSTANCE_CREATE, EventType::StageInstanceDelete => Self::STAGE_INSTANCE_DELETE, EventType::StageInstanceUpdate => Self::STAGE_INSTANCE_UPDATE, diff --git a/twilight-model/src/gateway/event/dispatch.rs b/twilight-model/src/gateway/event/dispatch.rs index b6b5a5187c..9d67790228 100644 --- a/twilight-model/src/gateway/event/dispatch.rs +++ b/twilight-model/src/gateway/event/dispatch.rs @@ -70,6 +70,7 @@ pub enum DispatchEvent { RoleCreate(RoleCreate), RoleDelete(RoleDelete), RoleUpdate(RoleUpdate), + SoundboardSounds(SoundboardSounds), StageInstanceCreate(StageInstanceCreate), StageInstanceDelete(StageInstanceDelete), StageInstanceUpdate(StageInstanceUpdate), @@ -148,6 +149,7 @@ impl DispatchEvent { Self::RoleCreate(_) => EventType::RoleCreate, Self::RoleDelete(_) => EventType::RoleDelete, Self::RoleUpdate(_) => EventType::RoleUpdate, + Self::SoundboardSounds(_) => EventType::SoundboardSounds, Self::StageInstanceCreate(_) => EventType::StageInstanceCreate, Self::StageInstanceDelete(_) => EventType::StageInstanceDelete, Self::StageInstanceUpdate(_) => EventType::StageInstanceUpdate, @@ -221,6 +223,7 @@ impl TryFrom for DispatchEvent { Event::RoleCreate(v) => Self::RoleCreate(v), Event::RoleDelete(v) => Self::RoleDelete(v), Event::RoleUpdate(v) => Self::RoleUpdate(v), + Event::SoundboardSounds(v) => Self::SoundboardSounds(v), Event::StageInstanceCreate(v) => Self::StageInstanceCreate(v), Event::StageInstanceDelete(v) => Self::StageInstanceDelete(v), Event::StageInstanceUpdate(v) => Self::StageInstanceUpdate(v), @@ -342,18 +345,18 @@ impl<'de> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'_> { "GUILD_ROLE_UPDATE" => { DispatchEvent::RoleUpdate(RoleUpdate::deserialize(deserializer)?) } - "GUILD_SOUNDBOARD_SOUND_CREATE" => { - DispatchEvent::GuildSoundboardSoundCreate(Box::new(GuildSoundboardSoundCreate::deserialize(deserializer)?)) - } - "GUILD_SOUNDBOARD_SOUND_DELETE" => { - DispatchEvent::GuildSoundboardSoundDelete(GuildSoundboardSoundDelete::deserialize(deserializer)?) - } - "GUILD_SOUNDBOARD_SOUND_UPDATE" => { - DispatchEvent::GuildSoundboardSoundUpdate(Box::new(GuildSoundboardSoundUpdate::deserialize(deserializer)?)) - } - "GUILD_SOUNDBOARD_SOUNDS_UPDATE" => { - DispatchEvent::GuildSoundboardSoundsUpdate(GuildSoundboardSoundsUpdate::deserialize(deserializer)?) - } + "GUILD_SOUNDBOARD_SOUND_CREATE" => DispatchEvent::GuildSoundboardSoundCreate(Box::new( + GuildSoundboardSoundCreate::deserialize(deserializer)?, + )), + "GUILD_SOUNDBOARD_SOUND_DELETE" => DispatchEvent::GuildSoundboardSoundDelete( + GuildSoundboardSoundDelete::deserialize(deserializer)?, + ), + "GUILD_SOUNDBOARD_SOUND_UPDATE" => DispatchEvent::GuildSoundboardSoundUpdate(Box::new( + GuildSoundboardSoundUpdate::deserialize(deserializer)?, + )), + "GUILD_SOUNDBOARD_SOUNDS_UPDATE" => DispatchEvent::GuildSoundboardSoundsUpdate( + GuildSoundboardSoundsUpdate::deserialize(deserializer)?, + ), "GUILD_STICKERS_UPDATE" => { DispatchEvent::GuildStickersUpdate(GuildStickersUpdate::deserialize(deserializer)?) } @@ -417,6 +420,9 @@ impl<'de> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'_> { DispatchEvent::Resumed } + "SOUNDBOARD_SOUNDS" => { + DispatchEvent::SoundboardSounds(SoundboardSounds::deserialize(deserializer)?) + } "STAGE_INSTANCE_CREATE" => { DispatchEvent::StageInstanceCreate(StageInstanceCreate::deserialize(deserializer)?) } @@ -448,9 +454,9 @@ impl<'de> DeserializeSeed<'de> for DispatchEventWithTypeDeserializer<'_> { DispatchEvent::TypingStart(Box::new(TypingStart::deserialize(deserializer)?)) } "USER_UPDATE" => DispatchEvent::UserUpdate(UserUpdate::deserialize(deserializer)?), - "VOICE_CHANNEL_EFFECT_SEND" => { - DispatchEvent::VoiceChannelEffectSend(Box::new(VoiceChannelEffectSend::deserialize(deserializer)?)) - } + "VOICE_CHANNEL_EFFECT_SEND" => DispatchEvent::VoiceChannelEffectSend(Box::new( + VoiceChannelEffectSend::deserialize(deserializer)?, + )), "VOICE_SERVER_UPDATE" => { DispatchEvent::VoiceServerUpdate(VoiceServerUpdate::deserialize(deserializer)?) } diff --git a/twilight-model/src/gateway/event/kind.rs b/twilight-model/src/gateway/event/kind.rs index a08e54fc1c..b62cb880a4 100644 --- a/twilight-model/src/gateway/event/kind.rs +++ b/twilight-model/src/gateway/event/kind.rs @@ -80,6 +80,7 @@ pub enum EventType { RoleDelete, #[serde(rename = "GUILD_ROLE_UPDATE")] RoleUpdate, + SoundboardSounds, StageInstanceCreate, StageInstanceDelete, StageInstanceUpdate, @@ -157,6 +158,7 @@ impl EventType { Self::RoleCreate => Some("GUILD_ROLE_CREATE"), Self::RoleDelete => Some("GUILD_ROLE_DELETE"), Self::RoleUpdate => Some("GUILD_ROLE_UPDATE"), + Self::SoundboardSounds => Some("SOUNDBOARD_SOUNDS"), Self::StageInstanceCreate => Some("STAGE_INSTANCE_CREATE"), Self::StageInstanceDelete => Some("STAGE_INSTANCE_DELETE"), Self::StageInstanceUpdate => Some("STAGE_INSTANCE_UPDATE"), @@ -241,6 +243,7 @@ impl<'a> TryFrom<&'a str> for EventType { "GUILD_ROLE_CREATE" => Ok(Self::RoleCreate), "GUILD_ROLE_DELETE" => Ok(Self::RoleDelete), "GUILD_ROLE_UPDATE" => Ok(Self::RoleUpdate), + "SOUNDBOARD_SOUNDS" => Ok(Self::SoundboardSounds), "STAGE_INSTANCE_CREATE" => Ok(Self::StageInstanceCreate), "STAGE_INSTANCE_DELETE" => Ok(Self::StageInstanceDelete), "STAGE_INSTANCE_UPDATE" => Ok(Self::StageInstanceUpdate), @@ -392,6 +395,7 @@ mod tests { assert_variant(EventType::RoleCreate, "GUILD_ROLE_CREATE"); assert_variant(EventType::RoleDelete, "GUILD_ROLE_DELETE"); assert_variant(EventType::RoleUpdate, "GUILD_ROLE_UPDATE"); + assert_variant(EventType::SoundboardSounds, "SOUNDBOARD_SOUNDS"); assert_variant(EventType::StageInstanceCreate, "STAGE_INSTANCE_CREATE"); assert_variant(EventType::StageInstanceDelete, "STAGE_INSTANCE_DELETE"); assert_variant(EventType::StageInstanceUpdate, "STAGE_INSTANCE_UPDATE"); diff --git a/twilight-model/src/gateway/event/mod.rs b/twilight-model/src/gateway/event/mod.rs index 2c04d848d3..22eae626d3 100644 --- a/twilight-model/src/gateway/event/mod.rs +++ b/twilight-model/src/gateway/event/mod.rs @@ -156,6 +156,8 @@ pub enum Event { RoleDelete(RoleDelete), /// A role was updated in a guild. RoleUpdate(RoleUpdate), + /// Soundboard sounds in a guild. + SoundboardSounds(SoundboardSounds), /// A stage instance was created in a stage channel. StageInstanceCreate(StageInstanceCreate), /// A stage instance was deleted in a stage channel. @@ -246,6 +248,7 @@ impl Event { Event::RoleCreate(e) => Some(e.guild_id), Event::RoleDelete(e) => Some(e.guild_id), Event::RoleUpdate(e) => Some(e.guild_id), + Event::SoundboardSounds(e) => Some(e.guild_id), Event::StageInstanceCreate(e) => Some(e.0.guild_id), Event::StageInstanceDelete(e) => Some(e.0.guild_id), Event::StageInstanceUpdate(e) => Some(e.0.guild_id), @@ -340,6 +343,7 @@ impl Event { Self::RoleCreate(_) => EventType::RoleCreate, Self::RoleDelete(_) => EventType::RoleDelete, Self::RoleUpdate(_) => EventType::RoleUpdate, + Self::SoundboardSounds(_) => EventType::SoundboardSounds, Self::StageInstanceCreate(_) => EventType::StageInstanceCreate, Self::StageInstanceDelete(_) => EventType::StageInstanceDelete, Self::StageInstanceUpdate(_) => EventType::StageInstanceUpdate, @@ -423,6 +427,7 @@ impl From for Event { DispatchEvent::ReactionRemoveEmoji(v) => Self::ReactionRemoveEmoji(v), DispatchEvent::Ready(v) => Self::Ready(v), DispatchEvent::Resumed => Self::Resumed, + DispatchEvent::SoundboardSounds(v) => Self::SoundboardSounds(v), DispatchEvent::StageInstanceCreate(v) => Self::StageInstanceCreate(v), DispatchEvent::StageInstanceDelete(v) => Self::StageInstanceDelete(v), DispatchEvent::StageInstanceUpdate(v) => Self::StageInstanceUpdate(v), @@ -570,6 +575,7 @@ mod tests { const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); + const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); const_assert!(mem::size_of::() <= EVENT_THRESHOLD); diff --git a/twilight-model/src/gateway/payload/incoming/mod.rs b/twilight-model/src/gateway/payload/incoming/mod.rs index 35edcbcfdb..2da7715735 100644 --- a/twilight-model/src/gateway/payload/incoming/mod.rs +++ b/twilight-model/src/gateway/payload/incoming/mod.rs @@ -69,6 +69,7 @@ mod ready; mod role_create; mod role_delete; mod role_update; +mod soundboard_sounds; mod stage_instance_create; mod stage_instance_delete; mod stage_instance_update; @@ -117,11 +118,12 @@ pub use self::{ presence_update::PresenceUpdate, reaction_add::ReactionAdd, reaction_remove::ReactionRemove, reaction_remove_all::ReactionRemoveAll, reaction_remove_emoji::ReactionRemoveEmoji, ready::Ready, role_create::RoleCreate, role_delete::RoleDelete, role_update::RoleUpdate, - stage_instance_create::StageInstanceCreate, stage_instance_delete::StageInstanceDelete, - stage_instance_update::StageInstanceUpdate, thread_create::ThreadCreate, - thread_delete::ThreadDelete, thread_list_sync::ThreadListSync, + soundboard_sounds::SoundboardSounds, stage_instance_create::StageInstanceCreate, + stage_instance_delete::StageInstanceDelete, stage_instance_update::StageInstanceUpdate, + thread_create::ThreadCreate, thread_delete::ThreadDelete, thread_list_sync::ThreadListSync, thread_member_update::ThreadMemberUpdate, thread_members_update::ThreadMembersUpdate, thread_update::ThreadUpdate, typing_start::TypingStart, unavailable_guild::UnavailableGuild, - user_update::UserUpdate, voice_channel_effect_send::VoiceChannelEffectSend, voice_server_update::VoiceServerUpdate, - voice_state_update::VoiceStateUpdate, webhooks_update::WebhooksUpdate, + user_update::UserUpdate, voice_channel_effect_send::VoiceChannelEffectSend, + voice_server_update::VoiceServerUpdate, voice_state_update::VoiceStateUpdate, + webhooks_update::WebhooksUpdate, }; diff --git a/twilight-model/src/gateway/payload/incoming/soundboard_sounds.rs b/twilight-model/src/gateway/payload/incoming/soundboard_sounds.rs new file mode 100644 index 0000000000..067e9b0d21 --- /dev/null +++ b/twilight-model/src/gateway/payload/incoming/soundboard_sounds.rs @@ -0,0 +1,9 @@ +use crate::guild::SoundboardSound; +use crate::id::{marker::GuildMarker, Id}; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct SoundboardSounds { + pub guild_id: Id, + pub soundboard_sounds: Vec, +} diff --git a/twilight-model/src/guild/soundboard.rs b/twilight-model/src/guild/soundboard.rs index d41e4020d3..35a5b80f88 100644 --- a/twilight-model/src/guild/soundboard.rs +++ b/twilight-model/src/guild/soundboard.rs @@ -63,9 +63,12 @@ mod tests { }; serde_test::assert_tokens( - &sound, + &sound, &[ - Token::Struct { name: "SoundboardSound", len: 4 }, + Token::Struct { + name: "SoundboardSound", + len: 4, + }, Token::Str("available"), Token::Bool(true), Token::Str("name"), @@ -74,7 +77,7 @@ mod tests { Token::Str("123"), Token::Str("volume"), Token::F64(50.0), - ] + ], ); } } From 9fb32de1917144f2a29968c3a4a6ca9db79518f3 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 09:58:45 +0800 Subject: [PATCH 10/27] fix: soundboard model test --- twilight-cache-inmemory/src/lib.rs | 2 ++ twilight-model/src/guild/soundboard.rs | 1 + 2 files changed, 3 insertions(+) diff --git a/twilight-cache-inmemory/src/lib.rs b/twilight-cache-inmemory/src/lib.rs index 7336a34d8a..70353fa42f 100644 --- a/twilight-cache-inmemory/src/lib.rs +++ b/twilight-cache-inmemory/src/lib.rs @@ -1039,6 +1039,8 @@ impl UpdateCache for Event { | Event::GuildSoundboardSoundDelete(_) | Event::GuildSoundboardSoundUpdate(_) | Event::GuildSoundboardSoundsUpdate(_) + | Event::SoundboardSounds(_) + // TODO: end of above TODO | Event::InviteCreate(_) | Event::InviteDelete(_) | Event::MessagePollVoteAdd(_) diff --git a/twilight-model/src/guild/soundboard.rs b/twilight-model/src/guild/soundboard.rs index 35a5b80f88..281e9c959d 100644 --- a/twilight-model/src/guild/soundboard.rs +++ b/twilight-model/src/guild/soundboard.rs @@ -74,6 +74,7 @@ mod tests { Token::Str("name"), Token::Str("test"), Token::Str("sound_id"), + Token::NewtypeStruct { name: "Id" }, Token::Str("123"), Token::Str("volume"), Token::F64(50.0), From 4e320895a95cde76d18491ffb2f74c3fe742e036 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 10:02:30 +0800 Subject: [PATCH 11/27] fix: soundboard model test --- twilight-model/src/guild/soundboard.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/twilight-model/src/guild/soundboard.rs b/twilight-model/src/guild/soundboard.rs index 281e9c959d..27e2c6e6db 100644 --- a/twilight-model/src/guild/soundboard.rs +++ b/twilight-model/src/guild/soundboard.rs @@ -78,6 +78,7 @@ mod tests { Token::Str("123"), Token::Str("volume"), Token::F64(50.0), + Token::StructEnd, ], ); } From ed7168bd9468e5511e683fc79cb43714ea072db3 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 14:03:17 +0800 Subject: [PATCH 12/27] gateway: assert impl Command --- twilight-gateway/src/command.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/twilight-gateway/src/command.rs b/twilight-gateway/src/command.rs index dfc570224a..608cdc2a4f 100644 --- a/twilight-gateway/src/command.rs +++ b/twilight-gateway/src/command.rs @@ -54,10 +54,11 @@ mod tests { use super::Command; use static_assertions::assert_impl_all; use twilight_model::gateway::payload::outgoing::{ - RequestGuildMembers, UpdatePresence, UpdateVoiceState, + RequestGuildMembers, RequestSoundboardSounds, UpdatePresence, UpdateVoiceState }; assert_impl_all!(RequestGuildMembers: Command); + assert_impl_all!(RequestSoundboardSounds: Command); assert_impl_all!(UpdatePresence: Command); assert_impl_all!(UpdateVoiceState: Command); } From 0064b31df32f4bb62a0fce75ad4ec50f0f4a41b8 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 15:04:01 +0800 Subject: [PATCH 13/27] cache: soundboards not to be cached --- twilight-cache-inmemory/src/lib.rs | 2 -- twilight-gateway/src/command.rs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/twilight-cache-inmemory/src/lib.rs b/twilight-cache-inmemory/src/lib.rs index 70353fa42f..82e61b35ff 100644 --- a/twilight-cache-inmemory/src/lib.rs +++ b/twilight-cache-inmemory/src/lib.rs @@ -1034,13 +1034,11 @@ impl UpdateCache for Event { | Event::GatewayReconnect | Event::GuildAuditLogEntryCreate(_) | Event::GuildIntegrationsUpdate(_) - // TODO: maybe soundboard sounds will be cached after all | Event::GuildSoundboardSoundCreate(_) | Event::GuildSoundboardSoundDelete(_) | Event::GuildSoundboardSoundUpdate(_) | Event::GuildSoundboardSoundsUpdate(_) | Event::SoundboardSounds(_) - // TODO: end of above TODO | Event::InviteCreate(_) | Event::InviteDelete(_) | Event::MessagePollVoteAdd(_) diff --git a/twilight-gateway/src/command.rs b/twilight-gateway/src/command.rs index 608cdc2a4f..7ea7c437a4 100644 --- a/twilight-gateway/src/command.rs +++ b/twilight-gateway/src/command.rs @@ -54,7 +54,7 @@ mod tests { use super::Command; use static_assertions::assert_impl_all; use twilight_model::gateway::payload::outgoing::{ - RequestGuildMembers, RequestSoundboardSounds, UpdatePresence, UpdateVoiceState + RequestGuildMembers, RequestSoundboardSounds, UpdatePresence, UpdateVoiceState, }; assert_impl_all!(RequestGuildMembers: Command); From 5d7a627fe5548dce6945d417718226d3f4ebb351 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 16:23:06 +0800 Subject: [PATCH 14/27] http-ratelimiting: add paths for http requests --- twilight-http-ratelimiting/src/request.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/twilight-http-ratelimiting/src/request.rs b/twilight-http-ratelimiting/src/request.rs index 9272808bed..0d1b9fb11a 100644 --- a/twilight-http-ratelimiting/src/request.rs +++ b/twilight-http-ratelimiting/src/request.rs @@ -160,6 +160,8 @@ pub enum Path { ChannelsIdPolls(u64), /// Operating on a group DM's recipients. ChannelsIdRecipients(u64), + /// Operating on a channel's sending soundboard sound. + ChannelsIdSendSoundboardSound(u64), /// Operating on a thread's members. ChannelsIdThreadMembers(u64), /// Operating on a thread's member. @@ -238,6 +240,10 @@ pub enum Path { GuildsIdScheduledEventsId(u64), /// Operating on a particular guild's scheduled event users. GuildsIdScheduledEventsIdUsers(u64), + /// Operating on a guild's soundboard sounds + GuildsIdSoundboardSounds(u64), + /// Operating on a guild's particular soundboard sounds + GuildsIdSoundboardSoundsId(u64), /// Operating on one of the user's guilds' stickers. GuildsIdStickers(u64), /// Operating on one of the user's guilds' templates. @@ -270,6 +276,8 @@ pub enum Path { OauthApplicationsMe, /// Operating on the current authorization's information. OauthMe, + /// Operating on soundboard default sounds. + SoundboardDefaultSounds, /// Operating on stage instances. StageInstances, /// Operating on sticker packs. @@ -391,6 +399,9 @@ impl FromStr for Path { ["channels", id, "recipients"] | ["channels", id, "recipients", _] => { ChannelsIdRecipients(parse_id(id)?) } + ["channels", id, "send-soundboard-sound"] => { + ChannelsIdSendSoundboardSound(parse_id(id)?) + } ["channels", id, "thread-members"] => ChannelsIdThreadMembers(parse_id(id)?), ["channels", id, "thread-members", _] => ChannelsIdThreadMembersId(parse_id(id)?), ["channels", id, "threads"] => ChannelsIdThreads(parse_id(id)?), @@ -435,6 +446,8 @@ impl FromStr for Path { ["guilds", id, "scheduled-events", _, "users"] => { GuildsIdScheduledEventsIdUsers(parse_id(id)?) } + ["guilds", id, "soundboard-sounds"] => GuildsIdSoundboardSounds(parse_id(id)?), + ["guilds", id, "soundboard-sounds", _] => GuildsIdSoundboardSoundsId(parse_id(id)?), ["guilds", id, "stickers"] | ["guilds", id, "stickers", _] => { GuildsIdStickers(parse_id(id)?) } @@ -451,6 +464,7 @@ impl FromStr for Path { ["guilds", id, "widget.json"] => GuildsIdWidgetJson(parse_id(id)?), ["invites", _] => InvitesCode, ["interactions", id, _, "callback"] => InteractionCallback(parse_id(id)?), + ["soundboard-default-sounds"] => SoundboardDefaultSounds, ["stage-instances", _] => StageInstances, ["sticker-packs"] => StickerPacks, ["stickers", _] => Stickers, From 8b76d64b917bfb8bacaf35f1fb51bc7acf44e057 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Wed, 9 Jul 2025 20:59:54 +0800 Subject: [PATCH 15/27] http: add send soundboard sound endpoint --- twilight-http/src/client/mod.rs | 10 +++ twilight-http/src/request/channel/mod.rs | 4 +- .../request/channel/send_soundboard_sound.rs | 78 +++++++++++++++++++ twilight-http/src/request/try_into_request.rs | 2 + twilight-http/src/routing.rs | 15 ++++ 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 twilight-http/src/request/channel/send_soundboard_sound.rs diff --git a/twilight-http/src/client/mod.rs b/twilight-http/src/client/mod.rs index 474af02f7e..31425f68bb 100644 --- a/twilight-http/src/client/mod.rs +++ b/twilight-http/src/client/mod.rs @@ -4,6 +4,7 @@ mod interaction; pub use self::{builder::ClientBuilder, interaction::InteractionClient}; +use crate::request::channel::SendSoundboardSound; use crate::request::{ application::{ emoji::{ @@ -115,6 +116,7 @@ use std::{ }; use tokio::time; use twilight_http_ratelimiting::Ratelimiter; +use twilight_model::id::marker::SoundboardSoundMarker; use twilight_model::{ channel::{message::AllowedMentions, ChannelType}, guild::{ @@ -2882,6 +2884,14 @@ impl Client { DeleteApplicationEmoji::new(self, application_id, emoji_id) } + pub const fn send_soundboard_sound( + &self, + channel_id: Id, + sound_id: Id, + ) -> SendSoundboardSound<'_> { + SendSoundboardSound::new(self, channel_id, sound_id) + } + /// Execute a request, returning a future resolving to a [`Response`]. /// /// # Errors diff --git a/twilight-http/src/request/channel/mod.rs b/twilight-http/src/request/channel/mod.rs index 7b402806ed..74b633a885 100644 --- a/twilight-http/src/request/channel/mod.rs +++ b/twilight-http/src/request/channel/mod.rs @@ -14,6 +14,7 @@ mod delete_pin; mod follow_news_channel; mod get_channel; mod get_pins; +mod send_soundboard_sound; mod update_channel; mod update_channel_permission; @@ -22,5 +23,6 @@ pub use self::{ delete_channel::DeleteChannel, delete_channel_permission::DeleteChannelPermission, delete_channel_permission_configured::DeleteChannelPermissionConfigured, delete_pin::DeletePin, follow_news_channel::FollowNewsChannel, get_channel::GetChannel, get_pins::GetPins, - update_channel::UpdateChannel, update_channel_permission::UpdateChannelPermission, + send_soundboard_sound::SendSoundboardSound, update_channel::UpdateChannel, + update_channel_permission::UpdateChannelPermission, }; diff --git a/twilight-http/src/request/channel/send_soundboard_sound.rs b/twilight-http/src/request/channel/send_soundboard_sound.rs new file mode 100644 index 0000000000..d4ba2c8da4 --- /dev/null +++ b/twilight-http/src/request/channel/send_soundboard_sound.rs @@ -0,0 +1,78 @@ +use crate::{ + client::Client, + error::Error, + request::Request, + request::TryIntoRequest, + response::{marker::EmptyBody, Response, ResponseFuture}, + routing::Route, +}; +use serde::Serialize; +use std::future::IntoFuture; +use twilight_model::id::{ + marker::{ChannelMarker, GuildMarker, SoundboardSoundMarker}, + Id, +}; + +#[derive(Serialize)] +pub(crate) struct SendSoundboardSoundFields { + sound_id: Id, + guild_id: Option>, +} + +/// Send a soundboard sound in a channel. +#[must_use = "requests must be configured and executed"] +pub struct SendSoundboardSound<'a> { + channel_id: Id, + fields: SendSoundboardSoundFields, + http: &'a Client, +} + +impl<'a> SendSoundboardSound<'a> { + pub(crate) const fn new( + http: &'a Client, + channel_id: Id, + sound_id: Id, + ) -> Self { + Self { + channel_id, + fields: SendSoundboardSoundFields { + sound_id, + guild_id: None, + }, + http, + } + } + + /// Set the guild ID the soundboard sound specified is associated with. + /// + /// This is required to use soundboard sounds from other servers. + pub fn guild_id(mut self, guild_id: Id) -> Self { + self.fields.guild_id.replace(guild_id); + self + } +} + +impl IntoFuture for SendSoundboardSound<'_> { + type Output = Result, Error>; + + type IntoFuture = ResponseFuture; + + fn into_future(self) -> Self::IntoFuture { + let http = self.http; + + match self.try_into_request() { + Ok(request) => http.request(request), + Err(source) => ResponseFuture::error(source), + } + } +} + +impl TryIntoRequest for SendSoundboardSound<'_> { + fn try_into_request(self) -> Result { + Request::builder(&Route::SendSoundboardSound { + channel_id: self.channel_id.get(), + }) + .json(&self.fields) + .build() + } +} diff --git a/twilight-http/src/request/try_into_request.rs b/twilight-http/src/request/try_into_request.rs index ef5df69d41..10b2a56e88 100644 --- a/twilight-http/src/request/try_into_request.rs +++ b/twilight-http/src/request/try_into_request.rs @@ -1,4 +1,5 @@ mod private { + use crate::request::channel::SendSoundboardSound; use crate::request::{ application::{ command::{ @@ -264,6 +265,7 @@ mod private { impl Sealed for RemoveRoleFromMember<'_> {} impl Sealed for RemoveThreadMember<'_> {} impl Sealed for SearchGuildMembers<'_> {} + impl Sealed for SendSoundboardSound<'_> {} impl Sealed for SetGlobalCommands<'_> {} impl Sealed for SetGuildCommands<'_> {} impl Sealed for SyncTemplate<'_> {} diff --git a/twilight-http/src/routing.rs b/twilight-http/src/routing.rs index 93005d2d24..812c6594ac 100644 --- a/twilight-http/src/routing.rs +++ b/twilight-http/src/routing.rs @@ -986,6 +986,11 @@ pub enum Route<'a> { /// Query to search by. query: &'a str, }, + /// Route information to send soundboard sound. + SendSoundboardSound { + /// ID of the channel to send the soundboard sound in. + channel_id: u64, + }, /// Route information to set global commands. SetGlobalCommands { /// The ID of the owner application. @@ -1398,6 +1403,7 @@ impl Route<'_> { | Self::ExecuteWebhook { .. } | Self::FollowNewsChannel { .. } | Self::InteractionCallback { .. } + | Self::SendSoundboardSound { .. } | Self::SyncGuildIntegration { .. } => Method::Post, Self::AddGuildMember { .. } | Self::AddMemberRole { .. } @@ -1744,6 +1750,9 @@ impl Route<'_> { Self::EndPoll { channel_id, .. } | Self::GetAnswerVoters { channel_id, .. } => { Path::ChannelsIdPolls(channel_id) } + Self::SendSoundboardSound { channel_id } => { + Path::ChannelsIdSendSoundboardSound(channel_id) + } } } } @@ -3039,6 +3048,12 @@ impl Display for Route<'_> { f.write_str("/skus") } + Route::SendSoundboardSound { channel_id } => { + f.write_str("channels/")?; + Display::fmt(channel_id, f)?; + + f.write_str("/send-soundboard-sound") + } } } } From b9161e48c87231ba32c9ad7720330180b686a674 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Sun, 13 Jul 2025 15:36:27 +0800 Subject: [PATCH 16/27] cache: add soundboard (stubs) --- twilight-cache-inmemory/src/event/mod.rs | 1 + .../src/event/soundboard.rs | 6 +++ twilight-cache-inmemory/src/lib.rs | 48 ++++++++++++++----- twilight-cache-inmemory/src/traits.rs | 14 +++++- 4 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 twilight-cache-inmemory/src/event/soundboard.rs diff --git a/twilight-cache-inmemory/src/event/mod.rs b/twilight-cache-inmemory/src/event/mod.rs index 60351c58ec..4568303347 100644 --- a/twilight-cache-inmemory/src/event/mod.rs +++ b/twilight-cache-inmemory/src/event/mod.rs @@ -9,6 +9,7 @@ pub mod message; pub mod presence; pub mod reaction; pub mod role; +pub mod soundboard; pub mod stage_instance; pub mod sticker; pub mod thread; diff --git a/twilight-cache-inmemory/src/event/soundboard.rs b/twilight-cache-inmemory/src/event/soundboard.rs new file mode 100644 index 0000000000..a2e46132b8 --- /dev/null +++ b/twilight-cache-inmemory/src/event/soundboard.rs @@ -0,0 +1,6 @@ +use crate::{traits::CacheableModels, InMemoryCache, UpdateCache}; +use twilight_model::gateway::payload::incoming::GuildSoundboardSoundCreate; + +impl UpdateCache for GuildSoundboardSoundCreate {} + +impl InMemoryCache {} diff --git a/twilight-cache-inmemory/src/lib.rs b/twilight-cache-inmemory/src/lib.rs index 82e61b35ff..b35d699660 100644 --- a/twilight-cache-inmemory/src/lib.rs +++ b/twilight-cache-inmemory/src/lib.rs @@ -57,11 +57,11 @@ use std::{ use twilight_model::{ channel::{Channel, StageInstance}, gateway::event::Event, - guild::{scheduled_event::GuildScheduledEvent, GuildIntegration, Role}, + guild::{scheduled_event::GuildScheduledEvent, GuildIntegration, Role, SoundboardSound}, id::{ marker::{ ChannelMarker, EmojiMarker, GuildMarker, IntegrationMarker, MessageMarker, RoleMarker, - ScheduledEventMarker, StageMarker, StickerMarker, UserMarker, + ScheduledEventMarker, SoundboardSoundMarker, StageMarker, StickerMarker, UserMarker, }, Id, }, @@ -217,6 +217,7 @@ pub struct InMemoryCache { roles: DashMap, GuildResource>, scheduled_events: DashMap, GuildResource>, + soundboard_sound: DashMap, CacheModels::SoundboardSound>, stage_instances: DashMap, GuildResource>, stickers: DashMap, GuildResource>, unavailable_guilds: DashSet>, @@ -245,6 +246,7 @@ impl CacheableModels for DefaultCacheModels { type Message = model::CachedMessage; type Presence = model::CachedPresence; type Role = Role; + type SoundboardSound = SoundboardSound; type StageInstance = StageInstance; type Sticker = model::CachedSticker; type User = User; @@ -474,11 +476,11 @@ impl InMemoryCache { /// Gets the set of emojis in a guild. /// - /// This requires both the [`GUILDS`] and [`GUILD_EMOJIS_AND_STICKERS`] + /// This requires both the [`GUILDS`] and [`GUILD_EXPRESSIONS`] /// intents. /// /// [`GUILDS`]: ::twilight_model::gateway::Intents::GUILDS - /// [`GUILD_EMOJIS_AND_STICKERS`]: ::twilight_model::gateway::Intents::GUILD_EMOJIS_AND_STICKERS + /// [`GUILD_EXPRESSIONS`]: ::twilight_model::gateway::Intents::GUILD_EXPRESSIONS pub fn guild_emojis( &self, guild_id: Id, @@ -570,11 +572,11 @@ impl InMemoryCache { /// Gets the set of the stickers in a guild. /// /// This is an O(m) operation, where m is the amount of stickers in the - /// guild. This requires the [`GUILDS`] and [`GUILD_EMOJIS_AND_STICKERS`] + /// guild. This requires the [`GUILDS`] and [`GUILD_EXPRESSIONS`] /// intents and the [`STICKER`] resource type. /// /// [`GUILDS`]: twilight_model::gateway::Intents::GUILDS - /// [`GUILD_EMOJIS_AND_STICKERS`]: ::twilight_model::gateway::Intents::GUILD_EMOJIS_AND_STICKERS + /// [`GUILD_EXPRESSIONS`]: ::twilight_model::gateway::Intents::GUILD_EXPRESSIONS /// [`STICKER`]: crate::config::ResourceType::STICKER pub fn guild_stickers( &self, @@ -687,6 +689,20 @@ impl InMemoryCache { self.scheduled_events.get(&event_id).map(Reference::new) } + /// Gets a soundboard sound by ID. + /// + /// This requires the [`GUILD_EXPRESSIONS`] intent. + /// + /// [`GUILD_EXPRESSIONS`]: ::twilight_model::gateway::Intents::GUILD_EXPRESSIONS + pub fn soundboard_sound( + &self, + soundboard_sound_id: Id, + ) -> Option, CacheModels::SoundboardSound>> { + self.soundboard_sound + .get(&soundboard_sound_id) + .map(Reference::new) + } + /// Gets a stage instance by ID. /// /// This requires the [`GUILDS`] intent. @@ -851,6 +867,7 @@ impl Default for InMemoryCache { presences: DashMap::new(), roles: DashMap::new(), scheduled_events: DashMap::new(), + soundboard_sound: DashMap::new(), stage_instances: DashMap::new(), stickers: DashMap::new(), unavailable_guilds: DashSet::new(), @@ -870,13 +887,14 @@ mod private { ChannelCreate, ChannelDelete, ChannelPinsUpdate, ChannelUpdate, GuildCreate, GuildDelete, GuildEmojisUpdate, GuildScheduledEventCreate, GuildScheduledEventDelete, GuildScheduledEventUpdate, GuildScheduledEventUserAdd, GuildScheduledEventUserRemove, - GuildStickersUpdate, GuildUpdate, IntegrationCreate, IntegrationDelete, - IntegrationUpdate, InteractionCreate, MemberAdd, MemberChunk, MemberRemove, - MemberUpdate, MessageCreate, MessageDelete, MessageDeleteBulk, MessageUpdate, - PresenceUpdate, ReactionAdd, ReactionRemove, ReactionRemoveAll, ReactionRemoveEmoji, - Ready, RoleCreate, RoleDelete, RoleUpdate, StageInstanceCreate, StageInstanceDelete, - StageInstanceUpdate, ThreadCreate, ThreadDelete, ThreadListSync, ThreadUpdate, - UnavailableGuild, UserUpdate, VoiceStateUpdate, + GuildSoundboardSoundCreate, GuildSoundboardSoundDelete, GuildSoundboardSoundUpdate, + GuildSoundboardSoundsUpdate, GuildStickersUpdate, GuildUpdate, IntegrationCreate, + IntegrationDelete, IntegrationUpdate, InteractionCreate, MemberAdd, MemberChunk, + MemberRemove, MemberUpdate, MessageCreate, MessageDelete, MessageDeleteBulk, + MessageUpdate, PresenceUpdate, ReactionAdd, ReactionRemove, ReactionRemoveAll, + ReactionRemoveEmoji, Ready, RoleCreate, RoleDelete, RoleUpdate, StageInstanceCreate, + StageInstanceDelete, StageInstanceUpdate, ThreadCreate, ThreadDelete, ThreadListSync, + ThreadUpdate, UnavailableGuild, UserUpdate, VoiceStateUpdate, }, }; @@ -928,6 +946,10 @@ mod private { impl Sealed for GuildScheduledEventUpdate {} impl Sealed for GuildScheduledEventUserAdd {} impl Sealed for GuildScheduledEventUserRemove {} + impl Sealed for GuildSoundboardSoundCreate {} + impl Sealed for GuildSoundboardSoundDelete {} + impl Sealed for GuildSoundboardSoundUpdate {} + impl Sealed for GuildSoundboardSoundsUpdate {} } /// Implemented for dispatch events. diff --git a/twilight-cache-inmemory/src/traits.rs b/twilight-cache-inmemory/src/traits.rs index 5a7d9a5c29..5f544f156e 100644 --- a/twilight-cache-inmemory/src/traits.rs +++ b/twilight-cache-inmemory/src/traits.rs @@ -36,7 +36,7 @@ use twilight_model::{ }, guild::{ scheduled_event::GuildScheduledEvent, Emoji, Guild, GuildIntegration, Member, - PartialMember, Role, + PartialMember, Role, SoundboardSound, }, id::{ marker::{ @@ -63,7 +63,7 @@ pub trait CacheableModels: Clone + Debug { type Guild: CacheableGuild; /// The cached [`GuildIntegration`] model representation. type GuildIntegration: CacheableGuildIntegration; - /// The cached [`GuildScheduledEvent` model representation. + /// The cached [`GuildScheduledEvent`] model representation. type GuildScheduledEvent: CacheableGuildScheduledEvent; /// The cached [`Member`] model representation. type Member: CacheableMember; @@ -73,6 +73,8 @@ pub trait CacheableModels: Clone + Debug { type Presence: CacheablePresence; /// The cached [`Role`] model representation. type Role: CacheableRole; + /// The cached [`SoundboardSound`] model representation/ + type SoundboardSound: CacheableSoundboardSound; /// The cached [`StageInstance`] model representation. type StageInstance: CacheableStageInstance; /// The cached [`Sticker`] model representation. @@ -293,6 +295,14 @@ pub trait CacheablePresence: { } +/// Trait for a generic cached representation of a [`SoundboardSound`]. +pub trait CacheableSoundboardSound: + From + PartialEq + Clone + Debug +{ +} + +impl CacheableSoundboardSound for SoundboardSound {} + /// Trait for a generic cached representation of a [`StageInstance`]. pub trait CacheableStageInstance: From + PartialEq + PartialEq + Clone + Debug From 108bb95a6712f9331c5d994683968aa10bc64cce Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Sun, 13 Jul 2025 20:47:58 +0800 Subject: [PATCH 17/27] cache: add create event update cache and fix example --- examples/cache-optimization/models/mod.rs | 2 ++ .../cache-optimization/models/soundboard.rs | 6 +++++ twilight-cache-inmemory/src/config.rs | 2 ++ .../src/event/soundboard.rs | 27 ++++++++++++++++--- twilight-cache-inmemory/src/lib.rs | 27 ++++++++++++++++--- 5 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 examples/cache-optimization/models/soundboard.rs diff --git a/examples/cache-optimization/models/mod.rs b/examples/cache-optimization/models/mod.rs index 10288f1508..29d43cbe65 100644 --- a/examples/cache-optimization/models/mod.rs +++ b/examples/cache-optimization/models/mod.rs @@ -13,6 +13,7 @@ pub mod member; pub mod message; pub mod presence; pub mod role; +pub mod soundboard; pub mod stage_instance; pub mod sticker; pub mod user; @@ -32,6 +33,7 @@ impl CacheableModels for CustomCacheModels { type Message = message::MinimalCachedMessage; type Presence = presence::MinimalCachedPresence; type Role = role::MinimalCachedRole; + type SoundboardSound = soundboard::MinimalCachedSoundboardSound; type StageInstance = stage_instance::MinimalCachedStageInstance; type Sticker = sticker::MinimalCachedSticker; type User = user::MinimalCachedUser; diff --git a/examples/cache-optimization/models/soundboard.rs b/examples/cache-optimization/models/soundboard.rs new file mode 100644 index 0000000000..03781efe1e --- /dev/null +++ b/examples/cache-optimization/models/soundboard.rs @@ -0,0 +1,6 @@ +use twilight_cache_inmemory::traits::CacheableSoundboardSound; + +#[derive(Clone, Debug, PartialEq)] +pub struct MinimalCachedSoundboardSound; + +impl CacheableSoundboardSound for MinimalCachedSoundboardSound {} diff --git a/twilight-cache-inmemory/src/config.rs b/twilight-cache-inmemory/src/config.rs index 550017c2d0..069b430f9c 100644 --- a/twilight-cache-inmemory/src/config.rs +++ b/twilight-cache-inmemory/src/config.rs @@ -39,6 +39,8 @@ bitflags! { const STICKER = 1 << 13; /// Information relating to guild scheduled events. const GUILD_SCHEDULED_EVENT = 1 << 14; + /// information relating to guild soundboard sounds + const SOUNDBOARD_SOUNDS = 1 << 15; } } diff --git a/twilight-cache-inmemory/src/event/soundboard.rs b/twilight-cache-inmemory/src/event/soundboard.rs index a2e46132b8..436fbe4402 100644 --- a/twilight-cache-inmemory/src/event/soundboard.rs +++ b/twilight-cache-inmemory/src/event/soundboard.rs @@ -1,6 +1,25 @@ -use crate::{traits::CacheableModels, InMemoryCache, UpdateCache}; -use twilight_model::gateway::payload::incoming::GuildSoundboardSoundCreate; +use crate::{traits::CacheableModels, InMemoryCache, ResourceType, UpdateCache}; +use twilight_model::{gateway::payload::incoming::GuildSoundboardSoundCreate, guild::SoundboardSound}; -impl UpdateCache for GuildSoundboardSoundCreate {} +impl UpdateCache for GuildSoundboardSoundCreate { + fn update(&self, cache: &InMemoryCache) { + if !cache.wants(ResourceType::SOUNDBOARD_SOUNDS) { + return; + } -impl InMemoryCache {} + cache.cache_soundboard_sound(self.0.clone()) + } +} + +impl InMemoryCache { + pub(crate) fn cache_soundboard_sound(&self, soundboard_sound: SoundboardSound) { + if let Some(guild_id) = soundboard_sound.guild_id { + self.guild_soundboard_sounds + .entry(guild_id) + .or_default() + .insert(soundboard_sound.sound_id); + } + + self.soundboard_sound.insert(soundboard_sound.sound_id, soundboard_sound); + } +} diff --git a/twilight-cache-inmemory/src/lib.rs b/twilight-cache-inmemory/src/lib.rs index b35d699660..842c403e61 100644 --- a/twilight-cache-inmemory/src/lib.rs +++ b/twilight-cache-inmemory/src/lib.rs @@ -205,6 +205,7 @@ pub struct InMemoryCache { guild_presences: DashMap, HashSet>>, guild_roles: DashMap, HashSet>>, guild_scheduled_events: DashMap, HashSet>>, + guild_soundboard_sounds: DashMap, HashSet>>, guild_stage_instances: DashMap, HashSet>>, guild_stickers: DashMap, HashSet>>, integrations: DashMap< @@ -585,6 +586,22 @@ impl InMemoryCache { self.guild_stickers.get(&guild_id).map(Reference::new) } + /// Gets the set of the soundboard sounds in a guild. + /// + /// This is an O(m) operation, where m is the amount of stickers in the + /// guild. This requires the [`GUILDS`] and [`GUILD_EXPRESSIONS`] + /// intents and the [`SOUNDBOARD_SOUND`] resource type. + /// + /// [`GUILDS`]: twilight_model::gateway::Intents::GUILDS + /// [`GUILD_EXPRESSIONS`]: ::twilight_model::gateway::Intents::GUILD_EXPRESSIONS + /// [`SOUNDBOARD_SOUNDS`]: crate::config::ResourceType::SOUNDBOARD_SOUNDS + pub fn guild_soundboard_sounds( + &self, + guild_id: Id, + ) -> Option, HashSet>>> { + self.guild_soundboard_sounds.get(&guild_id).map(Reference::new) + } + /// Gets the set of voice states in a guild. /// /// This requires both the [`GUILDS`] and [`GUILD_VOICE_STATES`] intents. @@ -691,9 +708,10 @@ impl InMemoryCache { /// Gets a soundboard sound by ID. /// - /// This requires the [`GUILD_EXPRESSIONS`] intent. + /// This requires the [`GUILD_EXPRESSIONS`] intent and the [`SOUNDBOARD_SOUNDS`] resource type. /// /// [`GUILD_EXPRESSIONS`]: ::twilight_model::gateway::Intents::GUILD_EXPRESSIONS + /// [`SOUNDBOARD_SOUNDS`]: crate::config::ResourceType::SOUNDBOARD_SOUNDS pub fn soundboard_sound( &self, soundboard_sound_id: Id, @@ -718,10 +736,10 @@ impl InMemoryCache { /// Gets a sticker by ID. /// /// This is the O(1) operation. This requires the [`GUILDS`] and the - /// [`GUILD_EMOJIS_AND_STICKERS`] intents and the [`STICKER`] resource type. + /// [`GUILD_EXPRESSIONS`] intents and the [`STICKER`] resource type. /// /// [`GUILDS`]: twilight_model::gateway::Intents::GUILDS - /// [`GUILD_EMOJIS_AND_STICKERS`]: ::twilight_model::gateway::Intents::GUILD_EMOJIS_AND_STICKERS + /// [`GUILD_EXPRESSIONS`]: ::twilight_model::gateway::Intents::GUILD_EXPRESSIONS /// [`STICKER`]: crate::config::ResourceType::STICKER pub fn sticker( &self, @@ -858,6 +876,7 @@ impl Default for InMemoryCache { guild_presences: DashMap::new(), guild_roles: DashMap::new(), guild_scheduled_events: DashMap::new(), + guild_soundboard_sounds: DashMap::new(), guild_stage_instances: DashMap::new(), guild_stickers: DashMap::new(), guilds: DashMap::new(), @@ -1056,11 +1075,13 @@ impl UpdateCache for Event { | Event::GatewayReconnect | Event::GuildAuditLogEntryCreate(_) | Event::GuildIntegrationsUpdate(_) + // todo | Event::GuildSoundboardSoundCreate(_) | Event::GuildSoundboardSoundDelete(_) | Event::GuildSoundboardSoundUpdate(_) | Event::GuildSoundboardSoundsUpdate(_) | Event::SoundboardSounds(_) + // todo | Event::InviteCreate(_) | Event::InviteDelete(_) | Event::MessagePollVoteAdd(_) From 3a743b2010ae9a92e5ebf51f8965405e07be8bb3 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Sun, 13 Jul 2025 20:51:18 +0800 Subject: [PATCH 18/27] cache: use from impl --- twilight-cache-inmemory/src/event/soundboard.rs | 9 +++++++-- twilight-cache-inmemory/src/lib.rs | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/twilight-cache-inmemory/src/event/soundboard.rs b/twilight-cache-inmemory/src/event/soundboard.rs index 436fbe4402..89cea9a79d 100644 --- a/twilight-cache-inmemory/src/event/soundboard.rs +++ b/twilight-cache-inmemory/src/event/soundboard.rs @@ -1,5 +1,7 @@ use crate::{traits::CacheableModels, InMemoryCache, ResourceType, UpdateCache}; -use twilight_model::{gateway::payload::incoming::GuildSoundboardSoundCreate, guild::SoundboardSound}; +use twilight_model::{ + gateway::payload::incoming::GuildSoundboardSoundCreate, guild::SoundboardSound, +}; impl UpdateCache for GuildSoundboardSoundCreate { fn update(&self, cache: &InMemoryCache) { @@ -20,6 +22,9 @@ impl InMemoryCache { .insert(soundboard_sound.sound_id); } - self.soundboard_sound.insert(soundboard_sound.sound_id, soundboard_sound); + self.soundboard_sound.insert( + soundboard_sound.sound_id, + CacheModels::SoundboardSound::from(soundboard_sound), + ); } } diff --git a/twilight-cache-inmemory/src/lib.rs b/twilight-cache-inmemory/src/lib.rs index 842c403e61..86e2193976 100644 --- a/twilight-cache-inmemory/src/lib.rs +++ b/twilight-cache-inmemory/src/lib.rs @@ -599,7 +599,9 @@ impl InMemoryCache { &self, guild_id: Id, ) -> Option, HashSet>>> { - self.guild_soundboard_sounds.get(&guild_id).map(Reference::new) + self.guild_soundboard_sounds + .get(&guild_id) + .map(Reference::new) } /// Gets the set of voice states in a guild. From 629c54b668bfc1c99f9181e521b6bf8104642ec5 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Sun, 13 Jul 2025 20:57:48 +0800 Subject: [PATCH 19/27] fixes --- .../cache-optimization/models/soundboard.rs | 20 ++++++++++++++++++- .../src/event/soundboard.rs | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/examples/cache-optimization/models/soundboard.rs b/examples/cache-optimization/models/soundboard.rs index 03781efe1e..06c59145fc 100644 --- a/examples/cache-optimization/models/soundboard.rs +++ b/examples/cache-optimization/models/soundboard.rs @@ -1,6 +1,24 @@ use twilight_cache_inmemory::traits::CacheableSoundboardSound; +use twilight_model::{ + guild::SoundboardSound, + id::{marker::SoundboardSoundMarker, Id}, +}; #[derive(Clone, Debug, PartialEq)] -pub struct MinimalCachedSoundboardSound; +pub struct MinimalCachedSoundboardSound { + pub sound_id: Id, +} impl CacheableSoundboardSound for MinimalCachedSoundboardSound {} + +impl From for MinimalCachedSoundboardSound { + fn from(_: SoundboardSound) -> Self { + Self + } +} + +impl PartialEq for MinimalCachedSoundboardSound { + fn eq(&self, other: &SoundboardSound) -> bool { + self.sound_id == other.sound_id + } +} diff --git a/twilight-cache-inmemory/src/event/soundboard.rs b/twilight-cache-inmemory/src/event/soundboard.rs index 89cea9a79d..8bf92ef0cd 100644 --- a/twilight-cache-inmemory/src/event/soundboard.rs +++ b/twilight-cache-inmemory/src/event/soundboard.rs @@ -9,7 +9,7 @@ impl UpdateCache for GuildSoundboardS return; } - cache.cache_soundboard_sound(self.0.clone()) + cache.cache_soundboard_sound(self.0.clone()); } } From 7e0b29f94d9f6a19f42ad4b1ec8bef3ecd0fe903 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Sun, 13 Jul 2025 21:41:20 +0800 Subject: [PATCH 20/27] examples: fix --- examples/cache-optimization/models/soundboard.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cache-optimization/models/soundboard.rs b/examples/cache-optimization/models/soundboard.rs index 06c59145fc..9a8c4d363b 100644 --- a/examples/cache-optimization/models/soundboard.rs +++ b/examples/cache-optimization/models/soundboard.rs @@ -12,8 +12,8 @@ pub struct MinimalCachedSoundboardSound { impl CacheableSoundboardSound for MinimalCachedSoundboardSound {} impl From for MinimalCachedSoundboardSound { - fn from(_: SoundboardSound) -> Self { - Self + fn from(sound: SoundboardSound) -> Self { + Self { sound_id: sound.sound_id } } } From 2a811b2a7f82a1a5bb783095a7dfcb18a277286e Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Sun, 13 Jul 2025 22:12:39 +0800 Subject: [PATCH 21/27] cache: add delete event update --- .../cache-optimization/models/soundboard.rs | 17 ++++++++++--- .../src/event/soundboard.rs | 25 ++++++++++++++++++- twilight-cache-inmemory/src/traits.rs | 8 +++++- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/examples/cache-optimization/models/soundboard.rs b/examples/cache-optimization/models/soundboard.rs index 9a8c4d363b..bc7cfd1f14 100644 --- a/examples/cache-optimization/models/soundboard.rs +++ b/examples/cache-optimization/models/soundboard.rs @@ -1,19 +1,30 @@ use twilight_cache_inmemory::traits::CacheableSoundboardSound; use twilight_model::{ guild::SoundboardSound, - id::{marker::SoundboardSoundMarker, Id}, + id::{ + marker::{GuildMarker, SoundboardSoundMarker}, + Id, + }, }; #[derive(Clone, Debug, PartialEq)] pub struct MinimalCachedSoundboardSound { + pub guild_id: Option>, pub sound_id: Id, } -impl CacheableSoundboardSound for MinimalCachedSoundboardSound {} +impl CacheableSoundboardSound for MinimalCachedSoundboardSound { + fn guild_id(&self) -> Option> { + self.guild_id + } +} impl From for MinimalCachedSoundboardSound { fn from(sound: SoundboardSound) -> Self { - Self { sound_id: sound.sound_id } + Self { + guild_id: sound.guild_id, + sound_id: sound.sound_id, + } } } diff --git a/twilight-cache-inmemory/src/event/soundboard.rs b/twilight-cache-inmemory/src/event/soundboard.rs index 8bf92ef0cd..c0b5bfe673 100644 --- a/twilight-cache-inmemory/src/event/soundboard.rs +++ b/twilight-cache-inmemory/src/event/soundboard.rs @@ -1,6 +1,10 @@ +use std::ops::Deref; +use crate::traits::CacheableSoundboardSound; use crate::{traits::CacheableModels, InMemoryCache, ResourceType, UpdateCache}; use twilight_model::{ - gateway::payload::incoming::GuildSoundboardSoundCreate, guild::SoundboardSound, + gateway::payload::incoming::{GuildSoundboardSoundCreate, GuildSoundboardSoundDelete}, + guild::SoundboardSound, + id::{marker::SoundboardSoundMarker, Id}, }; impl UpdateCache for GuildSoundboardSoundCreate { @@ -13,6 +17,12 @@ impl UpdateCache for GuildSoundboardS } } +impl UpdateCache for GuildSoundboardSoundDelete { + fn update(&self, cache: &InMemoryCache) { + cache.delete_soundboard_sound(self.sound_id); + } +} + impl InMemoryCache { pub(crate) fn cache_soundboard_sound(&self, soundboard_sound: SoundboardSound) { if let Some(guild_id) = soundboard_sound.guild_id { @@ -27,4 +37,17 @@ impl InMemoryCache { CacheModels::SoundboardSound::from(soundboard_sound), ); } + + pub(crate) fn delete_soundboard_sound(&self, sound_id: Id) { + let Some((_, sound)) = self.soundboard_sound.remove(&sound_id) else { + return; + }; + let Some(guild_id) = sound.guild_id() else { + return; + }; + let Some(mut sounds) = self.guild_soundboard_sounds.get_mut(&guild_id) else { + return; + }; + sounds.remove(&sound_id); + } } diff --git a/twilight-cache-inmemory/src/traits.rs b/twilight-cache-inmemory/src/traits.rs index 5f544f156e..d4c5dd942d 100644 --- a/twilight-cache-inmemory/src/traits.rs +++ b/twilight-cache-inmemory/src/traits.rs @@ -299,9 +299,15 @@ pub trait CacheablePresence: pub trait CacheableSoundboardSound: From + PartialEq + Clone + Debug { + /// Guild ID of the soundboard sound, if any. + fn guild_id(&self) -> Option>; } -impl CacheableSoundboardSound for SoundboardSound {} +impl CacheableSoundboardSound for SoundboardSound { + fn guild_id(&self) -> Option> { + self.guild_id + } +} /// Trait for a generic cached representation of a [`StageInstance`]. pub trait CacheableStageInstance: From fcbbd7998e7e5bf44aa852bc0f2e3a970e46112d Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Sun, 13 Jul 2025 22:21:31 +0800 Subject: [PATCH 22/27] fmt --- twilight-cache-inmemory/src/event/soundboard.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/twilight-cache-inmemory/src/event/soundboard.rs b/twilight-cache-inmemory/src/event/soundboard.rs index c0b5bfe673..166c0fd44f 100644 --- a/twilight-cache-inmemory/src/event/soundboard.rs +++ b/twilight-cache-inmemory/src/event/soundboard.rs @@ -1,4 +1,3 @@ -use std::ops::Deref; use crate::traits::CacheableSoundboardSound; use crate::{traits::CacheableModels, InMemoryCache, ResourceType, UpdateCache}; use twilight_model::{ From 15d4ec6fc5cb9d0d767e5a4bd29a234f1f565884 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Mon, 14 Jul 2025 19:18:45 +0800 Subject: [PATCH 23/27] cache: add updates --- .../src/event/soundboard.rs | 54 +++++++++++++------ 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/twilight-cache-inmemory/src/event/soundboard.rs b/twilight-cache-inmemory/src/event/soundboard.rs index 166c0fd44f..4f8163bbc5 100644 --- a/twilight-cache-inmemory/src/event/soundboard.rs +++ b/twilight-cache-inmemory/src/event/soundboard.rs @@ -1,28 +1,24 @@ use crate::traits::CacheableSoundboardSound; use crate::{traits::CacheableModels, InMemoryCache, ResourceType, UpdateCache}; use twilight_model::{ - gateway::payload::incoming::{GuildSoundboardSoundCreate, GuildSoundboardSoundDelete}, + gateway::payload::incoming::{ + GuildSoundboardSoundCreate, GuildSoundboardSoundDelete, GuildSoundboardSoundUpdate, + GuildSoundboardSoundsUpdate, + }, guild::SoundboardSound, id::{marker::SoundboardSoundMarker, Id}, }; -impl UpdateCache for GuildSoundboardSoundCreate { - fn update(&self, cache: &InMemoryCache) { - if !cache.wants(ResourceType::SOUNDBOARD_SOUNDS) { - return; +impl InMemoryCache { + pub(crate) fn cache_soundboard_sounds( + &self, + sounds: impl IntoIterator, + ) { + for sound in sounds { + self.cache_soundboard_sound(sound); } - - cache.cache_soundboard_sound(self.0.clone()); - } -} - -impl UpdateCache for GuildSoundboardSoundDelete { - fn update(&self, cache: &InMemoryCache) { - cache.delete_soundboard_sound(self.sound_id); } -} -impl InMemoryCache { pub(crate) fn cache_soundboard_sound(&self, soundboard_sound: SoundboardSound) { if let Some(guild_id) = soundboard_sound.guild_id { self.guild_soundboard_sounds @@ -50,3 +46,31 @@ impl InMemoryCache { sounds.remove(&sound_id); } } + +impl UpdateCache for GuildSoundboardSoundCreate { + fn update(&self, cache: &InMemoryCache) { + if !cache.wants(ResourceType::SOUNDBOARD_SOUNDS) { + return; + } + + cache.cache_soundboard_sound(self.0.clone()); + } +} + +impl UpdateCache for GuildSoundboardSoundDelete { + fn update(&self, cache: &InMemoryCache) { + cache.delete_soundboard_sound(self.sound_id); + } +} + +impl UpdateCache for GuildSoundboardSoundUpdate { + fn update(&self, cache: &InMemoryCache) { + cache.cache_soundboard_sound(self.0.clone()); + } +} + +impl UpdateCache for GuildSoundboardSoundsUpdate { + fn update(&self, cache: &InMemoryCache) { + cache.cache_soundboard_sounds(self.soundboard_sounds.clone()); + } +} From 88787ce12ac4b753633e99cfe4807c0efb8fc437 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Mon, 14 Jul 2025 21:21:41 +0800 Subject: [PATCH 24/27] http: List Soundboard Default Sounds --- twilight-http/src/client/mod.rs | 50 +++++++++++++++++-- .../request/get_soundboard_default_sounds.rs | 41 +++++++++++++++ twilight-http/src/request/mod.rs | 2 + twilight-http/src/request/try_into_request.rs | 5 +- twilight-http/src/routing.rs | 5 ++ 5 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 twilight-http/src/request/get_soundboard_default_sounds.rs diff --git a/twilight-http/src/client/mod.rs b/twilight-http/src/client/mod.rs index 31425f68bb..409e493fe3 100644 --- a/twilight-http/src/client/mod.rs +++ b/twilight-http/src/client/mod.rs @@ -4,7 +4,6 @@ mod interaction; pub use self::{builder::ClientBuilder, interaction::InteractionClient}; -use crate::request::channel::SendSoundboardSound; use crate::request::{ application::{ emoji::{ @@ -48,7 +47,8 @@ use crate::{ UpdateWebhookMessage, UpdateWebhookWithToken, }, CreatePin, CreateTypingTrigger, DeleteChannel, DeleteChannelPermission, DeletePin, - FollowNewsChannel, GetChannel, GetPins, UpdateChannel, UpdateChannelPermission, + FollowNewsChannel, GetChannel, GetPins, SendSoundboardSound, UpdateChannel, + UpdateChannelPermission, }, guild::{ auto_moderation::{ @@ -93,8 +93,8 @@ use crate::{ GetCurrentUserGuildMember, GetCurrentUserGuilds, GetUser, LeaveGuild, UpdateCurrentUser, }, - GetCurrentAuthorizationInformation, GetGateway, GetUserApplicationInfo, GetVoiceRegions, - Method, Request, UpdateCurrentUserApplication, + GetCurrentAuthorizationInformation, GetDefaultSoundboardSounds, GetGateway, + GetUserApplicationInfo, GetVoiceRegions, Method, Request, UpdateCurrentUserApplication, }, response::ResponseFuture, API_VERSION, @@ -2884,6 +2884,27 @@ impl Client { DeleteApplicationEmoji::new(self, application_id, emoji_id) } + /// Send a soundboard sound in a voice channel the current user is connected to. + /// + /// # Examples + /// + /// ```no_run + /// use twilight_http::Client; + /// use twilight_model::id::Id; + /// + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// let client = Client::new("my token".to_owned()); + /// + /// let channel_id = Id::new(1); + /// let sound_id = Id::new(2); + /// + /// client + /// .send_soundboard_sound(channel_id, sound_id) + /// .await?; + /// + /// # Ok(()) } + /// ``` pub const fn send_soundboard_sound( &self, channel_id: Id, @@ -2892,6 +2913,27 @@ impl Client { SendSoundboardSound::new(self, channel_id, sound_id) } + /// Retrieve the soundboard default sounds provided by Discord. + /// + /// # Examples + /// + /// ```no_run + /// use twilight_http::Client; + /// + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// let client = Client::new("my token".to_owned()); + /// + /// client + /// .soundboard_default_sounds() + /// .await?; + /// + /// # Ok(()) } + /// ``` + pub const fn soundboard_default_sounds(&self) -> GetDefaultSoundboardSounds<'_> { + GetDefaultSoundboardSounds::new(self) + } + /// Execute a request, returning a future resolving to a [`Response`]. /// /// # Errors diff --git a/twilight-http/src/request/get_soundboard_default_sounds.rs b/twilight-http/src/request/get_soundboard_default_sounds.rs new file mode 100644 index 0000000000..0a4e3bbae2 --- /dev/null +++ b/twilight-http/src/request/get_soundboard_default_sounds.rs @@ -0,0 +1,41 @@ +use crate::routing::Route; +use crate::{ + client::Client, + request::{Request, TryIntoRequest}, + response::{marker::ListBody, Response, ResponseFuture}, + Error, +}; +use std::future::IntoFuture; +use twilight_model::guild::SoundboardSound; + +#[must_use = "requests must be configured and executed"] +pub struct GetDefaultSoundboardSounds<'a> { + http: &'a Client, +} + +impl<'a> GetDefaultSoundboardSounds<'a> { + pub(crate) const fn new(http: &'a Client) -> Self { + Self { http } + } +} + +impl IntoFuture for GetDefaultSoundboardSounds<'_> { + type Output = Result>, Error>; + + type IntoFuture = ResponseFuture>; + + fn into_future(self) -> Self::IntoFuture { + let http = self.http; + + match self.try_into_request() { + Ok(request) => http.request(request), + Err(source) => ResponseFuture::error(source), + } + } +} + +impl TryIntoRequest for GetDefaultSoundboardSounds<'_> { + fn try_into_request(self) -> Result { + Ok(Request::from_route(&Route::GetSoundboardDefaultSounds)) + } +} diff --git a/twilight-http/src/request/mod.rs b/twilight-http/src/request/mod.rs index 77e07abf12..4a436965b7 100644 --- a/twilight-http/src/request/mod.rs +++ b/twilight-http/src/request/mod.rs @@ -55,6 +55,7 @@ mod base; mod get_current_authorization_information; mod get_gateway; mod get_gateway_authed; +mod get_soundboard_default_sounds; mod get_user_application; mod get_voice_regions; mod multipart; @@ -67,6 +68,7 @@ pub use self::{ get_current_authorization_information::GetCurrentAuthorizationInformation, get_gateway::GetGateway, get_gateway_authed::GetGatewayAuthed, + get_soundboard_default_sounds::GetDefaultSoundboardSounds, get_user_application::GetUserApplicationInfo, get_voice_regions::GetVoiceRegions, multipart::Form, diff --git a/twilight-http/src/request/try_into_request.rs b/twilight-http/src/request/try_into_request.rs index 10b2a56e88..548789f73f 100644 --- a/twilight-http/src/request/try_into_request.rs +++ b/twilight-http/src/request/try_into_request.rs @@ -103,8 +103,8 @@ mod private { GetCurrentUserGuildMember, GetCurrentUserGuilds, GetUser, LeaveGuild, UpdateCurrentUser, }, - GetCurrentAuthorizationInformation, GetGateway, GetGatewayAuthed, GetUserApplicationInfo, - GetVoiceRegions, + GetCurrentAuthorizationInformation, GetDefaultSoundboardSounds, GetGateway, + GetGatewayAuthed, GetUserApplicationInfo, GetVoiceRegions, }; pub trait Sealed {} @@ -202,6 +202,7 @@ mod private { impl Sealed for GetCurrentUserGuildMember<'_> {} impl Sealed for GetCurrentUserGuilds<'_> {} impl Sealed for GetCurrentUserVoiceState<'_> {} + impl Sealed for GetDefaultSoundboardSounds<'_> {} impl Sealed for GetEmoji<'_> {} impl Sealed for GetEmojis<'_> {} impl Sealed for GetEntitlements<'_> {} diff --git a/twilight-http/src/routing.rs b/twilight-http/src/routing.rs index 812c6594ac..354b0969d8 100644 --- a/twilight-http/src/routing.rs +++ b/twilight-http/src/routing.rs @@ -1003,6 +1003,8 @@ pub enum Route<'a> { /// The ID of the guild. guild_id: u64, }, + /// Route information to get soundboard default sounds. + GetSoundboardDefaultSounds, /// Route information to sync a guild's integration. SyncGuildIntegration { /// The ID of the guild. @@ -1329,6 +1331,7 @@ impl Route<'_> { | Self::GetPublicArchivedThreads { .. } | Self::GetReactionUsers { .. } | Self::GetRole { .. } + | Self::GetSoundboardDefaultSounds | Self::GetSKUs { .. } | Self::GetStageInstance { .. } | Self::GetSticker { .. } @@ -1753,6 +1756,7 @@ impl Route<'_> { Self::SendSoundboardSound { channel_id } => { Path::ChannelsIdSendSoundboardSound(channel_id) } + Self::GetSoundboardDefaultSounds => Path::SoundboardDefaultSounds, } } } @@ -3054,6 +3058,7 @@ impl Display for Route<'_> { f.write_str("/send-soundboard-sound") } + Route::GetSoundboardDefaultSounds => f.write_str("soundboard-default-sounds"), } } } From fbe29ec859b45cb4041771abb5ac34efc84635c8 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Mon, 14 Jul 2025 21:25:05 +0800 Subject: [PATCH 25/27] fmt --- twilight-http/src/client/mod.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/twilight-http/src/client/mod.rs b/twilight-http/src/client/mod.rs index 409e493fe3..34cfeb1cae 100644 --- a/twilight-http/src/client/mod.rs +++ b/twilight-http/src/client/mod.rs @@ -2899,9 +2899,7 @@ impl Client { /// let channel_id = Id::new(1); /// let sound_id = Id::new(2); /// - /// client - /// .send_soundboard_sound(channel_id, sound_id) - /// .await?; + /// client.send_soundboard_sound(channel_id, sound_id).await?; /// /// # Ok(()) } /// ``` @@ -2924,9 +2922,7 @@ impl Client { /// # async fn main() -> Result<(), Box> { /// let client = Client::new("my token".to_owned()); /// - /// client - /// .soundboard_default_sounds() - /// .await?; + /// client.soundboard_default_sounds().await?; /// /// # Ok(()) } /// ``` From 5a764b9dae5d4a79aa5a6d897987bfc78fd982ed Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Mon, 14 Jul 2025 23:01:03 +0800 Subject: [PATCH 26/27] http: List Guild Soundboard Sounds --- twilight-http/src/client/mod.rs | 26 ++++++++++ twilight-http/src/request/guild/mod.rs | 1 + .../soundboard/get_guild_soundboard_sounds.rs | 47 +++++++++++++++++++ .../src/request/guild/soundboard/mod.rs | 3 ++ twilight-http/src/request/try_into_request.rs | 5 +- twilight-http/src/routing.rs | 12 +++++ 6 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 twilight-http/src/request/guild/soundboard/get_guild_soundboard_sounds.rs create mode 100644 twilight-http/src/request/guild/soundboard/mod.rs diff --git a/twilight-http/src/client/mod.rs b/twilight-http/src/client/mod.rs index 34cfeb1cae..71c707ad09 100644 --- a/twilight-http/src/client/mod.rs +++ b/twilight-http/src/client/mod.rs @@ -65,6 +65,7 @@ use crate::{ role::{ CreateRole, DeleteRole, GetGuildRoles, GetRole, UpdateRole, UpdateRolePositions, }, + soundboard::GetGuildSoundboardSounds, sticker::{ CreateGuildSticker, DeleteGuildSticker, GetGuildSticker, GetGuildStickers, UpdateGuildSticker, @@ -2930,6 +2931,31 @@ impl Client { GetDefaultSoundboardSounds::new(self) } + /// Retrieve the soundboard sounds of a guild. + /// + /// # Examples + /// + /// ```no_run + /// use twilight_http::Client; + /// use twilight_model::id::Id; + /// + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// let client = Client::new("my token".to_owned()); + /// + /// let guild_id = Id::new(1); + /// + /// client.guild_soundboard_sounds(guild_id).await?; + /// + /// # Ok(()) } + /// ``` + pub const fn guild_soundboard_sounds( + &self, + guild_id: Id, + ) -> GetGuildSoundboardSounds<'_> { + GetGuildSoundboardSounds::new(self, guild_id) + } + /// Execute a request, returning a future resolving to a [`Response`]. /// /// # Errors diff --git a/twilight-http/src/request/guild/mod.rs b/twilight-http/src/request/guild/mod.rs index 3a7bac4699..95e385f928 100644 --- a/twilight-http/src/request/guild/mod.rs +++ b/twilight-http/src/request/guild/mod.rs @@ -5,6 +5,7 @@ pub mod emoji; pub mod integration; pub mod member; pub mod role; +pub mod soundboard; pub mod sticker; pub mod update_guild_channel_positions; pub mod update_guild_onboarding; diff --git a/twilight-http/src/request/guild/soundboard/get_guild_soundboard_sounds.rs b/twilight-http/src/request/guild/soundboard/get_guild_soundboard_sounds.rs new file mode 100644 index 0000000000..48a0757895 --- /dev/null +++ b/twilight-http/src/request/guild/soundboard/get_guild_soundboard_sounds.rs @@ -0,0 +1,47 @@ +use crate::{ + client::Client, + error::Error, + request::{Request, TryIntoRequest}, + response::{marker::ListBody, Response, ResponseFuture}, + routing::Route, +}; +use std::future::IntoFuture; +use twilight_model::{ + guild::SoundboardSound, + id::{marker::GuildMarker, Id}, +}; + +#[must_use = "requests must be configured and executed"] +pub struct GetGuildSoundboardSounds<'a> { + guild_id: Id, + http: &'a Client, +} + +impl<'a> GetGuildSoundboardSounds<'a> { + pub(crate) const fn new(http: &'a Client, guild_id: Id) -> Self { + Self { guild_id, http } + } +} + +impl IntoFuture for GetGuildSoundboardSounds<'_> { + type Output = Result>, Error>; + + type IntoFuture = ResponseFuture>; + + fn into_future(self) -> Self::IntoFuture { + let http = self.http; + + match self.try_into_request() { + Ok(request) => http.request(request), + Err(source) => ResponseFuture::error(source), + } + } +} + +impl TryIntoRequest for GetGuildSoundboardSounds<'_> { + fn try_into_request(self) -> Result { + Ok(Request::from_route(&Route::GetGuildSoundboardSounds { + guild_id: self.guild_id.get(), + })) + } +} diff --git a/twilight-http/src/request/guild/soundboard/mod.rs b/twilight-http/src/request/guild/soundboard/mod.rs new file mode 100644 index 0000000000..81eaa1837e --- /dev/null +++ b/twilight-http/src/request/guild/soundboard/mod.rs @@ -0,0 +1,3 @@ +mod get_guild_soundboard_sounds; + +pub use get_guild_soundboard_sounds::GetGuildSoundboardSounds; diff --git a/twilight-http/src/request/try_into_request.rs b/twilight-http/src/request/try_into_request.rs index 548789f73f..67c8cdc1fa 100644 --- a/twilight-http/src/request/try_into_request.rs +++ b/twilight-http/src/request/try_into_request.rs @@ -1,5 +1,4 @@ mod private { - use crate::request::channel::SendSoundboardSound; use crate::request::{ application::{ command::{ @@ -53,7 +52,7 @@ mod private { }, CreatePin, CreateTypingTrigger, DeleteChannel, DeleteChannelPermission, DeleteChannelPermissionConfigured, DeletePin, FollowNewsChannel, GetChannel, GetPins, - UpdateChannel, UpdateChannelPermission, + SendSoundboardSound, UpdateChannel, UpdateChannelPermission, }, guild::{ auto_moderation::{ @@ -70,6 +69,7 @@ mod private { role::{ CreateRole, DeleteRole, GetGuildRoles, GetRole, UpdateRole, UpdateRolePositions, }, + soundboard::GetGuildSoundboardSounds, sticker::{ CreateGuildSticker, DeleteGuildSticker, GetGuildSticker, GetGuildStickers, UpdateGuildSticker, @@ -227,6 +227,7 @@ mod private { impl Sealed for GetGuildScheduledEvent<'_> {} impl Sealed for GetGuildScheduledEventUsers<'_> {} impl Sealed for GetGuildScheduledEvents<'_> {} + impl Sealed for GetGuildSoundboardSounds<'_> {} impl Sealed for GetGuildSticker<'_> {} impl Sealed for GetGuildStickers<'_> {} impl Sealed for GetGuildVanityUrl<'_> {} diff --git a/twilight-http/src/routing.rs b/twilight-http/src/routing.rs index 354b0969d8..64c46ad012 100644 --- a/twilight-http/src/routing.rs +++ b/twilight-http/src/routing.rs @@ -683,6 +683,10 @@ pub enum Route<'a> { /// Whether to include user counts. with_user_count: bool, }, + /// Route information to get a guild's soundboard sounds. + GetGuildSoundboardSounds { + guild_id: u64, + }, /// Route information to get a guild's sticker. GetGuildSticker { /// ID of the guild. @@ -1306,6 +1310,7 @@ impl Route<'_> { | Self::GetGuildPreview { .. } | Self::GetGuildPruneCount { .. } | Self::GetGuildRoles { .. } + | Self::GetGuildSoundboardSounds { .. } | Self::GetGuildScheduledEvent { .. } | Self::GetGuildScheduledEventUsers { .. } | Self::GetGuildScheduledEvents { .. } @@ -1757,6 +1762,7 @@ impl Route<'_> { Path::ChannelsIdSendSoundboardSound(channel_id) } Self::GetSoundboardDefaultSounds => Path::SoundboardDefaultSounds, + Self::GetGuildSoundboardSounds { guild_id } => Path::GuildsIdSoundboardSounds(guild_id), } } } @@ -3059,6 +3065,12 @@ impl Display for Route<'_> { f.write_str("/send-soundboard-sound") } Route::GetSoundboardDefaultSounds => f.write_str("soundboard-default-sounds"), + Route::GetGuildSoundboardSounds { guild_id } => { + f.write_str("guilds/")?; + Display::fmt(guild_id, f)?; + + f.write_str("/soundboard-sounds") + } } } } From 54c01ec80e8c4bd3439a3c6e3cf15f6ce2a3a8f5 Mon Sep 17 00:00:00 2001 From: HTGAzureX1212 <39023054+HTGAzureX1212@users.noreply.github.com> Date: Tue, 15 Jul 2025 09:58:03 +0800 Subject: [PATCH 27/27] http: Get Guild Soundboard Sound --- twilight-http/src/client/mod.rs | 29 ++++++++- .../soundboard/get_guild_soundboard_sound.rs | 60 +++++++++++++++++++ .../src/request/guild/soundboard/mod.rs | 2 + twilight-http/src/request/try_into_request.rs | 3 +- twilight-http/src/routing.rs | 16 +++++ 5 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 twilight-http/src/request/guild/soundboard/get_guild_soundboard_sound.rs diff --git a/twilight-http/src/client/mod.rs b/twilight-http/src/client/mod.rs index 71c707ad09..74ec5f5e14 100644 --- a/twilight-http/src/client/mod.rs +++ b/twilight-http/src/client/mod.rs @@ -65,7 +65,7 @@ use crate::{ role::{ CreateRole, DeleteRole, GetGuildRoles, GetRole, UpdateRole, UpdateRolePositions, }, - soundboard::GetGuildSoundboardSounds, + soundboard::{GetGuildSoundboardSound, GetGuildSoundboardSounds}, sticker::{ CreateGuildSticker, DeleteGuildSticker, GetGuildSticker, GetGuildStickers, UpdateGuildSticker, @@ -2931,6 +2931,33 @@ impl Client { GetDefaultSoundboardSounds::new(self) } + /// Retrieve a soundboard sound of a guild. + /// + /// # Examples + /// + /// ```no_run + /// use twilight_http::Client; + /// use twilight_model::id::Id; + /// + /// # #[tokio::main] + /// # async fn main() -> Result<(), Box> { + /// let client = Client::new("my token".to_owned()); + /// + /// let guild_id = Id::new(1); + /// let sound_id = Id::new(1); + /// + /// client.guild_soundboard_sound(guild_id, sound_id).await?; + /// + /// # Ok(()) } + /// ``` + pub const fn guild_soundboard_sound( + &self, + guild_id: Id, + sound_id: Id, + ) -> GetGuildSoundboardSound<'_> { + GetGuildSoundboardSound::new(self, guild_id, sound_id) + } + /// Retrieve the soundboard sounds of a guild. /// /// # Examples diff --git a/twilight-http/src/request/guild/soundboard/get_guild_soundboard_sound.rs b/twilight-http/src/request/guild/soundboard/get_guild_soundboard_sound.rs new file mode 100644 index 0000000000..4429b23e9f --- /dev/null +++ b/twilight-http/src/request/guild/soundboard/get_guild_soundboard_sound.rs @@ -0,0 +1,60 @@ +use crate::{ + client::Client, + request::{Request, TryIntoRequest}, + response::{Response, ResponseFuture}, + routing::Route, + Error, +}; +use std::future::IntoFuture; +use twilight_model::{ + guild::SoundboardSound, + id::{ + marker::{GuildMarker, SoundboardSoundMarker}, + Id, + }, +}; + +#[must_use = "requests must be configured and executed"] +pub struct GetGuildSoundboardSound<'a> { + http: &'a Client, + guild_id: Id, + sound_id: Id, +} + +impl<'a> GetGuildSoundboardSound<'a> { + pub(crate) const fn new( + http: &'a Client, + guild_id: Id, + sound_id: Id, + ) -> Self { + Self { + http, + guild_id, + sound_id, + } + } +} + +impl IntoFuture for GetGuildSoundboardSound<'_> { + type Output = Result, Error>; + + type IntoFuture = ResponseFuture; + + fn into_future(self) -> Self::IntoFuture { + let http = self.http; + + match self.try_into_request() { + Ok(request) => http.request(request), + Err(source) => ResponseFuture::error(source), + } + } +} + +impl TryIntoRequest for GetGuildSoundboardSound<'_> { + fn try_into_request(self) -> Result { + Ok(Request::from_route(&Route::GetGuildSoundboardSound { + guild_id: self.guild_id.get(), + sound_id: self.sound_id.get(), + })) + } +} diff --git a/twilight-http/src/request/guild/soundboard/mod.rs b/twilight-http/src/request/guild/soundboard/mod.rs index 81eaa1837e..cc6f039ac6 100644 --- a/twilight-http/src/request/guild/soundboard/mod.rs +++ b/twilight-http/src/request/guild/soundboard/mod.rs @@ -1,3 +1,5 @@ +mod get_guild_soundboard_sound; mod get_guild_soundboard_sounds; +pub use get_guild_soundboard_sound::GetGuildSoundboardSound; pub use get_guild_soundboard_sounds::GetGuildSoundboardSounds; diff --git a/twilight-http/src/request/try_into_request.rs b/twilight-http/src/request/try_into_request.rs index 67c8cdc1fa..196fa676fb 100644 --- a/twilight-http/src/request/try_into_request.rs +++ b/twilight-http/src/request/try_into_request.rs @@ -69,7 +69,7 @@ mod private { role::{ CreateRole, DeleteRole, GetGuildRoles, GetRole, UpdateRole, UpdateRolePositions, }, - soundboard::GetGuildSoundboardSounds, + soundboard::{GetGuildSoundboardSound, GetGuildSoundboardSounds}, sticker::{ CreateGuildSticker, DeleteGuildSticker, GetGuildSticker, GetGuildStickers, UpdateGuildSticker, @@ -227,6 +227,7 @@ mod private { impl Sealed for GetGuildScheduledEvent<'_> {} impl Sealed for GetGuildScheduledEventUsers<'_> {} impl Sealed for GetGuildScheduledEvents<'_> {} + impl Sealed for GetGuildSoundboardSound<'_> {} impl Sealed for GetGuildSoundboardSounds<'_> {} impl Sealed for GetGuildSticker<'_> {} impl Sealed for GetGuildStickers<'_> {} diff --git a/twilight-http/src/routing.rs b/twilight-http/src/routing.rs index 64c46ad012..f85e6d1edc 100644 --- a/twilight-http/src/routing.rs +++ b/twilight-http/src/routing.rs @@ -683,6 +683,11 @@ pub enum Route<'a> { /// Whether to include user counts. with_user_count: bool, }, + /// Route information to get a guild's soundboard sound. + GetGuildSoundboardSound { + guild_id: u64, + sound_id: u64, + }, /// Route information to get a guild's soundboard sounds. GetGuildSoundboardSounds { guild_id: u64, @@ -1310,6 +1315,7 @@ impl Route<'_> { | Self::GetGuildPreview { .. } | Self::GetGuildPruneCount { .. } | Self::GetGuildRoles { .. } + | Self::GetGuildSoundboardSound { .. } | Self::GetGuildSoundboardSounds { .. } | Self::GetGuildScheduledEvent { .. } | Self::GetGuildScheduledEventUsers { .. } @@ -1762,6 +1768,9 @@ impl Route<'_> { Path::ChannelsIdSendSoundboardSound(channel_id) } Self::GetSoundboardDefaultSounds => Path::SoundboardDefaultSounds, + Self::GetGuildSoundboardSound { guild_id, .. } => { + Path::GuildsIdSoundboardSoundsId(guild_id) + } Self::GetGuildSoundboardSounds { guild_id } => Path::GuildsIdSoundboardSounds(guild_id), } } @@ -3065,6 +3074,13 @@ impl Display for Route<'_> { f.write_str("/send-soundboard-sound") } Route::GetSoundboardDefaultSounds => f.write_str("soundboard-default-sounds"), + Route::GetGuildSoundboardSound { guild_id, sound_id } => { + f.write_str("guilds/")?; + Display::fmt(guild_id, f)?; + f.write_str("/soundboard-sounds/")?; + + Display::fmt(sound_id, f) + } Route::GetGuildSoundboardSounds { guild_id } => { f.write_str("guilds/")?; Display::fmt(guild_id, f)?;