feat: keep track of remote profiles for user directory and local requests
Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
parent
ef8dcdfe3c
commit
b28a2fad97
6 changed files with 110 additions and 36 deletions
|
@ -1426,7 +1426,7 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<Strin
|
||||||
.update_membership(
|
.update_membership(
|
||||||
room_id,
|
room_id,
|
||||||
user_id,
|
user_id,
|
||||||
MembershipState::Leave,
|
RoomMemberEventContent::new(MembershipState::Leave),
|
||||||
user_id,
|
user_id,
|
||||||
last_state,
|
last_state,
|
||||||
true,
|
true,
|
||||||
|
@ -1461,7 +1461,7 @@ pub async fn leave_room(user_id: &UserId, room_id: &RoomId, reason: Option<Strin
|
||||||
.update_membership(
|
.update_membership(
|
||||||
room_id,
|
room_id,
|
||||||
user_id,
|
user_id,
|
||||||
MembershipState::Leave,
|
RoomMemberEventContent::new(MembershipState::Leave),
|
||||||
user_id,
|
user_id,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -103,15 +103,18 @@ pub async fn set_displayname_route(
|
||||||
Ok(set_display_name::v3::Response {})
|
Ok(set_display_name::v3::Response {})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/profile/{userId}/displayname`
|
/// # `GET /_matrix/client/v3/profile/{userId}/displayname`
|
||||||
///
|
///
|
||||||
/// Returns the displayname of the user.
|
/// Returns the displayname of the user.
|
||||||
///
|
///
|
||||||
/// - If user is on another server: Fetches displayname over federation
|
/// - If user is on another server and we do not have a local copy already
|
||||||
|
/// fetch displayname over federation
|
||||||
pub async fn get_displayname_route(
|
pub async fn get_displayname_route(
|
||||||
body: Ruma<get_display_name::v3::Request>,
|
body: Ruma<get_display_name::v3::Request>,
|
||||||
) -> Result<get_display_name::v3::Response> {
|
) -> Result<get_display_name::v3::Response> {
|
||||||
if body.user_id.server_name() != services().globals.server_name() {
|
if (services().users.exists(&body.user_id)?)
|
||||||
|
&& (body.user_id.server_name() != services().globals.server_name())
|
||||||
|
{
|
||||||
let response = services()
|
let response = services()
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
|
@ -123,6 +126,18 @@ pub async fn get_displayname_route(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// Create and update our local copy of the user
|
||||||
|
services().users.create(&body.user_id, None)?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_displayname(&body.user_id, response.displayname.clone())?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_avatar_url(&body.user_id, response.avatar_url)?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_blurhash(&body.user_id, response.blurhash)?;
|
||||||
|
|
||||||
return Ok(get_display_name::v3::Response {
|
return Ok(get_display_name::v3::Response {
|
||||||
displayname: response.displayname,
|
displayname: response.displayname,
|
||||||
});
|
});
|
||||||
|
@ -225,15 +240,18 @@ pub async fn set_avatar_url_route(
|
||||||
Ok(set_avatar_url::v3::Response {})
|
Ok(set_avatar_url::v3::Response {})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/profile/{userId}/avatar_url`
|
/// # `GET /_matrix/client/v3/profile/{userId}/avatar_url`
|
||||||
///
|
///
|
||||||
/// Returns the avatar_url and blurhash of the user.
|
/// Returns the avatar_url and blurhash of the user.
|
||||||
///
|
///
|
||||||
/// - If user is on another server: Fetches avatar_url and blurhash over federation
|
/// - If user is on another server and we do not have a local copy already
|
||||||
|
/// fetch avatar_url and blurhash over federation
|
||||||
pub async fn get_avatar_url_route(
|
pub async fn get_avatar_url_route(
|
||||||
body: Ruma<get_avatar_url::v3::Request>,
|
body: Ruma<get_avatar_url::v3::Request>,
|
||||||
) -> Result<get_avatar_url::v3::Response> {
|
) -> Result<get_avatar_url::v3::Response> {
|
||||||
if body.user_id.server_name() != services().globals.server_name() {
|
if (services().users.exists(&body.user_id)?)
|
||||||
|
&& (body.user_id.server_name() != services().globals.server_name())
|
||||||
|
{
|
||||||
let response = services()
|
let response = services()
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
|
@ -245,6 +263,18 @@ pub async fn get_avatar_url_route(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// Create and update our local copy of the user
|
||||||
|
services().users.create(&body.user_id, None)?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_displayname(&body.user_id, response.displayname)?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_avatar_url(&body.user_id, response.avatar_url.clone())?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_blurhash(&body.user_id, response.blurhash.clone())?;
|
||||||
|
|
||||||
return Ok(get_avatar_url::v3::Response {
|
return Ok(get_avatar_url::v3::Response {
|
||||||
avatar_url: response.avatar_url,
|
avatar_url: response.avatar_url,
|
||||||
blurhash: response.blurhash,
|
blurhash: response.blurhash,
|
||||||
|
@ -257,15 +287,18 @@ pub async fn get_avatar_url_route(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # `GET /_matrix/client/r0/profile/{userId}`
|
/// # `GET /_matrix/client/v3/profile/{userId}`
|
||||||
///
|
///
|
||||||
/// Returns the displayname, avatar_url and blurhash of the user.
|
/// Returns the displayname, avatar_url and blurhash of the user.
|
||||||
///
|
///
|
||||||
/// - If user is on another server: Fetches profile over federation
|
/// - If user is on another server and we do not have a local copy already,
|
||||||
|
/// fetch profile over federation.
|
||||||
pub async fn get_profile_route(
|
pub async fn get_profile_route(
|
||||||
body: Ruma<get_profile::v3::Request>,
|
body: Ruma<get_profile::v3::Request>,
|
||||||
) -> Result<get_profile::v3::Response> {
|
) -> Result<get_profile::v3::Response> {
|
||||||
if body.user_id.server_name() != services().globals.server_name() {
|
if (services().users.exists(&body.user_id)?)
|
||||||
|
&& (body.user_id.server_name() != services().globals.server_name())
|
||||||
|
{
|
||||||
let response = services()
|
let response = services()
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
|
@ -277,6 +310,18 @@ pub async fn get_profile_route(
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
// Create and update our local copy of the user
|
||||||
|
services().users.create(&body.user_id, None)?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_displayname(&body.user_id, response.displayname.clone())?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_avatar_url(&body.user_id, response.avatar_url.clone())?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_blurhash(&body.user_id, response.blurhash.clone())?;
|
||||||
|
|
||||||
return Ok(get_profile::v3::Response {
|
return Ok(get_profile::v3::Response {
|
||||||
displayname: response.displayname,
|
displayname: response.displayname,
|
||||||
avatar_url: response.avatar_url,
|
avatar_url: response.avatar_url,
|
||||||
|
@ -285,7 +330,7 @@ pub async fn get_profile_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !services().users.exists(&body.user_id)? {
|
if !services().users.exists(&body.user_id)? {
|
||||||
// Return 404 if this user doesn't exist
|
// Return 404 if this user doesn't exist and we couldn't fetch it over federation
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::NotFound,
|
ErrorKind::NotFound,
|
||||||
"Profile was not found.",
|
"Profile was not found.",
|
||||||
|
|
|
@ -1883,7 +1883,7 @@ pub async fn create_invite_route(
|
||||||
.update_membership(
|
.update_membership(
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&invited_user,
|
&invited_user,
|
||||||
MembershipState::Invite,
|
RoomMemberEventContent::new(MembershipState::Invite),
|
||||||
&sender,
|
&sender,
|
||||||
Some(invite_state),
|
Some(invite_state),
|
||||||
true,
|
true,
|
||||||
|
|
|
@ -8,14 +8,13 @@ pub use data::Data;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::client::error::ErrorKind,
|
api::client::error::ErrorKind,
|
||||||
events::{
|
events::{
|
||||||
room::{create::RoomCreateEventContent, member::MembershipState},
|
room::{create::RoomCreateEventContent, member::RoomMemberEventContent},
|
||||||
AnyStrippedStateEvent, StateEventType, TimelineEventType,
|
AnyStrippedStateEvent, StateEventType, TimelineEventType,
|
||||||
},
|
},
|
||||||
serde::Raw,
|
serde::Raw,
|
||||||
state_res::{self, StateMap},
|
state_res::{self, StateMap},
|
||||||
EventId, OwnedEventId, RoomId, RoomVersionId, UserId,
|
EventId, OwnedEventId, RoomId, RoomVersionId, UserId,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
|
||||||
use tokio::sync::MutexGuard;
|
use tokio::sync::MutexGuard;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
||||||
|
@ -59,14 +58,9 @@ impl Service {
|
||||||
|
|
||||||
match pdu.kind {
|
match pdu.kind {
|
||||||
TimelineEventType::RoomMember => {
|
TimelineEventType::RoomMember => {
|
||||||
#[derive(Deserialize)]
|
let membership_event =
|
||||||
struct ExtractMembership {
|
match serde_json::from_str::<RoomMemberEventContent>(pdu.content.get()) {
|
||||||
membership: MembershipState,
|
Ok(e) => e,
|
||||||
}
|
|
||||||
|
|
||||||
let membership =
|
|
||||||
match serde_json::from_str::<ExtractMembership>(pdu.content.get()) {
|
|
||||||
Ok(e) => e.membership,
|
|
||||||
Err(_) => continue,
|
Err(_) => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,7 +77,14 @@ impl Service {
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.update_membership(room_id, &user_id, membership, &pdu.sender, None, false)
|
.update_membership(
|
||||||
|
room_id,
|
||||||
|
&user_id,
|
||||||
|
membership_event,
|
||||||
|
&pdu.sender,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
TimelineEventType::SpaceChild => {
|
TimelineEventType::SpaceChild => {
|
||||||
|
|
|
@ -4,10 +4,14 @@ use std::{collections::HashSet, sync::Arc};
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
|
api::federation::{self, query::get_profile_information::v1::ProfileField},
|
||||||
events::{
|
events::{
|
||||||
direct::DirectEvent,
|
direct::DirectEvent,
|
||||||
ignored_user_list::IgnoredUserListEvent,
|
ignored_user_list::IgnoredUserListEvent,
|
||||||
room::{create::RoomCreateEventContent, member::MembershipState},
|
room::{
|
||||||
|
create::RoomCreateEventContent,
|
||||||
|
member::{MembershipState, RoomMemberEventContent},
|
||||||
|
},
|
||||||
AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType,
|
AnyStrippedStateEvent, AnySyncStateEvent, GlobalAccountDataEventType,
|
||||||
RoomAccountDataEventType, StateEventType,
|
RoomAccountDataEventType, StateEventType,
|
||||||
},
|
},
|
||||||
|
@ -29,15 +33,39 @@ impl Service {
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
membership: MembershipState,
|
membership_event: RoomMemberEventContent,
|
||||||
sender: &UserId,
|
sender: &UserId,
|
||||||
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
|
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
|
||||||
update_joined_count: bool,
|
update_joined_count: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
|
let membership = membership_event.membership;
|
||||||
// Keep track what remote users exist by adding them as "deactivated" users
|
// Keep track what remote users exist by adding them as "deactivated" users
|
||||||
if user_id.server_name() != services().globals.server_name() {
|
if user_id.server_name() != services().globals.server_name() {
|
||||||
services().users.create(user_id, None)?;
|
services().users.create(user_id, None)?;
|
||||||
// TODO: displayname, avatar url
|
// Try to update our local copy of the user if ours does not match
|
||||||
|
if ((services().users.displayname(user_id)? != membership_event.displayname)
|
||||||
|
|| (services().users.avatar_url(user_id)? != membership_event.avatar_url)
|
||||||
|
|| (services().users.blurhash(user_id)? != membership_event.blurhash))
|
||||||
|
&& (membership != MembershipState::Leave)
|
||||||
|
{
|
||||||
|
let response = services()
|
||||||
|
.sending
|
||||||
|
.send_federation_request(
|
||||||
|
user_id.server_name(),
|
||||||
|
federation::query::get_profile_information::v1::Request {
|
||||||
|
user_id: user_id.into(),
|
||||||
|
field: Some(ProfileField::AvatarUrl),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_displayname(user_id, response.displayname.clone())?;
|
||||||
|
services()
|
||||||
|
.users
|
||||||
|
.set_avatar_url(user_id, response.avatar_url)?;
|
||||||
|
services().users.set_blurhash(user_id, response.blurhash)?;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match &membership {
|
match &membership {
|
||||||
|
|
|
@ -18,7 +18,9 @@ use ruma::{
|
||||||
events::{
|
events::{
|
||||||
push_rules::PushRulesEvent,
|
push_rules::PushRulesEvent,
|
||||||
room::{
|
room::{
|
||||||
create::RoomCreateEventContent, encrypted::Relation, member::MembershipState,
|
create::RoomCreateEventContent,
|
||||||
|
encrypted::Relation,
|
||||||
|
member::{MembershipState, RoomMemberEventContent},
|
||||||
power_levels::RoomPowerLevelsEventContent,
|
power_levels::RoomPowerLevelsEventContent,
|
||||||
},
|
},
|
||||||
GlobalAccountDataEventType, StateEventType, TimelineEventType,
|
GlobalAccountDataEventType, StateEventType, TimelineEventType,
|
||||||
|
@ -453,17 +455,15 @@ impl Service {
|
||||||
}
|
}
|
||||||
TimelineEventType::RoomMember => {
|
TimelineEventType::RoomMember => {
|
||||||
if let Some(state_key) = &pdu.state_key {
|
if let Some(state_key) = &pdu.state_key {
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct ExtractMembership {
|
|
||||||
membership: MembershipState,
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the state_key fails
|
// if the state_key fails
|
||||||
let target_user_id = UserId::parse(state_key.clone())
|
let target_user_id = UserId::parse(state_key.clone())
|
||||||
.expect("This state_key was previously validated");
|
.expect("This state_key was previously validated");
|
||||||
|
|
||||||
let content = serde_json::from_str::<ExtractMembership>(pdu.content.get())
|
let content = serde_json::from_str::<RoomMemberEventContent>(pdu.content.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
|
.map_err(|e| {
|
||||||
|
error!("Invalid room member event content in pdu: {e}");
|
||||||
|
Error::bad_database("Invalid room member event content in pdu.")
|
||||||
|
})?;
|
||||||
|
|
||||||
let invite_state = match content.membership {
|
let invite_state = match content.membership {
|
||||||
MembershipState::Invite => {
|
MembershipState::Invite => {
|
||||||
|
@ -481,7 +481,7 @@ impl Service {
|
||||||
.update_membership(
|
.update_membership(
|
||||||
&pdu.room_id,
|
&pdu.room_id,
|
||||||
&target_user_id,
|
&target_user_id,
|
||||||
content.membership,
|
content,
|
||||||
&pdu.sender,
|
&pdu.sender,
|
||||||
invite_state,
|
invite_state,
|
||||||
true,
|
true,
|
||||||
|
|
Loading…
Add table
Reference in a new issue