Merge remote-tracking branch 'origin' into feat/livekit
This commit is contained in:
1190
Cargo.lock
generated
1190
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -145,7 +145,13 @@ pub enum EventV1 {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/// User joins server
|
/// User joins server
|
||||||
ServerMemberJoin { id: String, user: String },
|
ServerMemberJoin {
|
||||||
|
id: String,
|
||||||
|
// Deprecated: use member.id.user
|
||||||
|
#[deprecated = "Use member.id.user instead"]
|
||||||
|
user: String,
|
||||||
|
member: Member,
|
||||||
|
},
|
||||||
|
|
||||||
/// User left server
|
/// User left server
|
||||||
ServerMemberLeave {
|
ServerMemberLeave {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use serde::Deserialize;
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
use super::client::Ping;
|
use super::client::Ping;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum ClientMessage {
|
pub enum ClientMessage {
|
||||||
Authenticate { token: String },
|
Authenticate { token: String },
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ auto_derived_partial!(
|
|||||||
/// Whether the member is server-wide voice deafened
|
/// Whether the member is server-wide voice deafened
|
||||||
#[serde(skip_serializing_if = "is_true", default = "default_true")]
|
#[serde(skip_serializing_if = "is_true", default = "default_true")]
|
||||||
pub can_receive: bool,
|
pub can_receive: bool,
|
||||||
|
|
||||||
|
// This value only exists in the database, not the models.
|
||||||
|
// If it is not-None, the database layer should return None to member fetching queries.
|
||||||
|
// pub pending_deletion_at: Option<Timestamp>
|
||||||
},
|
},
|
||||||
"PartialMember"
|
"PartialMember"
|
||||||
);
|
);
|
||||||
@@ -68,6 +72,7 @@ auto_derived!(
|
|||||||
Timeout,
|
Timeout,
|
||||||
CanReceive,
|
CanReceive,
|
||||||
CanPublish,
|
CanPublish,
|
||||||
|
JoinedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Member removal intention
|
/// Member removal intention
|
||||||
@@ -110,7 +115,7 @@ impl Member {
|
|||||||
return Err(create_error!(AlreadyInServer));
|
return Err(create_error!(AlreadyInServer));
|
||||||
}
|
}
|
||||||
|
|
||||||
let member = Member {
|
let mut member = Member {
|
||||||
id: MemberCompositeKey {
|
id: MemberCompositeKey {
|
||||||
server: server.id.to_string(),
|
server: server.id.to_string(),
|
||||||
user: user.id.to_string(),
|
user: user.id.to_string(),
|
||||||
@@ -118,7 +123,9 @@ impl Member {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
db.insert_member(&member).await?;
|
if let Some(updated) = db.insert_or_merge_member(&member).await? {
|
||||||
|
member = updated;
|
||||||
|
}
|
||||||
|
|
||||||
let should_fetch = channels.is_none();
|
let should_fetch = channels.is_none();
|
||||||
let mut channels = channels.unwrap_or_default();
|
let mut channels = channels.unwrap_or_default();
|
||||||
@@ -152,6 +159,7 @@ impl Member {
|
|||||||
EventV1::ServerMemberJoin {
|
EventV1::ServerMemberJoin {
|
||||||
id: server.id.clone(),
|
id: server.id.clone(),
|
||||||
user: user.id.clone(),
|
user: user.id.clone(),
|
||||||
|
member: member.clone().into(),
|
||||||
}
|
}
|
||||||
.p(server.id.clone())
|
.p(server.id.clone())
|
||||||
.await;
|
.await;
|
||||||
@@ -215,6 +223,7 @@ impl Member {
|
|||||||
|
|
||||||
pub fn remove_field(&mut self, field: &FieldsMember) {
|
pub fn remove_field(&mut self, field: &FieldsMember) {
|
||||||
match field {
|
match field {
|
||||||
|
FieldsMember::JoinedAt => (),
|
||||||
FieldsMember::Avatar => self.avatar = None,
|
FieldsMember::Avatar => self.avatar = None,
|
||||||
FieldsMember::Nickname => self.nickname = None,
|
FieldsMember::Nickname => self.nickname = None,
|
||||||
FieldsMember::Roles => self.roles.clear(),
|
FieldsMember::Roles => self.roles.clear(),
|
||||||
@@ -255,7 +264,7 @@ impl Member {
|
|||||||
intention: RemovalIntention,
|
intention: RemovalIntention,
|
||||||
silent: bool,
|
silent: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
db.delete_member(&self.id).await?;
|
db.soft_delete_member(&self.id).await?;
|
||||||
|
|
||||||
EventV1::ServerMemberLeave {
|
EventV1::ServerMemberLeave {
|
||||||
id: self.id.server.to_string(),
|
id: self.id.server.to_string(),
|
||||||
@@ -291,3 +300,74 @@ impl Member {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use iso8601_timestamp::{Duration, Timestamp};
|
||||||
|
use revolt_models::v0::DataCreateServer;
|
||||||
|
|
||||||
|
use crate::{Member, PartialMember, RemovalIntention, Server, User};
|
||||||
|
|
||||||
|
#[async_std::test]
|
||||||
|
async fn muted_member_rejoin() {
|
||||||
|
database_test!(|db| async move {
|
||||||
|
match db {
|
||||||
|
crate::Database::Reference(_) => return,
|
||||||
|
crate::Database::MongoDb(_) => (),
|
||||||
|
}
|
||||||
|
let owner = User::create(&db, "Server Owner".to_string(), None, None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let kickable_user = User::create(&db, "Member".to_string(), None, None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let server = Server::create(
|
||||||
|
&db,
|
||||||
|
DataCreateServer {
|
||||||
|
name: "Server".to_string(),
|
||||||
|
description: None,
|
||||||
|
nsfw: None,
|
||||||
|
},
|
||||||
|
&owner,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
|
||||||
|
Member::create(&db, &server, &owner, None).await.unwrap();
|
||||||
|
let mut kickable_member = Member::create(&db, &server, &kickable_user, None)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
|
||||||
|
kickable_member
|
||||||
|
.update(
|
||||||
|
&db,
|
||||||
|
PartialMember {
|
||||||
|
timeout: Some(Timestamp::now_utc() + Duration::minutes(5)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
vec![],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(kickable_member.in_timeout());
|
||||||
|
|
||||||
|
kickable_member
|
||||||
|
.remove(&db, &server, RemovalIntention::Kick, false)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let kickable_member = Member::create(&db, &server, &kickable_user, None)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
|
||||||
|
assert!(kickable_member.in_timeout())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,13 +73,13 @@ impl ChunkedServerMembersGenerator {
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait AbstractServerMembers: Sync + Send {
|
pub trait AbstractServerMembers: Sync + Send {
|
||||||
/// Insert a new server member into the database
|
/// Insert a new server member into the database
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()>;
|
async fn insert_or_merge_member(&self, member: &Member) -> Result<Option<Member>>;
|
||||||
|
|
||||||
/// Fetch a server member by their id
|
/// Fetch a server member by their id
|
||||||
async fn fetch_member(&self, server_id: &str, user_id: &str) -> Result<Member>;
|
async fn fetch_member(&self, server_id: &str, user_id: &str) -> Result<Member>;
|
||||||
|
|
||||||
/// Fetch all members in a server
|
/// Fetch all members in a server
|
||||||
async fn fetch_all_members<'a>(&self, server_id: &str) -> Result<Vec<Member>>;
|
async fn fetch_all_members(&self, server_id: &str) -> Result<Vec<Member>>;
|
||||||
|
|
||||||
/// Fetch all members in a server as an iterator
|
/// Fetch all members in a server as an iterator
|
||||||
async fn fetch_all_members_chunked(
|
async fn fetch_all_members_chunked(
|
||||||
@@ -100,10 +100,10 @@ pub trait AbstractServerMembers: Sync + Send {
|
|||||||
) -> Result<ChunkedServerMembersGenerator>;
|
) -> Result<ChunkedServerMembersGenerator>;
|
||||||
|
|
||||||
/// Fetch all memberships for a user
|
/// Fetch all memberships for a user
|
||||||
async fn fetch_all_memberships<'a>(&self, user_id: &str) -> Result<Vec<Member>>;
|
async fn fetch_all_memberships(&self, user_id: &str) -> Result<Vec<Member>>;
|
||||||
|
|
||||||
/// Fetch multiple members by their ids
|
/// Fetch multiple members by their ids
|
||||||
async fn fetch_members<'a>(&self, server_id: &str, ids: &'a [String]) -> Result<Vec<Member>>;
|
async fn fetch_members(&self, server_id: &str, ids: &[String]) -> Result<Vec<Member>>;
|
||||||
|
|
||||||
/// Fetch member count of a server
|
/// Fetch member count of a server
|
||||||
async fn fetch_member_count(&self, server_id: &str) -> Result<usize>;
|
async fn fetch_member_count(&self, server_id: &str) -> Result<usize>;
|
||||||
@@ -119,6 +119,14 @@ pub trait AbstractServerMembers: Sync + Send {
|
|||||||
remove: Vec<FieldsMember>,
|
remove: Vec<FieldsMember>,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
|
|
||||||
/// Delete a server member by their id
|
/// Marks a user as no longer a member of a server, while retaining the database value.
|
||||||
async fn delete_member(&self, id: &MemberCompositeKey) -> Result<()>;
|
/// This is used to keep information such as timeouts in place, but will remove information such as join date and applied roles.
|
||||||
|
async fn soft_delete_member(&self, id: &MemberCompositeKey) -> Result<()>;
|
||||||
|
|
||||||
|
/// Forcibly delete a server member by their id.
|
||||||
|
/// This will cancel any pending timeouts or other longer term actions, and they will not be reapplied on rejoin.
|
||||||
|
async fn force_delete_member(&self, id: &MemberCompositeKey) -> Result<()>;
|
||||||
|
|
||||||
|
/// Fetch all members who have been marked for deletion.
|
||||||
|
async fn remove_dangling_members(&self) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
use bson::Document;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
use mongodb::options::ReadConcern;
|
use mongodb::options::ReadConcern;
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
|
|
||||||
@@ -11,9 +13,42 @@ static COL: &str = "server_members";
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AbstractServerMembers for MongoDb {
|
impl AbstractServerMembers for MongoDb {
|
||||||
/// Insert a new server member into the database
|
/// Insert a new server member (or use the existing member if one is found)
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()> {
|
async fn insert_or_merge_member(&self, member: &Member) -> Result<Option<Member>> {
|
||||||
query!(self, insert_one, COL, &member).map(|_| ())
|
let existing: Result<Option<Document>> = query!(
|
||||||
|
self,
|
||||||
|
find_one,
|
||||||
|
COL,
|
||||||
|
doc! {
|
||||||
|
"_id.server": &member.id.server,
|
||||||
|
"_id.user": &member.id.user,
|
||||||
|
"pending_deletion_at": {"$exists": true}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// Update the existing record if it exist, otherwise make a new record
|
||||||
|
if existing.is_ok_and(|x| x.is_some()) {
|
||||||
|
self.col::<Member>(COL)
|
||||||
|
.find_one_and_update(
|
||||||
|
doc! {
|
||||||
|
"_id.server": &member.id.server,
|
||||||
|
"_id.user": &member.id.user,
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"joined_at": member.joined_at.duration_since(Timestamp::UNIX_EPOCH).whole_seconds(),
|
||||||
|
},
|
||||||
|
"$unset": {
|
||||||
|
"pending_deletion_at": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.return_document(mongodb::options::ReturnDocument::After)
|
||||||
|
.await
|
||||||
|
.map_err(|_| create_database_error!("update_one", COL))
|
||||||
|
} else {
|
||||||
|
query!(self, insert_one, COL, &member).map(|_| ())?;
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch a server member by their id
|
/// Fetch a server member by their id
|
||||||
@@ -24,18 +59,20 @@ impl AbstractServerMembers for MongoDb {
|
|||||||
COL,
|
COL,
|
||||||
doc! {
|
doc! {
|
||||||
"_id.server": server_id,
|
"_id.server": server_id,
|
||||||
"_id.user": user_id
|
"_id.user": user_id,
|
||||||
|
"pending_deletion_at": {"$exists": false}
|
||||||
}
|
}
|
||||||
)?
|
)?
|
||||||
.ok_or_else(|| create_error!(NotFound))
|
.ok_or_else(|| create_error!(NotFound))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch all members in a server
|
/// Fetch all members in a server
|
||||||
async fn fetch_all_members<'a>(&self, server_id: &str) -> Result<Vec<Member>> {
|
async fn fetch_all_members(&self, server_id: &str) -> Result<Vec<Member>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.col::<Member>(COL)
|
.col::<Member>(COL)
|
||||||
.find(doc! {
|
.find(doc! {
|
||||||
"_id.server": server_id
|
"_id.server": server_id,
|
||||||
|
"pending_deletion_at": {"$exists": false}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|_| create_database_error!("find", COL))?
|
.map_err(|_| create_database_error!("find", COL))?
|
||||||
@@ -139,11 +176,12 @@ impl AbstractServerMembers for MongoDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch all memberships for a user
|
/// Fetch all memberships for a user
|
||||||
async fn fetch_all_memberships<'a>(&self, user_id: &str) -> Result<Vec<Member>> {
|
async fn fetch_all_memberships(&self, user_id: &str) -> Result<Vec<Member>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.col::<Member>(COL)
|
.col::<Member>(COL)
|
||||||
.find(doc! {
|
.find(doc! {
|
||||||
"_id.user": user_id
|
"_id.user": user_id,
|
||||||
|
"pending_deletion_at": {"$exists": false}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|_| create_database_error!("find", COL))?
|
.map_err(|_| create_database_error!("find", COL))?
|
||||||
@@ -159,11 +197,12 @@ impl AbstractServerMembers for MongoDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch multiple members by their ids
|
/// Fetch multiple members by their ids
|
||||||
async fn fetch_members<'a>(&self, server_id: &str, ids: &'a [String]) -> Result<Vec<Member>> {
|
async fn fetch_members(&self, server_id: &str, ids: &[String]) -> Result<Vec<Member>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.col::<Member>(COL)
|
.col::<Member>(COL)
|
||||||
.find(doc! {
|
.find(doc! {
|
||||||
"_id.server": server_id,
|
"_id.server": server_id,
|
||||||
|
"pending_deletion_at": {"$exists": false},
|
||||||
"_id.user": {
|
"_id.user": {
|
||||||
"$in": ids
|
"$in": ids
|
||||||
}
|
}
|
||||||
@@ -185,7 +224,8 @@ impl AbstractServerMembers for MongoDb {
|
|||||||
async fn fetch_member_count(&self, server_id: &str) -> Result<usize> {
|
async fn fetch_member_count(&self, server_id: &str) -> Result<usize> {
|
||||||
self.col::<Member>(COL)
|
self.col::<Member>(COL)
|
||||||
.count_documents(doc! {
|
.count_documents(doc! {
|
||||||
"_id.server": server_id
|
"_id.server": server_id,
|
||||||
|
"pending_deletion_at": {"$exists": false}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map(|c| c as usize)
|
.map(|c| c as usize)
|
||||||
@@ -196,7 +236,8 @@ impl AbstractServerMembers for MongoDb {
|
|||||||
async fn fetch_server_count(&self, user_id: &str) -> Result<usize> {
|
async fn fetch_server_count(&self, user_id: &str) -> Result<usize> {
|
||||||
self.col::<Member>(COL)
|
self.col::<Member>(COL)
|
||||||
.count_documents(doc! {
|
.count_documents(doc! {
|
||||||
"_id.user": user_id
|
"_id.user": user_id,
|
||||||
|
"pending_deletion_at": {"$exists": false}
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map(|c| c as usize)
|
.map(|c| c as usize)
|
||||||
@@ -225,8 +266,42 @@ impl AbstractServerMembers for MongoDb {
|
|||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marks a member for deletion.
|
||||||
|
/// This will remove the record if the user has no pending actions (eg. timeout),
|
||||||
|
/// otherwise will slate the record for deletion by revolt_crond once the actions expire.
|
||||||
|
async fn soft_delete_member(&self, id: &MemberCompositeKey) -> Result<()> {
|
||||||
|
let member = self.fetch_member(&id.server, &id.user).await;
|
||||||
|
if let Ok(member) = member {
|
||||||
|
if member.in_timeout() {
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.update_many(
|
||||||
|
doc! {
|
||||||
|
"_id.server": &id.server,
|
||||||
|
"_id.user": &id.user,
|
||||||
|
},
|
||||||
|
doc! {
|
||||||
|
"$set": {"pending_deletion_at": format!("{}", member.timeout.unwrap().format())},
|
||||||
|
"$unset": {
|
||||||
|
"joined_at": "",
|
||||||
|
"avatar": "",
|
||||||
|
"nickname": "",
|
||||||
|
"roles": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("update_many", COL))
|
||||||
|
} else {
|
||||||
|
self.force_delete_member(id).await
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(create_database_error!("fetch_member", COL))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Delete a server member by their id
|
/// Delete a server member by their id
|
||||||
async fn delete_member(&self, id: &MemberCompositeKey) -> Result<()> {
|
async fn force_delete_member(&self, id: &MemberCompositeKey) -> Result<()> {
|
||||||
query!(
|
query!(
|
||||||
self,
|
self,
|
||||||
delete_one,
|
delete_one,
|
||||||
@@ -238,11 +313,25 @@ impl AbstractServerMembers for MongoDb {
|
|||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn remove_dangling_members(&self) -> Result<()> {
|
||||||
|
let now = Timestamp::now_utc();
|
||||||
|
let date = bson::to_bson(&now).expect("Failed to serialize timestamp");
|
||||||
|
|
||||||
|
self.col::<Document>(COL)
|
||||||
|
.delete_many(doc! {
|
||||||
|
"pending_deletion_at": {"$lt": date}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| create_database_error!("count_documents", COL))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoDocumentPath for FieldsMember {
|
impl IntoDocumentPath for FieldsMember {
|
||||||
fn as_path(&self) -> Option<&'static str> {
|
fn as_path(&self) -> Option<&'static str> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
|
FieldsMember::JoinedAt => "joined_at",
|
||||||
FieldsMember::Avatar => "avatar",
|
FieldsMember::Avatar => "avatar",
|
||||||
FieldsMember::Nickname => "nickname",
|
FieldsMember::Nickname => "nickname",
|
||||||
FieldsMember::Roles => "roles",
|
FieldsMember::Roles => "roles",
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ use super::{AbstractServerMembers, ChunkedServerMembersGenerator};
|
|||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AbstractServerMembers for ReferenceDb {
|
impl AbstractServerMembers for ReferenceDb {
|
||||||
/// Insert a new server member into the database
|
/// Insert a new server member into the database
|
||||||
async fn insert_member(&self, member: &Member) -> Result<()> {
|
async fn insert_or_merge_member(&self, member: &Member) -> Result<Option<Member>> {
|
||||||
let mut server_members = self.server_members.lock().await;
|
let mut server_members = self.server_members.lock().await;
|
||||||
if server_members.contains_key(&member.id) {
|
if server_members.contains_key(&member.id) {
|
||||||
Err(create_database_error!("insert", "member"))
|
Err(create_database_error!("insert", "member"))
|
||||||
} else {
|
} else {
|
||||||
server_members.insert(member.id.clone(), member.clone());
|
server_members.insert(member.id.clone(), member.clone());
|
||||||
Ok(())
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ impl AbstractServerMembers for ReferenceDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch all members in a server
|
/// Fetch all members in a server
|
||||||
async fn fetch_all_members<'a>(&self, server_id: &str) -> Result<Vec<Member>> {
|
async fn fetch_all_members(&self, server_id: &str) -> Result<Vec<Member>> {
|
||||||
let server_members = self.server_members.lock().await;
|
let server_members = self.server_members.lock().await;
|
||||||
Ok(server_members
|
Ok(server_members
|
||||||
.values()
|
.values()
|
||||||
@@ -105,7 +105,7 @@ impl AbstractServerMembers for ReferenceDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch all memberships for a user
|
/// Fetch all memberships for a user
|
||||||
async fn fetch_all_memberships<'a>(&self, user_id: &str) -> Result<Vec<Member>> {
|
async fn fetch_all_memberships(&self, user_id: &str) -> Result<Vec<Member>> {
|
||||||
let server_members = self.server_members.lock().await;
|
let server_members = self.server_members.lock().await;
|
||||||
Ok(server_members
|
Ok(server_members
|
||||||
.values()
|
.values()
|
||||||
@@ -115,7 +115,7 @@ impl AbstractServerMembers for ReferenceDb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch multiple members by their ids
|
/// Fetch multiple members by their ids
|
||||||
async fn fetch_members<'a>(&self, server_id: &str, ids: &'a [String]) -> Result<Vec<Member>> {
|
async fn fetch_members(&self, server_id: &str, ids: &[String]) -> Result<Vec<Member>> {
|
||||||
let server_members = self.server_members.lock().await;
|
let server_members = self.server_members.lock().await;
|
||||||
Ok(ids
|
Ok(ids
|
||||||
.iter()
|
.iter()
|
||||||
@@ -169,8 +169,26 @@ impl AbstractServerMembers for ReferenceDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Soft delete a member
|
||||||
|
async fn soft_delete_member(&self, id: &MemberCompositeKey) -> Result<()> {
|
||||||
|
let mut server_members = self.server_members.lock().await;
|
||||||
|
|
||||||
|
let member = server_members.get_mut(id);
|
||||||
|
if let Some(member) = member {
|
||||||
|
if member.in_timeout() {
|
||||||
|
panic!("Soft deletion is not implemented.")
|
||||||
|
} else if server_members.remove(id).is_some() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(create_error!(NotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Delete a server member by their id
|
/// Delete a server member by their id
|
||||||
async fn delete_member(&self, id: &MemberCompositeKey) -> Result<()> {
|
async fn force_delete_member(&self, id: &MemberCompositeKey) -> Result<()> {
|
||||||
let mut server_members = self.server_members.lock().await;
|
let mut server_members = self.server_members.lock().await;
|
||||||
if server_members.remove(id).is_some() {
|
if server_members.remove(id).is_some() {
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -178,4 +196,8 @@ impl AbstractServerMembers for ReferenceDb {
|
|||||||
Err(create_error!(NotFound))
|
Err(create_error!(NotFound))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn remove_dangling_members(&self) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -688,6 +688,7 @@ impl From<crate::FieldsMember> for FieldsMember {
|
|||||||
crate::FieldsMember::Timeout => FieldsMember::Timeout,
|
crate::FieldsMember::Timeout => FieldsMember::Timeout,
|
||||||
crate::FieldsMember::CanReceive => FieldsMember::CanReceive,
|
crate::FieldsMember::CanReceive => FieldsMember::CanReceive,
|
||||||
crate::FieldsMember::CanPublish => FieldsMember::CanPublish,
|
crate::FieldsMember::CanPublish => FieldsMember::CanPublish,
|
||||||
|
crate::FieldsMember::JoinedAt => FieldsMember::JoinedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -701,6 +702,7 @@ impl From<FieldsMember> for crate::FieldsMember {
|
|||||||
FieldsMember::Timeout => crate::FieldsMember::Timeout,
|
FieldsMember::Timeout => crate::FieldsMember::Timeout,
|
||||||
FieldsMember::CanReceive => crate::FieldsMember::CanReceive,
|
FieldsMember::CanReceive => crate::FieldsMember::CanReceive,
|
||||||
FieldsMember::CanPublish => crate::FieldsMember::CanPublish,
|
FieldsMember::CanPublish => crate::FieldsMember::CanPublish,
|
||||||
|
FieldsMember::JoinedAt => crate::FieldsMember::JoinedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ pub async fn load_fixture(db: &Database, input: &str) -> HashMap<String, String>
|
|||||||
LoadedFixture::User(user) => db.insert_user(&user).await.unwrap(),
|
LoadedFixture::User(user) => db.insert_user(&user).await.unwrap(),
|
||||||
LoadedFixture::Channel(channel) => db.insert_channel(&channel).await.unwrap(),
|
LoadedFixture::Channel(channel) => db.insert_channel(&channel).await.unwrap(),
|
||||||
LoadedFixture::Server(server) => db.insert_server(&server).await.unwrap(),
|
LoadedFixture::Server(server) => db.insert_server(&server).await.unwrap(),
|
||||||
LoadedFixture::ServerMember(member) => db.insert_member(&member).await.unwrap(),
|
LoadedFixture::ServerMember(member) => {
|
||||||
|
db.insert_or_merge_member(&member).await.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ auto_derived!(
|
|||||||
Timeout,
|
Timeout,
|
||||||
CanReceive,
|
CanReceive,
|
||||||
CanPublish,
|
CanPublish,
|
||||||
|
JoinedAt,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Member removal intention
|
/// Member removal intention
|
||||||
|
|||||||
@@ -255,12 +255,14 @@ auto_derived!(
|
|||||||
pub flags: i32,
|
pub flags: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mutual friends and servers response
|
/// Mutual friends, servers, groups and DMs response
|
||||||
pub struct MutualResponse {
|
pub struct MutualResponse {
|
||||||
/// Array of mutual user IDs that both users are friends with
|
/// Array of mutual user IDs that both users are friends with
|
||||||
pub users: Vec<String>,
|
pub users: Vec<String>,
|
||||||
/// Array of mutual server IDs that both users are in
|
/// Array of mutual server IDs that both users are in
|
||||||
pub servers: Vec<String>,
|
pub servers: Vec<String>,
|
||||||
|
/// Array of mutual group and dm IDs that both users are in
|
||||||
|
pub channels: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Bot information for if the user is a bot
|
/// Bot information for if the user is a bot
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
name = "revolt-parser"
|
name = "revolt-parser"
|
||||||
version = "0.8.8"
|
version = "0.8.8"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "MIT"
|
||||||
|
authors = ["Zomatree <me@zomatree.live>", "Paul Makles <me@insrt.uk>"]
|
||||||
description = "Revolt Backend: Message Parser"
|
description = "Revolt Backend: Message Parser"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|||||||
9
crates/core/parser/LICENSE
Normal file
9
crates/core/parser/LICENSE
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Pawel Makles
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -4,15 +4,18 @@ use logos::Logos;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Logos, PartialEq)]
|
#[derive(Debug, Clone, Logos, PartialEq)]
|
||||||
#[logos(skip "\n")]
|
#[logos(skip "\n")]
|
||||||
pub enum MessageToken {
|
#[logos(subpattern id="[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}")]
|
||||||
|
pub enum MessageToken<'a> {
|
||||||
#[token("\\")]
|
#[token("\\")]
|
||||||
Escape,
|
Escape,
|
||||||
#[regex("(```[^`\n]*)|(``)|`", |lex| lex.slice().to_owned().chars().filter(|&c| c == '`').count())]
|
#[regex("```[^`\n]*", |_| 3)]
|
||||||
|
#[regex("``", |_| 2)]
|
||||||
|
#[regex("`", |_| 1)]
|
||||||
CodeblockMarker(usize),
|
CodeblockMarker(usize),
|
||||||
#[regex("<@[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}>", |lex| lex.slice()[2..lex.slice().len() - 1].to_owned())]
|
#[regex("<@(?&id)>", |lex| &lex.slice()[2..lex.slice().len() - 1])]
|
||||||
UserMention(String),
|
UserMention(&'a str),
|
||||||
#[regex("<%[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}>", |lex| lex.slice()[2..lex.slice().len() - 1].to_owned())]
|
#[regex("<%(?&id)>", |lex| &lex.slice()[2..lex.slice().len() - 1],)]
|
||||||
RoleMention(String),
|
RoleMention(&'a str),
|
||||||
#[token("@everyone")]
|
#[token("@everyone")]
|
||||||
MentionEveryone,
|
MentionEveryone,
|
||||||
#[token("@online")]
|
#[token("@online")]
|
||||||
@@ -27,13 +30,13 @@ pub struct MessageResults {
|
|||||||
pub mentions_online: bool
|
pub mentions_online: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MessageParserIterator<I> {
|
struct MessageParserIterator<'a, I> {
|
||||||
inner: I,
|
inner: I,
|
||||||
temp: VecDeque<MessageToken>
|
temp: VecDeque<MessageToken<'a>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Iterator<Item = MessageToken>> Iterator for MessageParserIterator<I> {
|
impl<'a, I: Iterator<Item = MessageToken<'a>>> Iterator for MessageParserIterator<'a, I> {
|
||||||
type Item = MessageToken;
|
type Item = MessageToken<'a>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if !self.temp.is_empty() {
|
if !self.temp.is_empty() {
|
||||||
@@ -80,8 +83,8 @@ pub fn parse_message(text: &str) -> MessageResults {
|
|||||||
match token {
|
match token {
|
||||||
MessageToken::Escape => {}
|
MessageToken::Escape => {}
|
||||||
MessageToken::CodeblockMarker(_) => {},
|
MessageToken::CodeblockMarker(_) => {},
|
||||||
MessageToken::UserMention(id) => { results.user_mentions.insert(id); },
|
MessageToken::UserMention(id) => { results.user_mentions.insert(id.to_string()); },
|
||||||
MessageToken::RoleMention(id) => { results.role_mentions.insert(id); },
|
MessageToken::RoleMention(id) => { results.role_mentions.insert(id.to_string()); },
|
||||||
MessageToken::MentionEveryone => results.mentions_everyone = true,
|
MessageToken::MentionEveryone => results.mentions_everyone = true,
|
||||||
MessageToken::MentionOnline => results.mentions_online = true,
|
MessageToken::MentionOnline => results.mentions_online = true,
|
||||||
};
|
};
|
||||||
@@ -106,7 +109,7 @@ mod tests {
|
|||||||
let output = parse_message_iter("Hello <@01FD58YK5W7QRV5H3D64KTQYX3>.").collect::<Vec<_>>();
|
let output = parse_message_iter("Hello <@01FD58YK5W7QRV5H3D64KTQYX3>.").collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(output.len(), 1);
|
assert_eq!(output.len(), 1);
|
||||||
assert_eq!(output[0], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[0], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -114,7 +117,7 @@ mod tests {
|
|||||||
let output = parse_message_iter("Hello <%01FD58YK5W7QRV5H3D64KTQYX3>.").collect::<Vec<_>>();
|
let output = parse_message_iter("Hello <%01FD58YK5W7QRV5H3D64KTQYX3>.").collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(output.len(), 1);
|
assert_eq!(output.len(), 1);
|
||||||
assert_eq!(output[0], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[0], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -138,8 +141,8 @@ mod tests {
|
|||||||
let output = parse_message_iter("Hello <@01FD58YK5W7QRV5H3D64KTQYX3>, <%01FD58YK5W7QRV5H3D64KTQYX3>, @everyone and @online.").collect::<Vec<_>>();
|
let output = parse_message_iter("Hello <@01FD58YK5W7QRV5H3D64KTQYX3>, <%01FD58YK5W7QRV5H3D64KTQYX3>, @everyone and @online.").collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(output.len(), 4);
|
assert_eq!(output.len(), 4);
|
||||||
assert_eq!(output[0], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[0], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
assert_eq!(output[1], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[1], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
assert_eq!(output[2], MessageToken::MentionEveryone);
|
assert_eq!(output[2], MessageToken::MentionEveryone);
|
||||||
assert_eq!(output[3], MessageToken::MentionOnline);
|
assert_eq!(output[3], MessageToken::MentionOnline);
|
||||||
}
|
}
|
||||||
@@ -149,8 +152,8 @@ mod tests {
|
|||||||
let output = parse_message_iter("<@01FD58YK5W7QRV5H3D64KTQYX3><%01FD58YK5W7QRV5H3D64KTQYX3>@everyone@online").collect::<Vec<_>>();
|
let output = parse_message_iter("<@01FD58YK5W7QRV5H3D64KTQYX3><%01FD58YK5W7QRV5H3D64KTQYX3>@everyone@online").collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(output.len(), 4);
|
assert_eq!(output.len(), 4);
|
||||||
assert_eq!(output[0], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[0], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
assert_eq!(output[1], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[1], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
assert_eq!(output[2], MessageToken::MentionEveryone);
|
assert_eq!(output[2], MessageToken::MentionEveryone);
|
||||||
assert_eq!(output[3], MessageToken::MentionOnline);
|
assert_eq!(output[3], MessageToken::MentionOnline);
|
||||||
}
|
}
|
||||||
@@ -170,15 +173,14 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(output.len(), 5);
|
assert_eq!(output.len(), 5);
|
||||||
assert_eq!(output[0], MessageToken::CodeblockMarker(3));
|
assert_eq!(output[0], MessageToken::CodeblockMarker(3));
|
||||||
assert_eq!(output[1], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[1], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
assert_eq!(output[2], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[2], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
assert_eq!(output[3], MessageToken::MentionEveryone);
|
assert_eq!(output[3], MessageToken::MentionEveryone);
|
||||||
assert_eq!(output[4], MessageToken::MentionOnline);
|
assert_eq!(output[4], MessageToken::MentionOnline);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_inline_codeblock_no_mentions() {
|
fn test_inline_codeblock_no_mentions() {
|
||||||
|
|
||||||
let output = parse_message_iter("`<@01FD58YK5W7QRV5H3D64KTQYX3><%01FD58YK5W7QRV5H3D64KTQYX3>@everyone@online`").collect::<Vec<_>>();
|
let output = parse_message_iter("`<@01FD58YK5W7QRV5H3D64KTQYX3><%01FD58YK5W7QRV5H3D64KTQYX3>@everyone@online`").collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(output.len(), 2);
|
assert_eq!(output.len(), 2);
|
||||||
@@ -192,12 +194,21 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(output.len(), 5);
|
assert_eq!(output.len(), 5);
|
||||||
assert_eq!(output[0], MessageToken::CodeblockMarker(1));
|
assert_eq!(output[0], MessageToken::CodeblockMarker(1));
|
||||||
assert_eq!(output[1], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[1], MessageToken::UserMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
assert_eq!(output[2], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3".to_string()));
|
assert_eq!(output[2], MessageToken::RoleMention("01FD58YK5W7QRV5H3D64KTQYX3"));
|
||||||
assert_eq!(output[3], MessageToken::MentionEveryone);
|
assert_eq!(output[3], MessageToken::MentionEveryone);
|
||||||
assert_eq!(output[4], MessageToken::MentionOnline);
|
assert_eq!(output[4], MessageToken::MentionOnline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_codeblock_with_language_no_mentions() {
|
||||||
|
let output = parse_message_iter("```rust\n<@01FD58YK5W7QRV5H3D64KTQYX3><%01FD58YK5W7QRV5H3D64KTQYX3>@everyone@online```").collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(output.len(), 2);
|
||||||
|
assert_eq!(output[0], MessageToken::CodeblockMarker(3));
|
||||||
|
assert_eq!(output[1], MessageToken::CodeblockMarker(3));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_double_inline_codeblock() {
|
fn test_double_inline_codeblock() {
|
||||||
let output = parse_message_iter("``this should not ping @everyone``").collect::<Vec<_>>();
|
let output = parse_message_iter("``this should not ping @everyone``").collect::<Vec<_>>();
|
||||||
|
|||||||
@@ -8,10 +8,18 @@ pub use server::*;
|
|||||||
pub use user::*;
|
pub use user::*;
|
||||||
|
|
||||||
/// Holds a permission value to manipulate.
|
/// Holds a permission value to manipulate.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct PermissionValue(u64);
|
pub struct PermissionValue(u64);
|
||||||
|
|
||||||
impl PermissionValue {
|
impl PermissionValue {
|
||||||
|
pub fn from_raw(value: u64) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_raw(self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
/// Apply a given override to this value
|
/// Apply a given override to this value
|
||||||
pub fn apply(&mut self, v: Override) {
|
pub fn apply(&mut self, v: Override) {
|
||||||
self.allow(v.allow);
|
self.allow(v.allow);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use revolt_config::configure;
|
use revolt_config::configure;
|
||||||
use revolt_database::DatabaseInfo;
|
use revolt_database::DatabaseInfo;
|
||||||
use revolt_result::Result;
|
use revolt_result::Result;
|
||||||
use tasks::{file_deletion, prune_dangling_files};
|
use tasks::{file_deletion, prune_dangling_files, prune_members};
|
||||||
use tokio::try_join;
|
use tokio::try_join;
|
||||||
|
|
||||||
pub mod tasks;
|
pub mod tasks;
|
||||||
@@ -13,7 +13,8 @@ async fn main() -> Result<()> {
|
|||||||
let db = DatabaseInfo::Auto.connect().await.expect("database");
|
let db = DatabaseInfo::Auto.connect().await.expect("database");
|
||||||
try_join!(
|
try_join!(
|
||||||
file_deletion::task(db.clone()),
|
file_deletion::task(db.clone()),
|
||||||
prune_dangling_files::task(db)
|
prune_dangling_files::task(db.clone()),
|
||||||
|
prune_members::task(db.clone())
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod file_deletion;
|
pub mod file_deletion;
|
||||||
pub mod prune_dangling_files;
|
pub mod prune_dangling_files;
|
||||||
|
pub mod prune_members;
|
||||||
|
|||||||
18
crates/daemons/crond/src/tasks/prune_members.rs
Normal file
18
crates/daemons/crond/src/tasks/prune_members.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use log::warn;
|
||||||
|
use revolt_database::Database;
|
||||||
|
use revolt_result::Result;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
pub async fn task(db: Database) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
let success = db.remove_dangling_members().await;
|
||||||
|
if let Err(s) = success {
|
||||||
|
revolt_config::capture_error(&s);
|
||||||
|
warn!("Failed to prune dangling members: {:?}", &s);
|
||||||
|
}
|
||||||
|
|
||||||
|
sleep(Duration::from_secs(90)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -168,8 +168,8 @@ mod test {
|
|||||||
.await;
|
.await;
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
EventV1::ServerMemberJoin { user, .. } => {
|
EventV1::ServerMemberJoin { member, .. } => {
|
||||||
assert_eq!(bot.id, user);
|
assert_eq!(bot.id, member.id.user);
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ use revolt_result::{create_error, Result};
|
|||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
|
|
||||||
/// # Fetch Mutual Friends And Servers
|
/// # Fetch Mutual Friends, Servers, Groups and DMs
|
||||||
///
|
///
|
||||||
/// Retrieve a list of mutual friends and servers with another user.
|
/// Retrieve a list of mutual friends, servers, groups and DMs with another user.
|
||||||
#[openapi(tag = "Relationships")]
|
#[openapi(tag = "Relationships")]
|
||||||
#[get("/<target>/mutual")]
|
#[get("/<target>/mutual")]
|
||||||
pub async fn mutual(
|
pub async fn mutual(
|
||||||
@@ -32,5 +32,6 @@ pub async fn mutual(
|
|||||||
Ok(Json(v0::MutualResponse {
|
Ok(Json(v0::MutualResponse {
|
||||||
users: db.fetch_mutual_user_ids(&user.id, &target.id).await?,
|
users: db.fetch_mutual_user_ids(&user.id, &target.id).await?,
|
||||||
servers: db.fetch_mutual_server_ids(&user.id, &target.id).await?,
|
servers: db.fetch_mutual_server_ids(&user.id, &target.id).await?,
|
||||||
|
channels: db.fetch_mutual_channel_ids(&user.id, &target.id).await?,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,9 @@ fn resolve_bucket<'r>(request: &'r rocket::Request<'_>) -> (&'r str, Option<&'r
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(segment) = segment {
|
if let Some(segment) = segment {
|
||||||
|
#[allow(clippy::redundant_locals)]
|
||||||
|
let resource = resource;
|
||||||
|
|
||||||
let method = request.method();
|
let method = request.method();
|
||||||
match (segment, resource, method) {
|
match (segment, resource, method) {
|
||||||
("users", target, Method::Patch) => ("user_edit", target),
|
("users", target, Method::Patch) => ("user_edit", target),
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/revoltchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
|
FROM debian:12 AS debian
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
FROM gcr.io/distroless/cc-debian12:nonroot
|
FROM gcr.io/distroless/cc-debian12:nonroot
|
||||||
COPY --from=builder /home/rust/src/target/release/revolt-autumn ./
|
COPY --from=builder /home/rust/src/target/release/revolt-autumn ./
|
||||||
COPY --from=mwader/static-ffmpeg:7.0.2 /ffmpeg /usr/local/bin/
|
COPY --from=mwader/static-ffmpeg:7.0.2 /ffmpeg /usr/local/bin/
|
||||||
COPY --from=mwader/static-ffmpeg:7.0.2 /ffprobe /usr/local/bin/
|
COPY --from=mwader/static-ffmpeg:7.0.2 /ffprobe /usr/local/bin/
|
||||||
|
COPY --from=debian /usr/bin/uname /usr/bin/uname
|
||||||
|
|
||||||
EXPOSE 14704
|
EXPOSE 14704
|
||||||
USER nonroot
|
USER nonroot
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# Build Stage
|
# Build Stage
|
||||||
FROM ghcr.io/revoltchat/base:latest AS builder
|
FROM ghcr.io/revoltchat/base:latest AS builder
|
||||||
|
FROM debian:12 AS debian
|
||||||
|
|
||||||
# Bundle Stage
|
# Bundle Stage
|
||||||
FROM gcr.io/distroless/cc-debian12:nonroot
|
FROM gcr.io/distroless/cc-debian12:nonroot
|
||||||
COPY --from=builder /home/rust/src/target/release/revolt-january ./
|
COPY --from=builder /home/rust/src/target/release/revolt-january ./
|
||||||
COPY --from=mwader/static-ffmpeg:7.0.2 /ffmpeg /usr/local/bin/
|
COPY --from=mwader/static-ffmpeg:7.0.2 /ffmpeg /usr/local/bin/
|
||||||
COPY --from=mwader/static-ffmpeg:7.0.2 /ffprobe /usr/local/bin/
|
COPY --from=mwader/static-ffmpeg:7.0.2 /ffprobe /usr/local/bin/
|
||||||
|
COPY --from=debian /usr/bin/uname /usr/bin/uname
|
||||||
|
|
||||||
EXPOSE 14705
|
EXPOSE 14705
|
||||||
USER nonroot
|
USER nonroot
|
||||||
|
|||||||
Reference in New Issue
Block a user