using MongoDB.Entities; using PrivaPub.Domain.Statuses; using PrivaPub.Federation.Actors; using PrivaPub.Federation.Objects; using PrivaPub.Federation.Outbox; using PrivaPub.Federation.Rendering; using PrivaPub.Models.Federation; using PrivaPub.Models.Group; using PrivaPub.Models.Post; using PrivaPub.Models.Social; using PrivaPub.Models.User; using PrivaPub.StaticServices; using System.Text.Json.Nodes; using static PrivaPub.Federation.Inbox.ForeignMembers; using static PrivaPub.Federation.Objects.ActivityJson; using PostEntity = PrivaPub.Models.Post.Post; namespace PrivaPub.Federation.Inbox.Handlers { public class DeleteHandler : IActivityHandler { readonly DbEntities _dbEntities; readonly ILocalActorService _localActors; readonly IRemoteActorService _remoteActors; readonly IDeliveryService _delivery; readonly IGroupDistributor _groups; readonly IQuoteService _quotes; public DeleteHandler(DbEntities dbEntities, ILocalActorService localActors, IRemoteActorService remoteActors, IDeliveryService delivery, IGroupDistributor groups, IQuoteService quotes) { _quotes = quotes; _groups = groups; _dbEntities = dbEntities; _localActors = localActors; _remoteActors = remoteActors; _delivery = delivery; } public string Type => "Delete"; public async Task Handle(JsonNode activity, ForeignAvatar actor, CancellationToken token) { var delete = activity; var objectUri = Id(delete["object"]); if (!Origin.Same(objectUri, actor.ActorURI)) { Arrival.Drop("cross-origin"); return; } if (objectUri == actor.ActorURI) { Arrival.Accept("actor-delete"); Arrival.About(actor.AvatarType.ToString(), created: actor.Published); actor.DeletionAt = DateTime.UtcNow; actor.AccountState = AvatarAccountState.Deleted; await DB.Default.SaveAsync(actor, token); var followers = await _dbEntities.Followers.Match(f => f.ActorURI == actor.ActorURI).ExecuteAsync(token); foreach (var follower in followers) { await DB.Default.DeleteAsync(follower.ID); if (follower.LocalActorKind == LocalActorKind.Group) await RemoveForeignMember(follower.LocalActorId, actor.ActorURI, token); } await DB.Default.DeleteAsync(f => f.TargetActorURI == actor.ActorURI); await DB.Default.DeleteAsync(e => e.AuthorAccountId == actor.ID); return; } await DB.Default.Update() .Match(d => d.ObjectURI == objectUri) .Modify(d => d.DeletedAt, DateTime.UtcNow) .Option(o => o.IsUpsert = true) .ExecuteAsync(token); var post = await _dbEntities.Posts.Match(p => p.ObjectURI == objectUri && p.ActorURI == actor.ActorURI).ExecuteFirstAsync(token); if (post == default) { Arrival.Accept("tombstone-only"); await _quotes.Revoke(objectUri, token); return; } Arrival.Accept("removed"); Arrival.About(post.ObjectType, post.Visibility, post.CreationDate); await DB.Default.DeleteAsync(post.ID); await DB.Default.DeleteAsync(r => r.PostId == post.ID || r.ObjectURI == objectUri); await DB.Default.DeleteAsync(e => e.PostId == post.ID || e.ReblogOfPostId == post.ID); if (!string.IsNullOrEmpty(post.GroupId) && await _localActors.FindById(LocalActorKind.Group, post.GroupId, token) is { IsCircle: false } community) await _groups.Announce(community, activity.AsObject(), post.ObjectURI, isNewPost: false, token); await DB.Default.DeleteAsync(p => p.ReblogOfPostId == post.ID); if (!string.IsNullOrEmpty(post.AnsweringToPostId)) await DB.Default.Update().MatchID(post.AnsweringToPostId).Modify(b => b.Inc(p => p.RepliesCount, -1)).ExecuteAsync(token); } } }