fix: remove VoiceChannel channel type

fix: calculate user overwrites correctly
fix: dont include personal info in livekit user metadata
fix: revoke video permissons on denied publish
fix: add video to default permissions
This commit is contained in:
Zomatree
2025-08-19 17:37:09 +01:00
parent c6f121f585
commit 0a4d77ffd3
27 changed files with 172 additions and 282 deletions

View File

@@ -4,7 +4,7 @@ use futures::future::join_all;
use revolt_database::{ use revolt_database::{
events::client::{EventV1, ReadyPayloadFields}, events::client::{EventV1, ReadyPayloadFields},
util::permissions::DatabasePermissionQuery, util::permissions::DatabasePermissionQuery,
voice::{delete_voice_state, get_voice_channel_members, get_voice_state, get_channel_voice_state}, voice::get_channel_voice_state,
Channel, Database, Member, MemberCompositeKey, Presence, RelationshipStatus, Channel, Database, Member, MemberCompositeKey, Presence, RelationshipStatus,
}; };
use revolt_models::v0; use revolt_models::v0;
@@ -21,7 +21,7 @@ impl Cache {
pub async fn can_view_channel(&self, db: &Database, channel: &Channel) -> bool { pub async fn can_view_channel(&self, db: &Database, channel: &Channel) -> bool {
#[allow(deprecated)] #[allow(deprecated)]
match &channel { match &channel {
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => { Channel::TextChannel { server, .. } => {
let member = self.members.get(server); let member = self.members.get(server);
let server = self.servers.get(server); let server = self.servers.get(server);
let mut query = let mut query =
@@ -298,20 +298,14 @@ impl State {
let id = &id.to_string(); let id = &id.to_string();
for (channel_id, channel) in &self.cache.channels { for (channel_id, channel) in &self.cache.channels {
#[allow(deprecated)] if channel.server() == Some(id) {
match channel { channel_ids.insert(channel_id.clone());
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
if server == id {
channel_ids.insert(channel_id.clone());
if self.cache.can_view_channel(db, channel).await { if self.cache.can_view_channel(db, channel).await {
added_channels.push(channel_id.clone()); added_channels.push(channel_id.clone());
} else { } else {
removed_channels.push(channel_id.clone()); removed_channels.push(channel_id.clone());
}
}
} }
_ => {}
} }
} }

View File

@@ -9,14 +9,13 @@ use crate::{
bson::{doc, from_bson, from_document, to_document, Bson, DateTime, Document}, bson::{doc, from_bson, from_document, to_document, Bson, DateTime, Document},
options::FindOptions, options::FindOptions,
}, },
AbstractChannels, AbstractServers, Channel, Invite, MongoDb, User, DISCRIMINATOR_SEARCH_SPACE, AbstractServers, Invite, MongoDb, User, DISCRIMINATOR_SEARCH_SPACE,
}; };
use bson::{oid::ObjectId, to_bson}; use bson::{oid::ObjectId, to_bson};
use futures::StreamExt; use futures::StreamExt;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use revolt_permissions::DEFAULT_WEBHOOK_PERMISSIONS; use revolt_permissions::{ChannelPermission, DEFAULT_WEBHOOK_PERMISSIONS};
use revolt_result::{Error, ErrorType};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
@@ -1081,6 +1080,14 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
channel_id: String, channel_id: String,
} }
#[allow(clippy::enum_variant_names)]
#[derive(serde::Serialize, serde::Deserialize)]
enum Channel {
Group { owner: String },
TextChannel { server: String },
VoiceChannel { server: String }
}
let webhooks = db let webhooks = db
.db() .db()
.collection::<WebhookShell>("channel_webhooks") .collection::<WebhookShell>("channel_webhooks")
@@ -1092,9 +1099,8 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
.await; .await;
for webhook in webhooks { for webhook in webhooks {
#[allow(deprecated)] match db.col::<Channel>("channels").find_one(doc! { "_id": &webhook.channel_id }).await.unwrap() {
match db.fetch_channel(&webhook.channel_id).await { Some(channel) => {
Ok(channel) => {
let creator_id = match channel { let creator_id = match channel {
Channel::Group { owner, .. } => owner, Channel::Group { owner, .. } => owner,
Channel::TextChannel { server, .. } Channel::TextChannel { server, .. }
@@ -1102,7 +1108,6 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
let server = db.fetch_server(&server).await.expect("server"); let server = db.fetch_server(&server).await.expect("server");
server.owner server.owner
} }
_ => unreachable!("not server or group channel!"),
}; };
db.db() db.db()
@@ -1120,17 +1125,13 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
.await .await
.expect("update webhook"); .expect("update webhook");
} }
Err(Error { None => {
error_type: ErrorType::NotFound,
..
}) => {
db.db() db.db()
.collection::<WebhookShell>("channel_webhooks") .collection::<WebhookShell>("channel_webhooks")
.delete_one(doc! { "_id": webhook._id }) .delete_one(doc! { "_id": webhook._id })
.await .await
.expect("failed to delete invalid webhook"); .expect("failed to delete invalid webhook");
} }
Err(err) => panic!("{err:?}"),
} }
} }
} }
@@ -1171,40 +1172,6 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
.expect("failed to update users"); .expect("failed to update users");
} }
if revision <= 41 {
info!("Running migration [revision 32 / 26-01-2025]: Add `is_publishing` and `is_receiving` to members");
db.col::<Document>("server_members")
.update_many(
doc! {},
doc! {
"$set": {
"is_publishing": true,
"is_receiving": true
}
}
)
.await
.expect("Failed to update members");
}
if revision <= 42 {
info!("Running migration [revision 33 / 29-04-2025]: Convert all `VoiceChannel`'s into `TextChannel` ");
db.col::<Document>("channels")
.update_many(
doc! { "channel_type": "VoiceChannel" },
doc! {
"$set": {
"channel_type": "TextChannel",
"voice": {}
}
}
)
.await
.expect("Failed to update voice channels");
};
if revision <= 43 { if revision <= 43 {
info!( info!(
"Running migration [revision 43 / 05-06-2025]: convert role ranks to uniform numbers." "Running migration [revision 43 / 05-06-2025]: convert role ranks to uniform numbers."
@@ -1262,6 +1229,41 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
} }
} }
if revision <= 46 {
info!("Running migration [revision 46 / 29-04-2025]: Convert all `VoiceChannel`'s into `TextChannel`");
db.col::<Document>("channels")
.update_many(
doc! { "channel_type": "VoiceChannel" },
doc! {
"$set": {
"channel_type": "TextChannel",
"voice": {}
}
}
)
.await
.expect("Failed to update voice channels");
};
if revision <= 47 {
info!("Running migration [revision 47 / 29-04-2025]: Add Video to default permissions");
db.col::<Document>("servers")
.update_many(
doc! { },
doc! {
"$bit": {
"default_permissions": {
"or": ChannelPermission::Video as i64
},
}
}
)
.await
.expect("Failed to update voice channels");
};
// Reminder to update LATEST_REVISION when adding new migrations. // Reminder to update LATEST_REVISION when adding new migrations.
LATEST_REVISION.max(revision) LATEST_REVISION.max(revision)
} }

