using MongoDB.Entities; using PrivaPub.Federation.Actors; using PrivaPub.Federation.Objects; using PrivaPub.Federation.Outbox; using PrivaPub.Models.Post; using PrivaPub.Models.User; using PrivaPub.StaticServices; using System.Text.Json.Nodes; using static PrivaPub.Federation.Objects.ActivityJson; using PostEntity = PrivaPub.Models.Post.Post; namespace PrivaPub.Federation.Inbox.Handlers { public class UpdateHandler : IActivityHandler { const int MaxRevisions = 20; readonly DbEntities _dbEntities; readonly ILocalActorService _localActors; readonly IRemoteActorService _remoteActors; readonly IGroupDistributor _groups; readonly IObjectRecords _records; public UpdateHandler(DbEntities dbEntities, ILocalActorService localActors, IRemoteActorService remoteActors, IGroupDistributor groups, IObjectRecords records) { _records = records; _groups = groups; _dbEntities = dbEntities; _localActors = localActors; _remoteActors = remoteActors; } public string Type => "Update"; public async Task Handle(JsonNode activity, ForeignAvatar actor, CancellationToken token) { var inner = activity["object"]; if (Id(inner) == actor.ActorURI) { await _remoteActors.GetActor(actor.ActorURI, refresh: true, token); return; } if (inner is not JsonObject || !Origin.Same(Id(inner), actor.ActorURI)) return; var note = NoteParser.Parse(inner); if (note == default || note.AttributedTo != actor.ActorURI) return; var post = await _dbEntities.Posts.Match(p => p.ObjectURI == note.Id && p.ActorURI == actor.ActorURI).ExecuteFirstAsync(token); if (post == default || post.DeletedAt.HasValue) return; post.Revisions.Add(new PostRevision { Title = post.Title, SpoilerText = post.SpoilerText, ContentHtml = post.ContentHtml, HasContentWarning = post.HasContentWarning, EditedAt = post.EditedAt ?? post.CreationDate }); if (post.Revisions.Count > MaxRevisions) post.Revisions.RemoveRange(0, post.Revisions.Count - MaxRevisions); var mentions = new List(); foreach (var mention in note.Mentions) { var local = await _localActors.FindByUri(mention.ActorURI, token); mentions.Add(new PostMention { ActorURI = mention.ActorURI, Handle = mention.Handle, IsLocal = local != default, AccountId = local?.Id }); } post.Title = note.Title; post.SpoilerText = note.SpoilerText; post.Excerpt = note.Excerpt; post.Source = note.Source; post.CoverURL = note.CoverURL; post.Link = note.Link; post.Video = note.Video; post.Audio = note.Audio; post.Event = note.Event; post.HasContentWarning = note.Sensitive; post.Text = note.ContentHtml; post.ContentHtml = note.ContentHtml; post.Language = note.Language; post.Tags = note.Tags.ToList(); post.Mentions = mentions; post.Media = note.Attachments.ToList(); post.EditedAt = note.Updated ?? DateTime.UtcNow; post.UpdateDate = DateTime.UtcNow; await DB.Default.SaveAsync(post, token); await _records.Revise(note, Id(activity), token); if (!string.IsNullOrEmpty(post.GroupId) && await _localActors.FindById(Models.Federation.LocalActorKind.Group, post.GroupId, token) is { IsCircle: false } community) await _groups.Announce(community, activity.AsObject(), post.ObjectURI, isNewPost: false, token); } } }