using MongoDB.Entities; using PrivaPub.Domain.Social; 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 { // Lemmy 1.0's warning: a community's moderator tells one of our personas, about one of its posts there, why (Warn{to: // [persona], object: post, summary: reason, audience: community}), sent to the persona's inbox alone. It becomes the // persona's moderation_warning notification, once the sender is shown to moderate that community and the post is the // persona's own, in it. public class WarnHandler : IActivityHandler { const int MaxReason = 1000; readonly DbEntities _dbEntities; readonly ILocalActorService _localActors; readonly IRemoteActorService _remoteActors; public WarnHandler(DbEntities dbEntities, ILocalActorService localActors, IRemoteActorService remoteActors) { _dbEntities = dbEntities; _localActors = localActors; _remoteActors = remoteActors; } public string Type => "Warn"; public async Task Handle(JsonNode activity, ForeignAvatar actor, CancellationToken token) { LocalActor persona = default; foreach (var uri in Recipients(activity["to"])) if (await _localActors.FindByUri(uri, token) is { Kind: LocalActorKind.Person } found) { persona = found; break; } if (persona == default) { Arrival.Drop("not-ours"); return; } var communityUri = Id(activity["audience"]); var community = communityUri == default ? default : await _remoteActors.GetActor(communityUri, refresh: false, token); if (community is not { AvatarType: AvatarType.Group }) { Arrival.Drop("unknown-community"); return; } var objectUri = Id(activity["object"]); var post = objectUri == default ? default : await _dbEntities.Posts.Match(p => p.ObjectURI == objectUri && p.GroupUserId == persona.Id && !p.IsFederatedCopy).ExecuteFirstAsync(token); if (post == default || await Communities.Of(post, _dbEntities, token) != community.ActorURI) { Arrival.Drop("unknown-object"); return; } Arrival.About(visibility: post.Visibility, created: post.CreationDate, local: persona); if (!await Communities.Moderates(community, actor.ActorURI, _remoteActors, token)) { Arrival.Drop("not-moderator"); return; } var reason = Value(activity, "summary") ?? Value(activity, "content"); await Notifications.Add(persona.Id, NotificationType.ModerationWarning, community.ID, community.ActorURI, post.ID, token, text: string.IsNullOrWhiteSpace(reason) ? default : reason.Trim().Length <= MaxReason ? reason.Trim() : reason.Trim()[..MaxReason]); Arrival.Accept("warned"); } static IEnumerable Recipients(JsonNode node) => node switch { JsonArray array => array.Select(Id).Where(id => id != default), JsonNode single when Id(single) is { } id => new[] { id }, _ => Enumerable.Empty() }; } }