72 lines
3.0 KiB
C#
72 lines
3.0 KiB
C#
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<Following>(f => f.AvatarId == target.Id && f.TargetActorURI == actor.ActorURI);
|
||
|
|
await DB.Default.DeleteAsync<Follower>(f => f.LocalActorId == target.Id && f.ActorURI == actor.ActorURI);
|
||
|
|
await DB.Default.DeleteAsync<TimelineEntry>(e => e.AvatarId == target.Id && e.AuthorAccountId == actor.ID);
|
||
|
|
await DB.Default.DeleteAsync<Notification>(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<bool> Undo(JsonNode inner, string innerId, ForeignAvatar actor, ILocalActorService localActors, CancellationToken token)
|
||
|
|
{
|
||
|
|
var block = innerId == default
|
||
|
|
? default
|
||
|
|
: await DB.Default.Find<BlockedBy>().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<BlockedBy>().Match(b => b.AvatarId == persona.Id && b.ActorURI == actor.ActorURI).ExecuteFirstAsync(token);
|
||
|
|
if (block == default)
|
||
|
|
return false;
|
||
|
|
await DB.Default.DeleteAsync<BlockedBy>(block.ID);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|