using PrivaPub.Federation.Actors; using PrivaPub.Infrastructure.Statistics; using PrivaPub.Models.Federation; using PrivaPub.Models.Post; namespace PrivaPub.Federation.Inbox { // Raw: the activity as it came, when its own author sent it (ReplyRelay passes it on as it is) public sealed record Arrival(string ActivityId, string ActivityType, string ActorURI, string Inbox, string KeyId, string Algorithm, IReadOnlyList SignedHeaders, DateTime ReceivedAt, string Context = default, string Raw = default) { static readonly AsyncLocal current = new(); public static Arrival Current { get => current.Value; set => current.Value = value; } public ArrivalVerdict Verdict { get; } = new(); public static void Drop(string reason) => Current?.Verdict.Drop(reason); public static void Reject(string reason) => Current?.Verdict.Reject(reason); public static void Accept(string reason) => Current?.Verdict.Accept(reason); public static void About(string objectType = default, PostVisibility? visibility = default, DateTime? created = default, LocalActor local = default) => Current?.Verdict.About(objectType, visibility, created, local); } public sealed class ArrivalVerdict { public string Outcome { get; private set; } public string Reason { get; private set; } public string ObjectType { get; private set; } public string Audience { get; private set; } public string LocalKind { get; private set; } public long? AgeSeconds { get; private set; } public void Drop(string reason) => Decide(Interactions.Dropped, reason); public void Reject(string reason) => Decide(Interactions.Rejected, reason); public void Accept(string reason) => Decide(Interactions.Accepted, reason); void Decide(string outcome, string reason) { if (Outcome != default) return; Outcome = outcome; Reason = reason; } public void About(string objectType, PostVisibility? visibility, DateTime? created, LocalActor local) { ObjectType = objectType ?? ObjectType; if (visibility is { } known) Audience = AudienceOf(known); if (created is { } at) AgeSeconds = Math.Max(0, (long)(DateTime.UtcNow - at).TotalSeconds); if (local != default) LocalKind = local.Kind switch { LocalActorKind.Person => "person", LocalActorKind.Group when !local.IsCircle => "group", LocalActorKind.Application => "application", _ => default }; } public static string AudienceOf(PostVisibility visibility) => visibility switch { PostVisibility.Public => Interactions.Public, PostVisibility.Unlisted => Interactions.Unlisted, _ => Interactions.Private }; } }