diff --git a/command_attr/src/structures.rs b/command_attr/src/structures.rs index 82265213c4e..5b5375d9f37 100644 --- a/command_attr/src/structures.rs +++ b/command_attr/src/structures.rs @@ -345,6 +345,7 @@ impl Permissions { "SEND_MESSAGES_IN_THREADS" => 1 << 38, "USE_EMBEDDED_ACTIVITIES" => 1 << 39, "MODERATE_MEMBERS" => 1 << 40, + "SET_VOICE_CHANNEL_STATUS" => 1 << 48, _ => return None, })) } diff --git a/src/builder/edit_channel.rs b/src/builder/edit_channel.rs index 1b895506687..defd40aa1f1 100644 --- a/src/builder/edit_channel.rs +++ b/src/builder/edit_channel.rs @@ -72,6 +72,8 @@ pub struct EditChannel<'a> { default_sort_order: Option, #[serde(skip_serializing_if = "Option::is_none")] default_forum_layout: Option, + #[serde(skip_serializing_if = "Option::is_none")] + status: Option, #[serde(skip)] audit_log_reason: Option<&'a str>, diff --git a/src/cache/event.rs b/src/cache/event.rs index a8b00cecaa8..87723707329 100644 --- a/src/cache/event.rs +++ b/src/cache/event.rs @@ -28,6 +28,7 @@ use crate::model::event::{ ThreadDeleteEvent, ThreadUpdateEvent, UserUpdateEvent, + VoiceChannelStatusUpdateEvent, VoiceStateUpdateEvent, }; use crate::model::gateway::ShardInfo; @@ -624,3 +625,19 @@ impl CacheUpdate for VoiceStateUpdateEvent { } } } + +impl CacheUpdate for VoiceChannelStatusUpdateEvent { + type Output = String; + + fn update(&mut self, cache: &Cache) -> Option { + if let Some(mut channel) = cache.channels.get_mut(&self.id) { + let old = channel.status.clone(); + channel.status = self.status.clone(); + // Discord updates topic but doesn't fire ChannelUpdate. + channel.topic = self.status.clone(); + old + } else { + None + } + } +} diff --git a/src/client/dispatch.rs b/src/client/dispatch.rs index a503043fbe2..cb231f38e05 100644 --- a/src/client/dispatch.rs +++ b/src/client/dispatch.rs @@ -424,6 +424,18 @@ fn update_cache_with_event(ctx: Context, event: Event) -> Option<(FullEvent, Opt new: event.voice_state, } }, + Event::VoiceChannelStatusUpdate(mut event) => { + let old = if_cache!(update_cache(&ctx, &mut event)); + + FullEvent::VoiceChannelStatusUpdate { + ctx, + old, + status: event.status, + id: event.id, + guild_id: event.guild_id, + } + }, + Event::WebhookUpdate(event) => FullEvent::WebhookUpdate { ctx, guild_id: event.guild_id, diff --git a/src/client/event_handler.rs b/src/client/event_handler.rs index 49357acbea2..1faf82cd07c 100644 --- a/src/client/event_handler.rs +++ b/src/client/event_handler.rs @@ -337,6 +337,11 @@ event_handler! { /// [`GatewayIntents::GUILDS`] is enabled) and the new state of the guild's voice channels. async fn voice_state_update(&self, VoiceStateUpdate { ctx: Context, old: Option, new: VoiceState }); + /// Dispatched when a voice channel's status is updated. + /// + /// Provides the status, channel's id and the guild's id. + async fn voice_channel_status_update(&self, VoiceChannelStatusUpdate { ctx: Context, old: Option, status: Option, id: ChannelId, guild_id: GuildId }); + /// Dispatched when a guild's webhook is updated. /// /// Provides the guild's id and the channel's id the webhook belongs in. diff --git a/src/model/channel/guild_channel.rs b/src/model/channel/guild_channel.rs index ec8c2b6b5b8..17537d1f379 100644 --- a/src/model/channel/guild_channel.rs +++ b/src/model/channel/guild_channel.rs @@ -159,6 +159,10 @@ pub struct GuildChannel { /// /// **Note**: This is only available in a forum or text channel. pub default_thread_rate_limit_per_user: Option, + /// The status of a voice channel. + /// + /// **Note**: This is only available in voice channels. + pub status: Option, /// The default sort order type used to order posts /// /// **Note**: This is only available in a forum. diff --git a/src/model/event.rs b/src/model/event.rs index 929564eddf2..9c2a7cadfd2 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -733,6 +733,17 @@ pub struct VoiceStateUpdateEvent { pub voice_state: VoiceState, } +/// Requires [`GatewayIntents::GUILDS`]. +/// +/// [Incomplete documentation](https://github.com/discord/discord-api-docs/pull/6398) +#[derive(Clone, Debug, Deserialize, Serialize)] +#[non_exhaustive] +pub struct VoiceChannelStatusUpdateEvent { + pub status: Option, + pub id: ChannelId, + pub guild_id: GuildId, +} + /// Requires [`GatewayIntents::GUILD_WEBHOOKS`]. /// /// [Discord docs](https://discord.com/developers/docs/topics/gateway-events#webhooks-update). @@ -1149,6 +1160,8 @@ pub enum Event { VoiceStateUpdate(VoiceStateUpdateEvent), /// Voice server information is available VoiceServerUpdate(VoiceServerUpdateEvent), + /// Fired when the status of a Voice Channel changes. + VoiceChannelStatusUpdate(VoiceChannelStatusUpdateEvent), /// A webhook for a [channel][`GuildChannel`] was updated in a [`Guild`]. WebhookUpdate(WebhookUpdateEvent), /// An interaction was created. diff --git a/src/model/guild/audit_log/mod.rs b/src/model/guild/audit_log/mod.rs index 0acd81a6d01..f2f095d3a84 100644 --- a/src/model/guild/audit_log/mod.rs +++ b/src/model/guild/audit_log/mod.rs @@ -35,6 +35,7 @@ pub enum Action { ScheduledEvent(ScheduledEventAction), Thread(ThreadAction), AutoMod(AutoModAction), + VoiceChannelStatus(VoiceChannelStatusAction), Unknown(u8), } @@ -57,6 +58,7 @@ impl Action { Self::ScheduledEvent(x) => x as u8, Self::Thread(x) => x as u8, Self::AutoMod(x) => x as u8, + Self::VoiceChannelStatus(x) => x as u8, Self::Unknown(x) => x, } } @@ -79,6 +81,7 @@ impl Action { 100..=102 => Action::ScheduledEvent(unsafe { transmute(value) }), 110..=112 => Action::Thread(unsafe { transmute(value) }), 140..=145 => Action::AutoMod(unsafe { transmute(value) }), + 192..=193 => Action::VoiceChannelStatus(unsafe { transmute(value) }), _ => Action::Unknown(value), } } @@ -248,6 +251,15 @@ pub enum AutoModAction { UserCommunicationDisabled = 145, } +/// [Incomplete documentation](https://github.com/discord/discord-api-docs/pull/6398) +#[derive(Copy, Clone, Debug)] +#[non_exhaustive] +#[repr(u8)] +pub enum VoiceChannelStatusAction { + StatusUpdate = 192, + StatusDelete = 193, +} + /// [Discord docs](https://discord.com/developers/docs/resources/audit-log#audit-log-object). #[derive(Debug, Deserialize, Serialize)] #[non_exhaustive] @@ -346,6 +358,9 @@ pub struct Options { /// Name of the role if type is "role" #[serde(default)] pub role_name: Option, + /// The status of a voice channel when set. + #[serde(default)] + pub status: Option, } #[cfg(test)] diff --git a/src/model/permissions.rs b/src/model/permissions.rs index 302116130a3..64544682ec3 100644 --- a/src/model/permissions.rs +++ b/src/model/permissions.rs @@ -347,6 +347,9 @@ bitflags::bitflags! { const USE_EXTERNAL_SOUNDS = 1 << 45; /// Allows sending voice messages. const SEND_VOICE_MESSAGES = 1 << 46; + // Allows setting the status of a voice channel. + const SET_VOICE_CHANNEL_STATUS = 1 << 48; + } }