View File

@@ -9,8 +9,7 @@ use serde::{Deserialize, Serialize};
use ulid::Ulid; use ulid::Ulid;
use crate::{ use crate::{
events::client::EventV1, Database, File, PartialServer, events::client::EventV1, Database, File, PartialServer, Server, SystemMessage, User, AMQP,
Server, SystemMessage, User, AMQP,
}; };
#[cfg(feature = "mongodb")] #[cfg(feature = "mongodb")]
@@ -112,37 +111,6 @@ auto_derived!(
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
voice: Option<VoiceInformation>, voice: Option<VoiceInformation>,
}, },
#[deprecated = "Use TextChannel { voice } instead"]
VoiceChannel {
/// Unique Id
#[serde(rename = "_id")]
id: String,
/// Id of the server this channel belongs to
server: String,
/// Display name of the channel
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
/// Channel description
description: Option<String>,
/// Custom icon attachment
#[serde(skip_serializing_if = "Option::is_none")]
icon: Option<File>,
/// Default permissions assigned to users in this channel
#[serde(skip_serializing_if = "Option::is_none")]
default_permissions: Option<OverrideField>,
/// Permissions assigned based on role to this channel
#[serde(
default = "HashMap::<String, OverrideField>::new",
skip_serializing_if = "HashMap::<String, OverrideField>::is_empty"
)]
role_permissions: HashMap<String, OverrideField>,
/// Whether this channel is marked as not safe for work
#[serde(skip_serializing_if = "crate::if_false", default)]
nsfw: bool,
},
} }
#[derive(Default)] #[derive(Default)]
@@ -449,15 +417,14 @@ impl Channel {
Channel::DirectMessage { id, .. } Channel::DirectMessage { id, .. }
| Channel::Group { id, .. } | Channel::Group { id, .. }
| Channel::SavedMessages { id, .. } | Channel::SavedMessages { id, .. }
| Channel::TextChannel { id, .. } | Channel::TextChannel { id, .. } => id,
| Channel::VoiceChannel { id, .. } => id,
} }
} }
/// 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::VoiceChannel { server, .. } => { Channel::TextChannel { server, .. } => {
Some(server) Some(server)
} }
_ => None, _ => None,
@@ -467,7 +434,7 @@ impl Channel {
/// Gets this channel's voice information /// Gets this channel's voice information
pub fn voice(&self) -> Option<Cow<VoiceInformation>> { pub fn voice(&self) -> Option<Cow<VoiceInformation>> {
match self { match self {
Self::DirectMessage { .. } | Channel::VoiceChannel { .. } => { Self::DirectMessage { .. } | Self::Group { .. } => {
Some(Cow::Owned(VoiceInformation::default())) Some(Cow::Owned(VoiceInformation::default()))
} }
Self::TextChannel { Self::TextChannel {
@@ -659,7 +626,6 @@ impl Channel {
voice.replace(v); voice.replace(v);
} }
} }
Self::VoiceChannel { .. } => {}
} }
} }

