using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using BTCPayServer.Contracts; using BTCPayServer.Data; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; namespace BTCPayServer.Services.Notifications { public class UserNotificationsUpdatedEvent { public string UserId { get; set; } } public class NotificationSender { private readonly ApplicationDbContextFactory _contextFactory; private readonly UserManager _userManager; private readonly NotificationManager _notificationManager; public NotificationSender(ApplicationDbContextFactory contextFactory, UserManager userManager, NotificationManager notificationManager) { _contextFactory = contextFactory; _userManager = userManager; _notificationManager = notificationManager; } public async Task SendNotification(NotificationScope scope, BaseNotification notification) { if (scope == null) throw new ArgumentNullException(nameof(scope)); if (notification == null) throw new ArgumentNullException(nameof(notification)); var users = await GetUsers(scope, notification.Identifier); using (var db = _contextFactory.CreateContext()) { foreach (var uid in users) { var obj = JsonConvert.SerializeObject(notification); var data = new NotificationData { Id = Guid.NewGuid().ToString(), Created = DateTimeOffset.UtcNow, ApplicationUserId = uid, NotificationType = notification.NotificationType, Blob = ZipUtils.Zip(obj), Seen = false }; db.Notifications.Add(data); } await db.SaveChangesAsync(); } foreach (string user in users) { _notificationManager.InvalidateNotificationCache(user); } } private async Task GetUsers(NotificationScope scope, string notificationIdentifier) { await using var ctx = _contextFactory.CreateContext(); var split = notificationIdentifier.Split('_', StringSplitOptions.None); var terms = new List(); foreach (var t in split) { terms.Add(terms.Any() ? $"{terms.Last().TrimEnd(';')}_{t};" : $"{t};"); } IQueryable query; switch (scope) { case AdminScope _: { query = _userManager.GetUsersInRoleAsync(Roles.ServerAdmin).Result.AsQueryable(); break; } case StoreScope s: query = ctx.UserStore .Include(store => store.ApplicationUser) .Where(u => u.StoreDataId == s.StoreId) .Select(u => u.ApplicationUser); break; case UserScope userScope: query = ctx.Users .Where(user => user.Id == userScope.UserId); break; default: throw new NotSupportedException("Notification scope not supported"); } query = query.Where(store => store.DisabledNotifications != "all"); foreach (string term in terms) { // ReSharper disable once CA1307 query = query.Where(user => user.DisabledNotifications == null || !user.DisabledNotifications.Contains(term )); } return query.Select(user => user.Id).ToArray(); } } }