Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Soundboard #3045

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions src/builder/create_guild_soundboard_sound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use std::borrow::Cow;

use super::CreateAttachment;
#[cfg(feature = "http")]
use crate::all::Http;
use crate::model::prelude::*;

/// A builder to create a guild soundboard sound
///
/// [Discord docs](https://discord.com/developers/docs/resources/soundboard#get-guild-soundboard-sound)
#[derive(serde::Serialize, Clone, Debug)]
#[must_use]
pub struct CreateGuildSoundboardSound<'a> {
name: Cow<'static, str>,
sound: Cow<'static, str>,
#[serde(skip_serializing_if = "Option::is_none")]
volume: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
emoji_id: Option<EmojiId>,
#[serde(skip_serializing_if = "Option::is_none")]
emoji_name: Option<Cow<'static, str>>,
#[serde(skip)]
audit_log_reason: Option<&'a str>,
}

impl<'a> CreateGuildSoundboardSound<'a> {
/// Creates a new builder with the given data.
pub fn new(name: impl Into<Cow<'static, str>>, sound: &CreateAttachment<'a>) -> Self {
Self {
name: name.into(),
sound: sound.to_base64().into(),
volume: None,
emoji_id: None,
emoji_name: None,
audit_log_reason: None,
}
}

/// Set the name of the guild soundboard sound, replacing the current value as set in
/// [`Self::new`].
///
/// **Note**: Must be between 2 and 32 characters long.
pub fn name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.name = name.into();
self
}

/// Set the soundboard file. Replaces the current value as set in [`Self::new`].
///
/// **Note**: Must be a MPG or OGG, max 512 KB and max duration of 5.2 secons.
pub fn sound(mut self, sound: &CreateAttachment<'a>) -> Self {
self.sound = sound.to_base64().into();
self
}

/// Set the volume of the soundboard sound.
///
/// **Note**: Must be between 0.0 and 1.0.
pub fn volume(mut self, volume: f64) -> Self {
self.volume = Some(volume);
self
}

/// Set the emoji id of the soundboard sound.
pub fn emoji_id(mut self, emoji_id: EmojiId) -> Self {
self.emoji_id = Some(emoji_id);
self
}

/// Set the unicode character (emoji name) of a standard emoji for the soundboard sound
pub fn emoji_name(mut self, emoji_name: impl Into<Cow<'static, str>>) -> Self {
self.emoji_name = Some(emoji_name.into());
self
}

/// Sets the request's audit log reason.
pub fn audit_log_reason(mut self, reason: &'a str) -> Self {
self.audit_log_reason = Some(reason);
self
}

/// Creates a new guild soundboard sound in the guild with the data set, if any.
///
/// **Note**: Requires the [Create Guild Expressions] permission.
///
/// # Errors
///
/// Returns [`Error::Http`] if the current user lacks permission or if invalid data is given.
///
/// [Create Guild Expressions]: Permissions::CREATE_GUILD_EXPRESSIONS
#[cfg(feature = "http")]
pub async fn execute(self, http: &Http, guild_id: GuildId) -> Result<SoundboardSound> {
http.create_guild_soundboard_sound(guild_id, &self, self.audit_log_reason).await
}
}
84 changes: 84 additions & 0 deletions src/builder/edit_guild_soundboard_sound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::borrow::Cow;

#[cfg(feature = "http")]
use crate::all::Http;
use crate::model::prelude::*;

/// A builder to modify a guild soundboard sound
///
/// [Discord docs](https://discord.com/developers/docs/resources/soundboard#get-guild-soundboard-sound)
#[derive(Default, serde::Serialize, Clone, Debug)]
#[must_use]
pub struct EditGuildSoundboardSound<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<Cow<'static, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
volume: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
emoji_id: Option<EmojiId>,
#[serde(skip_serializing_if = "Option::is_none")]
emoji_name: Option<Cow<'static, str>>,
#[serde(skip)]
audit_log_reason: Option<&'a str>,
}

impl<'a> EditGuildSoundboardSound<'a> {
/// Equivalent to [`Self::default`].
pub fn new() -> Self {
Self::default()
}

/// Set the name of the guild soundboard sound, replacing the current value as set in
/// [`Self::new`].
///
/// **Note**: Must be between 2 and 32 characters long.
pub fn name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.name = Some(name.into());
self
}

/// Set the volume of the soundboard sound.
///
/// **Note**: Must be between 0.0 and 1.0.
pub fn volume(mut self, volume: f64) -> Self {
self.volume = Some(volume);
self
}

