[backend/masto-client] Enforce renote/reply visibility in ws/streaming (ISH-248)
This commit is contained in:
parent
f6734aea11
commit
bc50aa0259
3 changed files with 75 additions and 40 deletions
|
@ -51,8 +51,17 @@ public class PublicChannel(
|
|||
if (!local && note.UserHost == null) return false;
|
||||
if (!remote && note.UserHost != null) return false;
|
||||
if (onlyMedia && note.FileIds.Count == 0) return false;
|
||||
return EnforceRenoteReplyVisibility(note) is not { IsPureRenote: true, Renote: null };
|
||||
}
|
||||
|
||||
private Note EnforceRenoteReplyVisibility(Note note)
|
||||
{
|
||||
if (note.Renote?.IsVisibleFor(connection.Token.User, connection.Following) ?? false)
|
||||
note.Renote = null;
|
||||
if (note.Reply?.IsVisibleFor(connection.Token.User, connection.Following) ?? false)
|
||||
note.Reply = null;
|
||||
|
||||
return true;
|
||||
return note;
|
||||
}
|
||||
|
||||
private bool IsFiltered(Note note) => connection.IsFiltered(note.User) ||
|
||||
|
|
|
@ -5,7 +5,6 @@ using Iceshrimp.Backend.Core.Database;
|
|||
using Iceshrimp.Backend.Core.Database.Tables;
|
||||
using Iceshrimp.Backend.Core.Events;
|
||||
using Iceshrimp.Backend.Core.Middleware;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Mastodon.Streaming.Channels;
|
||||
|
||||
|
@ -14,10 +13,9 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
|||
public readonly ILogger<UserChannel> Logger =
|
||||
connection.Scope.ServiceProvider.GetRequiredService<ILogger<UserChannel>>();
|
||||
|
||||
private List<string> _followedUsers = [];
|
||||
public string Name => notificationsOnly ? "user:notification" : "user";
|
||||
public List<string> Scopes => ["read:statuses", "read:notifications"];
|
||||
public bool IsSubscribed { get; private set; }
|
||||
public string Name => notificationsOnly ? "user:notification" : "user";
|
||||
public List<string> Scopes => ["read:statuses", "read:notifications"];
|
||||
public bool IsSubscribed { get; private set; }
|
||||
|
||||
public async Task Subscribe(StreamingRequestMessage _)
|
||||
{
|
||||
|
@ -29,17 +27,9 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
|||
|
||||
if (!notificationsOnly)
|
||||
{
|
||||
_followedUsers = await db.Users.Where(p => p == connection.Token.User)
|
||||
.SelectMany(p => p.Following)
|
||||
.Select(p => p.Id)
|
||||
.ToListAsync();
|
||||
|
||||
connection.EventService.NotePublished += OnNotePublished;
|
||||
connection.EventService.NoteUpdated += OnNoteUpdated;
|
||||
connection.EventService.NoteDeleted += OnNoteDeleted;
|
||||
connection.EventService.UserFollowed += OnRelationChange;
|
||||
connection.EventService.UserUnfollowed += OnRelationChange;
|
||||
connection.EventService.UserBlocked += OnRelationChange;
|
||||
}
|
||||
|
||||
connection.EventService.Notification += OnNotification;
|
||||
|
@ -60,15 +50,15 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
|||
connection.EventService.NotePublished -= OnNotePublished;
|
||||
connection.EventService.NoteUpdated -= OnNoteUpdated;
|
||||
connection.EventService.NoteDeleted -= OnNoteDeleted;
|
||||
connection.EventService.UserFollowed -= OnRelationChange;
|
||||
connection.EventService.UserUnfollowed -= OnRelationChange;
|
||||
connection.EventService.UserBlocked -= OnRelationChange;
|
||||
}
|
||||
|
||||
connection.EventService.Notification -= OnNotification;
|
||||
}
|
||||
|
||||
private bool IsApplicable(Note note) => _followedUsers.Prepend(connection.Token.User.Id).Contains(note.UserId);
|
||||
private bool IsApplicable(Note note) =>
|
||||
connection.Following.Prepend(connection.Token.User.Id).Contains(note.UserId) &&
|
||||
EnforceRenoteReplyVisibility(note) is not { IsPureRenote: true, Renote: null };
|
||||
|
||||
private bool IsApplicable(Notification notification) => notification.NotifieeId == connection.Token.User.Id;
|
||||
|
||||
private bool IsApplicable(UserInteraction interaction) => interaction.Actor.Id == connection.Token.User.Id ||
|
||||
|
@ -82,6 +72,16 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
|||
private bool IsFiltered(Notification notification) =>
|
||||
(notification.Notifier != null && connection.IsFiltered(notification.Notifier)) ||
|
||||
(notification.Note != null && IsFiltered(notification.Note));
|
||||
|
||||
private Note EnforceRenoteReplyVisibility(Note note)
|
||||
{
|
||||
if (note.Renote?.IsVisibleFor(connection.Token.User, connection.Following) ?? false)
|
||||
note.Renote = null;
|
||||
if (note.Reply?.IsVisibleFor(connection.Token.User, connection.Following) ?? false)
|
||||
note.Reply = null;
|
||||
|
||||
return note;
|
||||
}
|
||||
|
||||
private async void OnNotePublished(object? _, Note note)
|
||||
{
|
||||
|
@ -185,22 +185,4 @@ public class UserChannel(WebSocketConnection connection, bool notificationsOnly)
|
|||
Logger.LogError("Event handler OnNotification threw exception: {e}", e);
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnRelationChange(object? _, UserInteraction interaction)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsApplicable(interaction)) return;
|
||||
await using var scope = connection.ScopeFactory.CreateAsyncScope();
|
||||
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
||||
_followedUsers = await db.Users.Where(p => p == connection.Token.User)
|
||||
.SelectMany(p => p.Following)
|
||||
.Select(p => p.Id)
|
||||
.ToListAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError("Event handler OnRelationChange threw exception: {e}", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,10 +3,12 @@ using System.Net.WebSockets;
|
|||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Iceshrimp.Backend.Controllers.Mastodon.Streaming.Channels;
|
||||
using Iceshrimp.Backend.Core.Database;
|
||||
using Iceshrimp.Backend.Core.Database.Tables;
|
||||
using Iceshrimp.Backend.Core.Events;
|
||||
using Iceshrimp.Backend.Core.Helpers;
|
||||
using Iceshrimp.Backend.Core.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Mastodon.Streaming;
|
||||
|
||||
|
@ -24,6 +26,7 @@ public sealed class WebSocketConnection(
|
|||
public readonly IServiceScope Scope = scopeFactory.CreateScope();
|
||||
public readonly IServiceScopeFactory ScopeFactory = scopeFactory;
|
||||
public readonly OauthToken Token = token;
|
||||
public readonly WriteLockingList<string> Following = [];
|
||||
private readonly WriteLockingList<string> _blocking = [];
|
||||
private readonly WriteLockingList<string> _blockedBy = [];
|
||||
private readonly WriteLockingList<string> _mutedUsers = [];
|
||||
|
@ -54,10 +57,23 @@ public sealed class WebSocketConnection(
|
|||
Channels.Add(new PublicChannel(this, "public:remote", false, true, false));
|
||||
Channels.Add(new PublicChannel(this, "public:remote:media", false, true, true));
|
||||
|
||||
EventService.UserBlocked += OnUserUnblock;
|
||||
EventService.UserUnblocked += OnUserBlock;
|
||||
EventService.UserMuted += OnUserMute;
|
||||
EventService.UserUnmuted += OnUserUnmute;
|
||||
EventService.UserBlocked += OnUserUnblock;
|
||||
EventService.UserUnblocked += OnUserBlock;
|
||||
EventService.UserMuted += OnUserMute;
|
||||
EventService.UserUnmuted += OnUserUnmute;
|
||||
EventService.UserFollowed -= OnUserFollow;
|
||||
EventService.UserUnfollowed -= OnUserUnfollow;
|
||||
|
||||
_ = InitializeFollowing();
|
||||
}
|
||||
|
||||
private async Task InitializeFollowing()
|
||||
{
|
||||
await using var db = Scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
||||
Following.AddRange(await db.Users.Where(p => p == Token.User)
|
||||
.SelectMany(p => p.Following)
|
||||
.Select(p => p.Id)
|
||||
.ToListAsync());
|
||||
}
|
||||
|
||||
public async Task HandleSocketMessageAsync(string payload)
|
||||
|
@ -184,6 +200,34 @@ public sealed class WebSocketConnection(
|
|||
logger.LogError("Event handler OnUserUnmute threw exception: {e}", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUserFollow(object? _, UserInteraction interaction)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (interaction.Actor.Id == Token.User.Id)
|
||||
Following.Add(interaction.Object.Id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var logger = Scope.ServiceProvider.GetRequiredService<Logger<WebSocketConnection>>();
|
||||
logger.LogError("Event handler OnUserFollow threw exception: {e}", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUserUnfollow(object? _, UserInteraction interaction)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (interaction.Actor.Id == Token.User.Id)
|
||||
Following.Remove(interaction.Object.Id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var logger = Scope.ServiceProvider.GetRequiredService<Logger<WebSocketConnection>>();
|
||||
logger.LogError("Event handler OnUserUnfollow threw exception: {e}", e);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")]
|
||||
public bool IsFiltered(User user) =>
|
||||
|
|
Loading…
Add table
Reference in a new issue