feat: Distinguish between ignore and block internally

This commit is contained in:
Ginger committed 2025-09-11 16:21:03 +00:00
1 parent 00d369c441
commit 3c941734cd
10 files changed
+98 -35

No files matched your search

+6 -7
View File
@@ -48,14 +48,13 @@ pub(crate) async fn invite_user_route(
match &body.recipient {
| invite_user::v3::InvitationRecipient::UserId { user_id } => {
let sender_ignored_recipient = services.users.user_is_ignored(sender_user, user_id);
let recipient_ignored_by_sender =
services.users.user_is_ignored(user_id, sender_user);
let sender_filter_level = services.users.user_filter_level(sender_user, user_id);
let recipient_filter_level = services.users.user_filter_level(user_id, sender_user);
let (sender_ignored_recipient, recipient_ignored_by_sender) =
join!(sender_ignored_recipient, recipient_ignored_by_sender);
let (sender_filter_level, recipient_filter_level) =
join!(sender_filter_level, recipient_filter_level);
if sender_ignored_recipient {
if !sender_filter_level.allowed() {
return Ok(invite_user::v3::Response {});
}
@@ -70,7 +69,7 @@ pub(crate) async fn invite_user_route(
}
}
if recipient_ignored_by_sender {
if !recipient_filter_level.allowed() {
// silently drop the invite to the recipient if they've been ignored by the
// sender, pretend it worked
return Ok(invite_user::v3::Response {});
+5 -4
View File
@@ -286,11 +286,12 @@ where
if ignored_type
&& (ignored_server
|| (!services.config.send_messages_from_ignored_users_to_client
&& services
|| !(services.config.send_messages_from_ignored_users_to_client
|| services
.users
.user_is_ignored(event.sender(), user_id)
.await))
.user_filter_level(event.sender(), user_id)
.await
.allowed()))
{
return true;
}
+26 -4
View File
@@ -223,9 +223,20 @@ pub(crate) async fn create_room_route(
if preset == RoomPreset::TrustedPrivateChat {
for invite in &body.invite {
if services.users.user_is_ignored(sender_user, invite).await {
if !services
.users
.user_filter_level(sender_user, invite)
.await
.allowed()
{
// don't send invites to users the sender has ignored
continue;
} else if services.users.user_is_ignored(invite, sender_user).await {
} else if !services
.users
.user_filter_level(invite, sender_user)
.await
.allowed()
{
// silently drop the invite to the recipient if they've been ignored by the
// sender, pretend it worked
continue;
@@ -400,9 +411,20 @@ pub(crate) async fn create_room_route(
// 8. Events implied by invite (and TODO: invite_3pid)
drop(state_lock);
for user_id in &body.invite {
if services.users.user_is_ignored(sender_user, user_id).await {
if !services
.users
.user_filter_level(sender_user, user_id)
.await
.allowed()
{
// don't send invites to users the sender has ignored
continue;
} else if services.users.user_is_ignored(user_id, sender_user).await {
} else if !services
.users
.user_filter_level(user_id, sender_user)
.await
.allowed()
{
// silently drop the invite to the recipient if they've been ignored by the
// sender, pretend it worked
continue;
+3 -2
View File
@@ -624,9 +624,10 @@ async fn load_joined_room(
.filter_map(|(read_user, _, edu)| async move {
services
.users
.user_is_ignored(read_user, sender_user)
.user_filter_level(read_user, sender_user)
.await
.or_some((read_user.to_owned(), edu))
.allowed()
.then_some((read_user.to_owned(), edu))
})
.collect::<HashMap<OwnedUserId, Raw<AnySyncEphemeralRoomEvent>>>()
.map(Ok);
+3 -2
View File
@@ -558,9 +558,10 @@ pub(crate) async fn sync_events_v4_route(
.filter_map(|(read_user, _ts, v)| async move {
services
.users
.user_is_ignored(read_user, sender_user)
.user_filter_level(read_user, sender_user)
.await
.or_some(v)
.allowed()
.then_some(v)
})
.collect()
.await;
+3 -2
View File
@@ -455,9 +455,10 @@ where
.filter_map(|(read_user, _ts, v)| async move {
services
.users
.user_is_ignored(read_user, sender_user)
.user_filter_level(read_user, sender_user)
.await
.or_some(v)
.allowed()
.then_some(v)
})
.collect()
.await;
+7 -1
View File
@@ -122,7 +122,13 @@ pub async fn update_membership(
},
| MembershipState::Invite => {
// We want to know if the sender is ignored by the receiver
if self.services.users.user_is_ignored(sender, user_id).await {
if !self
.services
.users
.user_filter_level(sender, user_id)
.await
.allowed()
{
return Ok(());
}
+2 -2
View File
@@ -193,9 +193,9 @@ where
.state_cache
.active_local_users_in_room(pdu.room_id())
.map(ToOwned::to_owned)
// Don't notify the sender of their own events, and dont send from ignored users
// Don't notify the sender of their own events, and don't send from ignored users
.ready_filter(|user| *user != pdu.sender())
.filter_map(|recipient_user| async move { (!self.services.users.user_is_ignored(pdu.sender(), &recipient_user).await).then_some(recipient_user) })
.filter_map(|recipient_user| async move { (self.services.users.user_filter_level(pdu.sender(), &recipient_user).await.allowed()).then_some(recipient_user) })
.collect()
.await;
+9 -5
View File
@@ -12,7 +12,11 @@ use ruma::{
};
use tokio::sync::{RwLock, broadcast};
use crate::{Dep, globals, sending, sending::EduBuf, users};
use crate::{
Dep, globals,
sending::{self, EduBuf},
users::{self},
};
pub struct Service {
server: Arc<Server>,
@@ -197,11 +201,11 @@ impl Service {
.into_keys()
.stream()
.filter_map(|typing_user_id| async move {
(!self
.services
self.services
.users
.user_is_ignored(&typing_user_id, sender_user)
.await)
.user_filter_level(&typing_user_id, sender_user)
.await
.allowed()
.then_some(typing_user_id)
})
.collect()
+34 -6
View File
@@ -39,6 +39,22 @@ pub struct UserSuspension {
pub suspended_by: String,
}
/// Represents a user's level of filtering on actions from another user.
/// "Ignore" and "block" are defined in [MSC4283].
/// This may be moved into Ruwuma in the future.
///
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UserFilterLevel {
Allow,
Ignore,
Block,
}
impl UserFilterLevel {
pub fn allowed(&self) -> bool { return *self == UserFilterLevel::Allow; }
}
pub struct Service {
services: Services,
db: Data,
@@ -123,20 +139,32 @@ impl crate::Service for Service {
}
impl Service {
/// Returns true/false based on whether the recipient/receiving user has
/// blocked the sender
pub async fn user_is_ignored(&self, sender_user: &UserId, recipient_user: &UserId) -> bool {
/// Returns a UserActionResult based on whether the recipient/receiving user
/// has ignored or blocked the sender
pub async fn user_filter_level(
&self,
sender_user: &UserId,
recipient_user: &UserId,
) -> UserFilterLevel {
// can only return Allow or Ignore for now
self.services
.account_data
.get_global(recipient_user, GlobalAccountDataEventType::IgnoredUserList)
.await
.is_ok_and(|ignored: IgnoredUserListEvent| {
ignored
.map(|ignored: IgnoredUserListEvent| {
let sender_ignored_recipient = ignored
.content
.ignored_users
.keys()
.any(|blocked_user| blocked_user == sender_user)
.any(|blocked_user| blocked_user == sender_user);
if sender_ignored_recipient {
UserFilterLevel::Ignore
} else {
UserFilterLevel::Allow
}
})
.unwrap_or(UserFilterLevel::Allow)
}
/// Check if a user is an admin