- A Warn from a community's moderator about a persona's own post there becomes that persona's moderation_warning
notification (Mastodon's AccountWarning, with the reason and the post), believed from the community's own server or
from an account the community's moderators collection lists. Remote communities keep that collection's address
(attributedTo) as ForeignAvatar.ModeratorsURL; it is read only when a warning needs it.
- A Resolve{Flag} for one of our reports (`/grunts/flag-<report id>`) is kept as the report's remote resolution when it
comes from the server holding what was reported, or the community it was reported to; our own moderators'
resolution is never replaced. The moderators' report list shows both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
This commit is contained in:
1 parent
6fc2d001b2
commit
587997dd67
19 files changed
+344
-8
No files matched your search
@@ -0,0 +1,86 @@
|
||||
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<string> 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<string>()
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user