diff --git a/src/Tgstation.Server.Host/Components/Chat/ChatManager.cs b/src/Tgstation.Server.Host/Components/Chat/ChatManager.cs index b5f105ea3e..3aaf187d80 100644 --- a/src/Tgstation.Server.Host/Components/Chat/ChatManager.cs +++ b/src/Tgstation.Server.Host/Components/Chat/ChatManager.cs @@ -190,9 +190,16 @@ namespace Tgstation.Server.Host.Components.Chat throw new ArgumentNullException(nameof(newChannels)); logger.LogTrace("ChangeChannels {0}...", connectionId); - var provider = await RemoveProvider(connectionId, false, cancellationToken).ConfigureAwait(false); + var provider = await RemoveProviderChannels(connectionId, false, cancellationToken).ConfigureAwait(false); if (provider == null) return; + + if (!provider.Connected) + { + logger.LogDebug("Cannot map channels, provider {providerId} disconnected!", connectionId); + return; + } + var results = await provider.MapChannels(newChannels, cancellationToken).ConfigureAwait(false); lock (activeChatBots) { @@ -260,29 +267,14 @@ namespace Tgstation.Server.Host.Components.Chat throw new ArgumentNullException(nameof(newSettings)); logger.LogTrace("ChangeSettings..."); - IProvider provider; - - async Task DisconnectProvider(IProvider p) - { - try - { - await p.Disconnect(cancellationToken).ConfigureAwait(false); - } - finally - { - await p.DisposeAsync().ConfigureAwait(false); - } - } Task disconnectTask; + IProvider provider = null; lock (providers) { // raw settings changes forces a rebuild of the provider - if (providers.TryGetValue(newSettings.Id.Value, out provider)) - { - providers.Remove(newSettings.Id.Value); - disconnectTask = DisconnectProvider(provider); - } + if (providers.ContainsKey(newSettings.Id.Value)) + disconnectTask = DeleteConnection(newSettings.Id.Value, cancellationToken); else disconnectTask = Task.CompletedTask; if (newSettings.Enabled.Value) @@ -430,7 +422,6 @@ namespace Tgstation.Server.Host.Components.Chat builtinCommands.Add(tgsCommand.Name.ToUpperInvariant(), tgsCommand); var initialChatBots = activeChatBots.ToList(); await Task.WhenAll(initialChatBots.Select(x => ChangeSettings(x, cancellationToken))).ConfigureAwait(false); - await Task.WhenAll(initialChatBots.Select(x => ChangeChannels(x.Id.Value, x.Channels, cancellationToken))).ConfigureAwait(false); initialProviderConnectionsTask = InitialConnection(); chatHandler = MonitorMessages(handlerCts.Token); } @@ -441,7 +432,7 @@ namespace Tgstation.Server.Host.Components.Chat handlerCts.Cancel(); if (chatHandler != null) await chatHandler.ConfigureAwait(false); - await Task.WhenAll(providers.Select(x => x.Value).Select(x => x.Disconnect(cancellationToken))).ConfigureAwait(false); + await Task.WhenAll(providers.Select(x => x.Key).Select(x => DeleteConnection(x, cancellationToken))).ConfigureAwait(false); await messageSendTask.ConfigureAwait(false); } @@ -480,7 +471,7 @@ namespace Tgstation.Server.Host.Components.Chat /// public async Task DeleteConnection(long connectionId, CancellationToken cancellationToken) { - var provider = await RemoveProvider(connectionId, true, cancellationToken).ConfigureAwait(false); + var provider = await RemoveProviderChannels(connectionId, true, cancellationToken).ConfigureAwait(false); if (provider != null) try { @@ -510,23 +501,28 @@ namespace Tgstation.Server.Host.Components.Chat } /// - /// Remove a from and optionally updating the as well. + /// Remove a from optionally removing the provider itself from and updating the as well. /// /// The of the to delete. - /// If should be update. + /// If the provider should be removed from and should be update. /// The for the operation. /// A resulting in the being removed if it exists, otherwise. - async Task RemoveProvider(long connectionId, bool updateTrackings, CancellationToken cancellationToken) + async Task RemoveProviderChannels(long connectionId, bool removeProvider, CancellationToken cancellationToken) { - logger.LogTrace("RemoveProvider {0}...", connectionId); + logger.LogTrace("RemoveProviderChannels {0}...", connectionId); IProvider provider; lock (providers) + { if (!providers.TryGetValue(connectionId, out provider)) { logger.LogTrace("Aborted, no such provider!"); return null; } + if (removeProvider) + providers.Remove(connectionId); + } + Task trackingContextsUpdateTask; lock (mappedChannels) { @@ -535,7 +531,7 @@ namespace Tgstation.Server.Host.Components.Chat var newMappedChannels = mappedChannels.Select(y => y.Value.Channel).ToList(); - if (updateTrackings) + if (removeProvider) lock (trackingContexts) trackingContextsUpdateTask = Task.WhenAll(trackingContexts.Select(x => x.UpdateChannels(newMappedChannels, cancellationToken))); else @@ -607,6 +603,7 @@ namespace Tgstation.Server.Host.Components.Chat .First(); mappedChannel = mappedChannels .Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == providerChannelId) + .Select(x => (KeyValuePair?)x) .FirstOrDefault(); } @@ -652,12 +649,11 @@ namespace Tgstation.Server.Host.Components.Chat return; } - var mappingNonNullableKvp = mappedChannel.Value; - var mapping = mappingNonNullableKvp.Value; + var mappingChannelRepresentation = mappedChannel.Value.Value.Channel; - message.User.Channel.Id = mapping.Channel.Id; - message.User.Channel.Tag = mapping.Channel.Tag; - message.User.Channel.IsAdminChannel = mapping.Channel.IsAdminChannel; + message.User.Channel.Id = mappingChannelRepresentation.Id; + message.User.Channel.Tag = mappingChannelRepresentation.Tag; + message.User.Channel.IsAdminChannel = mappingChannelRepresentation.IsAdminChannel; } var splits = new List(message.Content.Trim().Split(' ')); diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs index cc6942b1d2..dc018973f8 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -93,6 +93,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// Snowflake currentUserId; + /// + /// The bot's username at the time of connection. + /// + string initialUserName; + /// /// Normalize a discord mention string. /// @@ -194,20 +199,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers if (channels == null) throw new ArgumentNullException(nameof(channels)); - if (!Connected) - { - Logger.LogWarning("Cannot map channels, provider disconnected!"); - return Array.Empty(); - } - - var usersClient = serviceProvider.GetRequiredService(); - var currentUserResponse = await usersClient.GetCurrentUserAsync(cancellationToken).ConfigureAwait(false); - - if (!currentUserResponse.IsSuccess) - { - Logger.LogWarning("Error retrieving current Discord user: {0}", currentUserResponse.Error.Message); - return Array.Empty(); - } + bool remapRequired = false; async Task GetModelChannelFromDBChannel(Api.Models.ChatChannel channelFromDB) { @@ -215,14 +207,12 @@ namespace Tgstation.Server.Host.Components.Chat.Providers throw new InvalidOperationException("ChatChannel missing DiscordChannelId!"); var channelId = channelFromDB.DiscordChannelId.Value; - ulong discordChannelId; string connectionName; string friendlyName; if (channelId == 0) { - connectionName = currentUserResponse.Entity.Username; + connectionName = initialUserName; friendlyName = "(Unmapped accessible channels)"; - discordChannelId = 0; } else { @@ -231,6 +221,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers if (!discordChannelResponse.IsSuccess) { Logger.LogWarning("Error retrieving discord channel {0}: {1}", channelId, discordChannelResponse.Error.Message); + remapRequired = true; return null; } @@ -240,7 +231,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers return null; } - discordChannelId = discordChannelResponse.Entity.ID.Value; friendlyName = discordChannelResponse.Entity.Name.Value; var guildsClient = serviceProvider.GetRequiredService(); @@ -254,6 +244,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers "Error retrieving discord guild {0}: {1}", discordChannelResponse.Entity.GuildID.Value, discordChannelResponse.Error.Message); + remapRequired = true; return null; } @@ -262,7 +253,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers var channelModel = new ChannelRepresentation { - RealId = discordChannelId, + RealId = channelId, IsAdminChannel = channelFromDB.IsAdminChannel == true, ConnectionName = connectionName, FriendlyName = friendlyName, @@ -276,13 +267,13 @@ namespace Tgstation.Server.Host.Components.Chat.Providers var tasks = channels .Select(x => GetModelChannelFromDBChannel(x)) - .Where(x => x != null) .ToList(); await Task.WhenAll(tasks); var enumerator = tasks .Select(x => x.Result) + .Where(x => x != null) .ToList(); lock (mappedChannels) @@ -291,6 +282,9 @@ namespace Tgstation.Server.Host.Components.Chat.Providers mappedChannels.AddRange(enumerator.Select(x => x.RealId)); } + if (remapRequired) + EnqueueMessage(null); + return enumerator; } @@ -634,6 +628,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers } currentUserId = currentUserResult.Entity.ID; + initialUserName = currentUserResult.Entity.Username; } finally { diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs index bfdebd4dc8..1b589fa4a3 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs @@ -183,6 +183,9 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// The to queue. protected void EnqueueMessage(Message message) { + if (message == null) + Logger.LogTrace("Requesting channel remap..."); + lock (messageQueue) { messageQueue.Enqueue(message);