using PrivaPub.Federation.Actors; using PrivaPub.Models.User; using System.Text.Json.Nodes; namespace PrivaPub.Federation.Inbox.Handlers { // An account pins one of its posts (Add to its `featured` collection) or unpins it (Remove), as Mastodon and Akkoma // send it; inside a community's announce, the community features a post made in it. A Hashtag added to or removed from // its `featured` collection (as Mastodon sends it) or its `featuredTags` is a featured tag (R8). On its wall // (Smithereen's, FEP-400e) it tells of a post someone wrote there, or takes it off. Any other target (a community's // moderators) is not ours to keep. public class FeaturedHandler : IActivityHandler { readonly IFeaturedPosts _featured; readonly IWalls _walls; readonly IRemoteActorService _remoteActors; readonly Infrastructure.Jobs.IJobQueue _queue; readonly Domain.Social.IFeaturedTags _tags; readonly bool _add; public FeaturedHandler(IFeaturedPosts featured, bool add, IWalls walls = default, IRemoteActorService remoteActors = default, Infrastructure.Jobs.IJobQueue queue = default, Domain.Social.IFeaturedTags tags = default) { _tags = tags; _featured = featured; _walls = walls; _remoteActors = remoteActors; _queue = queue; _add = add; } public string Type => _add ? "Add" : "Remove"; public async Task Handle(JsonNode activity, ForeignAvatar actor, CancellationToken token) { if (_add && _queue != default && await ConversationContainers.Unwrap(activity, actor, _queue, token)) return; var target = Objects.ActivityJson.Id(activity["target"]); // a collection of its own we do not know of: its document read again, at most hourly, in case it was kept before // we read that collection (a wall) if (_remoteActors != default && target != default && target != actor.FeaturedURL && target != actor.WallURL && target != actor.FeaturedTagsURL && Objects.Origin.Same(target, actor.ActorURI) && actor.UpdatedAt < DateTime.UtcNow.AddHours(-1)) actor = await _remoteActors.GetActor(actor.ActorURI, refresh: true, token) ?? actor; // a featured hashtag: an object of its own, with no id (its href is no post), so before anything reads it as one if (Objects.ActivityJson.Value(activity["object"], "type") == "Hashtag") { if (_tags != default && target != default && (target == actor.FeaturedURL || target == actor.FeaturedTagsURL)) await _tags.Receive(activity, actor, _add, token); else Arrival.Drop("unknown-target"); return; } if (_walls != default && !string.IsNullOrEmpty(actor.WallURL) && target == actor.WallURL) await _walls.Receive(activity, actor, _add, token); else await _featured.Receive(activity, actor, _add, token); } } }