fix: deleting newest message renders channels unackable until a new message is posted (#899)

fix: deleting messages renders channels unackable until a new message is posted

Signed-off-by: IAmTomahawkx <iamtomahawkx@gmail.com>
This commit is contained in:
Tom
2026-08-14 14:10:05 -07:00
committed by GitHub
parent 9ab2ae92a6
commit df2e1408a1
4 changed files with 144 additions and 8 deletions

View File

@@ -1,4 +1,6 @@
use crate::{Channel, FieldsChannel, PartialChannel, revolt_result::Result, util::ChunkedDatabaseGenerator};
use crate::{
revolt_result::Result, util::ChunkedDatabaseGenerator, Channel, FieldsChannel, PartialChannel,
};
use revolt_permissions::OverrideField;
#[cfg(feature = "mongodb")]
@@ -20,7 +22,10 @@ pub trait AbstractChannels: Sync + Send {
async fn find_direct_messages(&self, user_id: &str) -> Result<Vec<Channel>>;
// Fetch all group dms for a user
async fn find_group_message_channels(&self, user_id: &str) -> Result<ChunkedDatabaseGenerator<Channel>>;
async fn find_group_message_channels(
&self,
user_id: &str,
) -> Result<ChunkedDatabaseGenerator<Channel>>;
// Fetch saved messages channel
async fn find_saved_messages_channel(&self, user_id: &str) -> Result<Channel>;
@@ -55,4 +60,16 @@ pub trait AbstractChannels: Sync + Send {
// Delete a channel
async fn delete_channel(&self, channel_id: &Channel) -> Result<()>;
// Fetch the last message in the channel
// Used to update the last_message_id when the current last_message_id is deleted.
async fn fetch_last_message(&self, channel_id: &str) -> Result<Option<String>>;
// Update the last_message_id on the channel
// Setting this to None will remove the last_message_id
async fn update_last_messsage_id(
&self,
channel_id: &str,
message_id: Option<&str>,
) -> Result<()>;
}

View File

@@ -1,5 +1,8 @@
use super::AbstractChannels;
use crate::{AbstractServers, Channel, FieldsChannel, IntoDocumentPath, MongoDb, PartialChannel, util::ChunkedDatabaseGenerator};
use crate::{
util::ChunkedDatabaseGenerator, AbstractServers, Channel, FieldsChannel, IntoDocumentPath,
MongoDb, PartialChannel,
};
use bson::{Bson, Document};
use futures::StreamExt;
use mongodb::options::ReadConcern;
@@ -71,7 +74,10 @@ impl AbstractChannels for MongoDb {
}
// Fetch all group dms for a user
async fn find_group_message_channels(&self, user_id: &str) -> Result<ChunkedDatabaseGenerator<Channel>> {
async fn find_group_message_channels(
&self,
user_id: &str,
) -> Result<ChunkedDatabaseGenerator<Channel>> {
let mut session = self
.start_session()
.await
@@ -83,7 +89,8 @@ impl AbstractChannels for MongoDb {
.await
.map_err(|_| create_database_error!("start_transaction", COL))?;
let cursor = self.col(COL)
let cursor = self
.col(COL)
.find(doc! {
"channel_type": "Group",
"recipients": user_id
@@ -304,6 +311,42 @@ impl AbstractChannels for MongoDb {
// Delete the channel itself
query!(self, delete_one_by_id, COL, channel.id()).map(|_| ())
}
async fn fetch_last_message(&self, channel_id: &str) -> Result<Option<String>> {
self.col::<Document>("messages")
.find_one(doc! {"channel": channel_id})
.sort(doc! {"_id": -1})
.projection(doc! {"_id": 1})
.await
.map(|doc| doc.map(|d| d.get("_id").expect("Missing _id").to_string()))
.map_err(|_| create_database_error!("find_one", "messages"))
}
async fn update_last_messsage_id(
&self,
channel_id: &str,
message_id: Option<&str>,
) -> Result<()> {
if let Some(message_id) = message_id {
self.col::<Document>(COL)
.update_one(
doc! {"_id": channel_id},
doc! {"$set": {"last_message_id": message_id}},
)
.await
.map(|_| ())
.map_err(|_| create_database_error!("update_one", "channels"))
} else {
self.col::<Document>(COL)
.update_one(
doc! {"_id": channel_id},
doc! {"$unset": {"last_message_id": ""}},
)
.await
.map(|_| ())
.map_err(|_| create_database_error!("update_one", "channels"))
}
}
}
impl MongoDb {

View File

@@ -1,8 +1,8 @@
use std::collections::hash_map::Entry;
use super::AbstractChannels;
use crate::ReferenceDb;
use crate::util::ChunkedDatabaseGenerator;
use crate::ReferenceDb;
use crate::{Channel, FieldsChannel, PartialChannel};
use revolt_permissions::OverrideField;
use revolt_result::Result;
@@ -53,7 +53,10 @@ impl AbstractChannels for ReferenceDb {
}
// Fetch all group dms for a user
async fn find_group_message_channels(&self, user_id: &str) -> Result<ChunkedDatabaseGenerator<Channel>> {
async fn find_group_message_channels(
&self,
user_id: &str,
) -> Result<ChunkedDatabaseGenerator<Channel>> {
let channels = self.channels.lock().await;
let groups = channels
.values()
@@ -168,7 +171,7 @@ impl AbstractChannels for ReferenceDb {
if let Some(Channel::Group { recipients, .. }) = channels.get_mut(&channel_id) {
recipients.retain(|recipient| recipient != user_id);
}
};
}
Ok(())
}
@@ -182,4 +185,41 @@ impl AbstractChannels for ReferenceDb {
Err(create_error!(NotFound))
}
}
async fn fetch_last_message(&self, channel_id: &str) -> Result<Option<String>> {
let messages = self.messages.lock().await;
let mut channel_messages: Vec<&crate::Message> = messages
.iter()
.filter(|(_, msg)| msg.channel == channel_id)
.map(|(_, msg)| msg)
.collect();
channel_messages.sort_unstable_by_key(|msg| &msg.id);
Ok(channel_messages.last().map(|msg| msg.id.clone()).or(None))
}
async fn update_last_messsage_id(
&self,
channel_id: &str,
message_id: Option<&str>,
) -> Result<()> {
let mut channels = self.channels.lock().await;
let channel = channels
.get_mut(channel_id)
.ok_or_else(|| create_error!(NotFound))?;
match channel {
Channel::DirectMessage {
last_message_id, ..
}
| Channel::Group {
last_message_id, ..
}
| Channel::TextChannel {
last_message_id, ..
} => *last_message_id = message_id.map(|id| id.to_string()),
_ => (),
};
Ok(())
}
}

View File

@@ -1012,6 +1012,42 @@ impl Message {
db.delete_message(&self.id).await?;
if let Ok(mut channel) = db.fetch_channel(&self.channel).await {
match &channel {
Channel::DirectMessage {
last_message_id, ..
}
| Channel::Group {
last_message_id, ..
}
| Channel::TextChannel {
last_message_id, ..
} => {
if last_message_id.is_some() && last_message_id.as_ref().unwrap() == &self.id {
let new_last_message_id =
db.fetch_last_message(channel.id()).await.unwrap();
db.update_last_messsage_id(channel.id(), new_last_message_id.as_deref())
.await?;
if new_last_message_id.is_some() {
EventV1::ChannelUpdate {
id: channel.id().to_string(),
data: revolt_models::v0::PartialChannel {
last_message_id: new_last_message_id,
..Default::default()
},
clear: vec![],
}
.p(channel.id().to_string())
.await;
}
}
}
_ => (),
}
}
EventV1::MessageDelete {
id: self.id.clone(),
channel: self.channel.clone(),