Blocks, mutes, bookmarks, pins and reports
- Blocks are per persona and never federate (a Block activity would tell
the other server who blocked whom): the blocked account is removed as a
follower, with a Reject{Follow} if it is remote, unfollowed, cleared from
home and notifications, and refused with Reject if it follows again.
- Mutes (optionally timed, optionally sparing notifications) and per-
persona domain blocks keep authors out of home timelines, notifications
and every status list the API returns; the boosts of a hidden author's
posts are hidden too.
- Bookmarks and pins (at most five, public or unlisted, own posts) with
their Mastodon endpoints and flags; pinned posts are the actor's
`featured` collection at /trophies, and `featuredTags` points at
/tattoos.
- Reports: /api/v1/reports stores the report and, when forwarding to a
remote account, sends Flag from the instance actor, so the reporting
persona is never named to the other server. An inbound Flag about a local
persona or its posts becomes a report; moderators list and resolve them
under /clientapi/moderator/reports.
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
525b5368a1
commit
8f75317050
20 files changed
+813
-21
No files matched your search
@@ -5,6 +5,7 @@ using MongoDB.Entities;
|
||||
|
||||
using PrivaPub.Models.Federation;
|
||||
using PrivaPub.Models.Post;
|
||||
using PrivaPub.Models.Social;
|
||||
using PrivaPub.StaticServices;
|
||||
|
||||
using System.Text.Json.Nodes;
|
||||
@@ -112,6 +113,34 @@ namespace PrivaPub.Federation.Controllers
|
||||
return local is not { IsFederated: true } ? NotFound() : Activity(ActivityPubRenderer.OrderedCollection(local.Following, 0, default));
|
||||
}
|
||||
|
||||
[HttpGet, Route("{actor}/trophies")]
|
||||
public async Task<IActionResult> Featured(string actor, CancellationToken token)
|
||||
{
|
||||
var local = await _localActors.FindByUserName(actor, token);
|
||||
if (local is not { IsFederated: true })
|
||||
return NotFound();
|
||||
var pinIds = (await DB.Default.Find<Pin>().Match(p => p.AvatarId == local.Id).Sort(p => p.ID, Order.Descending).ExecuteAsync(token))
|
||||
.Select(p => p.PostId).ToList();
|
||||
var posts = pinIds.Count == 0
|
||||
? new List<PostEntity>()
|
||||
: await _dbEntities.Posts.Match(p => pinIds.Contains(p.ID) && p.GroupUserId == local.Id && p.ReblogOfPostId == null)
|
||||
.Match(VisibilityPolicy.IsPublic).ExecuteAsync(token);
|
||||
var notes = new List<JsonNode>();
|
||||
foreach (var id in pinIds)
|
||||
if (posts.FirstOrDefault(p => p.ID == id) is { } post)
|
||||
notes.Add(ActivityPubRenderer.Note(post, local, default, post.InReplyToURI));
|
||||
return Activity(ActivityPubRenderer.OrderedCollection(local.Featured, notes.Count, notes));
|
||||
}
|
||||
|
||||
[HttpGet, Route("{actor}/tattoos")]
|
||||
public async Task<IActionResult> FeaturedTags(string actor, CancellationToken token)
|
||||
{
|
||||
var local = await _localActors.FindByUserName(actor, token);
|
||||
return local is not { IsFederated: true }
|
||||
? NotFound()
|
||||
: Activity(ActivityPubRenderer.OrderedCollection(local.FeaturedTags, 0, Enumerable.Empty<JsonNode>()));
|
||||
}
|
||||
|
||||
[HttpGet, Route("{actor}/scribbles/{postId}")]
|
||||
public async Task<IActionResult> Post(string actor, string postId, CancellationToken token)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user