diff --git a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs index f14215e8..87ecea90 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityPub/ActivityHandlerService.cs @@ -122,6 +122,20 @@ public class ActivityHandlerService( await noteSvc.LikeNoteAsync(note, activity.Actor); return; } + case ASUpdate: { + switch (activity.Object) { + case ASActor actor: + if (actor.Id != activity.Actor.Id) + throw GracefulException.UnprocessableEntity("Refusing to update actor with mismatching id"); + await userSvc.UpdateUserAsync(resolvedActor, actor); + return; + case ASNote note: + throw new NotImplementedException("Note updates haven't been implemented yet"); + //return; + default: + throw GracefulException.UnprocessableEntity("Update activity object is invalid"); + } + } default: { throw new NotImplementedException($"Activity type {activity.Type} is unknown"); } diff --git a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASActivity.cs b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASActivity.cs index bf91731e..74aba627 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASActivity.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASActivity.cs @@ -17,6 +17,7 @@ public class ASActivity : ASObject { private const string Ns = Constants.ActivityStreamsNs; public const string Create = $"{Ns}#Create"; + public const string Update = $"{Ns}#Update"; public const string Delete = $"{Ns}#Delete"; public const string Follow = $"{Ns}#Follow"; public const string Unfollow = $"{Ns}#Unfollow"; @@ -59,6 +60,10 @@ public class ASLike : ASActivity { public ASLike() => Type = Types.Like; } +public class ASUpdate : ASActivity { + public ASUpdate() => Type = Types.Update; +} + //TODO: add the rest public sealed class ASActivityConverter : ASSerializer.ListSingleObjectConverter; \ No newline at end of file diff --git a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASObject.cs b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASObject.cs index 63a72d73..1612a6ce 100644 --- a/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASObject.cs +++ b/Iceshrimp.Backend/Core/Federation/ActivityStreams/Types/ASObject.cs @@ -37,6 +37,7 @@ public class ASObject : ASObjectBase { ASNote.Types.Note => token.ToObject(), Types.Tombstone => token.ToObject(), ASActivity.Types.Create => token.ToObject(), + ASActivity.Types.Update => token.ToObject(), ASActivity.Types.Delete => token.ToObject(), ASActivity.Types.Follow => token.ToObject(), ASActivity.Types.Unfollow => token.ToObject(), diff --git a/Iceshrimp.Backend/Core/Services/UserService.cs b/Iceshrimp.Backend/Core/Services/UserService.cs index e5cf21b7..ff45850d 100644 --- a/Iceshrimp.Backend/Core/Services/UserService.cs +++ b/Iceshrimp.Backend/Core/Services/UserService.cs @@ -145,8 +145,10 @@ public class UserService( } } - public async Task UpdateUserAsync(User user) { - if (!user.NeedsUpdate) return user; + public async Task UpdateUserAsync(User user, ASActor? actor = null) { + if (!user.NeedsUpdate && actor == null) return user; + if (actor is { IsUnresolved: true } or { Username: null }) + actor = null; // This will trigger a fetch a couple lines down // Prevent multiple update jobs from running concurrently db.Update(user); @@ -156,7 +158,7 @@ public class UserService( var uri = user.Uri ?? throw new Exception("Encountered remote user without a Uri"); logger.LogDebug("Updating user with uri {uri}", uri); - var actor = await fetchSvc.FetchActorAsync(user.Uri); + actor ??= await fetchSvc.FetchActorAsync(user.Uri); actor.Normalize(uri, user.AcctWithPrefix); user.UserProfile ??= await db.UserProfiles.FirstOrDefaultAsync(p => p.User == user);