View File

@@ -115,7 +115,10 @@ auto_derived!(
#[serde(rename = "message_unpinned")] #[serde(rename = "message_unpinned")]
MessageUnpinned { id: String, by: String }, MessageUnpinned { id: String, by: String },
#[serde(rename = "call_started")] #[serde(rename = "call_started")]
CallStarted { by: String, finished_at: Option<Timestamp> }, CallStarted {
by: String,
finished_at: Option<Timestamp>,
},
} }
/// Name and / or avatar override information /// Name and / or avatar override information
@@ -328,9 +331,7 @@ impl Message {
} }
let server_id = match channel { let server_id = match channel {
Channel::TextChannel { ref server, .. } | Channel::VoiceChannel { ref server, .. } => { Channel::TextChannel { ref server, .. } => Some(server.clone()),
Some(server.clone())
}
_ => None, _ => None,
}; };
@@ -488,8 +489,7 @@ impl Message {
user_mentions.retain(|m| recipients_hash.contains(m)); user_mentions.retain(|m| recipients_hash.contains(m));
role_mentions.clear(); role_mentions.clear();
} }
Channel::TextChannel { ref server, .. } Channel::TextChannel { ref server, .. }=> {
| Channel::VoiceChannel { ref server, .. } => {
let mentions_vec = Vec::from_iter(user_mentions.iter().cloned()); let mentions_vec = Vec::from_iter(user_mentions.iter().cloned());
let valid_members = db.fetch_members(server.as_str(), &mentions_vec[..]).await; let valid_members = db.fetch_members(server.as_str(), &mentions_vec[..]).await;
@@ -681,7 +681,6 @@ impl Message {
) )
.await?; .await?;
if !self.has_suppressed_notifications() if !self.has_suppressed_notifications()
&& (self.mentions.is_some() || self.contains_mass_push_mention()) && (self.mentions.is_some() || self.contains_mass_push_mention())
{ {
@@ -797,7 +796,7 @@ impl Message {
query: MessageQuery, query: MessageQuery,
perspective: &User, perspective: &User,
include_users: Option<bool>, include_users: Option<bool>,
server_id: Option<String>, server_id: Option<&str>,
) -> Result<BulkMessageResponse> { ) -> Result<BulkMessageResponse> {
let messages: Vec<v0::Message> = db let messages: Vec<v0::Message> = db
.fetch_messages(query) .fetch_messages(query)
@@ -840,9 +839,7 @@ impl Message {
v0::SystemMessage::MessageUnpinned { by, .. } => { v0::SystemMessage::MessageUnpinned { by, .. } => {
users.push(by.clone()); users.push(by.clone());
} }
v0::SystemMessage::CallStarted { by, .. } => { v0::SystemMessage::CallStarted { by, .. } => users.push(by.clone()),
users.push(by.clone())
}
} }
} }
users users
@@ -857,7 +854,7 @@ impl Message {
users, users,
members: if let Some(server_id) = server_id { members: if let Some(server_id) = server_id {
Some( Some(
db.fetch_members(&server_id, &user_ids) db.fetch_members(server_id, &user_ids)
.await? .await?
.into_iter() .into_iter()
.map(Into::into) .map(Into::into)

View File

@@ -4,10 +4,18 @@ use revolt_result::{create_error, Result};
use crate::voice::get_channel_voice_state; use crate::voice::get_channel_voice_state;
use crate::{ use crate::{
events::client::EventV1, if_false, util::permissions::DatabasePermissionQuery, Channel, events::client::EventV1, util::permissions::DatabasePermissionQuery, Channel,
Database, File, Server, SystemMessage, User, Database, File, Server, SystemMessage, User,
}; };
fn default_true() -> bool {
true
}
fn is_true(x: &bool) -> bool {
*x
}
auto_derived_partial!( auto_derived_partial!(
/// Server Member /// Server Member
pub struct Member { pub struct Member {
@@ -33,11 +41,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 = "Option::is_none")] #[serde(skip_serializing_if = "is_true", default = "default_true")]
pub can_publish: Option<bool>, pub can_publish: bool,
/// Whether the member is server-wide voice deafened /// Whether the member is server-wide voice deafened
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "is_true", default = "default_true")]
pub can_receive: Option<bool>, pub can_receive: bool,
}, },
"PartialMember" "PartialMember"
); );
@@ -79,8 +87,8 @@ impl Default for Member {
avatar: None, avatar: None,
roles: vec![], roles: vec![],
timeout: None, timeout: None,
can_publish: None, can_publish: true,
can_receive: None, can_receive: true,
} }
} }
} }
@@ -211,8 +219,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::CanReceive => self.can_receive = true,
FieldsMember::CanPublish => self.can_publish = None, FieldsMember::CanPublish => self.can_publish = true,
} }
} }

