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..bc7cfd1f14 --- /dev/null +++ b/examples/cache-optimization/models/soundboard.rs @@ -0,0 +1,35 @@ +use twilight_cache_inmemory::traits::CacheableSoundboardSound; +use twilight_model::{ + guild::SoundboardSound, + id::{ + marker::{GuildMarker, SoundboardSoundMarker}, + Id, + }, +}; + +#[derive(Clone, Debug, PartialEq)] +pub struct MinimalCachedSoundboardSound { + pub guild_id: Option>, + pub sound_id: Id, +} + +impl CacheableSoundboardSound for MinimalCachedSoundboardSound { + fn guild_id(&self) -> Option> { + self.guild_id + } +} + +impl From for MinimalCachedSoundboardSound { + fn from(sound: SoundboardSound) -> Self { + Self { + guild_id: sound.guild_id, + sound_id: sound.sound_id, + } + } +} + +impl PartialEq for MinimalCachedSoundboardSound { + fn eq(&self, other: &SoundboardSound) -> bool { + self.sound_id == other.sound_id + } +} 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/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..4f8163bbc5 --- /dev/null +++ b/twilight-cache-inmemory/src/event/soundboard.rs @@ -0,0 +1,76 @@ +use crate::traits::CacheableSoundboardSound; +use crate::{traits::CacheableModels, InMemoryCache, ResourceType, UpdateCache}; +use twilight_model::{ + gateway::payload::incoming::{ + GuildSoundboardSoundCreate, GuildSoundboardSoundDelete, GuildSoundboardSoundUpdate, + GuildSoundboardSoundsUpdate, + }, + guild::SoundboardSound, + id::{marker::SoundboardSoundMarker, Id}, +}; + +impl InMemoryCache { + pub(crate) fn cache_soundboard_sounds( + &self, + sounds: impl IntoIterator, + ) { + for sound in sounds { + self.cache_soundboard_sound(sound); + } + } + + 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, + 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); + } +} + +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()); + } +} diff --git a/twilight-cache-inmemory/src/lib.rs b/twilight-cache-inmemory/src/lib.rs index 0a4c164b52..86e2193976 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, }, @@ -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< @@ -217,6 +218,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 +247,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 +477,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 +573,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, @@ -583,6 +586,24 @@ 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. @@ -687,6 +708,21 @@ impl InMemoryCache { self.scheduled_events.get(&event_id).map(Reference::new) } + /// Gets a soundboard sound by ID. + /// + /// 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, + ) -> Option, CacheModels::SoundboardSound>> { + self.soundboard_sound + .get(&soundboard_sound_id) + .map(Reference::new) + } + /// Gets a stage instance by ID. /// /// This requires the [`GUILDS`] intent. @@ -702,10 +738,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, @@ -842,6 +878,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(), @@ -851,6 +888,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 +908,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 +967,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. @@ -1034,6 +1077,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(_) @@ -1042,6 +1092,7 @@ impl UpdateCache for Event { | Event::ThreadMembersUpdate(_) | Event::ThreadMemberUpdate(_) | Event::TypingStart(_) + | Event::VoiceChannelEffectSend(_) | Event::VoiceServerUpdate(_) | Event::WebhooksUpdate(_) => {} } diff --git a/twilight-cache-inmemory/src/traits.rs b/twilight-cache-inmemory/src/traits.rs index 5a7d9a5c29..d4c5dd942d 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,20 @@ pub trait CacheablePresence: { } +/// Trait for a generic cached representation of a [`SoundboardSound`]. +pub trait CacheableSoundboardSound: + From + PartialEq + Clone + Debug +{ + /// Guild ID of the soundboard sound, if any. + fn guild_id(&self) -> Option>; +} + +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 + PartialEq + PartialEq + Clone + Debug diff --git a/twilight-gateway/src/command.rs b/twilight-gateway/src/command.rs index 61e4da0205..7ea7c437a4 100644 --- a/twilight-gateway/src/command.rs +++ b/twilight-gateway/src/command.rs @@ -3,7 +3,7 @@ //! [`Shard::command`]: crate::Shard::command use twilight_model::gateway::payload::outgoing::{ - RequestGuildMembers, UpdatePresence, UpdateVoiceState, + RequestGuildMembers, RequestSoundboardSounds, UpdatePresence, UpdateVoiceState, }; mod private { @@ -14,13 +14,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 +45,7 @@ mod private { pub trait Command: private::Sealed {} impl Command for RequestGuildMembers {} +impl Command for RequestSoundboardSounds {} impl Command for UpdatePresence {} impl Command for UpdateVoiceState {} @@ -52,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); } diff --git a/twilight-gateway/src/event.rs b/twilight-gateway/src/event.rs index 86e1ce1eb7..44e78d6d85 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 soundboard sound have been created + const GUILD_SOUNDBOARD_SOUND_CREATE = 1 << 79; + /// A guild soundboard sound have been deleted + const GUILD_SOUNDBOARD_SOUND_DELETE = 1 << 80; + /// 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; /// A guild's stickers have been updated. const GUILD_STICKERS_UPDATE = 1 << 63; /// A guild has been updated. @@ -164,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. @@ -189,6 +199,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 +266,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 +349,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 +392,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, @@ -393,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, @@ -405,6 +437,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-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, diff --git a/twilight-http/src/client/mod.rs b/twilight-http/src/client/mod.rs index 474af02f7e..74ec5f5e14 100644 --- a/twilight-http/src/client/mod.rs +++ b/twilight-http/src/client/mod.rs @@ -47,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::{ @@ -64,6 +65,7 @@ use crate::{ role::{ CreateRole, DeleteRole, GetGuildRoles, GetRole, UpdateRole, UpdateRolePositions, }, + soundboard::{GetGuildSoundboardSound, GetGuildSoundboardSounds}, sticker::{ CreateGuildSticker, DeleteGuildSticker, GetGuildSticker, GetGuildStickers, UpdateGuildSticker, @@ -92,8 +94,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, @@ -115,6 +117,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 +2885,104 @@ 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, + sound_id: Id, + ) -> SendSoundboardSound<'_> { + 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) + } + + /// 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 + /// + /// ```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/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/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/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_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/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..cc6f039ac6 --- /dev/null +++ b/twilight-http/src/request/guild/soundboard/mod.rs @@ -0,0 +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/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 ef5df69d41..196fa676fb 100644 --- a/twilight-http/src/request/try_into_request.rs +++ b/twilight-http/src/request/try_into_request.rs @@ -52,7 +52,7 @@ mod private { }, CreatePin, CreateTypingTrigger, DeleteChannel, DeleteChannelPermission, DeleteChannelPermissionConfigured, DeletePin, FollowNewsChannel, GetChannel, GetPins, - UpdateChannel, UpdateChannelPermission, + SendSoundboardSound, UpdateChannel, UpdateChannelPermission, }, guild::{ auto_moderation::{ @@ -69,6 +69,7 @@ mod private { role::{ CreateRole, DeleteRole, GetGuildRoles, GetRole, UpdateRole, UpdateRolePositions, }, + soundboard::{GetGuildSoundboardSound, GetGuildSoundboardSounds}, sticker::{ CreateGuildSticker, DeleteGuildSticker, GetGuildSticker, GetGuildStickers, UpdateGuildSticker, @@ -102,8 +103,8 @@ mod private { GetCurrentUserGuildMember, GetCurrentUserGuilds, GetUser, LeaveGuild, UpdateCurrentUser, }, - GetCurrentAuthorizationInformation, GetGateway, GetGatewayAuthed, GetUserApplicationInfo, - GetVoiceRegions, + GetCurrentAuthorizationInformation, GetDefaultSoundboardSounds, GetGateway, + GetGatewayAuthed, GetUserApplicationInfo, GetVoiceRegions, }; pub trait Sealed {} @@ -201,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<'_> {} @@ -225,6 +227,8 @@ 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<'_> {} impl Sealed for GetGuildVanityUrl<'_> {} @@ -264,6 +268,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..f85e6d1edc 100644 --- a/twilight-http/src/routing.rs +++ b/twilight-http/src/routing.rs @@ -683,6 +683,15 @@ 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, + }, /// Route information to get a guild's sticker. GetGuildSticker { /// ID of the guild. @@ -986,6 +995,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. @@ -998,6 +1012,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. @@ -1299,6 +1315,8 @@ impl Route<'_> { | Self::GetGuildPreview { .. } | Self::GetGuildPruneCount { .. } | Self::GetGuildRoles { .. } + | Self::GetGuildSoundboardSound { .. } + | Self::GetGuildSoundboardSounds { .. } | Self::GetGuildScheduledEvent { .. } | Self::GetGuildScheduledEventUsers { .. } | Self::GetGuildScheduledEvents { .. } @@ -1324,6 +1342,7 @@ impl Route<'_> { | Self::GetPublicArchivedThreads { .. } | Self::GetReactionUsers { .. } | Self::GetRole { .. } + | Self::GetSoundboardDefaultSounds | Self::GetSKUs { .. } | Self::GetStageInstance { .. } | Self::GetSticker { .. } @@ -1398,6 +1417,7 @@ impl Route<'_> { | Self::ExecuteWebhook { .. } | Self::FollowNewsChannel { .. } | Self::InteractionCallback { .. } + | Self::SendSoundboardSound { .. } | Self::SyncGuildIntegration { .. } => Method::Post, Self::AddGuildMember { .. } | Self::AddMemberRole { .. } @@ -1744,6 +1764,14 @@ impl Route<'_> { Self::EndPoll { channel_id, .. } | Self::GetAnswerVoters { channel_id, .. } => { Path::ChannelsIdPolls(channel_id) } + Self::SendSoundboardSound { channel_id } => { + Path::ChannelsIdSendSoundboardSound(channel_id) + } + Self::GetSoundboardDefaultSounds => Path::SoundboardDefaultSounds, + Self::GetGuildSoundboardSound { guild_id, .. } => { + Path::GuildsIdSoundboardSoundsId(guild_id) + } + Self::GetGuildSoundboardSounds { guild_id } => Path::GuildsIdSoundboardSounds(guild_id), } } } @@ -3039,6 +3067,26 @@ 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") + } + 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)?; + + f.write_str("/soundboard-sounds") + } } } } diff --git a/twilight-model/src/gateway/event/dispatch.rs b/twilight-model/src/gateway/event/dispatch.rs index 6ced713cca..9d67790228 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), @@ -66,6 +70,7 @@ pub enum DispatchEvent { RoleCreate(RoleCreate), RoleDelete(RoleDelete), RoleUpdate(RoleUpdate), + SoundboardSounds(SoundboardSounds), StageInstanceCreate(StageInstanceCreate), StageInstanceDelete(StageInstanceDelete), StageInstanceUpdate(StageInstanceUpdate), @@ -78,6 +83,7 @@ pub enum DispatchEvent { TypingStart(Box), UnavailableGuild(UnavailableGuild), UserUpdate(UserUpdate), + VoiceChannelEffectSend(Box), VoiceServerUpdate(VoiceServerUpdate), VoiceStateUpdate(Box), WebhooksUpdate(WebhooksUpdate), @@ -111,6 +117,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, @@ -139,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, @@ -151,6 +162,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, @@ -211,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), @@ -332,6 +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_STICKERS_UPDATE" => { DispatchEvent::GuildStickersUpdate(GuildStickersUpdate::deserialize(deserializer)?) } @@ -395,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)?) } @@ -426,6 +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_SERVER_UPDATE" => { DispatchEvent::VoiceServerUpdate(VoiceServerUpdate::deserialize(deserializer)?) } 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)) diff --git a/twilight-model/src/gateway/event/kind.rs b/twilight-model/src/gateway/event/kind.rs index 5dfeeb77f1..b62cb880a4 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, @@ -76,6 +80,7 @@ pub enum EventType { RoleDelete, #[serde(rename = "GUILD_ROLE_UPDATE")] RoleUpdate, + SoundboardSounds, StageInstanceCreate, StageInstanceDelete, StageInstanceUpdate, @@ -88,6 +93,7 @@ pub enum EventType { TypingStart, UnavailableGuild, UserUpdate, + VoiceChannelEffectSend, VoiceServerUpdate, VoiceStateUpdate, WebhooksUpdate, @@ -121,6 +127,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"), @@ -148,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"), @@ -160,6 +171,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 +212,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), @@ -227,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), @@ -239,6 +256,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 +349,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"); @@ -361,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 efdbe0e86c..22eae626d3 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. @@ -148,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. @@ -173,6 +183,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 +218,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, @@ -232,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), @@ -243,6 +260,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 +311,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, @@ -321,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, @@ -333,6 +356,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 +395,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), @@ -399,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), @@ -411,6 +440,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 +527,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 +546,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 +563,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); @@ -540,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/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: 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/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..2da7715735 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; @@ -38,6 +39,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; @@ -64,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; @@ -97,6 +103,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, @@ -108,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_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/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..ada2074cbc --- /dev/null +++ b/twilight-model/src/gateway/payload/incoming/voice_channel_effect_send.rs @@ -0,0 +1,32 @@ +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/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>, +} 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..27e2c6e6db --- /dev/null +++ b/twilight-model/src/guild/soundboard.rs @@ -0,0 +1,85 @@ +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, + #[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, +} + +#[cfg(test)] +mod tests { + use crate::id::Id; + + use super::SoundboardSound; + use serde::{Deserialize, Serialize}; + use serde_test::Token; + use static_assertions::{assert_fields, assert_impl_all}; + use std::fmt::Debug; + + 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() { + 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::NewtypeStruct { name: "Id" }, + Token::Str("123"), + Token::Str("volume"), + Token::F64(50.0), + Token::StructEnd, + ], + ); + } +} diff --git a/twilight-model/src/id/marker.rs b/twilight-model/src/id/marker.rs index d99741d350..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`] @@ -220,6 +225,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.