public async Task <ActionResult> SendNotification(int id) { var sub = db.PushSubscriptions.Find(id); if (sub == null) { return(HttpNotFound()); } else { var publicKey = ConfigurationManager.AppSettings["VapidPublicKey"]; var privateKey = ConfigurationManager.AppSettings["VapidPrivateKey"]; var subject = @"mailto: [email protected]"; Uri uri = new Uri(sub.Url); string audience = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}"; var subscription = new WebPush.PushSubscription(sub.Url, sub.P256dh, sub.Auth); var vapidDetails = new WebPush.VapidDetails(subject, publicKey, privateKey); var client = new WebPush.WebPushClient(); try { var opts = new Dictionary <string, object>(); opts["vapidDetails"] = vapidDetails; var notification = new PushNotification(); notification.Title = "Something New Happened!"; notification.Body = "There was some activity on your account and you should go do something about it."; notification.Icon = "https://s3.amazonaws.com/images.productionhub.com/icons/asterisk.png"; notification.Data.Url = "/"; notification.Data.ListingId = "142484"; notification.Actions.Add(new NotificationAction { Action = "ignore", Title = "Ignore Lead", Icon = "https://s3.amazonaws.com/images.productionhub.com/icons/icon_remove_red.png" }); notification.Actions.Add(new NotificationAction { Action = "buy", Title = "Purchase Lead", Icon = "https://s3.amazonaws.com/images.productionhub.com/icons/icon_credit.png" }); var payload = JsonConvert.SerializeObject(notification, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); var requestDetails = client.GenerateRequestDetails(subscription, payload, opts); //await client.SendNotificationAsync(subscription, "payload", vapidDetails); var httpClient = new HttpClient(); await httpClient.SendAsync(requestDetails); return(Json(true)); } catch (WebPush.WebPushException exc) { return(new HttpStatusCodeResult(exc.StatusCode)); } } }
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 async Task SendNotificationToRecipients(IEnumerable <PushSubscription> recipients, PushNotification notification, string senderEmail, string publicKey, string privateKey, ILogger logger, CancellationToken cancelToken) { // This is run in a background thread. We should not access or update any mutable state. var vapidDetails = new WebPush.VapidDetails($"mailto:{senderEmail}", publicKey, privateKey); var client = new WebPush.WebPushClient(); var jsonSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }; foreach (var subscription in recipients) { if (!cancelToken.IsCancellationRequested) { await TrySendPushNotification(notification, subscription, vapidDetails, client, jsonSettings, logger); } } }
private static async Task TrySendPushNotification(PushNotification notification, PushSubscription recipient, WebPush.VapidDetails details, WebPush.WebPushClient client, JsonSerializerSettings serializerSettings, ILogger logger) { // This is run in a background thread. We should not access or update mutable state. try { var payload = JsonConvert.SerializeObject(notification, serializerSettings); var subscription = new WebPush.PushSubscription(recipient.Endpoint, recipient.Keys["p256dh"], recipient.Keys["auth"]); await client.SendNotificationAsync(subscription, payload, details); } catch (Exception error) { using (logger.BeginKeyValueScope("recipient", recipient)) using (logger.BeginKeyValueScope("publicKey", details.PublicKey)) using (logger.BeginKeyValueScope("publicKey", details.PrivateKey)) { logger.LogError(error, "Error sending push notification"); } } }
public async Task <ContentResult> Push([Required, FromBody] string name) { var user = await _userMgr.GetUserAsync(User); string ret; try { if (user.InGroupId == null) { ret = "not_in_group"; } else { if (user.WebSubscription == null) { ret = "s_not_subscribed"; } else { var rcvr = _groupsDb.Users.Where(c => c.InGroupId == user.InGroupId).SingleOrDefault(c => c.UserName == name); if (rcvr == null) { ret = "not_found"; } else { if (rcvr.InGroupId != user.InGroupId) { ret = "not_same_group"; } else { var web_subscription = StaticData.GetPushSubscription(rcvr.WebSubscription); if (web_subscription == null) { ret = "r_not_subscribed"; } else { if (rcvr.LastNotified > DateTime.UtcNow.Subtract(TimeSpan.FromHours(1)).Ticks) { ret = "is_notified_hour"; } else { if (rcvr.ConnectionId != null) { ret = "usr_active"; } else { ValueTask <Chatterer> groupN = _groupsDb.Users.FindAsync(rcvr.InGroupId); WebPush.WebPushClient client = new WebPush.WebPushClient(); WebPush.VapidDetails det = new WebPush.VapidDetails("mailto:[email protected]", _connections.vapidPublic, _connections.vapidPrivate); client.SetVapidDetails(det); var groupName = (await groupN).Group; var send_task = client.SendNotificationAsync(web_subscription, $"\"{groupName}\" by {user.UserName}"); rcvr.LastNotified = DateTime.UtcNow.Ticks; var save_task = _groupsDb.SaveChangesAsync(); await Task.WhenAll(send_task, save_task); ret = "OK"; } } } } } } } } catch (Exception e) { ret = e.Message; } return(Content(ret, "text/plain")); }
public ErrorTrackerSvc() { Settings.data.Load(); Logger.CatchAll((sender, e) => { Emailer.SendError(null, sender, e); }); Settings.data.SaveIfNoExist(); if (Settings.data.CountUsers() == 0) { User defaultAdmin = new User("admin", "admin", null, true) { Permanent = true }; Settings.data.TryAddUser(defaultAdmin); defaultAdmin.InitializeUserId(); Settings.data.Save(); } if (string.IsNullOrWhiteSpace(Settings.data.systemName)) { Settings.data.systemName = "Error Tracker"; Settings.data.Save(); } if (string.IsNullOrWhiteSpace(Settings.data.privateSigningKey)) { Settings.data.privateSigningKey = new SignatureFactory().ExportPrivateKey(); Settings.data.Save(); } if (string.IsNullOrWhiteSpace(Settings.data.vapidPrivateKey) || string.IsNullOrWhiteSpace(Settings.data.vapidPublicKey)) { WebPush.VapidDetails vapidKeys = WebPush.VapidHelper.GenerateVapidKeys(); Settings.data.vapidPrivateKey = vapidKeys.PrivateKey; Settings.data.vapidPublicKey = vapidKeys.PublicKey; foreach (User u in Settings.data.GetAllUsers()) { u.ClearAllPushNotificationSubscriptions(); } Settings.data.Save(); } BPUtil.PasswordReset.StatelessPasswordResetBase.Initialize(Settings.data.privateSigningKey); // Initialize User IDs. bool setAny = false; foreach (User user in Settings.data.GetAllUsers()) { if (user.InitializeUserId()) { setAny = true; } } if (setAny) { Settings.data.Save(); } InitializeComponent(); SvcName = this.ServiceName; ICertificateSelector certSelector = null; if (!string.IsNullOrWhiteSpace(Settings.data.certificatePath) && File.Exists(Settings.data.certificatePath)) { X509Certificate2 cert; if (!string.IsNullOrWhiteSpace(Settings.data.certificatePassword)) { cert = new X509Certificate2(Settings.data.certificatePath, Settings.data.certificatePassword); } else { cert = new X509Certificate2(Settings.data.certificatePath); } certSelector = SimpleCertificateSelector.FromCertificate(cert); } SimpleHttpLogger.RegisterLogger(Logger.httpLogger); srv = new WebServer(Settings.data.port_http, Settings.data.port_https, certSelector, IPAddress.Any); thrMaintainProjects = new Thread(maintainProjects); thrMaintainProjects.Name = "Maintain Projects"; thrMaintainProjects.IsBackground = false; }
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); } }