Membership refactor

Introduce deactivate, membership services

Move membership and user deactivation functions from api crate into those services
This commit is contained in:
dasha_uwu
2025-08-25 19:12:27 +05:00
parent 6810604629
commit 8e9c6661b2
28 changed files with 2318 additions and 2091 deletions

View File

@@ -5,15 +5,22 @@ mod profile;
use std::sync::Arc;
use futures::{Stream, StreamExt, TryFutureExt};
use futures::{FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt, future::join3};
use ruma::{
OwnedMxcUri, OwnedUserId, UserId,
OwnedMxcUri, OwnedRoomId, OwnedUserId, UserId,
api::client::filter::FilterDefinition,
events::{GlobalAccountDataEventType, ignored_user_list::IgnoredUserListEvent},
events::{
GlobalAccountDataEventType,
ignored_user_list::IgnoredUserListEvent,
room::member::{MembershipState, RoomMemberEventContent},
},
};
use tuwunel_core::{
Err, Result, debug_warn, err, is_equal_to, trace,
utils::{self, ReadyExt, stream::TryIgnore},
Err, Result, debug_warn, err, is_equal_to,
pdu::PduBuilder,
trace,
utils::{self, IterStream, ReadyExt, TryFutureExtExt, stream::TryIgnore},
warn,
};
use tuwunel_database::{Deserialized, Json, Map};
@@ -439,4 +446,124 @@ impl Service {
pub async fn auth_ldap(&self, _user_dn: &str, _password: &str) -> Result {
Err!(FeatureDisabled("ldap"))
}
pub async fn update_displayname(
&self,
user_id: &UserId,
displayname: Option<String>,
rooms: &[OwnedRoomId],
) {
let (current_avatar_url, current_blurhash, current_displayname) = join3(
self.services.users.avatar_url(user_id).ok(),
self.services.users.blurhash(user_id).ok(),
self.services.users.displayname(user_id).ok(),
)
.await;
if displayname == current_displayname {
return;
}
self.services
.users
.set_displayname(user_id, displayname.clone());
// Send a new join membership event into rooms
let avatar_url = &current_avatar_url;
let blurhash = &current_blurhash;
let displayname = &displayname;
let rooms: Vec<_> = rooms
.iter()
.try_stream()
.and_then(async |room_id: &OwnedRoomId| {
let pdu = PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
displayname: displayname.clone(),
membership: MembershipState::Join,
avatar_url: avatar_url.clone(),
blurhash: blurhash.clone(),
join_authorized_via_users_server: None,
reason: None,
is_direct: None,
third_party_invite: None,
});
Ok((pdu, room_id))
})
.ignore_err()
.collect()
.await;
self.update_all_rooms(user_id, rooms)
.boxed()
.await;
}
pub async fn update_avatar_url(
&self,
user_id: &UserId,
avatar_url: Option<OwnedMxcUri>,
blurhash: Option<String>,
rooms: &[OwnedRoomId],
) {
let (current_avatar_url, current_blurhash, current_displayname) = join3(
self.services.users.avatar_url(user_id).ok(),
self.services.users.blurhash(user_id).ok(),
self.services.users.displayname(user_id).ok(),
)
.await;
if current_avatar_url == avatar_url && current_blurhash == blurhash {
return;
}
self.services
.users
.set_avatar_url(user_id, avatar_url.clone());
self.services
.users
.set_blurhash(user_id, blurhash.clone());
// Send a new join membership event into rooms
let avatar_url = &avatar_url;
let blurhash = &blurhash;
let displayname = &current_displayname;
let rooms: Vec<_> = rooms
.iter()
.try_stream()
.and_then(async |room_id: &OwnedRoomId| {
let pdu = PduBuilder::state(user_id.to_string(), &RoomMemberEventContent {
avatar_url: avatar_url.clone(),
blurhash: blurhash.clone(),
membership: MembershipState::Join,
displayname: displayname.clone(),
join_authorized_via_users_server: None,
reason: None,
is_direct: None,
third_party_invite: None,
});
Ok((pdu, room_id))
})
.ignore_err()
.collect()
.await;
self.update_all_rooms(user_id, rooms)
.boxed()
.await;
}
async fn update_all_rooms(&self, user_id: &UserId, rooms: Vec<(PduBuilder, &OwnedRoomId)>) {
for (pdu_builder, room_id) in rooms {
let state_lock = self.services.state.mutex.lock(room_id).await;
if let Err(e) = self
.services
.timeline
.build_and_append_pdu(pdu_builder, user_id, room_id, &state_lock)
.await
{
warn!(%user_id, %room_id, "Failed to update/send new profile join membership update in room: {e}");
}
}
}
}