forked from jmug/stoatchat
feat(livekit): more permission handling
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -4182,6 +4182,7 @@ dependencies = [
|
|||||||
name = "revolt-result"
|
name = "revolt-result"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"log",
|
||||||
"revolt_okapi",
|
"revolt_okapi",
|
||||||
"revolt_rocket_okapi",
|
"revolt_rocket_okapi",
|
||||||
"rocket",
|
"rocket",
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ pub enum EventV1 {
|
|||||||
UserVoiceStateUpdate {
|
UserVoiceStateUpdate {
|
||||||
id: String,
|
id: String,
|
||||||
channel_id: String,
|
channel_id: String,
|
||||||
data: PartialUserVoiceState
|
data: PartialUserVoiceState,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,11 +32,11 @@ auto_derived_partial!(
|
|||||||
pub timeout: Option<Timestamp>,
|
pub timeout: Option<Timestamp>,
|
||||||
|
|
||||||
/// Whether the member is server-wide voice muted
|
/// Whether the member is server-wide voice muted
|
||||||
#[serde(skip_serializing_if = "if_false")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub can_publish: bool,
|
pub can_publish: Option<bool>,
|
||||||
/// Whether the member is server-wide voice deafened
|
/// Whether the member is server-wide voice deafened
|
||||||
#[serde(skip_serializing_if = "if_false")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub can_receive: bool,
|
pub can_receive: Option<bool>,
|
||||||
},
|
},
|
||||||
"PartialMember"
|
"PartialMember"
|
||||||
);
|
);
|
||||||
@@ -57,6 +57,8 @@ auto_derived!(
|
|||||||
Avatar,
|
Avatar,
|
||||||
Roles,
|
Roles,
|
||||||
Timeout,
|
Timeout,
|
||||||
|
CanReceive,
|
||||||
|
CanPublish,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Member removal intention
|
/// Member removal intention
|
||||||
@@ -76,8 +78,8 @@ impl Default for Member {
|
|||||||
avatar: None,
|
avatar: None,
|
||||||
roles: vec![],
|
roles: vec![],
|
||||||
timeout: None,
|
timeout: None,
|
||||||
can_publish: false,
|
can_publish: None,
|
||||||
can_receive: false,
|
can_receive: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -199,6 +201,8 @@ impl Member {
|
|||||||
FieldsMember::Nickname => self.nickname = None,
|
FieldsMember::Nickname => self.nickname = None,
|
||||||
FieldsMember::Roles => self.roles.clear(),
|
FieldsMember::Roles => self.roles.clear(),
|
||||||
FieldsMember::Timeout => self.timeout = None,
|
FieldsMember::Timeout => self.timeout = None,
|
||||||
|
FieldsMember::CanReceive => self.can_receive = None,
|
||||||
|
FieldsMember::CanPublish => self.can_publish = None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -173,6 +173,8 @@ impl IntoDocumentPath for FieldsMember {
|
|||||||
FieldsMember::Nickname => "nickname",
|
FieldsMember::Nickname => "nickname",
|
||||||
FieldsMember::Roles => "roles",
|
FieldsMember::Roles => "roles",
|
||||||
FieldsMember::Timeout => "timeout",
|
FieldsMember::Timeout => "timeout",
|
||||||
|
FieldsMember::CanPublish => "can_publish",
|
||||||
|
FieldsMember::CanReceive => "can_receive"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -690,6 +690,8 @@ impl From<crate::FieldsMember> for FieldsMember {
|
|||||||
crate::FieldsMember::Nickname => FieldsMember::Nickname,
|
crate::FieldsMember::Nickname => FieldsMember::Nickname,
|
||||||
crate::FieldsMember::Roles => FieldsMember::Roles,
|
crate::FieldsMember::Roles => FieldsMember::Roles,
|
||||||
crate::FieldsMember::Timeout => FieldsMember::Timeout,
|
crate::FieldsMember::Timeout => FieldsMember::Timeout,
|
||||||
|
crate::FieldsMember::CanReceive => FieldsMember::CanReceive,
|
||||||
|
crate::FieldsMember::CanPublish => FieldsMember::CanPublish,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -701,6 +703,8 @@ impl From<FieldsMember> for crate::FieldsMember {
|
|||||||
FieldsMember::Nickname => crate::FieldsMember::Nickname,
|
FieldsMember::Nickname => crate::FieldsMember::Nickname,
|
||||||
FieldsMember::Roles => crate::FieldsMember::Roles,
|
FieldsMember::Roles => crate::FieldsMember::Roles,
|
||||||
FieldsMember::Timeout => crate::FieldsMember::Timeout,
|
FieldsMember::Timeout => crate::FieldsMember::Timeout,
|
||||||
|
FieldsMember::CanReceive => crate::FieldsMember::CanReceive,
|
||||||
|
FieldsMember::CanPublish => crate::FieldsMember::CanPublish,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,6 +179,22 @@ impl PermissionQuery for DatabasePermissionQuery<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_publish_overwrites(&mut self) -> bool {
|
||||||
|
if let Some(member) = &self.member {
|
||||||
|
member.can_publish.unwrap_or(true)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_receive_overwrites(&mut self) -> bool {
|
||||||
|
if let Some(member) = &self.member {
|
||||||
|
member.can_receive.unwrap_or(true)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// * For calculating channel permission
|
// * For calculating channel permission
|
||||||
|
|
||||||
/// Get the type of the channel
|
/// Get the type of the channel
|
||||||
|
|||||||
@@ -60,11 +60,11 @@ auto_derived_partial!(
|
|||||||
pub timeout: Option<Timestamp>,
|
pub timeout: Option<Timestamp>,
|
||||||
|
|
||||||
/// Whether the member is server-wide voice muted
|
/// Whether the member is server-wide voice muted
|
||||||
#[serde(skip_serializing_if = "if_false")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub can_publish: bool,
|
pub can_publish: Option<bool>,
|
||||||
/// Whether the member is server-wide voice deafened
|
/// Whether the member is server-wide voice deafened
|
||||||
#[serde(skip_serializing_if = "if_false")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub can_receive: bool,
|
pub can_receive: Option<bool>,
|
||||||
},
|
},
|
||||||
"PartialMember"
|
"PartialMember"
|
||||||
);
|
);
|
||||||
@@ -85,6 +85,8 @@ auto_derived!(
|
|||||||
Avatar,
|
Avatar,
|
||||||
Roles,
|
Roles,
|
||||||
Timeout,
|
Timeout,
|
||||||
|
CanReceive,
|
||||||
|
CanPublish,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Member removal intention
|
/// Member removal intention
|
||||||
|
|||||||
@@ -61,6 +61,14 @@ pub async fn calculate_server_permissions<P: PermissionQuery>(query: &mut P) ->
|
|||||||
permissions.apply(role_override);
|
permissions.apply(role_override);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !query.do_we_have_publish_overwrites().await {
|
||||||
|
permissions.revoke(ChannelPermission::Speak as u64);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !query.do_we_have_receive_overwrites().await {
|
||||||
|
permissions.revoke(ChannelPermission::Listen as u64);
|
||||||
|
}
|
||||||
|
|
||||||
if query.are_we_timed_out().await {
|
if query.are_we_timed_out().await {
|
||||||
permissions.restrict(*ALLOW_IN_TIMEOUT);
|
permissions.restrict(*ALLOW_IN_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ pub enum ChannelPermission {
|
|||||||
DeafenMembers = 1 << 34,
|
DeafenMembers = 1 << 34,
|
||||||
/// Move members between voice channels
|
/// Move members between voice channels
|
||||||
MoveMembers = 1 << 35,
|
MoveMembers = 1 << 35,
|
||||||
|
/// Listen to other users
|
||||||
|
Listen = 1 << 36,
|
||||||
|
|
||||||
// * Misc. permissions
|
// * Misc. permissions
|
||||||
// % Bits 36 to 52: free area
|
// % Bits 36 to 52: free area
|
||||||
@@ -124,7 +126,8 @@ pub static DEFAULT_PERMISSION: Lazy<u64> = Lazy::new(|| {
|
|||||||
+ ChannelPermission::SendEmbeds
|
+ ChannelPermission::SendEmbeds
|
||||||
+ ChannelPermission::UploadFiles
|
+ ChannelPermission::UploadFiles
|
||||||
+ ChannelPermission::Connect
|
+ ChannelPermission::Connect
|
||||||
+ ChannelPermission::Speak,
|
+ ChannelPermission::Speak
|
||||||
|
+ ChannelPermission::Listen
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,14 @@ async fn validate_user_permissions() {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_publish_overwrites(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_receive_overwrites(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_channel_type(&mut self) -> ChannelType {
|
async fn get_channel_type(&mut self) -> ChannelType {
|
||||||
ChannelType::DirectMessage
|
ChannelType::DirectMessage
|
||||||
}
|
}
|
||||||
@@ -153,6 +161,14 @@ async fn validate_group_permissions() {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_publish_overwrites(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_receive_overwrites(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_channel_type(&mut self) -> ChannelType {
|
async fn get_channel_type(&mut self) -> ChannelType {
|
||||||
ChannelType::Group
|
ChannelType::Group
|
||||||
}
|
}
|
||||||
@@ -254,6 +270,14 @@ async fn validate_server_permissions() {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_publish_overwrites(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_receive_overwrites(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_channel_type(&mut self) -> ChannelType {
|
async fn get_channel_type(&mut self) -> ChannelType {
|
||||||
ChannelType::ServerChannel
|
ChannelType::ServerChannel
|
||||||
}
|
}
|
||||||
@@ -346,6 +370,14 @@ async fn validate_timed_out_member() {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_publish_overwrites(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_we_have_receive_overwrites(&mut self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_channel_type(&mut self) -> ChannelType {
|
async fn get_channel_type(&mut self) -> ChannelType {
|
||||||
ChannelType::ServerChannel
|
ChannelType::ServerChannel
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ pub trait PermissionQuery {
|
|||||||
/// Is our perspective user timed out on this server?
|
/// Is our perspective user timed out on this server?
|
||||||
async fn are_we_timed_out(&mut self) -> bool;
|
async fn are_we_timed_out(&mut self) -> bool;
|
||||||
|
|
||||||
|
/// Is the member muted?
|
||||||
|
async fn do_we_have_publish_overwrites(&mut self) -> bool;
|
||||||
|
|
||||||
|
/// Is the member deafend?
|
||||||
|
async fn do_we_have_receive_overwrites(&mut self) -> bool;
|
||||||
|
|
||||||
// * For calculating channel permission
|
// * For calculating channel permission
|
||||||
|
|
||||||
/// Get the type of the channel
|
/// Get the type of the channel
|
||||||
|
|||||||
@@ -28,3 +28,6 @@ schemars = { version = "0.8.8", optional = true }
|
|||||||
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false }
|
rocket = { optional = true, version = "0.5.0-rc.2", default-features = false }
|
||||||
revolt_rocket_okapi = { version = "0.9.1", optional = true }
|
revolt_rocket_okapi = { version = "0.9.1", optional = true }
|
||||||
revolt_okapi = { version = "0.9.1", optional = true }
|
revolt_okapi = { version = "0.9.1", optional = true }
|
||||||
|
|
||||||
|
# utilities
|
||||||
|
log = "0.4"
|
||||||
@@ -181,9 +181,11 @@ pub trait ToRevoltError<T> {
|
|||||||
fn to_internal_error(self) -> Result<T, Error>;
|
fn to_internal_error(self) -> Result<T, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, E> ToRevoltError<T> for Result<T, E> {
|
impl<T, E: std::fmt::Debug> ToRevoltError<T> for Result<T, E> {
|
||||||
fn to_internal_error(self) -> Result<T, Error> {
|
fn to_internal_error(self) -> Result<T, Error> {
|
||||||
self.map_err(|_| {
|
self
|
||||||
|
.inspect_err(|e| log::error!("{e:?}"))
|
||||||
|
.map_err(|_| {
|
||||||
let loc = Location::caller();
|
let loc = Location::caller();
|
||||||
|
|
||||||
Error {
|
Error {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use livekit_api::{access_token::{AccessToken, VideoGrants}, services::room::{CreateRoomOptions, RoomClient}};
|
use livekit_api::{access_token::{AccessToken, VideoGrants}, services::room::{CreateRoomOptions, RoomClient, UpdateParticipantOptions}};
|
||||||
use livekit_protocol::Room;
|
use livekit_protocol::{ParticipantInfo, ParticipantPermission, Room};
|
||||||
use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands};
|
use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands};
|
||||||
use revolt_database::{Channel, User};
|
use revolt_database::{Channel, User};
|
||||||
use revolt_models::v0::{self, PartialUserVoiceState, UserVoiceState};
|
use revolt_models::v0::{self, PartialUserVoiceState, UserVoiceState};
|
||||||
@@ -31,6 +31,22 @@ pub async fn raise_if_in_voice(user: &User, target: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_user_voice_channel_in_server(user_id: &str, server_id: &str) -> Result<Option<String>> {
|
||||||
|
let mut conn = get_connection()
|
||||||
|
.await
|
||||||
|
.to_internal_error()?;
|
||||||
|
|
||||||
|
let unique_key = format!(
|
||||||
|
"{}-{}",
|
||||||
|
user_id,
|
||||||
|
server_id
|
||||||
|
);
|
||||||
|
|
||||||
|
conn.get::<&str, Option<String>>(&unique_key)
|
||||||
|
.await
|
||||||
|
.to_internal_error()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_allowed_sources(permissions: PermissionValue) -> Vec<String> {
|
pub fn get_allowed_sources(permissions: PermissionValue) -> Vec<String> {
|
||||||
let mut allowed_sources = Vec::new();
|
let mut allowed_sources = Vec::new();
|
||||||
|
|
||||||
@@ -67,6 +83,7 @@ pub async fn create_voice_state(channel_id: &str, server_id: Option<&str>, user_
|
|||||||
Pipeline::new()
|
Pipeline::new()
|
||||||
.sadd(format!("vc-members-{channel_id}"), user_id)
|
.sadd(format!("vc-members-{channel_id}"), user_id)
|
||||||
.sadd(format!("vc-{user_id}"), channel_id)
|
.sadd(format!("vc-{user_id}"), channel_id)
|
||||||
|
.set(&unique_key, channel_id)
|
||||||
.set(format!("can_publish-{unique_key}"), voice_state.can_publish)
|
.set(format!("can_publish-{unique_key}"), voice_state.can_publish)
|
||||||
.set(format!("can_receive-{unique_key}"), voice_state.can_receive)
|
.set(format!("can_receive-{unique_key}"), voice_state.can_receive)
|
||||||
.set(format!("screensharing-{unique_key}"), voice_state.screensharing)
|
.set(format!("screensharing-{unique_key}"), voice_state.screensharing)
|
||||||
@@ -96,6 +113,7 @@ pub async fn delete_voice_state(channel_id: &str, server_id: Option<&str>, user_
|
|||||||
format!("can_receive-{unique_key}"),
|
format!("can_receive-{unique_key}"),
|
||||||
format!("screensharing-{unique_key}"),
|
format!("screensharing-{unique_key}"),
|
||||||
format!("camera-{unique_key}"),
|
format!("camera-{unique_key}"),
|
||||||
|
unique_key.clone(),
|
||||||
])
|
])
|
||||||
.query_async(&mut get_connection()
|
.query_async(&mut get_connection()
|
||||||
.await
|
.await
|
||||||
@@ -243,6 +261,7 @@ impl VoiceClient {
|
|||||||
.with_grants(VideoGrants {
|
.with_grants(VideoGrants {
|
||||||
room_join: true,
|
room_join: true,
|
||||||
can_publish_sources: allowed_sources,
|
can_publish_sources: allowed_sources,
|
||||||
|
can_subscribe: permissions.has_channel_permission(ChannelPermission::Listen),
|
||||||
room: channel.id().to_string(),
|
room: channel.id().to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
@@ -261,6 +280,21 @@ impl VoiceClient {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|_| create_error!(InternalError))
|
.to_internal_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_permissions(&self, user: &User, channel_id: &str, new_permissions: ParticipantPermission) -> Result<ParticipantInfo> {
|
||||||
|
self.rooms
|
||||||
|
.update_participant(
|
||||||
|
channel_id,
|
||||||
|
&user.id,
|
||||||
|
UpdateParticipantOptions {
|
||||||
|
permission: Some(new_permissions),
|
||||||
|
name: "".to_string(),
|
||||||
|
metadata: "".to_string(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.to_internal_error()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,8 +105,8 @@ pub async fn root() -> Result<Json<RevoltConfig>> {
|
|||||||
url: config.hosts.january,
|
url: config.hosts.january,
|
||||||
},
|
},
|
||||||
livekit: VoiceFeature {
|
livekit: VoiceFeature {
|
||||||
enabled: !config.api.livekit.url.is_empty(),
|
enabled: !config.hosts.livekit.is_empty(),
|
||||||
url: config.api.livekit.url.to_string(),
|
url: config.hosts.livekit.to_string(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ws: config.hosts.events,
|
ws: config.hosts.events,
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ use revolt_database::{
|
|||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
|
|
||||||
use revolt_permissions::{
|
use revolt_permissions::{
|
||||||
calculate_channel_permissions, calculate_server_permissions, ChannelPermission,
|
calculate_server_permissions, ChannelPermission,
|
||||||
};
|
};
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
|
use revolt_voice::{delete_voice_state, get_user_voice_channel_in_server, VoiceClient};
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
/// # Ban User
|
/// # Ban User
|
||||||
@@ -19,6 +19,7 @@ use validator::Validate;
|
|||||||
#[put("/<server>/bans/<target>", data = "<data>")]
|
#[put("/<server>/bans/<target>", data = "<data>")]
|
||||||
pub async fn ban(
|
pub async fn ban(
|
||||||
db: &State<Database>,
|
db: &State<Database>,
|
||||||
|
voice: &State<VoiceClient>,
|
||||||
user: User,
|
user: User,
|
||||||
server: Reference,
|
server: Reference,
|
||||||
target: Reference,
|
target: Reference,
|
||||||
@@ -57,6 +58,11 @@ pub async fn ban(
|
|||||||
member
|
member
|
||||||
.remove(db, &server, RemovalIntention::Ban, false)
|
.remove(db, &server, RemovalIntention::Ban, false)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// 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? {
|
||||||
|
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerBan::create(db, &server, &target.id, data.reason)
|
ServerBan::create(db, &server, &target.id, data.reason)
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ use redis_kiss::{get_connection, redis::Pipeline, AsyncCommands};
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, File, PartialMember, User
|
events::client::EventV1, util::{permissions::DatabasePermissionQuery, reference::Reference}, Database, File, PartialMember, User
|
||||||
};
|
};
|
||||||
use revolt_models::v0::{self, PartialUserVoiceState};
|
use revolt_models::v0::{self, FieldsMember, PartialUserVoiceState};
|
||||||
|
|
||||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
use revolt_result::{create_error, Result, ToRevoltError};
|
use revolt_result::{create_error, Result, ToRevoltError};
|
||||||
|
use revolt_voice::VoiceClient;
|
||||||
use rocket::{form::validate::Contains, serde::json::Json, State};
|
use rocket::{form::validate::Contains, serde::json::Json, State};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
@@ -20,7 +21,7 @@ use validator::Validate;
|
|||||||
#[openapi(tag = "Server Members")]
|
#[openapi(tag = "Server Members")]
|
||||||
#[patch("/<server>/members/<target>", data = "<data>")]
|
#[patch("/<server>/members/<target>", data = "<data>")]
|
||||||
pub async fn edit(
|
pub async fn edit(
|
||||||
room_client: &State<RoomClient>,
|
voice_client: &State<VoiceClient>,
|
||||||
db: &State<Database>,
|
db: &State<Database>,
|
||||||
user: User,
|
user: User,
|
||||||
server: Reference,
|
server: Reference,
|
||||||
@@ -37,6 +38,7 @@ pub async fn edit(
|
|||||||
// Fetch server, target member and current permissions
|
// Fetch server, target member and current permissions
|
||||||
let mut server = server.as_server(db).await?;
|
let mut server = server.as_server(db).await?;
|
||||||
let mut member = target.as_member(db, &server.id).await?;
|
let mut member = target.as_member(db, &server.id).await?;
|
||||||
|
let target_user = target.as_user(db).await?;
|
||||||
let mut query = DatabasePermissionQuery::new(db, &user)
|
let mut query = DatabasePermissionQuery::new(db, &user)
|
||||||
.server(&server)
|
.server(&server)
|
||||||
.member(&member);
|
.member(&member);
|
||||||
@@ -153,8 +155,8 @@ pub async fn edit(
|
|||||||
roles,
|
roles,
|
||||||
timeout,
|
timeout,
|
||||||
remove,
|
remove,
|
||||||
can_publish,
|
mut can_publish,
|
||||||
can_receive,
|
mut can_receive,
|
||||||
voice_channel
|
voice_channel
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
@@ -181,6 +183,10 @@ pub async fn edit(
|
|||||||
partial.avatar = Some(File::use_avatar(db, &avatar, &user.id).await?);
|
partial.avatar = Some(File::use_avatar(db, &avatar, &user.id).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let remove_contains_voice = remove
|
||||||
|
.as_ref()
|
||||||
|
.map(|r| r.contains(FieldsMember::CanPublish) || r.contains(FieldsMember::CanReceive)).unwrap_or_default();
|
||||||
|
|
||||||
member
|
member
|
||||||
.update(
|
.update(
|
||||||
db,
|
db,
|
||||||
@@ -191,7 +197,11 @@ pub async fn edit(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if can_publish.is_some() || can_receive.is_some() || voice_channel.is_some() {
|
if can_publish.is_some() ||
|
||||||
|
can_receive.is_some() ||
|
||||||
|
voice_channel.is_some() ||
|
||||||
|
remove_contains_voice
|
||||||
|
{
|
||||||
let mut conn = get_connection().await.to_internal_error()?;
|
let mut conn = get_connection().await.to_internal_error()?;
|
||||||
|
|
||||||
let unique_key = format!("{}-{}", &member.id.user, &member.id.server);
|
let unique_key = format!("{}-{}", &member.id.user, &member.id.server);
|
||||||
@@ -206,6 +216,22 @@ pub async fn edit(
|
|||||||
let mut pipeline = Pipeline::new();
|
let mut pipeline = Pipeline::new();
|
||||||
let mut new_perms = ParticipantPermission::default();
|
let mut new_perms = ParticipantPermission::default();
|
||||||
|
|
||||||
|
if remove_contains_voice {
|
||||||
|
let mut query = DatabasePermissionQuery::new(db, &target_user)
|
||||||
|
.server(&server)
|
||||||
|
.member(&member);
|
||||||
|
|
||||||
|
let permissions = calculate_server_permissions(&mut query).await;
|
||||||
|
|
||||||
|
if !permissions.has_channel_permission(ChannelPermission::Speak) {
|
||||||
|
can_publish = Some(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !permissions.has_channel_permission(ChannelPermission::Listen) {
|
||||||
|
can_receive = Some(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(can_publish) = can_publish {
|
if let Some(can_publish) = can_publish {
|
||||||
pipeline.set(
|
pipeline.set(
|
||||||
format!("can_publish-{}", unique_key),
|
format!("can_publish-{}", unique_key),
|
||||||
@@ -235,18 +261,7 @@ pub async fn edit(
|
|||||||
.await
|
.await
|
||||||
.to_internal_error()?;
|
.to_internal_error()?;
|
||||||
|
|
||||||
room_client
|
voice_client.update_permissions(&user, &channel, new_perms).await?;
|
||||||
.update_participant(
|
|
||||||
&channel,
|
|
||||||
&member.id.user,
|
|
||||||
UpdateParticipantOptions {
|
|
||||||
permission: Some(new_perms),
|
|
||||||
name: "".to_string(),
|
|
||||||
metadata: "".to_string(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.to_internal_error()?;
|
|
||||||
|
|
||||||
EventV1::UserVoiceStateUpdate {
|
EventV1::UserVoiceStateUpdate {
|
||||||
id: member.id.user.clone(),
|
id: member.id.user.clone(),
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ async fn ingress(db: &State<Database>, voice_client: &State<VoiceClient>, body:
|
|||||||
EventV1::UserVoiceStateUpdate {
|
EventV1::UserVoiceStateUpdate {
|
||||||
id: user_id.clone(),
|
id: user_id.clone(),
|
||||||
channel_id: channel_id.clone(),
|
channel_id: channel_id.clone(),
|
||||||
data: partial,
|
data: partial
|
||||||
}
|
}
|
||||||
.p(channel_id.clone())
|
.p(channel_id.clone())
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
Reference in New Issue
Block a user