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); }
public async Task StartSetActive(IClient client, bool active) { long[] sessions = await database.Sessions.AsQueryable() .Where(s => s.AccountId == client.AccountId) .Select(s => s.SessionId) .ToArrayAsync().ConfigureAwait(false); client.SoonActive = active; bool notify = true; foreach (long sessionId in sessions) { if (connections.TryGet(sessionId, out IClient _client) && !ReferenceEquals(_client, client) && _client.SoonActive && (_client.Active || !active)) { // If going online: No need to notify if another session is already online and staying online // If going offline: No need to notify if another session is online or coming online in the meantime notify = false; } } client.Active = active; if (!notify) { return; } long accountChannelId = await database.Channels.AsQueryable() .Where(c => c.OwnerId == client.AccountId && c.ChannelType == ChannelType.AccountData) .Select(c => c.ChannelId) .SingleAsync().ConfigureAwait(false); var packet = packets.New <P2BOnlineState>(); packet.OnlineState = active ? OnlineState.Active : OnlineState.Inactive; packet.LastActive = DateTime.Now; packet.MessageFlags = MessageFlags.Unencrypted | MessageFlags.NoSenderSync; Message message = await injector.CreateMessage(packet, accountChannelId, client.AccountId).ConfigureAwait(false); await delivery.StartSendMessage(message, client).ConfigureAwait(false); }
public async Task StartSendToAccount(Packet packet, long accountId, IClient exclude) { long[] sessions = await database.Sessions.AsQueryable() .Where(s => s.AccountId == accountId) .Select(s => s.SessionId) .ToArrayAsync().ConfigureAwait(false); foreach (long sessionId in sessions) { if (connections.TryGet(sessionId, out IClient client) && !ReferenceEquals(client, exclude)) { _ = client.Send(packet); } } }