From 5b50532d1501209a79efa77932afdd1898516b74 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Sat, 20 Sep 2025 05:26:58 +0100 Subject: [PATCH] feat: track join time --- crates/bonfire/src/events/impl.rs | 10 ++- .../database/src/models/channels/model.rs | 12 ++- crates/core/database/src/util/bridge/v0.rs | 2 + crates/core/database/src/voice/mod.rs | 75 ++++++++++++++++--- .../core/database/src/voice/voice_client.rs | 18 ++++- crates/core/models/src/v0/channels.rs | 1 + crates/core/models/src/v0/users.rs | 2 + crates/daemons/voice-ingress/src/api.rs | 26 +++---- crates/delta/src/routes/bots/delete.rs | 16 +++- .../src/routes/channels/channel_delete.rs | 11 ++- .../delta/src/routes/channels/channel_edit.rs | 17 ++++- .../delta/src/routes/channels/voice_join.rs | 1 + crates/delta/src/routes/servers/ban_create.rs | 2 + .../delta/src/routes/servers/member_remove.rs | 14 ++-- .../src/routes/servers/permissions_set.rs | 60 ++++++++------- .../routes/servers/permissions_set_default.rs | 12 +-- .../delta/src/routes/servers/roles_delete.rs | 35 ++++----- .../routes/servers/roles_edit_positions.rs | 10 ++- .../delta/src/routes/servers/server_delete.rs | 13 ++-- 19 files changed, 230 insertions(+), 107 deletions(-) diff --git a/crates/bonfire/src/events/impl.rs b/crates/bonfire/src/events/impl.rs index 6d51a823..e52d10a6 100644 --- a/crates/bonfire/src/events/impl.rs +++ b/crates/bonfire/src/events/impl.rs @@ -12,7 +12,6 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_presence::filter_online; use revolt_result::Result; - use super::state::{Cache, State}; /// Cache Manager @@ -217,7 +216,14 @@ impl State { // fetch voice states for all the channels we can see let mut voice_states = Vec::new(); - for channel in &channels { + for channel in channels.iter().filter(|c| { + matches!( + c, + Channel::DirectMessage { .. } + | Channel::Group { .. } + | Channel::TextChannel { voice: Some(_), .. } + ) + }) { if let Ok(Some(voice_state)) = get_channel_voice_state(channel).await { voice_states.push(voice_state) } diff --git a/crates/core/database/src/models/channels/model.rs b/crates/core/database/src/models/channels/model.rs index dcaa7839..d8a485b9 100644 --- a/crates/core/database/src/models/channels/model.rs +++ b/crates/core/database/src/models/channels/model.rs @@ -153,6 +153,7 @@ auto_derived!( Description, Icon, DefaultPermissions, + Voice, } ); @@ -424,9 +425,7 @@ impl Channel { /// Clone this channel's server id pub fn server(&self) -> Option<&str> { match self { - Channel::TextChannel { server, .. } => { - Some(server) - } + Channel::TextChannel { server, .. } => Some(server), _ => None, } } @@ -535,6 +534,12 @@ impl Channel { } _ => {} }, + FieldsChannel::Voice => match self { + Self::TextChannel { voice, .. } => { + voice.take(); + } + _ => {} + }, } } @@ -767,6 +772,7 @@ impl IntoDocumentPath for FieldsChannel { FieldsChannel::Description => "description", FieldsChannel::Icon => "icon", FieldsChannel::DefaultPermissions => "default_permissions", + FieldsChannel::Voice => "voice", }) } } diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index 33f7dac6..915b75ce 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -312,6 +312,7 @@ impl From for crate::FieldsChannel { FieldsChannel::Description => crate::FieldsChannel::Description, FieldsChannel::Icon => crate::FieldsChannel::Icon, FieldsChannel::DefaultPermissions => crate::FieldsChannel::DefaultPermissions, + FieldsChannel::Voice => crate::FieldsChannel::Voice, } } } @@ -322,6 +323,7 @@ impl From for FieldsChannel { crate::FieldsChannel::Description => FieldsChannel::Description, crate::FieldsChannel::Icon => FieldsChannel::Icon, crate::FieldsChannel::DefaultPermissions => FieldsChannel::DefaultPermissions, + crate::FieldsChannel::Voice => FieldsChannel::Voice, } } } diff --git a/crates/core/database/src/voice/mod.rs b/crates/core/database/src/voice/mod.rs index 6208db3f..8a66b7ed 100644 --- a/crates/core/database/src/voice/mod.rs +++ b/crates/core/database/src/voice/mod.rs @@ -4,6 +4,7 @@ use crate::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, Server, }; +use iso8601_timestamp::{Duration, Timestamp}; use livekit_protocol::ParticipantPermission; use redis_kiss::{get_connection as _get_connection, redis::Pipeline, AsyncCommands, Conn}; use revolt_config::FeaturesLimits; @@ -148,10 +149,12 @@ pub async fn create_voice_state( channel_id: &str, server_id: Option<&str>, user_id: &str, + joined_at: Timestamp, ) -> Result { let unique_key = format!("{}:{}", &user_id, server_id.unwrap_or(channel_id)); let voice_state = UserVoiceState { + joined_at, id: user_id.to_string(), is_receiving: true, is_publishing: false, @@ -163,6 +166,12 @@ pub async fn create_voice_state( .sadd(format!("vc_members:{channel_id}"), user_id) .sadd(format!("vc:{user_id}"), channel_id) .set(&unique_key, channel_id) + .set( + format!("joined_at:{unique_key}"), + joined_at + .duration_since(Timestamp::UNIX_EPOCH) + .whole_milliseconds() as i64, + ) .set( format!("is_publishing:{unique_key}"), voice_state.is_publishing, @@ -194,6 +203,7 @@ pub async fn delete_voice_state( .srem(format!("vc_members:{channel_id}"), user_id) .srem(format!("vc:{user_id}"), channel_id) .del(&[ + format!("joined_at:{unique_key}"), format!("is_publishing:{unique_key}"), format!("is_receiving:{unique_key}"), format!("screensharing:{unique_key}"), @@ -205,6 +215,35 @@ pub async fn delete_voice_state( .to_internal_error() } +pub async fn delete_channel_voice_state( + channel_id: &str, + server_id: Option<&str>, + user_ids: &[String], +) -> Result<()> { + let parent_id = server_id.unwrap_or(channel_id); + + let mut pipeline = Pipeline::new(); + pipeline.del(format!("vc_members:{channel_id}")); + + for user_id in user_ids { + let unique_key = format!("{user_id}:{parent_id}"); + + pipeline.srem(format!("vc:{user_id}"), channel_id).del(&[ + format!("joined_at:{unique_key}"), + format!("is_publishing:{unique_key}"), + format!("is_receiving:{unique_key}"), + format!("screensharing:{unique_key}"), + format!("camera:{unique_key}"), + unique_key.clone(), + ]); + } + + pipeline + .query_async(&mut get_connection().await?.into_inner()) + .await + .to_internal_error() +} + pub async fn update_voice_state_tracks( channel_id: &str, server_id: Option<&str>, @@ -285,9 +324,10 @@ pub async fn get_voice_state( ) -> Result> { let unique_key = format!("{}:{}", user_id, server_id.unwrap_or(channel_id)); - let (is_publishing, is_receiving, screensharing, camera) = get_connection() + let (joined_at, is_publishing, is_receiving, screensharing, camera) = get_connection() .await? .mget(&[ + format!("joined_at:{unique_key}"), format!("is_publishing:{unique_key}"), format!("is_receiving:{unique_key}"), format!("screensharing:{unique_key}"), @@ -296,16 +336,29 @@ pub async fn get_voice_state( .await .to_internal_error()?; - match (is_publishing, is_receiving, screensharing, camera) { - (Some(is_publishing), Some(is_receiving), Some(screensharing), Some(camera)) => { - Ok(Some(v0::UserVoiceState { - id: user_id.to_string(), - is_receiving, - is_publishing, - screensharing, - camera, - })) - } + match ( + joined_at, + is_publishing, + is_receiving, + screensharing, + camera, + ) { + ( + Some(joined_at), + Some(is_publishing), + Some(is_receiving), + Some(screensharing), + Some(camera), + ) => Ok(Some(v0::UserVoiceState { + joined_at: Timestamp::UNIX_EPOCH + .checked_add(Duration::milliseconds(joined_at)) + .unwrap(), + id: user_id.to_string(), + is_receiving, + is_publishing, + screensharing, + camera, + })), _ => Ok(None), } } diff --git a/crates/core/database/src/voice/voice_client.rs b/crates/core/database/src/voice/voice_client.rs index 5e31b7ba..967f3fef 100644 --- a/crates/core/database/src/voice/voice_client.rs +++ b/crates/core/database/src/voice/voice_client.rs @@ -1,4 +1,7 @@ -use crate::{models::{Channel, User}, Database}; +use crate::{ + models::{Channel, User}, + Database, +}; use livekit_api::{ access_token::{AccessToken, VideoGrants}, services::room::{CreateRoomOptions, RoomClient as InnerRoomClient, UpdateParticipantOptions}, @@ -76,7 +79,9 @@ impl VoiceClient { AccessToken::with_api_key(&room.node.key, &room.node.secret) .with_name(&format!("{}#{}", user.username, user.discriminator)) .with_identity(&user.id) - .with_metadata(&serde_json::to_string(&user.clone().into(db, None).await).to_internal_error()?) + .with_metadata( + &serde_json::to_string(&user.clone().into(db, None).await).to_internal_error()?, + ) .with_ttl(Duration::from_secs(10)) .with_grants(VideoGrants { room_join: true, @@ -139,4 +144,13 @@ impl VoiceClient { .await .to_internal_error() } + + pub async fn delete_room(&self, node: &str, channel_id: &str) -> Result<()> { + let room = self.get_node(node)?; + + room.client + .delete_room(channel_id) + .await + .to_internal_error() + } } diff --git a/crates/core/models/src/v0/channels.rs b/crates/core/models/src/v0/channels.rs index f35a33bf..d8b4e228 100644 --- a/crates/core/models/src/v0/channels.rs +++ b/crates/core/models/src/v0/channels.rs @@ -157,6 +157,7 @@ auto_derived!( Description, Icon, DefaultPermissions, + Voice, } /// New webhook information diff --git a/crates/core/models/src/v0/users.rs b/crates/core/models/src/v0/users.rs index 8a7e5f37..73957768 100644 --- a/crates/core/models/src/v0/users.rs +++ b/crates/core/models/src/v0/users.rs @@ -1,3 +1,4 @@ +use iso8601_timestamp::Timestamp; use once_cell::sync::Lazy; use regex::Regex; @@ -283,6 +284,7 @@ auto_derived_partial!( /// Voice State information for a user pub struct UserVoiceState { pub id: String, + pub joined_at: Timestamp, pub is_receiving: bool, pub is_publishing: bool, pub screensharing: bool, diff --git a/crates/daemons/voice-ingress/src/api.rs b/crates/daemons/voice-ingress/src/api.rs index 6c6fabc1..9905f548 100644 --- a/crates/daemons/voice-ingress/src/api.rs +++ b/crates/daemons/voice-ingress/src/api.rs @@ -3,7 +3,7 @@ use livekit_api::{access_token::TokenVerifier, webhooks::WebhookReceiver}; use livekit_protocol::TrackType; use revolt_database::{ events::client::EventV1, - iso8601_timestamp::Timestamp, + iso8601_timestamp::{Duration, Timestamp}, util::reference::Reference, voice::{ create_voice_state, delete_voice_state, get_call_notification_recipients, @@ -64,7 +64,12 @@ pub async fn ingress( let channel = Reference::from_unchecked(channel_id).as_channel(db).await?; - let voice_state = create_voice_state(channel_id, channel.server(), user_id).await?; + let joined_at = Timestamp::UNIX_EPOCH + .checked_add(Duration::seconds(event.created_at)) + .unwrap(); + + let voice_state = + create_voice_state(channel_id, channel.server(), user_id, joined_at).await?; // Only publish one event when a user is moved from one channel to another. if let Some(moved_from) = get_user_moved_to_voice(channel_id, user_id).await? { @@ -89,16 +94,9 @@ pub async fn ingress( if event.room.as_ref().unwrap().num_participants == 1 { let user = Reference::from_unchecked(user_id).as_user(db).await?; - let started_at = Timestamp::now_utc(); - let message_id = Ulid::from_datetime( - DateTime::from_timestamp_millis( - started_at - .duration_since(Timestamp::UNIX_EPOCH) - .whole_milliseconds() as i64, - ) - .unwrap(), - ) - .to_string(); + let message_id = + Ulid::from_datetime(DateTime::from_timestamp_secs(event.created_at).unwrap()) + .to_string(); let mut call_started_message = SystemMessage::CallStarted { by: user_id.to_string(), @@ -127,7 +125,7 @@ pub async fn ingress( .await?; let recipients = get_call_notification_recipients(&channel_id, &user_id).await?; - let now = started_at.format_short().to_string(); + let now = joined_at.format_short().to_string(); if let Err(e) = amqp .dm_call_updated(&user.id, channel.id(), Some(&now), false, recipients) @@ -165,7 +163,7 @@ pub async fn ingress( if members.is_none_or(|m| m.is_empty()) { // The channel is empty so send out an "end" message for ringing if let Err(e) = amqp - .dm_call_updated(&user_id, &channel_id, None, true, None) + .dm_call_updated(user_id, channel_id, None, true, None) .await { revolt_config::capture_internal_error!(&e); diff --git a/crates/delta/src/routes/bots/delete.rs b/crates/delta/src/routes/bots/delete.rs index 4d4daefb..c8bf5cb3 100644 --- a/crates/delta/src/routes/bots/delete.rs +++ b/crates/delta/src/routes/bots/delete.rs @@ -1,4 +1,4 @@ -use revolt_database::{util::reference::Reference, Database, User}; +use revolt_database::{util::reference::Reference, voice::{delete_voice_state, get_channel_node, get_user_voice_channels, VoiceClient}, Database, User}; use revolt_result::{create_error, Result}; use rocket::State; use rocket_empty::EmptyResponse; @@ -10,6 +10,7 @@ use rocket_empty::EmptyResponse; #[delete("/")] pub async fn delete_bot( db: &State, + voice_client: &State, user: User, target: Reference<'_>, ) -> Result { @@ -18,7 +19,18 @@ pub async fn delete_bot( return Err(create_error!(NotFound)); } - bot.delete(db).await.map(|_| EmptyResponse) + bot.delete(db).await?; + + for channel_id in get_user_voice_channels(&bot.id).await? { + let node = get_channel_node(&channel_id).await?.unwrap(); + let channel = Reference::from_unchecked(&channel_id).as_channel(db).await?; + + voice_client.remove_user(&node, &bot.id, &channel_id).await?; + + delete_voice_state(&channel_id, channel.server(), &bot.id).await?; + } + + Ok(EmptyResponse) } #[cfg(test)] diff --git a/crates/delta/src/routes/channels/channel_delete.rs b/crates/delta/src/routes/channels/channel_delete.rs index 9a2309a7..792162f7 100644 --- a/crates/delta/src/routes/channels/channel_delete.rs +++ b/crates/delta/src/routes/channels/channel_delete.rs @@ -1,8 +1,7 @@ use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{ - delete_voice_state, get_channel_node, get_voice_channel_members, is_in_voice_channel, - VoiceClient, + delete_channel_voice_state, delete_voice_state, get_channel_node, get_voice_channel_members, is_in_voice_channel, VoiceClient }, Channel, Database, PartialChannel, User, AMQP, }; @@ -63,6 +62,7 @@ pub async fn delete( voice_client .remove_user(&node, &user.id, channel.id()) .await?; + delete_voice_state(channel.id(), None, &user.id).await?; }; } @@ -73,10 +73,9 @@ pub async fn delete( if let Some(users) = get_voice_channel_members(channel.id()).await? { let node = get_channel_node(channel.id()).await?.unwrap(); - for user in users { - voice_client.remove_user(&node, &user, channel.id()).await?; - delete_voice_state(channel.id(), channel.server(), &user).await?; - } + voice_client.delete_room(&node, channel.id()).await?; + + delete_channel_voice_state(channel.id(), channel.server(), &users).await?; }; } }; diff --git a/crates/delta/src/routes/channels/channel_edit.rs b/crates/delta/src/routes/channels/channel_edit.rs index 355fa246..7887679c 100644 --- a/crates/delta/src/routes/channels/channel_edit.rs +++ b/crates/delta/src/routes/channels/channel_edit.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Channel, Database, File, PartialChannel, SystemMessage, User, AMQP, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{delete_channel_voice_state, get_channel_node, get_voice_channel_members, VoiceClient}, Channel, Database, File, PartialChannel, SystemMessage, User, AMQP }; use revolt_models::v0; use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; @@ -15,6 +14,7 @@ use validator::Validate; #[patch("/", data = "")] pub async fn edit( db: &State, + voice_client: &State, amqp: &State, user: User, target: Reference<'_>, @@ -214,6 +214,9 @@ pub async fn edit( v0::FieldsChannel::Icon => { icon.take(); } + v0::FieldsChannel::Voice => { + voice.take(); + } _ => {} } } @@ -257,5 +260,15 @@ pub async fn edit( ) .await?; + if channel.voice().is_none() { + if let Some(users) = get_voice_channel_members(channel.id()).await? { + let node = get_channel_node(channel.id()).await?.unwrap(); + + voice_client.delete_room(&node, channel.id()).await?; + + delete_channel_voice_state(channel.id(), channel.server(), &users).await?; + }; + } + Ok(Json(channel.into())) } diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index c15fabfe..9ed3a0ba 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -87,6 +87,7 @@ pub async fn call( voice_client .remove_user(&node, &user.id, &channel_id) .await?; + delete_voice_state(&channel_id, channel.server(), &user.id).await?; } } else { diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index 727b3fb9..3d798501 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -60,7 +60,9 @@ pub async fn ban( // If the member is in a voice channel while banned kick them from the voice channel if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { let node = get_channel_node(&channel_id).await?.unwrap(); + voice_client.remove_user(&node, &user.id, &channel_id).await?; + delete_voice_state(&channel_id, Some(&server.id), &user.id).await? } } diff --git a/crates/delta/src/routes/servers/member_remove.rs b/crates/delta/src/routes/servers/member_remove.rs index 1ef7683b..f2a3b654 100644 --- a/crates/delta/src/routes/servers/member_remove.rs +++ b/crates/delta/src/routes/servers/member_remove.rs @@ -42,15 +42,17 @@ pub async fn kick( return Err(create_error!(NotElevated)); } + member + .remove(db, &server, RemovalIntention::Kick, false) + .await?; + if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { let node = get_channel_node(&channel_id).await?.unwrap(); voice_client.remove_user(&node, &user.id, &channel_id).await?; - delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; - } - member - .remove(db, &server, RemovalIntention::Kick, false) - .await - .map(|_| EmptyResponse) + delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; + }; + + Ok(EmptyResponse) } diff --git a/crates/delta/src/routes/servers/permissions_set.rs b/crates/delta/src/routes/servers/permissions_set.rs index 50d7eac8..41979a61 100644 --- a/crates/delta/src/routes/servers/permissions_set.rs +++ b/crates/delta/src/routes/servers/permissions_set.rs @@ -1,5 +1,7 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Database, User + util::{permissions::DatabasePermissionQuery, reference::Reference}, + voice::{sync_voice_permissions, VoiceClient}, + Database, User, }; use revolt_models::v0; use revolt_permissions::{calculate_server_permissions, ChannelPermission, Override}; @@ -22,36 +24,38 @@ pub async fn set_role_permission( let data = data.into_inner(); let mut server = target.as_server(db).await?; - if let Some((current_value, rank)) = server.roles.get(&role_id).map(|x| (x.permissions, x.rank)) - { - let mut query = DatabasePermissionQuery::new(db, &user).server(&server); - let permissions = calculate_server_permissions(&mut query).await; - permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?; + let (current_value, rank) = server + .roles + .get(&role_id) + .map(|x| (x.permissions, x.rank)) + .ok_or_else(|| create_error!(NotFound))?; - // Prevent us from editing roles above us - if rank <= query.get_member_rank().unwrap_or(i64::MIN) { - return Err(create_error!(NotElevated)); - } + let mut query = DatabasePermissionQuery::new(db, &user).server(&server); + let permissions = calculate_server_permissions(&mut query).await; - // Ensure we have access to grant these permissions forwards - let current_value: Override = current_value.into(); - permissions - .throw_permission_override(current_value, &data.permissions) - .await?; + permissions.throw_if_lacking_channel_permission(ChannelPermission::ManagePermissions)?; - for channel_id in &server.channels { - let channel = Reference::from_unchecked(channel_id).as_channel(db).await?; - - sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; - }; - - server - .set_role_permission(db, &role_id, data.permissions.into()) - .await?; - - Ok(Json(server.into())) - } else { - Err(create_error!(NotFound)) + // Prevent us from editing roles above us + if rank <= query.get_member_rank().unwrap_or(i64::MIN) { + return Err(create_error!(NotElevated)); } + + // Ensure we have access to grant these permissions forwards + let current_value: Override = current_value.into(); + permissions + .throw_permission_override(current_value, &data.permissions) + .await?; + + server + .set_role_permission(db, &role_id, data.permissions.into()) + .await?; + + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id).as_channel(db).await?; + + sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; + }; + + Ok(Json(server.into())) } diff --git a/crates/delta/src/routes/servers/permissions_set_default.rs b/crates/delta/src/routes/servers/permissions_set_default.rs index 863de71e..bf9dd304 100644 --- a/crates/delta/src/routes/servers/permissions_set_default.rs +++ b/crates/delta/src/routes/servers/permissions_set_default.rs @@ -39,12 +39,6 @@ pub async fn set_default_server_permissions( ) .await?; - for channel_id in &server.channels { - let channel = Reference::from_unchecked(channel_id).as_channel(db).await?; - - sync_voice_permissions(db, voice_client, &channel, Some(&server), None).await?; - }; - server .update( db, @@ -56,5 +50,11 @@ pub async fn set_default_server_permissions( ) .await?; + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id).as_channel(db).await?; + + sync_voice_permissions(db, voice_client, &channel, Some(&server), None).await?; + }; + Ok(Json(server.into())) } diff --git a/crates/delta/src/routes/servers/roles_delete.rs b/crates/delta/src/routes/servers/roles_delete.rs index f1ec5517..77df2501 100644 --- a/crates/delta/src/routes/servers/roles_delete.rs +++ b/crates/delta/src/routes/servers/roles_delete.rs @@ -1,7 +1,7 @@ use revolt_database::{ util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, - Database, User + Database, User, }; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; use revolt_result::{create_error, Result}; @@ -18,7 +18,7 @@ pub async fn delete( user: User, target: Reference<'_>, role_id: String, - voice_client: &State + voice_client: &State, ) -> Result { let mut server = target.as_server(db).await?; let mut query = DatabasePermissionQuery::new(db, &user).server(&server); @@ -28,21 +28,22 @@ pub async fn delete( let member_rank = query.get_member_rank().unwrap_or(i64::MIN); - if let Some(role) = server.roles.remove(&role_id) { - if role.rank <= member_rank { - return Err(create_error!(NotElevated)); - } + let role = server + .roles + .remove(&role_id) + .ok_or_else(|| create_error!(NotFound))?; - for channel_id in &server.channels { - let channel = Reference::from_unchecked(channel_id).as_channel(db).await?; - - sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; - }; - - role.delete(db, &server.id, &role_id) - .await - .map(|_| EmptyResponse) - } else { - Err(create_error!(NotFound)) + if role.rank <= member_rank { + return Err(create_error!(NotElevated)); } + + role.delete(db, &server.id, &role_id).await?; + + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id).as_channel(db).await?; + + sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).await?; + } + + Ok(EmptyResponse) } diff --git a/crates/delta/src/routes/servers/roles_edit_positions.rs b/crates/delta/src/routes/servers/roles_edit_positions.rs index d27259e5..4b55e95a 100644 --- a/crates/delta/src/routes/servers/roles_edit_positions.rs +++ b/crates/delta/src/routes/servers/roles_edit_positions.rs @@ -1,6 +1,5 @@ use revolt_database::{ - util::{permissions::DatabasePermissionQuery, reference::Reference}, - Database, User, + util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Database, User }; use revolt_models::v0; use revolt_permissions::{calculate_server_permissions, ChannelPermission}; @@ -14,6 +13,7 @@ use rocket::{serde::json::Json, State}; #[patch("//roles/ranks", data = "")] pub async fn edit_role_ranks( db: &State, + voice_client: &State, user: User, target: Reference<'_>, data: Json, @@ -70,6 +70,12 @@ pub async fn edit_role_ranks( server.set_role_ordering(db, new_order).await?; + for channel_id in &server.channels { + let channel = Reference::from_unchecked(channel_id).as_channel(db).await?; + + sync_voice_permissions(db, voice_client, &channel, Some(&server), None).await?; + }; + Ok(Json(server.into())) } diff --git a/crates/delta/src/routes/servers/server_delete.rs b/crates/delta/src/routes/servers/server_delete.rs index 58898984..ce323006 100644 --- a/crates/delta/src/routes/servers/server_delete.rs +++ b/crates/delta/src/routes/servers/server_delete.rs @@ -1,6 +1,6 @@ use revolt_database::{ util::reference::Reference, - voice::{delete_voice_state, get_channel_node, get_user_voice_channel_in_server, get_voice_channel_members, VoiceClient}, + voice::{delete_channel_voice_state, delete_voice_state, get_channel_node, get_user_voice_channel_in_server, get_voice_channel_members, VoiceClient}, Database, RemovalIntention, User, }; use revolt_models::v0; @@ -29,11 +29,10 @@ pub async fn delete( if let Some(users) = get_voice_channel_members(channel_id).await? { let node = get_channel_node(channel_id).await?.unwrap(); - for user in users { - voice_client.remove_user(&node, &user, channel_id).await?; - delete_voice_state(channel_id, Some(&server.id), &user).await?; - } - } + voice_client.delete_room(&node, channel_id).await?; + + delete_channel_voice_state(channel_id, Some(&server.id), &users).await?; + }; } server.delete(db).await @@ -41,7 +40,9 @@ pub async fn delete( if let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? { if server.channels.iter().any(|c| c == &channel_id) { let node = get_channel_node(&channel_id).await?.unwrap(); + voice_client.remove_user(&node, &user.id, &channel_id).await?; + delete_voice_state(&channel_id, Some(&server.id), &user.id).await?; } };