View File

@@ -14,7 +14,7 @@ use validator::HasLen;
use revolt_result::Result; use revolt_result::Result;
use super::DelayedTask; use super::DelayedTask;
use crate::Channel::{TextChannel, VoiceChannel}; use crate::Channel::TextChannel;
/// Enumeration of possible events /// Enumeration of possible events
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
@@ -191,17 +191,14 @@ pub async fn handle_ack_event(
.await .await
.expect("Failed to fetch channel from db"); .expect("Failed to fetch channel from db");
match channel { if let TextChannel { server, .. } = channel {
TextChannel { server, .. } | VoiceChannel { server, .. } => { if let Err(err) =
if let Err(err) = amqp.mass_mention_message_sent(server, mass_mentions).await
amqp.mass_mention_message_sent(server, mass_mentions).await {
{ revolt_config::capture_error(&err);
revolt_config::capture_error(&err);
}
}
_ => {
panic!("Unknown channel type when sending mass mention event");
} }
} else {
panic!("Unknown channel type when sending mass mention event");
} }
} }
} }

View File

@@ -202,25 +202,6 @@ impl From<crate::Channel> for Channel {
nsfw, nsfw,
voice: voice.map(|voice| voice.into()), voice: voice.map(|voice| voice.into()),
}, },
crate::Channel::VoiceChannel {
id,
server,
name,
description,
icon,
default_permissions,
role_permissions,
nsfw,
} => Channel::VoiceChannel {
id,
server,
name,
description,
icon: icon.map(|file| file.into()),
default_permissions,
role_permissions,
nsfw,
},
} }
} }
} }
@@ -285,25 +266,6 @@ impl From<Channel> for crate::Channel {
nsfw, nsfw,
voice: voice.map(|voice| voice.into()), voice: voice.map(|voice| voice.into()),
}, },
Channel::VoiceChannel {
id,
server,
name,
description,
icon,
default_permissions,
role_permissions,
nsfw,
} => crate::Channel::VoiceChannel {
id,
server,
name,
description,
icon: icon.map(|file| file.into()),
default_permissions,
role_permissions,
nsfw,
},
} }
} }
} }

View File

