public async Task SendAsync(string clientId, string userId, Notification notification) { if (string.IsNullOrEmpty(clientId)) { throw new ArgumentNullException(nameof(clientId)); } var subscriptions = await _db.SubscriptionSet .AsNoTracking() .Where(s => s.ClientId == clientId && s.Client.IsEnabled && (string.IsNullOrEmpty(userId) || s.UserId == userId) ) .ProjectTo <WebPush.PushSubscription>(_mapper.ConfigurationProvider) .ToListAsync(); // no subscriptions? if (subscriptions.Count == 0) { // no need to go further return; } string jsonNotification = System.Text.Json.JsonSerializer.Serialize(notification, Pacem.Push.Serialization.JsonSerializer.JsonSerializerOptions); VapidDetails vapidData = await _vapid.GetVapidDetailsAsync(clientId); WebPush.VapidDetails vapid = _mapper.Map <WebPush.VapidDetails>(vapidData); foreach (var subscription in subscriptions) { try { _logger.LogInformation("Sending {p256dh} subscription to encrypt message:\n{message}...", subscription.P256DH, jsonNotification); _client.SendNotification(subscription, jsonNotification, vapid); _logger.LogInformation("...sent!"); } catch (WebPush.WebPushException exc) { if (exc.Message == /* This is a magic string from the WebPush lib */ "Subscription no longer valid") { // tidy-up await UnsubscribeAsync(clientId, subscription.P256DH); } else { // what's wrong? _logger.LogError(exc, exc.Message); } } } }
private static void doPushBackgroundWork() { try { WebPush.WebPushClient client = new WebPush.WebPushClient(); while (true) { try { if (!newEvents.IsEmpty) { // Accumulate a list of events we want to notify each subscription about. // Map subscriptionkey > event list Dictionary <string, List <EventToNotifyAbout> > accumulatedEvents = new Dictionary <string, List <EventToNotifyAbout> >(); List <User> users = Settings.data.GetAllUsers(); while (newEvents.TryDequeue(out EventToNotifyAbout en)) { foreach (User u in users) { string[] subscriptionKeys = u.GetPushNotificationSubscriptions(en.projectName, en.ev.FolderId); foreach (string key in subscriptionKeys) { List <EventToNotifyAbout> events; if (!accumulatedEvents.TryGetValue(key, out events)) { accumulatedEvents[key] = events = new List <EventToNotifyAbout>(); } events.Add(en); } } } if (accumulatedEvents.Count > 0) { WebPush.VapidDetails vapidDetails = new WebPush.VapidDetails(GetVapidSubject(), Settings.data.vapidPublicKey, Settings.data.vapidPrivateKey); // Build and send one notification to each affected subscription. foreach (KeyValuePair <string, List <EventToNotifyAbout> > kvp in accumulatedEvents) { string subscriptionKey = kvp.Key; List <EventToNotifyAbout> events = kvp.Value; WebPush.PushSubscription subscription = null; try { dynamic dyn = JsonConvert.DeserializeObject(subscriptionKey); subscription = new WebPush.PushSubscription(); subscription.Endpoint = dyn.endpoint; subscription.P256DH = dyn.keys.p256dh; subscription.Auth = dyn.keys.auth; } catch { } // For now, we'll just ignore any unparseable subscription keys. // I know this is asking for trouble later on, and I'm sorry for that. if (subscription != null) { StringBuilder sb = new StringBuilder(); PushMessage message = new PushMessage(Settings.data.systemName, events.Count + " new events:"); if (events.Count == 1) { EventToNotifyAbout en = events[0]; message.message = en.ev.EventType.ToString() + ": " + en.ev.SubType; message.eventid = en.ev.EventId; } // If all events are in the same project, set message.project so that clicking the notification can open the correct project. string projectName = events[0].projectName; if (events.All(en => en.projectName == projectName)) { message.project = projectName; // If all events are in the same folder, set message.folderid so that clicking the notification can open the correct folder. int folderId = events[0].ev.FolderId; if (events.All(en => en.ev.FolderId == folderId)) { message.folderid = folderId; } } try { client.SendNotification(subscription, message.ToString(), vapidDetails); } catch (Exception ex) { Logger.Debug(ex, "Failed to send push notification."); } } } } } Thread.Sleep(1000); } catch (ThreadAbortException) { } catch (Exception ex) { Emailer.SendError(null, "Error in PushManager", ex); } } } catch (ThreadAbortException) { } catch (Exception ex) { Logger.Debug(ex); } }