Arrival carries a verdict that handlers set with one line before an existing return (Arrival.Drop, Reject, Accept, About), so no handler signature changes. InboxProcessor records it as an 'in' event, with the time taken, the wait since the inbox accepted it, the attempt, the audience (from the post's visibility, or else the activity's addressing), the local actor kind, the object's age for updates, deletes and reactions, and the FEP features of a delivered object. It also records types nothing handles (unknown-type), actors that cannot be loaded (deferred) and handler failures. Drop reasons: fetch-failed, unparseable, misattributed, duplicate, deleted, not-addressed, not-followed, not-public, not-visible, not-deleted, unknown-object, unknown-recipient, cross-origin, unsupported. Accepted sub-reasons: stored, poll-vote, edit, refresh, actor-refresh, actor-delete, removed, tombstone-only, auto-accepted, pending, follow-answer, quote-answer, quote-granted, undone, reaction, reported. Rejected: blocked, ignored, quote-refused. A circle's traffic is private and kindless, and a stranger posting into one is just not-addressed, so the event store cannot reveal a circle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ELjqpznMFMNrJoJUj6K5p2
81 lines
2.6 KiB
C#
81 lines
2.6 KiB
C#
using MongoDB.Entities;
|
|
|
|
using PrivaPub.Domain.Statuses;
|
|
using PrivaPub.Federation.Actors;
|
|
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
|
|
{
|
|
public class AcceptHandler : IActivityHandler
|
|
{
|
|
readonly DbEntities _dbEntities;
|
|
readonly ILocalActorService _localActors;
|
|
readonly IQuoteService _quotes;
|
|
|
|
public AcceptHandler(DbEntities dbEntities, ILocalActorService localActors, IQuoteService quotes)
|
|
{
|
|
_dbEntities = dbEntities;
|
|
_localActors = localActors;
|
|
_quotes = quotes;
|
|
}
|
|
|
|
public virtual string Type => "Accept";
|
|
|
|
public async Task Handle(JsonNode activity, ForeignAvatar actor, CancellationToken token)
|
|
{
|
|
if (await _quotes.Answered(activity, actor, accepted: Type == "Accept", token))
|
|
{
|
|
Arrival.Accept("quote-answer");
|
|
return;
|
|
}
|
|
var following = await FindFollowing(activity["object"], actor, _dbEntities, _localActors, token);
|
|
if (following == default)
|
|
{
|
|
Arrival.Drop("unknown-object");
|
|
return;
|
|
}
|
|
Arrival.About("Follow");
|
|
Arrival.Accept("follow-answer");
|
|
await Apply(following, token);
|
|
}
|
|
|
|
protected virtual Task Apply(Following following, CancellationToken token) =>
|
|
DB.Default.Update<Following>().MatchID(following.ID).Modify(f => f.State, FollowState.Accepted).ExecuteAsync(token);
|
|
|
|
public static async Task<Following> FindFollowing(JsonNode follow, ForeignAvatar target, DbEntities dbEntities, ILocalActorService localActors,
|
|
CancellationToken token)
|
|
{
|
|
if (follow is JsonObject && Value(follow, "type") is { } type && type != "Follow")
|
|
return default;
|
|
var followId = Id(follow);
|
|
var byId = followId == default
|
|
? default
|
|
: await dbEntities.Followings.Match(f => f.FollowActivityURI == followId && f.TargetActorURI == target.ActorURI).ExecuteFirstAsync(token);
|
|
if (byId != default || follow is not JsonObject)
|
|
return byId;
|
|
|
|
var follower = await localActors.FindByUri(Id(follow["actor"]), token);
|
|
return follower == default
|
|
? default
|
|
: await dbEntities.Followings.Match(f => f.AvatarId == follower.Id && f.TargetActorURI == target.ActorURI).ExecuteFirstAsync(token);
|
|
}
|
|
}
|
|
|
|
public class RejectHandler : AcceptHandler
|
|
{
|
|
public RejectHandler(DbEntities dbEntities, ILocalActorService localActors, IQuoteService quotes) : base(dbEntities, localActors, quotes)
|
|
{
|
|
}
|
|
|
|
public override string Type => "Reject";
|
|
|
|
protected override Task Apply(Following following, CancellationToken token) => DB.Default.DeleteAsync<Following>(following.ID);
|
|
}
|
|
}
|