290 lines
No EOL
9.8 KiB
C#
290 lines
No EOL
9.8 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using Iceshrimp.Backend.Core.Database;
|
|
using Iceshrimp.Backend.Core.Database.Tables;
|
|
using Iceshrimp.Backend.Core.Extensions;
|
|
using Iceshrimp.Backend.Core.Helpers;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Iceshrimp.Backend.Core.Services;
|
|
|
|
public class NotificationService(
|
|
[SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")]
|
|
DatabaseContext db,
|
|
EventService eventSvc
|
|
)
|
|
{
|
|
public async Task GenerateMentionNotifications(Note note, IReadOnlyCollection<string> mentionedLocalUserIds)
|
|
{
|
|
if (mentionedLocalUserIds.Count == 0) return;
|
|
|
|
var blocks = await db.Blockings
|
|
.Where(p => p.BlockeeId == note.UserId && mentionedLocalUserIds.Contains(p.BlockerId))
|
|
.Select(p => p.BlockerId)
|
|
.ToListAsync();
|
|
|
|
var notifications = mentionedLocalUserIds
|
|
.Where(p => p != note.UserId)
|
|
.Except(blocks)
|
|
.Select(p => new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Note = note,
|
|
NotifierId = note.UserId,
|
|
NotifieeId = p,
|
|
Type = Notification.NotificationType.Mention
|
|
})
|
|
.ToList();
|
|
|
|
await db.AddRangeAsync(notifications);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotifications(this, notifications);
|
|
}
|
|
|
|
public async Task GenerateReplyNotifications(Note note, IReadOnlyCollection<string> mentionedLocalUserIds)
|
|
{
|
|
var users = mentionedLocalUserIds
|
|
.Concat(note.VisibleUserIds)
|
|
.Append(note.ReplyUserId)
|
|
.NotNull()
|
|
.Distinct()
|
|
.Except(mentionedLocalUserIds)
|
|
.ToList();
|
|
|
|
if (users.Count == 0) return;
|
|
|
|
var blocks = await db.Blockings
|
|
.Where(p => p.BlockeeId == note.UserId && users.Contains(p.BlockerId))
|
|
.Select(p => p.BlockerId)
|
|
.ToListAsync();
|
|
|
|
var remote = await db.Users
|
|
.Where(p => users.Contains(p.Id) && p.IsRemoteUser)
|
|
.Select(p => p.Id)
|
|
.ToListAsync();
|
|
|
|
var notifications = users
|
|
.Where(p => p != note.UserId)
|
|
.Except(blocks)
|
|
.Except(remote)
|
|
.Select(p => new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Note = note,
|
|
NotifierId = note.UserId,
|
|
NotifieeId = p,
|
|
Type = Notification.NotificationType.Reply
|
|
})
|
|
.ToList();
|
|
|
|
await db.AddRangeAsync(notifications);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotifications(this, notifications);
|
|
}
|
|
|
|
[SuppressMessage("ReSharper", "EntityFramework.UnsupportedServerSideFunctionCall",
|
|
Justification = "Projectable functions are very much translatable")]
|
|
public async Task GenerateEditNotifications(Note note)
|
|
{
|
|
var notifications = await db.Users
|
|
.Where(p => p.IsLocalUser && p != note.User && p.HasInteractedWith(note))
|
|
.Select(p => new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(DateTime.UtcNow),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Note = note,
|
|
NotifierId = note.UserId,
|
|
Notifiee = p,
|
|
Type = Notification.NotificationType.Edit
|
|
})
|
|
.ToListAsync();
|
|
|
|
if (notifications.Count == 0)
|
|
return;
|
|
|
|
await db.AddRangeAsync(notifications);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotifications(this, notifications);
|
|
}
|
|
|
|
public async Task GenerateLikeNotification(Note note, User user)
|
|
{
|
|
if (note.UserHost != null) return;
|
|
if (note.User == user) return;
|
|
|
|
var notification = new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Note = note,
|
|
Notifiee = note.User,
|
|
Notifier = user,
|
|
Type = Notification.NotificationType.Like
|
|
};
|
|
|
|
await db.AddAsync(notification);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotification(this, notification);
|
|
}
|
|
|
|
public async Task GenerateReactionNotification(NoteReaction reaction)
|
|
{
|
|
if (reaction.Note.User.IsRemoteUser) return;
|
|
if (reaction.Note.User == reaction.User) return;
|
|
|
|
var notification = new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Note = reaction.Note,
|
|
Notifiee = reaction.Note.User,
|
|
Notifier = reaction.User,
|
|
Type = Notification.NotificationType.Reaction,
|
|
Reaction = reaction.Reaction
|
|
};
|
|
|
|
await db.AddAsync(notification);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotification(this, notification);
|
|
}
|
|
|
|
public async Task GenerateFollowNotification(User follower, User followee)
|
|
{
|
|
if (followee.IsRemoteUser) return;
|
|
|
|
var notification = new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Notifiee = followee,
|
|
Notifier = follower,
|
|
Type = Notification.NotificationType.Follow
|
|
};
|
|
|
|
await db.AddAsync(notification);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotification(this, notification);
|
|
eventSvc.RaiseUserFollowed(this, follower, followee);
|
|
}
|
|
|
|
public async Task GenerateFollowRequestReceivedNotification(FollowRequest followRequest)
|
|
{
|
|
if (followRequest.FolloweeHost != null) return;
|
|
|
|
var notification = new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
FollowRequest = followRequest,
|
|
Notifier = followRequest.Follower,
|
|
Notifiee = followRequest.Followee,
|
|
Type = Notification.NotificationType.FollowRequestReceived
|
|
};
|
|
|
|
await db.AddAsync(notification);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotification(this, notification);
|
|
}
|
|
|
|
public async Task GenerateFollowRequestAcceptedNotification(FollowRequest followRequest)
|
|
{
|
|
if (followRequest.FollowerHost != null) return;
|
|
if (!followRequest.Followee.IsLocked) return;
|
|
|
|
var notification = new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Notifier = followRequest.Followee,
|
|
Notifiee = followRequest.Follower,
|
|
Type = Notification.NotificationType.FollowRequestAccepted
|
|
};
|
|
|
|
await db.AddAsync(notification);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotification(this, notification);
|
|
eventSvc.RaiseUserFollowed(this, followRequest.Follower, followRequest.Followee);
|
|
}
|
|
|
|
public async Task GenerateBiteNotification(Bite bite)
|
|
{
|
|
var notification = new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Notifiee = (bite.TargetUser ?? bite.TargetNote?.User ?? bite.TargetBite?.User) ??
|
|
throw new InvalidOperationException("Null checks say one of these must not be null"),
|
|
Notifier = bite.User,
|
|
Note = bite.TargetNote,
|
|
Bite = bite,
|
|
Type = Notification.NotificationType.Bite
|
|
};
|
|
|
|
await db.AddAsync(notification);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotification(this, notification);
|
|
}
|
|
|
|
public async Task GeneratePollEndedNotifications(Note note)
|
|
{
|
|
var notifications = await db.PollVotes
|
|
.Where(p => p.Note == note)
|
|
.Where(p => p.User.IsLocalUser)
|
|
.Select(p => p.User)
|
|
.Concat(db.Users.Where(p => p == note.User && p.IsLocalUser))
|
|
.Distinct()
|
|
.Select(p => new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(DateTime.UtcNow),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Notifiee = p,
|
|
Notifier = note.User,
|
|
Note = note,
|
|
Type = Notification.NotificationType.PollEnded
|
|
})
|
|
.ToListAsync();
|
|
|
|
if (note.UserHost == null && notifications.All(p => p.Notifiee != note.User))
|
|
{
|
|
notifications.Add(new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(DateTime.UtcNow),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Notifiee = note.User,
|
|
Note = note,
|
|
Type = Notification.NotificationType.PollEnded
|
|
});
|
|
}
|
|
|
|
await db.AddRangeAsync(notifications);
|
|
await db.SaveChangesAsync();
|
|
|
|
foreach (var notification in notifications)
|
|
eventSvc.RaiseNotification(this, notification);
|
|
}
|
|
|
|
[SuppressMessage("ReSharper", "EntityFramework.UnsupportedServerSideFunctionCall", Justification = "Projectables")]
|
|
public async Task GenerateRenoteNotification(Note note)
|
|
{
|
|
if (note.Renote is not { UserHost: null }) return;
|
|
if (note.RenoteUserId == note.UserId) return;
|
|
if (!note.VisibilityIsPublicOrHome &&
|
|
!await db.Notes.AnyAsync(p => p.Id == note.Id && p.IsVisibleFor(note.Renote.User)))
|
|
return;
|
|
|
|
var notification = new Notification
|
|
{
|
|
Id = IdHelpers.GenerateSlowflakeId(),
|
|
CreatedAt = DateTime.UtcNow,
|
|
Note = note.IsQuote ? note : note.Renote,
|
|
Notifiee = note.Renote.User,
|
|
Notifier = note.User,
|
|
Type = note.IsQuote ? Notification.NotificationType.Quote : Notification.NotificationType.Renote
|
|
};
|
|
|
|
await db.AddAsync(notification);
|
|
await db.SaveChangesAsync();
|
|
eventSvc.RaiseNotification(this, notification);
|
|
}
|
|
} |