@@ -159,9 +159,7 @@ impl<'z> BulkDatabasePermissionQuery<'z> {
Channel::DirectMessage { .. } => ChannelType::DirectMessage, Channel::DirectMessage { .. } => ChannelType::DirectMessage,
Channel::Group { .. } => ChannelType::Group, Channel::Group { .. } => ChannelType::Group,
Channel::SavedMessages { .. } => ChannelType::SavedMessages, Channel::SavedMessages { .. } => ChannelType::SavedMessages,
Channel::TextChannel { .. } | Channel::VoiceChannel { .. } => { Channel::TextChannel { .. } => ChannelType::ServerChannel,
ChannelType::ServerChannel
}
} }
} else { } else {
ChannelType::Unknown ChannelType::Unknown

View File

@@ -187,17 +187,17 @@ impl PermissionQuery for DatabasePermissionQuery<'_> {
async fn do_we_have_publish_overwrites(&mut self) -> bool { async fn do_we_have_publish_overwrites(&mut self) -> bool {
if let Some(member) = &self.member { if let Some(member) = &self.member {
member.can_publish.unwrap_or(false) member.can_publish
} else { } else {
false true
} }
} }
async fn do_we_have_receive_overwrites(&mut self) -> bool { async fn do_we_have_receive_overwrites(&mut self) -> bool {
if let Some(member) = &self.member { if let Some(member) = &self.member {
member.can_receive.unwrap_or(false) member.can_receive
} else { } else {
false true
} }
} }
@@ -216,9 +216,7 @@ impl PermissionQuery for DatabasePermissionQuery<'_> {
Cow::Borrowed(Channel::SavedMessages { .. }) Cow::Borrowed(Channel::SavedMessages { .. })
| Cow::Owned(Channel::SavedMessages { .. }) => ChannelType::SavedMessages, | Cow::Owned(Channel::SavedMessages { .. }) => ChannelType::SavedMessages,
Cow::Borrowed(Channel::TextChannel { .. }) Cow::Borrowed(Channel::TextChannel { .. })
| Cow::Owned(Channel::TextChannel { .. }) | Cow::Owned(Channel::TextChannel { .. }) => ChannelType::ServerChannel,
| Cow::Borrowed(Channel::VoiceChannel { .. })
| Cow::Owned(Channel::VoiceChannel { .. }) => ChannelType::ServerChannel,
} }
} else { } else {
ChannelType::Unknown ChannelType::Unknown
@@ -349,9 +347,7 @@ impl PermissionQuery for DatabasePermissionQuery<'_> {
#[allow(deprecated)] #[allow(deprecated)]
match channel { match channel {
Cow::Borrowed(Channel::TextChannel { server, .. }) Cow::Borrowed(Channel::TextChannel { server, .. })
| Cow::Owned(Channel::TextChannel { server, .. }) | Cow::Owned(Channel::TextChannel { server, .. }) => {
| Cow::Borrowed(Channel::VoiceChannel { server, .. })
| Cow::Owned(Channel::VoiceChannel { server, .. }) => {
if let Some(known_server) = if let Some(known_server) =
// I'm not sure why I can't just pattern match both at once here? // I'm not sure why I can't just pattern match both at once here?
// It throws some weird error and the provided fix doesn't work :/ // It throws some weird error and the provided fix doesn't work :/

View File

@@ -129,14 +129,19 @@ pub async fn get_user_voice_channel_in_server(
.to_internal_error() .to_internal_error()
} }
pub fn get_allowed_sources(limits: &FeaturesLimits, permissions: PermissionValue) -> Vec<&'static str> { pub fn get_allowed_sources(
limits: &FeaturesLimits,
permissions: PermissionValue,
) -> Vec<&'static str> {
let mut allowed_sources = Vec::new(); let mut allowed_sources = Vec::new();
if permissions.has(ChannelPermission::Speak as u64) { if permissions.has(ChannelPermission::Speak as u64) {
println!("can speak");
allowed_sources.push("microphone") allowed_sources.push("microphone")
}; };
if permissions.has(ChannelPermission::Video as u64) && limits.video { if permissions.has(ChannelPermission::Video as u64) && limits.video {
println!("can video");
allowed_sources.extend(["camera", "screen_share", "screen_share_audio"]); allowed_sources.extend(["camera", "screen_share", "screen_share_audio"]);
}; };
@@ -362,9 +367,7 @@ pub async fn sync_voice_permissions(
.iter() .iter()
.flatten() .flatten()
{ {
let user = Reference::from_unchecked(&user_id) let user = Reference::from_unchecked(user_id).as_user(db).await?;
.as_user(db)
.await?;
sync_user_voice_permissions(db, voice_client, &node, &user, channel, server, role_id) sync_user_voice_permissions(db, voice_client, &node, &user, channel, server, role_id)
.await?; .await?;

View File

@@ -1,4 +1,4 @@
use crate::models::{Channel, User}; 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},
@@ -63,6 +63,7 @@ impl VoiceClient {
pub async fn create_token( pub async fn create_token(
&self, &self,
node: &str, node: &str,
db: &Database,
user: &User, user: &User,
permissions: PermissionValue, permissions: PermissionValue,
channel: &Channel, channel: &Channel,
@@ -75,11 +76,12 @@ 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).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,
can_publish: true, can_publish: true,
can_publish_data: false,
can_publish_sources: allowed_sources can_publish_sources: allowed_sources
.into_iter() .into_iter()
.map(ToString::to_string) .map(ToString::to_string)

View File

@@ -113,43 +113,6 @@ auto_derived!(
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
voice: Option<VoiceInformation>, voice: Option<VoiceInformation>,
}, },
#[deprecated = "Use TextChannel { voice } instead"]
VoiceChannel {
/// Unique Id
#[cfg_attr(feature = "serde", serde(rename = "_id"))]
id: String,
/// Id of the server this channel belongs to
server: String,
/// Display name of the channel
name: String,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
/// Channel description
description: Option<String>,
/// Custom icon attachment
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
icon: Option<File>,
/// Default permissions assigned to users in this channel
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
default_permissions: Option<OverrideField>,
/// Permissions assigned based on role to this channel
#[cfg_attr(
feature = "serde",
serde(
default = "HashMap::<String, OverrideField>::new",
skip_serializing_if = "HashMap::<String, OverrideField>::is_empty"
)
)]
role_permissions: HashMap<String, OverrideField>,
/// Whether this channel is marked as not safe for work
#[cfg_attr(
feature = "serde",
serde(skip_serializing_if = "crate::if_false", default)
)]
nsfw: bool,
},
} }
/// Voice information for a channel /// Voice information for a channel
@@ -294,7 +257,7 @@ auto_derived!(
permissions: u64, permissions: u64,
}, },
Field { Field {
/// Allow / deny values to set for members in this `TextChannel` or `VoiceChannel` /// Allow / deny values to set for members in this server channel
permissions: Override, permissions: Override,
}, },
} }
@@ -345,8 +308,7 @@ impl Channel {
Channel::DirectMessage { id, .. } Channel::DirectMessage { id, .. }
| Channel::Group { id, .. } | Channel::Group { id, .. }
| Channel::SavedMessages { id, .. } | Channel::SavedMessages { id, .. }
| Channel::TextChannel { id, .. } | Channel::TextChannel { id, .. } => id,
| Channel::VoiceChannel { id, .. } => id,
} }
} }
@@ -358,9 +320,7 @@ impl Channel {
match self { match self {
Channel::DirectMessage { .. } => None, Channel::DirectMessage { .. } => None,
Channel::SavedMessages { .. } => Some("Saved Messages"), Channel::SavedMessages { .. } => Some("Saved Messages"),
Channel::TextChannel { name, .. } Channel::TextChannel { name, .. } | Channel::Group { name, .. } => Some(name),
| Channel::Group { name, .. }
| Channel::VoiceChannel { name, .. } => Some(name),
} }
} }
} }

