using System.Text; using System.Text.Json; using DysonNetwork.Shared.Models; using EFCore.BulkExtensions; using Microsoft.EntityFrameworkCore; using NodaTime; using Microsoft.Extensions.Configuration; using System.Net.Http; namespace DysonNetwork.Pass.Account; public class NotificationService( AppDatabase db // WebSocketService ws, // IHttpClientFactory httpFactory, // IConfiguration config ) { // private readonly string _notifyTopic = config["Notifications:Topic"]!; // private readonly Uri _notifyEndpoint = new(config["Notifications:Endpoint"]!); public async Task UnsubscribePushNotifications(string deviceId) { // await db.NotificationPushSubscriptions // .Where(s => s.DeviceId == deviceId) // .ExecuteDeleteAsync(); } public async Task SubscribePushNotification( Shared.Models.Account account, NotificationPushProvider provider, string deviceId, string deviceToken ) { var now = SystemClock.Instance.GetCurrentInstant(); // First check if a matching subscription exists // var existingSubscription = await db.NotificationPushSubscriptions // .Where(s => s.AccountId == account.Id) // .Where(s => s.DeviceId == deviceId || s.DeviceToken == deviceToken) // .FirstOrDefaultAsync(); // if (existingSubscription is not null) // { // // Update the existing subscription directly in the database // await db.NotificationPushSubscriptions // .Where(s => s.Id == existingSubscription.Id) // .ExecuteUpdateAsync(setters => setters // .SetProperty(s => s.DeviceId, deviceId) // .SetProperty(s => s.DeviceToken, deviceToken) // .SetProperty(s => s.UpdatedAt, now)); // // Return the updated subscription // existingSubscription.DeviceId = deviceId; // existingSubscription.DeviceToken = deviceToken; // existingSubscription.UpdatedAt = now; // return existingSubscription; // } var subscription = new NotificationPushSubscription { DeviceId = deviceId, DeviceToken = deviceToken, Provider = provider, AccountId = account.Id, }; // db.NotificationPushSubscriptions.Add(subscription); // await db.SaveChangesAsync(); return subscription; } public async Task SendNotification( Shared.Models.Account account, string topic, string? title = null, string? subtitle = null, string? content = null, Dictionary? meta = null, string? actionUri = null, bool isSilent = false, bool save = true ) { if (title is null && subtitle is null && content is null) throw new ArgumentException("Unable to send notification that completely empty."); meta ??= new Dictionary(); if (actionUri is not null) meta["action_uri"] = actionUri; var notification = new Notification { Topic = topic, Title = title, Subtitle = subtitle, Content = content, Meta = meta, AccountId = account.Id, }; if (save) { // db.Add(notification); // await db.SaveChangesAsync(); } if (!isSilent) Console.WriteLine("Simulating notification delivery."); // _ = DeliveryNotification(notification); return notification; } public async Task DeliveryNotification(Notification notification) { // ws.SendPacketToAccount(notification.AccountId, new WebSocketPacket // { // Type = "notifications.new", // Data = notification // }); // Pushing the notification // var subscribers = await db.NotificationPushSubscriptions // .Where(s => s.AccountId == notification.AccountId) // .ToListAsync(); // await _PushNotification(notification, subscribers); } public async Task MarkNotificationsViewed(ICollection notifications) { var now = SystemClock.Instance.GetCurrentInstant(); var id = notifications.Where(n => n.ViewedAt == null).Select(n => n.Id).ToList(); if (id.Count == 0) return; // await db.Notifications // .Where(n => id.Contains(n.Id)) // .ExecuteUpdateAsync(s => s.SetProperty(n => n.ViewedAt, now) // ); } public async Task BroadcastNotification(Notification notification, bool save = false) { var accounts = new List(); // await db.Accounts.ToListAsync(); if (save) { var notifications = accounts.Select(x => { var newNotification = new Notification { Topic = notification.Topic, Title = notification.Title, Subtitle = notification.Subtitle, Content = notification.Content, Meta = notification.Meta, Priority = notification.Priority, Account = x, AccountId = x.Id }; return newNotification; }).ToList(); // await db.BulkInsertAsync(notifications); } foreach (var account in accounts) { notification.Account = account; notification.AccountId = account.Id; // ws.SendPacketToAccount(account.Id, new WebSocketPacket // { // Type = "notifications.new", // Data = notification // }); } // var subscribers = await db.NotificationPushSubscriptions // .ToListAsync(); // await _PushNotification(notification, subscribers); } public async Task SendNotificationBatch(Notification notification, List accounts, bool save = false) { if (save) { var notifications = accounts.Select(x => { var newNotification = new Notification { Topic = notification.Topic, Title = notification.Title, Subtitle = notification.Subtitle, Content = notification.Content, Meta = notification.Meta, Priority = notification.Priority, Account = x, AccountId = x.Id }; return newNotification; }).ToList(); // await db.BulkInsertAsync(notifications); } foreach (var account in accounts) { notification.Account = account; notification.AccountId = account.Id; // ws.SendPacketToAccount(account.Id, new WebSocketPacket // { // Type = "notifications.new", // Data = notification // }); } // var accountsId = accounts.Select(x => x.Id).ToList(); // var subscribers = await db.NotificationPushSubscriptions // .Where(s => accountsId.Contains(s.AccountId)) // .ToListAsync(); // await _PushNotification(notification, subscribers); } // private List> _BuildNotificationPayload(Notification notification, // IEnumerable subscriptions) // { // var subDict = subscriptions // .GroupBy(x => x.Provider) // .ToDictionary(x => x.Key, x => x.ToList()); // var notifications = subDict.Select(value => // { // var platformCode = value.Key switch // { // NotificationPushProvider.Apple => 1, // NotificationPushProvider.Google => 2, // _ => throw new InvalidOperationException($"Unknown push provider: {value.Key}") // }; // var tokens = value.Value.Select(x => x.DeviceToken).ToList(); // return _BuildNotificationPayload(notification, platformCode, tokens); // }).ToList(); // return notifications.ToList(); // } // private Dictionary _BuildNotificationPayload(Notification notification, int platformCode, // IEnumerable deviceTokens) // { // var alertDict = new Dictionary(); // var dict = new Dictionary // { // ["notif_id"] = notification.Id.ToString(), // ["apns_id"] = notification.Id.ToString(), // ["topic"] = _notifyTopic, // ["tokens"] = deviceTokens, // ["data"] = new Dictionary // { // ["type"] = notification.Topic, // ["meta"] = notification.Meta ?? new Dictionary(), // }, // ["mutable_content"] = true, // ["priority"] = notification.Priority >= 5 ? "high" : "normal", // }; // if (!string.IsNullOrWhiteSpace(notification.Title)) // { // dict["title"] = notification.Title; // alertDict["title"] = notification.Title; // } // if (!string.IsNullOrWhiteSpace(notification.Content)) // { // dict["message"] = notification.Content; // alertDict["body"] = notification.Content; // } // if (!string.IsNullOrWhiteSpace(notification.Subtitle)) // { // dict["message"] = $"{notification.Subtitle}\n{dict["message"]}"; // alertDict["subtitle"] = notification.Subtitle; // } // if (notification.Priority >= 5) // dict["name"] = "default"; // dict["platform"] = platformCode; // dict["alert"] = alertDict; // return dict; // } // private async Task _PushNotification(Notification notification, // IEnumerable subscriptions) // { // var subList = subscriptions.ToList(); // if (subList.Count == 0) return; // var requestDict = new Dictionary // { // ["notifications"] = _BuildNotificationPayload(notification, subList) // }; // var client = httpFactory.CreateClient(); // client.BaseAddress = _notifyEndpoint; // var request = await client.PostAsync("/push", new StringContent( // JsonSerializer.Serialize(requestDict), // Encoding.UTF8, // "application/json" // )); // request.EnsureSuccessStatusCode(); // } }