/// Set the emoji id of the soundboard sound.
pub fn emoji_id(mut self, emoji_id: EmojiId) -> Self {
self.emoji_id = Some(emoji_id);
self
}

/// Set the unicode character (emoji name) of a standard emoji for the soundboard sound
pub fn emoji_name(mut self, emoji_name: impl Into<Cow<'static, str>>) -> Self {
self.emoji_name = Some(emoji_name.into());
self
}

/// Sets the request's audit log reason.
pub fn audit_log_reason(mut self, reason: &'a str) -> Self {
self.audit_log_reason = Some(reason);
self
}

/// Modifies a new guild soundboard sound in the guild with the data set, if any.
///
/// **Note**: Requires the [Create Guild Expressions] permission.
///
/// # Errors
///
/// Returns [`Error::Http`] if the current user lacks permission or if invalid data is given.
///
/// [Create Guild Expressions]: Permissions::CREATE_GUILD_EXPRESSIONS
#[cfg(feature = "http")]
pub async fn execute(
self,
http: &Http,
guild_id: GuildId,
sound_id: SoundboardSoundId,
) -> Result<SoundboardSound> {
http.edit_guild_soundboard_sound(guild_id, sound_id, &self, self.audit_log_reason).await
}
}
6 changes: 6 additions & 0 deletions src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ mod create_components;
mod create_embed;
mod create_forum_post;
mod create_forum_tag;
mod create_guild_soundboard_sound;
mod create_interaction_response;
mod create_interaction_response_followup;
mod create_invite;
Expand All @@ -60,6 +61,7 @@ mod edit_automod_rule;
mod edit_channel;
mod edit_command;
mod edit_guild;
mod edit_guild_soundboard_sound;
mod edit_guild_welcome_screen;
mod edit_guild_widget;
mod edit_interaction_response;
Expand All @@ -77,6 +79,7 @@ mod edit_webhook_message;
mod execute_webhook;
mod get_entitlements;
mod get_messages;
mod send_soundboard_sound;

pub use add_member::*;
pub use bot_auth_parameters::*;
Expand All @@ -89,6 +92,7 @@ pub use create_components::*;
pub use create_embed::*;
pub use create_forum_post::*;
pub use create_forum_tag::*;
pub use create_guild_soundboard_sound::*;
pub use create_interaction_response::*;
pub use create_interaction_response_followup::*;
pub use create_invite::*;
Expand All @@ -103,6 +107,7 @@ pub use edit_automod_rule::*;
pub use edit_channel::*;
pub use edit_command::*;
pub use edit_guild::*;
pub use edit_guild_soundboard_sound::*;
pub use edit_guild_welcome_screen::*;
pub use edit_guild_widget::*;
pub use edit_interaction_response::*;
Expand All @@ -120,6 +125,7 @@ pub use edit_webhook_message::*;
pub use execute_webhook::*;
pub use get_entitlements::*;
pub use get_messages::*;
pub use send_soundboard_sound::*;