View File

@@ -1,7 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use super::{File, Role, User}; use super::{File, Role, User};
use crate::if_false;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@@ -32,6 +31,14 @@ pub static RE_COLOUR: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"(?i)^(?:[a-z ]+|var\(--[a-z\d-]+\)|rgba?\([\d, ]+\)|#[a-f0-9]+|(repeating-)?(linear|conic|radial)-gradient\(([a-z ]+|var\(--[a-z\d-]+\)|rgba?\([\d, ]+\)|#[a-f0-9]+|\d+deg)([ ]+(\d{1,3}%|0))?(,[ ]*([a-z ]+|var\(--[a-z\d-]+\)|rgba?\([\d, ]+\)|#[a-f0-9]+)([ ]+(\d{1,3}%|0))?)+\))$").unwrap() Regex::new(r"(?i)^(?:[a-z ]+|var\(--[a-z\d-]+\)|rgba?\([\d, ]+\)|#[a-f0-9]+|(repeating-)?(linear|conic|radial)-gradient\(([a-z ]+|var\(--[a-z\d-]+\)|rgba?\([\d, ]+\)|#[a-f0-9]+|\d+deg)([ ]+(\d{1,3}%|0))?(,[ ]*([a-z ]+|var\(--[a-z\d-]+\)|rgba?\([\d, ]+\)|#[a-f0-9]+)([ ]+(\d{1,3}%|0))?)+\))$").unwrap()
}); });
fn default_true() -> bool {
true
}
fn is_true(x: &bool) -> bool {
*x
}
auto_derived_partial!( auto_derived_partial!(
/// Server Member /// Server Member
pub struct Member { pub struct Member {
@@ -60,11 +67,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 = "Option::is_none")] #[serde(skip_serializing_if = "is_true", default = "default_true")]
pub can_publish: Option<bool>, pub can_publish: bool,
/// Whether the member is server-wide voice deafened /// Whether the member is server-wide voice deafened
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "is_true", default = "default_true")]
pub can_receive: Option<bool>, pub can_receive: bool,
}, },
"PartialMember" "PartialMember"
); );

View File

@@ -63,6 +63,7 @@ pub async fn calculate_server_permissions<P: PermissionQuery>(query: &mut P) ->
if !query.do_we_have_publish_overwrites().await { if !query.do_we_have_publish_overwrites().await {
permissions.revoke(ChannelPermission::Speak as u64); permissions.revoke(ChannelPermission::Speak as u64);
permissions.revoke(ChannelPermission::Video as u64);
} }
if !query.do_we_have_receive_overwrites().await { if !query.do_we_have_receive_overwrites().await {

View File

@@ -39,7 +39,7 @@ pub enum DataPermissionPoly {
permissions: u64, permissions: u64,
}, },
Field { Field {
/// Allow / deny values to set for members in this `TextChannel` or `VoiceChannel` /// Allow / deny values to set for members in this server channel
permissions: Override, permissions: Override,
}, },
} }

View File

