A persona's notification policy and its requests

Mastodon 4.3's policy was a stub that accepted everything. It is now kept per persona: notifications from accounts it
does not follow, accounts that do not follow it (or only for three days), accounts newer than 30 days, private mentions
it did not ask for and silenced accounts are accepted, filtered or dropped. Filtered ones stay out of every list and
count unless asked for, gathered in a request per account; letting a request in lets that account in for good,
dismissing it deletes them. Everything is accepted until the persona chooses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-06 15:48:33 +02:00
1 parent 9872b78677
commit 5d5f1d6909
10 files changed
+511 -29

No files matched your search

@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using MongoDB.Driver;
using MongoDB.Entities;
using PrivaPub.Api.Mastodon.Entities;
@@ -231,6 +232,9 @@ namespace PrivaPub.Api.Mastodon.Controllers
var types = Params.List("types").Select(Parse).Where(t => t.HasValue).Select(t => t.Value).ToList();
var excluded = Params.List("exclude_types").Select(Parse).Where(t => t.HasValue).Select(t => t.Value).ToList();
var query = _dbEntities.Notifications.Match(n => n.AvatarId == MyId);
// what the persona's notification policy filtered stays in its requests, unless asked for (a request's own page)
if (Params.Bool("include_filtered") != true)
query.Match(NotFiltered);
if (types.Count > 0)
query.Match(n => types.Contains(n.Type));
if (excluded.Count > 0)
@@ -240,6 +244,9 @@ namespace PrivaPub.Api.Mastodon.Controllers
return query;
}
// (notifications made before the policy have no Filtered at all)
static readonly FilterDefinition<NotificationEntity> NotFiltered = Builders<NotificationEntity>.Filter.Ne(n => n.Filtered, true);
[HttpGet("/api/v1/notifications"), Scope("read:notifications")]
public async Task<IActionResult> List(CancellationToken token)
{
@@ -283,7 +290,7 @@ namespace PrivaPub.Api.Mastodon.Controllers
async Task<List<NotificationEntity>> Members(string key, CancellationToken token) =>
InGroup(key) is { } filter
? await _dbEntities.Notifications.Match(filter).Sort(n => n.ID, Order.Descending).Limit(80).ExecuteAsync(token)
? await _dbEntities.Notifications.Match(filter).Match(NotFiltered).Sort(n => n.ID, Order.Descending).Limit(80).ExecuteAsync(token)
: new List<NotificationEntity>();
async Task<object> Groups(List<NotificationEntity> page, ISet<string> grouped, CancellationToken token)
@@ -294,7 +301,8 @@ namespace PrivaPub.Api.Mastodon.Controllers
foreach (var group in shown.GroupBy(n => GroupKey(n, grouped)))
{
var newest = mapped[group.First().ID];
var total = group.Key.StartsWith("ungrouped-", StringComparison.Ordinal) ? 1 : await DB.Default.CountAsync(InGroup(group.Key), token);
var total = group.Key.StartsWith("ungrouped-", StringComparison.Ordinal) ? 1
: await DB.Default.CountAsync(Builders<NotificationEntity>.Filter.And(Builders<NotificationEntity>.Filter.Where(InGroup(group.Key)), NotFiltered), token);
groups.Add(new
{
group_key = group.Key,
@@ -390,7 +398,7 @@ namespace PrivaPub.Api.Mastodon.Controllers
[HttpGet("/api/v1/notifications/unread_count"), Scope("read:notifications")]
public async Task<IActionResult> UnreadCount(CancellationToken token)
{
var unread = await DB.Default.Find<NotificationEntity>().Match(n => n.AvatarId == MyId && !n.IsRead)
var unread = await DB.Default.Find<NotificationEntity>().Match(n => n.AvatarId == MyId && !n.IsRead).Match(NotFiltered)
.Sort(n => n.ID, Order.Descending).Limit(Math.Clamp(Params.Int("limit") ?? 100, 1, 1000)).ExecuteAsync(token);
return Json(new { count = (await Map(unread, token)).Count });
}