63 lines
3.3 KiB
C#
63 lines
3.3 KiB
C#
using MongoDB.Entities;
|
|||
|
|
|
||
|
|
using PrivaPub.Domain.Privacy;
|
||
|
|
using PrivaPub.Models.Post;
|
||
|
|
using PrivaPub.Models.Social;
|
||
|
|
using PrivaPub.Models.User;
|
||
|
|
|
||
|
|
using PostEntity = PrivaPub.Models.Post.Post;
|
||
|
|
|
||
|
|
namespace PrivaPub.Federation.Inbox
|
||
|
|
{
|
||
|
|
// A remote account that deleted itself keeps its posts, hidden (owner decision 2026-10-03), but leaves nothing in our
|
||
|
|
// numbers: its likes, downvotes, reactions and poll votes go, and the counters they had moved come back, as do the
|
||
|
|
// reblog, reply and quote counts its own posts had added. Its notifications go too. ForgottenAt makes it run once.
|
||
|
|
public static class GoneActors
|
||
|
|
{
|
||
|
|
public static async Task Forget(ForeignAvatar actor, CancellationToken token)
|
||
|
|
{
|
||
|
|
if (actor == default || actor.ForgottenAt.HasValue)
|
||
|
|
return;
|
||
|
|
var uri = actor.ActorURI;
|
||
|
|
|
||
|
|
foreach (var like in await DB.Default.Find<Favourite>().Match(f => f.ActorURI == uri).ExecuteAsync(token))
|
||
|
|
{
|
||
|
|
await DB.Default.Update<PostEntity>().MatchID(like.PostId).Modify(b => b.Inc(p => p.FavouritesCount, -1)).ExecuteAsync(token);
|
||
|
|
await DB.Default.DeleteAsync<Favourite>(like.ID);
|
||
|
|
}
|
||
|
|
foreach (var down in await DB.Default.Find<Downvote>().Match(d => d.ActorURI == uri).ExecuteAsync(token))
|
||
|
|
{
|
||
|
|
await DB.Default.Update<PostEntity>().MatchID(down.PostId).Modify(b => b.Inc(p => p.DownvotesCount, -1)).ExecuteAsync(token);
|
||
|
|
await DB.Default.DeleteAsync<Downvote>(down.ID);
|
||
|
|
}
|
||
|
|
await DB.Default.DeleteAsync<Reaction>(r => r.ActorURI == uri);
|
||
|
|
foreach (var ballot in (await DB.Default.Find<PollVote>().Match(v => v.ActorURI == uri).ExecuteAsync(token)).GroupBy(v => v.PostId))
|
||
|
|
{
|
||
|
|
var poll = await DB.Default.Find<PostEntity>().MatchID(ballot.Key).ExecuteFirstAsync(token);
|
||
|
|
if (poll?.Poll != default)
|
||
|
|
{
|
||
|
|
var update = DB.Default.Update<PostEntity>().MatchID(poll.ID);
|
||
|
|
foreach (var vote in ballot.Where(v => v.Choice >= 0 && v.Choice < poll.Poll.Options.Count))
|
||
|
|
update = update.Modify(b => b.Inc($"{nameof(PostEntity.Poll)}.{nameof(PostPoll.Options)}.{vote.Choice}.{nameof(PollOption.Votes)}", -1));
|
||
|
|
if (poll.Poll.VotersCount.HasValue)
|
||
|
|
update = update.Modify(b => b.Inc($"{nameof(PostEntity.Poll)}.{nameof(PostPoll.VotersCount)}", -1));
|
||
|
|
await update.ExecuteAsync(token);
|
||
|
|
}
|
||
|
|
await DB.Default.DeleteAsync<PollVote>(v => v.PostId == ballot.Key && v.ActorURI == uri);
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var post in await DB.Default.Find<PostEntity>().Match(p => p.ActorURI == uri && !p.DeletedAt.HasValue).ExecuteAsync(token))
|
||
|
|
{
|
||
|
|
if (!string.IsNullOrEmpty(post.ReblogOfPostId))
|
||
|
|
await DB.Default.Update<PostEntity>().MatchID(post.ReblogOfPostId).Modify(b => b.Inc(p => p.ReblogsCount, -1)).ExecuteAsync(token);
|
||
|
|
if (!string.IsNullOrEmpty(post.AnsweringToPostId) && Counted.Reply(post))
|
||
|
|
await DB.Default.Update<PostEntity>().MatchID(post.AnsweringToPostId).Modify(b => b.Inc(p => p.RepliesCount, -1)).ExecuteAsync(token);
|
||
|
|
if (!string.IsNullOrEmpty(post.QuotedPostId) && post.QuoteState == QuoteState.Accepted)
|
||
|
|
await DB.Default.Update<PostEntity>().MatchID(post.QuotedPostId).Modify(b => b.Inc(p => p.QuotesCount, -1)).ExecuteAsync(token);
|
||
|
|
}
|
||
|
|
|
||
|
|
await DB.Default.DeleteAsync<Notification>(n => n.FromActorURI == uri);
|
||
|
|
await DB.Default.Update<ForeignAvatar>().MatchID(actor.ID).Modify(a => a.ForgottenAt, DateTime.UtcNow).ExecuteAsync(token);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|