forked from jmug/stoatchat
feat: track join time
This commit is contained in:
@@ -12,7 +12,6 @@ use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
|||||||
use revolt_presence::filter_online;
|
use revolt_presence::filter_online;
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
|
|
||||||
use super::state::{Cache, State};
|
use super::state::{Cache, State};
|
||||||
|
|
||||||
/// Cache Manager
|
/// Cache Manager
|
||||||
@@ -217,7 +216,14 @@ impl State {
|
|||||||
// fetch voice states for all the channels we can see
|
// fetch voice states for all the channels we can see
|
||||||
let mut voice_states = Vec::new();
|
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 {
|
if let Ok(Some(voice_state)) = get_channel_voice_state(channel).await {
|
||||||
voice_states.push(voice_state)
|
voice_states.push(voice_state)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -153,6 +153,7 @@ auto_derived!(
|
|||||||
Description,
|
Description,
|
||||||
Icon,
|
Icon,
|
||||||
DefaultPermissions,
|
DefaultPermissions,
|
||||||
|
Voice,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -424,9 +425,7 @@ impl Channel {
|
|||||||
/// Clone this channel's server id
|
/// Clone this channel's server id
|
||||||
pub fn server(&self) -> Option<&str> {
|
pub fn server(&self) -> Option<&str> {
|
||||||
match self {
|
match self {
|
||||||
Channel::TextChannel { server, .. } => {
|
Channel::TextChannel { server, .. } => Some(server),
|
||||||
Some(server)
|
|
||||||
}
|
|
||||||
_ => None,
|
_ => 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::Description => "description",
|
||||||
FieldsChannel::Icon => "icon",
|
FieldsChannel::Icon => "icon",
|
||||||
FieldsChannel::DefaultPermissions => "default_permissions",
|
FieldsChannel::DefaultPermissions => "default_permissions",
|
||||||
|
FieldsChannel::Voice => "voice",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -312,6 +312,7 @@ impl From<FieldsChannel> for crate::FieldsChannel {
|
|||||||
FieldsChannel::Description => crate::FieldsChannel::Description,
|
FieldsChannel::Description => crate::FieldsChannel::Description,
|
||||||
FieldsChannel::Icon => crate::FieldsChannel::Icon,
|
FieldsChannel::Icon => crate::FieldsChannel::Icon,
|
||||||
FieldsChannel::DefaultPermissions => crate::FieldsChannel::DefaultPermissions,
|
FieldsChannel::DefaultPermissions => crate::FieldsChannel::DefaultPermissions,
|
||||||
|
FieldsChannel::Voice => crate::FieldsChannel::Voice,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,6 +323,7 @@ impl From<crate::FieldsChannel> for FieldsChannel {
|
|||||||
crate::FieldsChannel::Description => FieldsChannel::Description,
|
crate::FieldsChannel::Description => FieldsChannel::Description,
|
||||||
crate::FieldsChannel::Icon => FieldsChannel::Icon,
|
crate::FieldsChannel::Icon => FieldsChannel::Icon,
|
||||||
crate::FieldsChannel::DefaultPermissions => FieldsChannel::DefaultPermissions,
|
crate::FieldsChannel::DefaultPermissions => FieldsChannel::DefaultPermissions,
|
||||||
|
crate::FieldsChannel::Voice => FieldsChannel::Voice,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use crate::{
|
|||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
Database, Server,
|
Database, Server,
|
||||||
};
|
};
|
||||||
|
use iso8601_timestamp::{Duration, Timestamp};
|
||||||
use livekit_protocol::ParticipantPermission;
|
use livekit_protocol::ParticipantPermission;
|
||||||
use redis_kiss::{get_connection as _get_connection, redis::Pipeline, AsyncCommands, Conn};
|
use redis_kiss::{get_connection as _get_connection, redis::Pipeline, AsyncCommands, Conn};
|
||||||
use revolt_config::FeaturesLimits;
|
use revolt_config::FeaturesLimits;
|
||||||
@@ -148,10 +149,12 @@ pub async fn create_voice_state(
|
|||||||
channel_id: &str,
|
channel_id: &str,
|
||||||
server_id: Option<&str>,
|
server_id: Option<&str>,
|
||||||
user_id: &str,
|
user_id: &str,
|
||||||
|
joined_at: Timestamp,
|
||||||
) -> Result<UserVoiceState> {
|
) -> Result<UserVoiceState> {
|
||||||
let unique_key = format!("{}:{}", &user_id, server_id.unwrap_or(channel_id));
|
let unique_key = format!("{}:{}", &user_id, server_id.unwrap_or(channel_id));
|
||||||
|
|
||||||
let voice_state = UserVoiceState {
|
let voice_state = UserVoiceState {
|
||||||
|
joined_at,
|
||||||
id: user_id.to_string(),
|
id: user_id.to_string(),
|
||||||
is_receiving: true,
|
is_receiving: true,
|
||||||
is_publishing: false,
|
is_publishing: false,
|
||||||
@@ -163,6 +166,12 @@ pub async fn create_voice_state(
|
|||||||
.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(&unique_key, channel_id)
|
||||||
|
.set(
|
||||||
|
format!("joined_at:{unique_key}"),
|
||||||
|
joined_at
|
||||||
|
.duration_since(Timestamp::UNIX_EPOCH)
|
||||||
|
.whole_milliseconds() as i64,
|
||||||
|
)
|
||||||
.set(
|
.set(
|
||||||
format!("is_publishing:{unique_key}"),
|
format!("is_publishing:{unique_key}"),
|
||||||
voice_state.is_publishing,
|
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_members:{channel_id}"), user_id)
|
||||||
.srem(format!("vc:{user_id}"), channel_id)
|
.srem(format!("vc:{user_id}"), channel_id)
|
||||||
.del(&[
|
.del(&[
|
||||||
|
format!("joined_at:{unique_key}"),
|
||||||
format!("is_publishing:{unique_key}"),
|
format!("is_publishing:{unique_key}"),
|
||||||
format!("is_receiving:{unique_key}"),
|
format!("is_receiving:{unique_key}"),
|
||||||
format!("screensharing:{unique_key}"),
|
format!("screensharing:{unique_key}"),
|
||||||
@@ -205,6 +215,35 @@ pub async fn delete_voice_state(
|
|||||||
.to_internal_error()
|
.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(
|
pub async fn update_voice_state_tracks(
|
||||||
channel_id: &str,
|
channel_id: &str,
|
||||||
server_id: Option<&str>,
|
server_id: Option<&str>,
|
||||||
@@ -285,9 +324,10 @@ pub async fn get_voice_state(
|
|||||||
) -> Result<Option<UserVoiceState>> {
|
) -> Result<Option<UserVoiceState>> {
|
||||||
let unique_key = format!("{}:{}", user_id, server_id.unwrap_or(channel_id));
|
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?
|
.await?
|
||||||
.mget(&[
|
.mget(&[
|
||||||
|
format!("joined_at:{unique_key}"),
|
||||||
format!("is_publishing:{unique_key}"),
|
format!("is_publishing:{unique_key}"),
|
||||||
format!("is_receiving:{unique_key}"),
|
format!("is_receiving:{unique_key}"),
|
||||||
format!("screensharing:{unique_key}"),
|
format!("screensharing:{unique_key}"),
|
||||||
@@ -296,16 +336,29 @@ pub async fn get_voice_state(
|
|||||||
.await
|
.await
|
||||||
.to_internal_error()?;
|
.to_internal_error()?;
|
||||||
|
|
||||||
match (is_publishing, is_receiving, screensharing, camera) {
|
match (
|
||||||
(Some(is_publishing), Some(is_receiving), Some(screensharing), Some(camera)) => {
|
joined_at,
|
||||||
Ok(Some(v0::UserVoiceState {
|
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(),
|
id: user_id.to_string(),
|
||||||
is_receiving,
|
is_receiving,
|
||||||
is_publishing,
|
is_publishing,
|
||||||
screensharing,
|
screensharing,
|
||||||
camera,
|
camera,
|
||||||
}))
|
})),
|
||||||
}
|
|
||||||
_ => Ok(None),
|
_ => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
use crate::{models::{Channel, User}, Database};
|
use crate::{
|
||||||
|
models::{Channel, User},
|
||||||
|
Database,
|
||||||
|
};
|
||||||
use livekit_api::{
|
use livekit_api::{
|
||||||
access_token::{AccessToken, VideoGrants},
|
access_token::{AccessToken, VideoGrants},
|
||||||
services::room::{CreateRoomOptions, RoomClient as InnerRoomClient, UpdateParticipantOptions},
|
services::room::{CreateRoomOptions, RoomClient as InnerRoomClient, UpdateParticipantOptions},
|
||||||
@@ -76,7 +79,9 @@ impl VoiceClient {
|
|||||||
AccessToken::with_api_key(&room.node.key, &room.node.secret)
|
AccessToken::with_api_key(&room.node.key, &room.node.secret)
|
||||||
.with_name(&format!("{}#{}", user.username, user.discriminator))
|
.with_name(&format!("{}#{}", user.username, user.discriminator))
|
||||||
.with_identity(&user.id)
|
.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_ttl(Duration::from_secs(10))
|
||||||
.with_grants(VideoGrants {
|
.with_grants(VideoGrants {
|
||||||
room_join: true,
|
room_join: true,
|
||||||
@@ -139,4 +144,13 @@ impl VoiceClient {
|
|||||||
.await
|
.await
|
||||||
.to_internal_error()
|
.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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ auto_derived!(
|
|||||||
Description,
|
Description,
|
||||||
Icon,
|
Icon,
|
||||||
DefaultPermissions,
|
DefaultPermissions,
|
||||||
|
Voice,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// New webhook information
|
/// New webhook information
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
@@ -283,6 +284,7 @@ auto_derived_partial!(
|
|||||||
/// Voice State information for a user
|
/// Voice State information for a user
|
||||||
pub struct UserVoiceState {
|
pub struct UserVoiceState {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
pub joined_at: Timestamp,
|
||||||
pub is_receiving: bool,
|
pub is_receiving: bool,
|
||||||
pub is_publishing: bool,
|
pub is_publishing: bool,
|
||||||
pub screensharing: bool,
|
pub screensharing: bool,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use livekit_api::{access_token::TokenVerifier, webhooks::WebhookReceiver};
|
|||||||
use livekit_protocol::TrackType;
|
use livekit_protocol::TrackType;
|
||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
events::client::EventV1,
|
events::client::EventV1,
|
||||||
iso8601_timestamp::Timestamp,
|
iso8601_timestamp::{Duration, Timestamp},
|
||||||
util::reference::Reference,
|
util::reference::Reference,
|
||||||
voice::{
|
voice::{
|
||||||
create_voice_state, delete_voice_state, get_call_notification_recipients,
|
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 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.
|
// 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? {
|
if let Some(moved_from) = get_user_moved_to_voice(channel_id, user_id).await? {
|
||||||
@@ -89,15 +94,8 @@ pub async fn ingress(
|
|||||||
if event.room.as_ref().unwrap().num_participants == 1 {
|
if event.room.as_ref().unwrap().num_participants == 1 {
|
||||||
let user = Reference::from_unchecked(user_id).as_user(db).await?;
|
let user = Reference::from_unchecked(user_id).as_user(db).await?;
|
||||||
|
|
||||||
let started_at = Timestamp::now_utc();
|
let message_id =
|
||||||
let message_id = Ulid::from_datetime(
|
Ulid::from_datetime(DateTime::from_timestamp_secs(event.created_at).unwrap())
|
||||||
DateTime::from_timestamp_millis(
|
|
||||||
started_at
|
|
||||||
.duration_since(Timestamp::UNIX_EPOCH)
|
|
||||||
.whole_milliseconds() as i64,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let mut call_started_message = SystemMessage::CallStarted {
|
let mut call_started_message = SystemMessage::CallStarted {
|
||||||
@@ -127,7 +125,7 @@ pub async fn ingress(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let recipients = get_call_notification_recipients(&channel_id, &user_id).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
|
if let Err(e) = amqp
|
||||||
.dm_call_updated(&user.id, channel.id(), Some(&now), false, recipients)
|
.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()) {
|
if members.is_none_or(|m| m.is_empty()) {
|
||||||
// The channel is empty so send out an "end" message for ringing
|
// The channel is empty so send out an "end" message for ringing
|
||||||
if let Err(e) = amqp
|
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
|
.await
|
||||||
{
|
{
|
||||||
revolt_config::capture_internal_error!(&e);
|
revolt_config::capture_internal_error!(&e);
|
||||||
|
|||||||
@@ -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 revolt_result::{create_error, Result};
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
use rocket_empty::EmptyResponse;
|
use rocket_empty::EmptyResponse;
|
||||||
@@ -10,6 +10,7 @@ use rocket_empty::EmptyResponse;
|
|||||||
#[delete("/<target>")]
|
#[delete("/<target>")]
|
||||||
pub async fn delete_bot(
|
pub async fn delete_bot(
|
||||||
db: &State<Database>,
|
db: &State<Database>,
|
||||||
|
voice_client: &State<VoiceClient>,
|
||||||
user: User,
|
user: User,
|
||||||
target: Reference<'_>,
|
target: Reference<'_>,
|
||||||
) -> Result<EmptyResponse> {
|
) -> Result<EmptyResponse> {
|
||||||
@@ -18,7 +19,18 @@ pub async fn delete_bot(
|
|||||||
return Err(create_error!(NotFound));
|
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)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
voice::{
|
voice::{
|
||||||
delete_voice_state, get_channel_node, get_voice_channel_members, is_in_voice_channel,
|
delete_channel_voice_state, delete_voice_state, get_channel_node, get_voice_channel_members, is_in_voice_channel, VoiceClient
|
||||||
VoiceClient,
|
|
||||||
},
|
},
|
||||||
Channel, Database, PartialChannel, User, AMQP,
|
Channel, Database, PartialChannel, User, AMQP,
|
||||||
};
|
};
|
||||||
@@ -63,6 +62,7 @@ pub async fn delete(
|
|||||||
voice_client
|
voice_client
|
||||||
.remove_user(&node, &user.id, channel.id())
|
.remove_user(&node, &user.id, channel.id())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
delete_voice_state(channel.id(), None, &user.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? {
|
if let Some(users) = get_voice_channel_members(channel.id()).await? {
|
||||||
let node = get_channel_node(channel.id()).await?.unwrap();
|
let node = get_channel_node(channel.id()).await?.unwrap();
|
||||||
|
|
||||||
for user in users {
|
voice_client.delete_room(&node, channel.id()).await?;
|
||||||
voice_client.remove_user(&node, &user, channel.id()).await?;
|
|
||||||
delete_voice_state(channel.id(), channel.server(), &user).await?;
|
delete_channel_voice_state(channel.id(), channel.server(), &users).await?;
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
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
|
||||||
Channel, Database, File, PartialChannel, SystemMessage, User, AMQP,
|
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
|
||||||
@@ -15,6 +14,7 @@ use validator::Validate;
|
|||||||
#[patch("/<target>", data = "<data>")]
|
#[patch("/<target>", data = "<data>")]
|
||||||
pub async fn edit(
|
pub async fn edit(
|
||||||
db: &State<Database>,
|
db: &State<Database>,
|
||||||
|
voice_client: &State<VoiceClient>,
|
||||||
amqp: &State<AMQP>,
|
amqp: &State<AMQP>,
|
||||||
user: User,
|
user: User,
|
||||||
target: Reference<'_>,
|
target: Reference<'_>,
|
||||||
@@ -214,6 +214,9 @@ pub async fn edit(
|
|||||||
v0::FieldsChannel::Icon => {
|
v0::FieldsChannel::Icon => {
|
||||||
icon.take();
|
icon.take();
|
||||||
}
|
}
|
||||||
|
v0::FieldsChannel::Voice => {
|
||||||
|
voice.take();
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,5 +260,15 @@ pub async fn edit(
|
|||||||
)
|
)
|
||||||
.await?;
|
.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()))
|
Ok(Json(channel.into()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ pub async fn call(
|
|||||||
voice_client
|
voice_client
|
||||||
.remove_user(&node, &user.id, &channel_id)
|
.remove_user(&node, &user.id, &channel_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
delete_voice_state(&channel_id, channel.server(), &user.id).await?;
|
delete_voice_state(&channel_id, channel.server(), &user.id).await?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -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 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? {
|
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();
|
let node = get_channel_node(&channel_id).await?.unwrap();
|
||||||
|
|
||||||
voice_client.remove_user(&node, &user.id, &channel_id).await?;
|
voice_client.remove_user(&node, &user.id, &channel_id).await?;
|
||||||
|
|
||||||
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?
|
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,15 +42,17 @@ pub async fn kick(
|
|||||||
return Err(create_error!(NotElevated));
|
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? {
|
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();
|
let node = get_channel_node(&channel_id).await?.unwrap();
|
||||||
|
|
||||||
voice_client.remove_user(&node, &user.id, &channel_id).await?;
|
voice_client.remove_user(&node, &user.id, &channel_id).await?;
|
||||||
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
member
|
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?;
|
||||||
.remove(db, &server, RemovalIntention::Kick, false)
|
};
|
||||||
.await
|
|
||||||
.map(|_| EmptyResponse)
|
Ok(EmptyResponse)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use revolt_database::{
|
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_models::v0;
|
||||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission, Override};
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission, Override};
|
||||||
@@ -22,8 +24,13 @@ pub async fn set_role_permission(
|
|||||||
let data = data.into_inner();
|
let data = data.into_inner();
|
||||||
|
|
||||||
let mut server = target.as_server(db).await?;
|
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 (current_value, rank) = server
|
||||||
|
.roles
|
||||||
|
.get(&role_id)
|
||||||
|
.map(|x| (x.permissions, x.rank))
|
||||||
|
.ok_or_else(|| create_error!(NotFound))?;
|
||||||
|
|
||||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||||
let permissions = calculate_server_permissions(&mut query).await;
|
let permissions = calculate_server_permissions(&mut query).await;
|
||||||
|
|
||||||
@@ -40,18 +47,15 @@ pub async fn set_role_permission(
|
|||||||
.throw_permission_override(current_value, &data.permissions)
|
.throw_permission_override(current_value, &data.permissions)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
server
|
||||||
|
.set_role_permission(db, &role_id, data.permissions.into())
|
||||||
|
.await?;
|
||||||
|
|
||||||
for channel_id in &server.channels {
|
for channel_id in &server.channels {
|
||||||
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
|
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
|
||||||
|
|
||||||
sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).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()))
|
Ok(Json(server.into()))
|
||||||
} else {
|
|
||||||
Err(create_error!(NotFound))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,6 @@ pub async fn set_default_server_permissions(
|
|||||||
)
|
)
|
||||||
.await?;
|
.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
|
server
|
||||||
.update(
|
.update(
|
||||||
db,
|
db,
|
||||||
@@ -56,5 +50,11 @@ pub async fn set_default_server_permissions(
|
|||||||
)
|
)
|
||||||
.await?;
|
.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()))
|
Ok(Json(server.into()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||||
voice::{sync_voice_permissions, VoiceClient},
|
voice::{sync_voice_permissions, VoiceClient},
|
||||||
Database, User
|
Database, User,
|
||||||
};
|
};
|
||||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
use revolt_result::{create_error, Result};
|
use revolt_result::{create_error, Result};
|
||||||
@@ -18,7 +18,7 @@ pub async fn delete(
|
|||||||
user: User,
|
user: User,
|
||||||
target: Reference<'_>,
|
target: Reference<'_>,
|
||||||
role_id: String,
|
role_id: String,
|
||||||
voice_client: &State<VoiceClient>
|
voice_client: &State<VoiceClient>,
|
||||||
) -> Result<EmptyResponse> {
|
) -> Result<EmptyResponse> {
|
||||||
let mut server = target.as_server(db).await?;
|
let mut server = target.as_server(db).await?;
|
||||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
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);
|
let member_rank = query.get_member_rank().unwrap_or(i64::MIN);
|
||||||
|
|
||||||
if let Some(role) = server.roles.remove(&role_id) {
|
let role = server
|
||||||
|
.roles
|
||||||
|
.remove(&role_id)
|
||||||
|
.ok_or_else(|| create_error!(NotFound))?;
|
||||||
|
|
||||||
if role.rank <= member_rank {
|
if role.rank <= member_rank {
|
||||||
return Err(create_error!(NotElevated));
|
return Err(create_error!(NotElevated));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
role.delete(db, &server.id, &role_id).await?;
|
||||||
|
|
||||||
for channel_id in &server.channels {
|
for channel_id in &server.channels {
|
||||||
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
|
let channel = Reference::from_unchecked(channel_id).as_channel(db).await?;
|
||||||
|
|
||||||
sync_voice_permissions(db, voice_client, &channel, Some(&server), Some(&role_id)).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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(EmptyResponse)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Database, User
|
||||||
Database, User,
|
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||||
@@ -14,6 +13,7 @@ use rocket::{serde::json::Json, State};
|
|||||||
#[patch("/<target>/roles/ranks", data = "<data>")]
|
#[patch("/<target>/roles/ranks", data = "<data>")]
|
||||||
pub async fn edit_role_ranks(
|
pub async fn edit_role_ranks(
|
||||||
db: &State<Database>,
|
db: &State<Database>,
|
||||||
|
voice_client: &State<VoiceClient>,
|
||||||
user: User,
|
user: User,
|
||||||
target: Reference<'_>,
|
target: Reference<'_>,
|
||||||
data: Json<v0::DataEditRoleRanks>,
|
data: Json<v0::DataEditRoleRanks>,
|
||||||
@@ -70,6 +70,12 @@ pub async fn edit_role_ranks(
|
|||||||
|
|
||||||
server.set_role_ordering(db, new_order).await?;
|
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()))
|
Ok(Json(server.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use revolt_database::{
|
use revolt_database::{
|
||||||
util::reference::Reference,
|
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,
|
Database, RemovalIntention, User,
|
||||||
};
|
};
|
||||||
use revolt_models::v0;
|
use revolt_models::v0;
|
||||||
@@ -29,11 +29,10 @@ pub async fn delete(
|
|||||||
if let Some(users) = get_voice_channel_members(channel_id).await? {
|
if let Some(users) = get_voice_channel_members(channel_id).await? {
|
||||||
let node = get_channel_node(channel_id).await?.unwrap();
|
let node = get_channel_node(channel_id).await?.unwrap();
|
||||||
|
|
||||||
for user in users {
|
voice_client.delete_room(&node, channel_id).await?;
|
||||||
voice_client.remove_user(&node, &user, channel_id).await?;
|
|
||||||
delete_voice_state(channel_id, Some(&server.id), &user).await?;
|
delete_channel_voice_state(channel_id, Some(&server.id), &users).await?;
|
||||||
}
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
server.delete(db).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 let Some(channel_id) = get_user_voice_channel_in_server(&user.id, &server.id).await? {
|
||||||
if server.channels.iter().any(|c| c == &channel_id) {
|
if server.channels.iter().any(|c| c == &channel_id) {
|
||||||
let node = get_channel_node(&channel_id).await?.unwrap();
|
let node = get_channel_node(&channel_id).await?.unwrap();
|
||||||
|
|
||||||
voice_client.remove_user(&node, &user.id, &channel_id).await?;
|
voice_client.remove_user(&node, &user.id, &channel_id).await?;
|
||||||
|
|
||||||
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?;
|
delete_voice_state(&channel_id, Some(&server.id), &user.id).await?;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user