using MongoDB.Driver; using MongoDB.Entities; using PrivaPub.Federation.Actors; using PrivaPub.Models.Federation; using PrivaPub.Models.Social; using PrivaPub.Models.User; using PrivaPub.StaticServices; using System.Text.Json.Nodes; using static PrivaPub.Federation.Objects.ActivityJson; namespace PrivaPub.Federation.Inbox.Handlers { // A remote account blocks one of our personas, as Mastodon does it: the follows between them end here too (the blocker // already ended its side, so nothing is sent back), the persona stops seeing the blocker's posts and notifications, the // relationship says blocked_by, and nothing of the persona's is addressed to the blocker any more. Undo{Block} lifts it. public class BlockHandler : IActivityHandler { readonly DbEntities _dbEntities; readonly ILocalActorService _localActors; public BlockHandler(DbEntities dbEntities, ILocalActorService localActors) { _dbEntities = dbEntities; _localActors = localActors; } public string Type => "Block"; public async Task Handle(JsonNode activity, ForeignAvatar actor, CancellationToken token) { var target = Id(activity["object"]) is { } uri ? await _localActors.FindByUri(uri, token) : default; if (target is not { Kind: LocalActorKind.Person }) { Arrival.Drop("unknown-object"); return; } try { await DB.Default.SaveAsync(new BlockedBy { AvatarId = target.Id, ActorURI = actor.ActorURI, AccountId = actor.ID, ActivityURI = Id(activity) }, token); } catch (MongoWriteException ex) when (ex.WriteError?.Category == ServerErrorCategory.DuplicateKey) { Arrival.Drop("duplicate"); return; } Arrival.Accept("stored"); await DB.Default.DeleteAsync(f => f.AvatarId == target.Id && f.TargetActorURI == actor.ActorURI); await DB.Default.DeleteAsync(f => f.LocalActorId == target.Id && f.ActorURI == actor.ActorURI); await DB.Default.DeleteAsync(e => e.AvatarId == target.Id && e.AuthorAccountId == actor.ID); await DB.Default.DeleteAsync(n => n.AvatarId == target.Id && n.FromAccountId == actor.ID); } // Undo{Block}: the block it names by id, or else the blocker's block of the persona the inner Block names public static async Task Undo(JsonNode inner, string innerId, ForeignAvatar actor, ILocalActorService localActors, CancellationToken token) { var block = innerId == default ? default : await DB.Default.Find().Match(b => b.ActivityURI == innerId && b.ActorURI == actor.ActorURI).ExecuteFirstAsync(token); if (block == default && inner is JsonObject && Id(inner["object"]) is { } objectUri && await localActors.FindByUri(objectUri, token) is { Kind: LocalActorKind.Person } persona) block = await DB.Default.Find().Match(b => b.AvatarId == persona.Id && b.ActorURI == actor.ActorURI).ExecuteFirstAsync(token); if (block == default) return false; await DB.Default.DeleteAsync(block.ID); return true; } } }