macro_rules! button_and_select_menu_convenience_methods {
($self:ident $(. $components_path:tt)+) => {
Expand Down
51 changes: 51 additions & 0 deletions src/builder/send_soundboard_sound.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#[cfg(feature = "http")]
use crate::http::Http;
use crate::model::prelude::*;

/// A builder which to send a soundboard sound, to be used in conjunction with
/// [`GuildChannel::send_soundboard_sound`].
///
/// Discord docs:
/// - [Send Soundboard Sound](https://discord.com/developers/docs/resources/soundboard#send-soundboard-sound)
#[derive(Clone, Debug, Default, Serialize)]
#[must_use]
pub struct SendSoundboardSound {
sound_id: SoundboardSoundId,
#[serde(skip_serializing_if = "Option::is_none")]
source_guild_id: Option<GuildId>,
}

impl SendSoundboardSound {
/// Create a new builder with the given soundboard sound id
pub fn new(sound_id: SoundboardSoundId) -> Self {
Self::default().sound_id(sound_id)
}

pub fn sound_id(mut self, sound_id: SoundboardSoundId) -> Self {
self.sound_id = sound_id;
self
}

pub fn source_guild_id(mut self, source_guild_id: GuildId) -> Self {
self.source_guild_id = Some(source_guild_id);
self
}

/// Edits the given user's voice state in a stage channel. Providing a [`UserId`] will edit
/// that user's voice state, otherwise the current user's voice state will be edited.
///
/// **Note**: Requires the [Request to Speak] permission. Also requires the [Mute Members]
/// permission to suppress another user or unsuppress the current user. This is not required if
/// suppressing the current user.
///
/// # Errors
///
/// Returns [`Error::Http`] if the user lacks permission, or if invalid data is given.
///
/// [Request to Speak]: Permissions::REQUEST_TO_SPEAK
/// [Mute Members]: Permissions::MUTE_MEMBERS
#[cfg(feature = "http")]
pub async fn execute(self, http: &Http, channel_id: ChannelId) -> Result<()> {
http.send_soundboard_sound(channel_id, &self).await
}
}
2 changes: 2 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ enum_number! {
Hello = 10,
/// Sent immediately following a client heartbeat that was received.
HeartbeatAck = 11,
/// Request information about soundboard sounds in a set of guilds.
RequestSoundboardSounds = 31,
_ => Unknown(u8),
}
}
Expand Down
23 changes: 23 additions & 0 deletions src/gateway/client/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,29 @@ fn update_cache_with_event(
Event::MessagePollVoteRemove(event) => FullEvent::MessagePollVoteRemove {
event,
},
Event::GuildSoundboardSoundCreate(event) => FullEvent::GuildSoundboardSoundCreate {
sound: event.soundboard_sound,
guild_id: event.guild_id,
},
Event::GuildSoundboardSoundUpdate(event) => FullEvent::GuildSoundboardSoundUpdate {
sound: event.soundboard_sound,
guild_id: event.guild_id,
},
Event::GuildSoundboardSoundDelete(event) => FullEvent::GuildSoundboardSoundDelete {
sound_id: event.sound_id,
guild_id: event.guild_id,
},
Event::GuildSoundboardSoundsUpdate(event) => FullEvent::GuildSoundboardSoundsUpdate {
sounds: event.soundboard_sounds,
guild_id: event.guild_id,
},
Event::VoiceChannelEffectSend(event) => FullEvent::VoiceChannelEffectSend {
event,
},
Event::SoundboardSounds(event) => FullEvent::SoundboardSounds {
sounds: event.soundboard_sounds,
guild_id: event.guild_id,
},
};

(event, extra_event)
Expand Down
20 changes: 20 additions & 0 deletions src/gateway/client/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,26 @@ event_handler! {

/// Dispatched when an HTTP rate limit is hit
Ratelimit { data: RatelimitInfo } => async fn ratelimit(&self);

/// Sent when a guild soundboard sound is created.
GuildSoundboardSoundCreate { sound: SoundboardSound, guild_id: GuildId } => async fn guild_soundboard_sound_create(&self, ctx: Context);

/// Sent when a guild soundboard sound is updated.
GuildSoundboardSoundUpdate { sound: SoundboardSound, guild_id: GuildId } => async fn guild_soundboard_sound_update(&self, ctx: Context);

/// Sent when a guild soundboard sound is deleted.
GuildSoundboardSoundDelete { sound_id: SoundboardSoundId, guild_id: GuildId } => async fn guild_soundboard_sound_delete(&self, ctx: Context);

/// Sent when multiple guild soundboard sounds are updated.
GuildSoundboardSoundsUpdate { sounds: Vec<SoundboardSound>, guild_id: GuildId } => async fn guild_soundboard_sounds_update(&self, ctx: Context);

/// Sent when someone sends an effect, such as an emoji reaction or a soundboard sound, in a voice channel the current user is connected to.
VoiceChannelEffectSend { event: VoiceChannelEffectSendEvent } => async fn voice_channel_effect_send(&self, ctx: Context);

/// Includes a guild's list of soundboard sounds.
///
/// Sent in response to Request Soundboard Sounds.
SoundboardSounds { guild_id: GuildId, sounds: Vec<SoundboardSound> } => async fn soundboard_sounds(&self, ctx: Context);
}

/// This core trait for handling raw events
Expand Down
14 changes: 14 additions & 0 deletions src/gateway/sharding/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,20 @@ impl Shard {
.await
}

/// Requests that soundboard sounds for a list of GuildId's. The server will send Soundboard
/// Sounds events for each guild in response.
///
///
/// # Errors
/// Errors if there is a problem with the WS connection.
///
/// [`Event::GuildMembersChunk`]: crate::model::event::Event::GuildMembersChunk
/// [`GuildId`]: crate::model::guild::GuildId
#[cfg_attr(feature = "tracing_instrument", instrument(skip(self)))]
pub async fn request_soundboard_sounds(&mut self, guild_ids: Vec<GuildId>) -> Result<()> {
self.client.send_request_soundboard_sounds(&self.shard_info, guild_ids).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`]
Expand Down
Loading
Loading