diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs index b42eebe1e0c..ec7a4af28d6 100644 --- a/src/client/dispatch.rs +++ b/src/client/dispatch.rs @@ -365,6 +365,21 @@ fn update_cache_with_event( Event::Resumed(event) => FullEvent::Resume { event, }, + Event::SoundboardSounds(event) => FullEvent::SoundboardSounds { + event, + }, + Event::SoundboardSoundCreate(event) => FullEvent::SoundboardSoundCreate { + event, + }, + Event::SoundboardSoundUpdate(event) => FullEvent::SoundboardSoundUpdate { + event, + }, + Event::SoundboardSoundsUpdate(event) => FullEvent::SoundboardSoundsUpdate { + event, + }, + Event::SoundboardSoundDelete(event) => FullEvent::SoundboardSoundDelete { + event, + }, Event::TypingStart(event) => FullEvent::TypingStart { event, }, diff --git a/src/client/event_handler.rs b/src/client/event_handler.rs index 22ae3a1dee0..ffc9ab744fa 100644 --- a/src/client/event_handler.rs +++ b/src/client/event_handler.rs @@ -327,6 +327,23 @@ event_handler! { /// Provides the context of the shard and the event information about the update. ShardStageUpdate { event: ShardStageUpdateEvent } => async fn shard_stage_update(&self, ctx: Context); + /// Dispatched when the data for soundboard sounds is requested. + /// + /// Provides the guild's id and the data. + SoundboardSounds { event: SoundboardSoundsEvent } => async fn soundboard_sounds(&self, ctx: Context); + + /// Dispatched when a soundboard sound is created. + SoundboardSoundCreate { event: SoundboardSoundCreateEvent } => async fn soundboard_sound_create(&self, ctx: Context); + + /// Dispatched when a soundboard sound is updated. + SoundboardSoundUpdate { event: SoundboardSoundUpdateEvent } => async fn soundboard_sound_update(&self, ctx: Context); + + /// Dispatched when multiple soundboard sounds at once are updated. + SoundboardSoundsUpdate { event: SoundboardSoundsUpdateEvent } => async fn soundboard_sounds_update(&self, ctx: Context); + + /// Dispatched when a soundboard sound is deleted. + SoundboardSoundDelete { event: SoundboardSoundDeleteEvent } => async fn soundboard_sound_delete(&self, ctx: Context); + /// Dispatched when a user starts typing. TypingStart { event: TypingStartEvent } => async fn typing_start(&self, ctx: Context); diff --git a/src/constants.rs b/src/constants.rs index 953051fd07f..7be4179327a 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -62,6 +62,8 @@ enum_number! { Hello = 10, /// Sent immediately following a client heartbeat that was received. HeartbeatAck = 11, + /// Used to request soundboard sounds from a list of guilds. + ReqeustSoundboardSounds = 31, _ => Unknown(u8), } } diff --git a/src/gateway/bridge/shard_messenger.rs b/src/gateway/bridge/shard_messenger.rs index 685cc7eb0ed..2ffef7bd0e2 100644 --- a/src/gateway/bridge/shard_messenger.rs +++ b/src/gateway/bridge/shard_messenger.rs @@ -129,6 +129,21 @@ impl ShardMessenger { }); } + /// Requests [Soundboard sounds][soundboard] to be fetched from one or multiple [`Guild`]s. + /// + /// This will ask the gateway to start sending soundboard sounds. + /// + /// Soundboard sounds are sent as the [`Event::SoundboardSounds`] event. + /// + /// [`Event::SoundboardSounds`]: crate::model::event::Event::SoundboardSounds + /// [`Guild`]: crate::model::guild::Guild + /// [soundboard]: crate::model::soundboard::Soundboard + pub fn request_soundboard_sounds(&self, guild_ids: Vec) { + self.send_to_shard(ShardRunnerMessage::SoundboardSounds { + guild_ids, + }); + } + /// Sets the user's current activity, if any. /// /// Other presence settings are maintained. diff --git a/src/gateway/bridge/shard_runner.rs b/src/gateway/bridge/shard_runner.rs index d1204788bca..a33bf6ca24a 100644 --- a/src/gateway/bridge/shard_runner.rs +++ b/src/gateway/bridge/shard_runner.rs @@ -299,6 +299,9 @@ impl ShardRunner { .chunk_guild(guild_id, limit, presences, filter, nonce.as_deref()) .await .is_ok(), + ShardRunnerMessage::SoundboardSounds { + guild_ids, + } => self.shard.request_soundboard_sounds(&guild_ids).await.is_ok(), ShardRunnerMessage::Close(code, reason) => { let reason = reason.unwrap_or_default(); let close = CloseFrame { diff --git a/src/gateway/bridge/shard_runner_message.rs b/src/gateway/bridge/shard_runner_message.rs index dc9ce9b97c7..edd8e3d8ecb 100644 --- a/src/gateway/bridge/shard_runner_message.rs +++ b/src/gateway/bridge/shard_runner_message.rs @@ -34,6 +34,13 @@ pub enum ShardRunnerMessage { /// [`GuildMembersChunkEvent`]: crate::model::event::GuildMembersChunkEvent nonce: Option, }, + /// Indicates that the client is to send a request soundboard sounds message. + SoundboardSounds { + /// The IDs of the [`Guild`] to request soundboard sounds from. + /// + /// [`Guild`]: crate::model::guild::Guild + guild_ids: Vec, + }, /// Indicates that the client is to close with the given status code and reason. /// /// You should rarely - if _ever_ - need this, but the option is available. Prefer to use the diff --git a/src/gateway/shard.rs b/src/gateway/shard.rs index 3089f03dd61..259640d2eba 100644 --- a/src/gateway/shard.rs +++ b/src/gateway/shard.rs @@ -650,6 +650,25 @@ impl Shard { self.client.send_chunk_guild(guild_id, &self.info, limit, presences, filter, nonce).await } + /// Requests [Soundboard sounds][soundboard] to be fetched from one or multiple [`Guild`]s. + /// + /// This will ask the gateway to start sending soundboard sounds. + /// + /// Soundboard sounds are sent as the [`Event::SoundboardSounds`] event. + /// + /// # Errors + /// Errors if there is a problem with the WS connection. + /// + /// [`Event::SoundboardSounds`]: crate::model::event::Event::SoundboardSounds + /// [`Guild`]: crate::model::guild::Guild + /// [soundboard]: crate::model::soundboard::Soundboard + #[instrument(skip(self))] + pub async fn request_soundboard_sounds(&mut self, guild_ids: &[GuildId]) -> Result<()> { + debug!("[{:?}] Requesting soundboard sounds", self.info); + + self.client.request_soundboard_sounds(guild_ids, &self.info).await + } + /// Sets the shard as going into identifying stage, which sets: /// - the time that the last heartbeat sent as being now /// - the `stage` to [`ConnectionStage::Identifying`] diff --git a/src/gateway/ws.rs b/src/gateway/ws.rs index df3bd17acbf..1ecb7bfa4be 100644 --- a/src/gateway/ws.rs +++ b/src/gateway/ws.rs @@ -70,6 +70,9 @@ struct PresenceUpdateMessage<'a> { enum WebSocketMessageData<'a> { Heartbeat(Option), ChunkGuild(ChunkGuildMessage<'a>), + SoundboardSounds { + guild_ids: &'a [GuildId], + }, Identify { compress: bool, token: &'a str, @@ -214,6 +217,25 @@ impl WsClient { .await } + /// # Errors + /// + /// Errors if there is a problem with the WS connection. + pub async fn request_soundboard_sounds( + &mut self, + guild_ids: &[GuildId], + shard_info: &ShardInfo, + ) -> Result<()> { + debug!("[{:?}] Requesting soundboard sounds", shard_info); + + self.send_json(&WebSocketMessage { + op: Opcode::ReqeustSoundboardSounds, + d: WebSocketMessageData::SoundboardSounds { + guild_ids, + }, + }) + .await + } + /// # Errors /// /// Errors if there is a problem with the WS connection. diff --git a/src/model/event.rs b/src/model/event.rs index 755f3b153e4..cf299dcdaa2 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -311,6 +311,63 @@ impl Serialize for GuildMembersChunkEvent { } } +/// Requires no gateway intents. +/// +/// [Discord docs](https://discord.com/developers/docs/events/gateway-events#soundboard-sounds). +#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[non_exhaustive] +pub struct SoundboardSoundsEvent { + /// ID of the guild. + pub guild_id: GuildId, + /// The guild's soundboard sounds. + pub soundboard_sounds: Vec, +} + +/// Requires no gateway intents. +/// +/// [Discord docs](https://discord.com/developers/docs/events/gateway-events#guild-soundboard-sound-create). +#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(transparent)] +#[non_exhaustive] +pub struct SoundboardSoundCreateEvent { + pub soundboard: Soundboard, +} + +/// Requires no gateway intents. +/// +/// [Discord docs](https://discord.com/developers/docs/events/gateway-events#guild-soundboard-sound-update). +#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(transparent)] +#[non_exhaustive] +pub struct SoundboardSoundUpdateEvent { + pub soundboard: Soundboard, +} + +/// Requires no gateway intents. +/// +/// [Discord docs](https://discord.com/developers/docs/events/gateway-events#guild-soundboard-sounds-update). +#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[non_exhaustive] +pub struct SoundboardSoundsUpdateEvent { + pub guild_id: GuildId, + pub soundboard_sounds: Vec, +} + +/// Requires no gateway intents. +/// +/// [Discord docs](https://discord.com/developers/docs/events/gateway-events#guild-soundboard-sound-delete). +#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] +#[derive(Clone, Debug, Deserialize, Serialize)] +#[non_exhaustive] +pub struct SoundboardSoundDeleteEvent { + pub guild_id: GuildId, + pub sound_id: SoundId, +} + /// Helper to deserialize `GuildRoleCreateEvent` and `GuildRoleUpdateEvent`. #[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))] #[derive(Deserialize)] @@ -1295,6 +1352,15 @@ pub enum Event { Ready(ReadyEvent), /// The connection has successfully resumed after a disconnect. Resumed(ResumedEvent), + SoundboardSounds(SoundboardSoundsEvent), + #[serde(rename = "GUILD_SOUNDBOARD_SOUND_CREATE")] + SoundboardSoundCreate(SoundboardSoundCreateEvent), + #[serde(rename = "GUILD_SOUNDBOARD_SOUND_UPDATE")] + SoundboardSoundUpdate(SoundboardSoundUpdateEvent), + #[serde(rename = "GUILD_SOUNDBOARD_SOUNDS_UPDATE")] + SoundboardSoundsUpdate(SoundboardSoundsUpdateEvent), + #[serde(rename = "GUILD_SOUNDBOARD_SOUND_DELETE")] + SoundboardSoundDelete(SoundboardSoundDeleteEvent), /// A user is typing; considered to last 5 seconds TypingStart(TypingStartEvent), /// Update to the logged-in user's information