public async Task SendFcmNotification(long accountId)
        {
            Session[] sessions;

            using (IServiceScope scope = serviceProvider.CreateScope())
            {
                var database = scope.ServiceProvider.GetRequiredService <DatabaseContext>();

                sessions = await database.Sessions.AsQueryable()
                           .Where(s => s.AccountId == accountId && s.FcmToken != null &&
                                  (s.LastFcmMessage < s.LastConnected || options.Value.NotifyForEveryMessage))
                           .ToArrayAsync().ConfigureAwait(false);
            }

            async Task process(Session session)
            {
                using IServiceScope scope = serviceProvider.CreateScope();
                var database = scope.ServiceProvider.GetRequiredService <DatabaseContext>();
                var injector = scope.ServiceProvider.GetRequiredService <MessageInjectionService>();
                var delivery = scope.ServiceProvider.GetRequiredService <DeliveryService>();
                var logger   = scope.ServiceProvider.GetRequiredService <ILogger <NotificationService> >();

                try
                {
                    await firebase.SendAsync(session.FcmToken).ConfigureAwait(false);

                    session.LastFcmMessage = DateTime.Now;
                    database.Entry(session).Property(s => s.LastFcmMessage).IsModified = true;
                    await database.SaveChangesAsync().ConfigureAwait(false);

                    logger.LogInformation($"Successfully sent FCM message to {session.FcmToken.Remove(16)}... last connected {session.LastConnected}");
                }
                catch (FirebaseMessagingException ex) when(ex.MessagingErrorCode == MessagingErrorCode.Unregistered)
                {
                    logger.LogWarning($"Failed to send FCM message to {session.FcmToken.Remove(16)}... {ex.Message}");
                    if (options.Value.DeleteSessionOnError)
                    {
                        // Prevent quick re-login after kick
                        session.SessionTokenHash = Array.Empty <byte>();
                        database.Entry(session).Property(s => s.SessionTokenHash).IsModified = true;
                        await database.SaveChangesAsync().ConfigureAwait(false);

                        // Kick client if connected to avoid conflicting information in RAM vs DB
                        if (connections.TryGet(session.SessionId, out IClient client))
                        {
                            await client.DisposeAsync().ConfigureAwait(false);
                        }
                        database.Sessions.Remove(session);
                        await database.SaveChangesAsync().ConfigureAwait(false);

                        Message deviceList = await injector.CreateDeviceList(session.AccountId).ConfigureAwait(false);

                        await delivery.StartSendMessage(deviceList, null).ConfigureAwait(false);
                    }
                }
            }

            await Task.WhenAll(sessions.Select(s => process(s))).ConfigureAwait(false);
        }
Exemple #2
0
        public async Task <int> Handle(NewNotificationCommand request, CancellationToken cancellationToken)
        {
            var targetFcmTokens = request.Recipients
                                  .Select(observer => _context.NotificationRegistrationData.AsQueryable().Where(regData => regData.ObserverId == int.Parse(observer))
                                          .First(regData => regData.ChannelName == request.Channel))
                                  .Select(regDataResult => regDataResult.Token)
                                  .ToList();

            var response = 0;

            if (targetFcmTokens.Count > 0)
            {
                response = _firebaseService.SendAsync(request.From, request.Title, request.Message, targetFcmTokens);
            }

            var notification = _mapper.Map <Entities.Notification>(request);

            _context.Notifications.AddRange(notification);
            await _context.SaveChangesAsync(cancellationToken);

            return(response);
        }