@@ -67,7 +67,7 @@ impl ApnsOutboundConsumer {
match &notification.channel { match &notification.channel {
Channel::DirectMessage { .. } => notification.author.clone(), Channel::DirectMessage { .. } => notification.author.clone(),
Channel::Group { name, .. } => format!("{}, #{}", notification.author, name), Channel::Group { name, .. } => format!("{}, #{}", notification.author, name),
Channel::TextChannel { name, .. } | Channel::VoiceChannel { name, .. } => { Channel::TextChannel { name, .. } => {
format!("{} in #{}", notification.author, name) format!("{} in #{}", notification.author, name)
} }
_ => "Unknown".to_string(), _ => "Unknown".to_string(),

View File

@@ -31,7 +31,7 @@ impl FcmOutboundConsumer {
match &notification.channel { match &notification.channel {
Channel::DirectMessage { .. } => notification.author.clone(), Channel::DirectMessage { .. } => notification.author.clone(),
Channel::Group { name, .. } => format!("{}, #{}", notification.author, name), Channel::Group { name, .. } => format!("{}, #{}", notification.author, name),
Channel::TextChannel { name, .. } | Channel::VoiceChannel { name, .. } => { Channel::TextChannel { name, .. } => {
format!("{} in #{}", notification.author, name) format!("{} in #{}", notification.author, name)
} }
_ => "Unknown".to_string(), _ => "Unknown".to_string(),

View File

@@ -59,7 +59,7 @@ pub async fn ingress(
let channel_id = channel_id.to_internal_error()?; let channel_id = channel_id.to_internal_error()?;
let user_id = user_id.to_internal_error()?; let user_id = user_id.to_internal_error()?;
let channel = Reference::from_unchecked(&channel_id) let channel = Reference::from_unchecked(channel_id)
.as_channel(db) .as_channel(db)
.await?; .await?;
@@ -86,7 +86,7 @@ pub async fn ingress(
// First user who joined - send call started system message. // First user who joined - send call started system message.
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) let user = Reference::from_unchecked(user_id)
.as_user(db) .as_user(db)
.await?; .await?;
@@ -120,7 +120,7 @@ pub async fn ingress(
let channel_id = channel_id.to_internal_error()?; let channel_id = channel_id.to_internal_error()?;
let user_id = user_id.to_internal_error()?; let user_id = user_id.to_internal_error()?;
let channel = Reference::from_unchecked(&channel_id) let channel = Reference::from_unchecked(channel_id)
.as_channel(db) .as_channel(db)
.await?; .await?;
@@ -179,11 +179,11 @@ pub async fn ingress(
let user_id = user_id.to_internal_error()?; let user_id = user_id.to_internal_error()?;
let track = event.track.as_ref().to_internal_error()?; let track = event.track.as_ref().to_internal_error()?;
let channel = Reference::from_unchecked(&channel_id) let channel = Reference::from_unchecked(channel_id)
.as_channel(db) .as_channel(db)
.await?; .await?;
let user = Reference::from_unchecked(&user_id) let user = Reference::from_unchecked(user_id)
.as_user(db) .as_user(db)
.await?; .await?;

View File

@@ -1,5 +1,10 @@
use revolt_database::{ use revolt_database::{
util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{delete_voice_state, get_channel_node, get_voice_channel_members, is_in_voice_channel, VoiceClient}, Channel, Database, PartialChannel, User, AMQP util::{permissions::DatabasePermissionQuery, reference::Reference},
voice::{
delete_voice_state, get_channel_node, get_voice_channel_members, is_in_voice_channel,
VoiceClient,
},
Channel, Database, PartialChannel, User, AMQP,
}; };
use revolt_models::v0; use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
@@ -29,16 +34,18 @@ pub async fn delete(
#[allow(deprecated)] #[allow(deprecated)]
match &channel { match &channel {
Channel::SavedMessages { .. } => Err(create_error!(NoEffect))?, Channel::SavedMessages { .. } => Err(create_error!(NoEffect))?,
Channel::DirectMessage { .. } => channel Channel::DirectMessage { .. } => {
.update( channel
db, .update(
PartialChannel { db,
active: Some(false), PartialChannel {
..Default::default() active: Some(false),
}, ..Default::default()
vec![], },
) vec![],
.await?, )
.await?
}
Channel::Group { .. } => { Channel::Group { .. } => {
channel channel
.remove_user_from_group( .remove_user_from_group(
@@ -53,11 +60,13 @@ pub async fn delete(
if is_in_voice_channel(&user.id, channel.id()).await? { if is_in_voice_channel(&user.id, channel.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(), None, &user.id).await?; delete_voice_state(channel.id(), None, &user.id).await?;
}; };
}, }
Channel::TextChannel { .. } | Channel::VoiceChannel { .. } => { Channel::TextChannel { .. } => {
permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?; permissions.throw_if_lacking_channel_permission(ChannelPermission::ManageChannel)?;
channel.delete(db).await?; channel.delete(db).await?;

View File

@@ -1,4 +1,4 @@
use revolt_database::{util::reference::Reference, voice::{delete_voice_state, get_channel_node, is_in_voice_channel, raise_if_in_voice, VoiceClient}, Channel, Database, User, AMQP}; use revolt_database::{util::reference::Reference, voice::{delete_voice_state, get_channel_node, is_in_voice_channel, VoiceClient}, Channel, Database, User, AMQP};
use revolt_permissions::ChannelPermission; use revolt_permissions::ChannelPermission;
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};

View File

@@ -1,6 +1,6 @@
use revolt_database::{ use revolt_database::{
util::{permissions::DatabasePermissionQuery, reference::Reference}, util::{permissions::DatabasePermissionQuery, reference::Reference},
Channel, Database, Message, MessageFilter, MessageQuery, MessageTimePeriod, User, Database, Message, MessageFilter, MessageQuery, MessageTimePeriod, User,
}; };
use revolt_models::v0::{self, MessageSort}; use revolt_models::v0::{self, MessageSort};
use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
@@ -65,13 +65,7 @@ pub async fn query(
}, },
&user, &user,
include_users, include_users,
#[allow(deprecated)] channel.server(),
match channel {
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
Some(server)
}
_ => None,
},
) )
.await .await
.map(Json) .map(Json)

View File

@@ -1,6 +1,6 @@
use revolt_database::{ use revolt_database::{
util::{permissions::DatabasePermissionQuery, reference::Reference}, util::{permissions::DatabasePermissionQuery, reference::Reference},
Channel, Database, Message, MessageFilter, MessageQuery, MessageTimePeriod, User, Database, Message, MessageFilter, MessageQuery, MessageTimePeriod, User,
}; };
use revolt_models::v0; use revolt_models::v0;
use revolt_permissions::{calculate_channel_permissions, ChannelPermission}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission};
@@ -31,7 +31,7 @@ pub async fn search(
})?; })?;
if options.query.is_some() && options.pinned.is_some() { if options.query.is_some() && options.pinned.is_some() {
return Err(create_error!(InvalidOperation)) return Err(create_error!(InvalidOperation));
} }
let channel = target.as_channel(db).await?; let channel = target.as_channel(db).await?;
@@ -69,13 +69,7 @@ pub async fn search(
}, },
&user, &user,
include_users, include_users,
#[allow(deprecated)] channel.server(),
match channel {
Channel::TextChannel { server, .. } | Channel::VoiceChannel { server, .. } => {
Some(server)
}
_ => None,
},
) )
.await .await
.map(Json) .map(Json)

View File

@@ -1,5 +1,5 @@
use revolt_database::{ use revolt_database::{
util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Channel, 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_channel_permissions, ChannelPermission, Override}; use revolt_permissions::{calculate_channel_permissions, ChannelPermission, Override};
@@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State};
/// ///
/// Sets permissions for the specified role in this channel. /// Sets permissions for the specified role in this channel.
/// ///
/// Channel must be a `TextChannel` or `VoiceChannel`. /// Channel must be a `TextChannel`.
#[openapi(tag = "Channel Permissions")] #[openapi(tag = "Channel Permissions")]
#[put("/<target>/permissions/<role_id>", data = "<data>", rank = 2)] #[put("/<target>/permissions/<role_id>", data = "<data>", rank = 2)]
pub async fn set_role_permissions( pub async fn set_role_permissions(

View File

@@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State};
/// ///
/// Sets permissions for the default role in this channel. /// Sets permissions for the default role in this channel.
/// ///
/// Channel must be a `Group`, `TextChannel` or `VoiceChannel`. /// Channel must be a `Group` or `TextChannel`.
#[openapi(tag = "Channel Permissions")] #[openapi(tag = "Channel Permissions")]
#[put("/<target>/permissions/default", data = "<data>", rank = 1)] #[put("/<target>/permissions/default", data = "<data>", rank = 1)]
pub async fn set_default_channel_permissions( pub async fn set_default_channel_permissions(

View File

@@ -93,7 +93,7 @@ pub async fn call(
} }
let token = voice_client let token = voice_client
.create_token(&node, &user, current_permissions, &channel) .create_token(&node, db, &user, current_permissions, &channel)
.await?; .await?;
let room = voice_client.create_room(&node, &channel).await?; let room = voice_client.create_room(&node, &channel).await?;

View File

@@ -44,7 +44,7 @@ pub async fn edit(
// Fetch server and member // Fetch server and member
let mut server = server.as_server(db).await?; let mut server = server.as_server(db).await?;
let target_user = member.as_user(&db).await?; let target_user = member.as_user(db).await?;
let mut member = member.as_member(db, &server.id).await?; let mut member = member.as_member(db, &server.id).await?;
// Fetch our currrent permissions // Fetch our currrent permissions
@@ -208,7 +208,7 @@ pub async fn edit(
.create_room(&new_node, &new_voice_channel) .create_room(&new_node, &new_voice_channel)
.await?; .await?;
let token = voice_client let token = voice_client
.create_token(&new_node, &target_user, permissions, &new_voice_channel) .create_token(&new_node, db, &target_user, permissions, &new_voice_channel)
.await?; .await?;
voice_client voice_client

View File

@@ -1,5 +1,5 @@
use revolt_database::{ use revolt_database::{
util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Database, PartialServer, Server, User util::{permissions::DatabasePermissionQuery, reference::Reference}, voice::{sync_voice_permissions, VoiceClient}, Database, PartialServer, User
}; };
use revolt_models::v0; use revolt_models::v0;
use revolt_permissions::{ use revolt_permissions::{