2using System.Collections.Generic;
3using System.Globalization;
6using System.Threading.Tasks;
8using Microsoft.Extensions.Logging;
24#pragma warning disable CA1506
55 readonly ILogger<ChatManager>
logger;
141 ILogger<ChatManager>
logger,
142 IEnumerable<Models.ChatBot> initialChatBots)
146 ArgumentNullException.ThrowIfNull(serverControl);
148 this.logger =
logger ??
throw new ArgumentNullException(nameof(
logger));
149 activeChatBots = initialChatBots?.ToList() ??
throw new ArgumentNullException(nameof(initialChatBots));
156 providers =
new Dictionary<long, IProvider>();
169 logger.LogTrace(
"Disposing...");
173 await providerKvp.Value.DisposeAsync();
179 public async Task
ChangeChannels(
long connectionId, IEnumerable<Models.ChatChannel> newChannels, CancellationToken cancellationToken)
181 ArgumentNullException.ThrowIfNull(newChannels);
183 logger.LogTrace(
"ChangeChannels {connectionId}...", connectionId);
185 if (provider ==
null)
188 if (!provider.Connected)
190 logger.LogDebug(
"Cannot map channels, provider {providerId} disconnected!", connectionId);
194 var results = await provider.MapChannels(newChannels, cancellationToken);
199 var botToUpdate =
activeChatBots.FirstOrDefault(bot => bot.Id == connectionId);
200 if (botToUpdate !=
null)
201 botToUpdate.Channels = newChannels
202 .Select(apiModel =>
new Models.ChatChannel
204 DiscordChannelId = apiModel.DiscordChannelId,
205 IrcChannel = apiModel.IrcChannel,
206 IsAdminChannel = apiModel.IsAdminChannel,
207 IsUpdatesChannel = apiModel.IsUpdatesChannel,
208 IsSystemChannel = apiModel.IsSystemChannel,
209 IsWatchdogChannel = apiModel.IsWatchdogChannel,
215 var newMappings = results.SelectMany(
216 kvp => kvp.Value.Select(
219 IsWatchdogChannel = kvp.Key.IsWatchdogChannel == true,
220 IsUpdatesChannel = kvp.Key.IsUpdatesChannel == true,
221 IsAdminChannel = kvp.Key.IsAdminChannel == true,
222 IsSystemChannel = kvp.Key.IsSystemChannel == true,
223 ProviderChannelId = channelRepresentation.RealId,
224 ProviderId = connectionId,
225 Channel = channelRepresentation,
240 foreach (var newMapping
in newMappings)
242 var newId = baseId++;
243 logger.LogTrace(
"Mapping channel {connectionName}:{channelFriendlyName} as {newId}", newMapping.Channel.ConnectionName, newMapping.Channel.FriendlyName, newId);
245 newMapping.Channel.RealId = newId;
257 provider.InitialMappingComplete();
262 public async Task
ChangeSettings(Models.ChatBot newSettings, CancellationToken cancellationToken)
264 ArgumentNullException.ThrowIfNull(newSettings);
266 logger.LogTrace(
"ChangeSettings...");
273 if (
providers.ContainsKey(newSettings.Id.Value))
276 disconnectTask = Task.CompletedTask;
277 if (newSettings.Enabled.Value)
280 providers.Add(newSettings.Id.Value, provider);
285 foreach (var oldMappedChannelId
in mappedChannels.Where(x => x.Value.ProviderId == newSettings.Id).Select(x => x.Key).ToList())
288 await disconnectTask;
298 var reconnectionUpdateTask = provider?.SetReconnectInterval(
299 newSettings.ReconnectionInterval.Value,
300 newSettings.Enabled.Value)
301 ?? Task.CompletedTask;
304 var originalChatBot =
activeChatBots.FirstOrDefault(bot => bot.Id == newSettings.Id);
305 if (originalChatBot !=
null)
311 ConnectionString = newSettings.ConnectionString,
312 Enabled = newSettings.Enabled,
313 Name = newSettings.Name,
314 ReconnectionInterval = newSettings.ReconnectionInterval,
315 Provider = newSettings.Provider,
316 Channels = newSettings.Channels,
320 await reconnectionUpdateTask;
326 ArgumentNullException.ThrowIfNull(message);
327 ArgumentNullException.ThrowIfNull(channelIds);
335 ArgumentNullException.ThrowIfNull(message);
337 message = String.Format(CultureInfo.InvariantCulture,
"WD: {0}", message);
340 logger.LogTrace(
"Waiting for initial provider connections before sending watchdog message...");
352 return mappedChannels.Where(x => x.Value.IsWatchdogChannel).Select(x => x.Key).ToList();
359 Models.RevisionInformation revisionInformation,
360 Version byondVersion,
361 DateTimeOffset? estimatedCompletionTime,
364 bool localCommitPushed)
366 List<ulong> wdChannels;
368 wdChannels =
mappedChannels.Where(x => x.Value.IsUpdatesChannel).Select(x => x.Key).ToList();
370 logger.LogTrace(
"Sending deployment message for RevisionInformation: {revisionInfoId}", revisionInformation.Id);
372 var callbacks =
new List<Func<string, string, Task>>();
374 var task = Task.WhenAll(
378 ChannelMapping channelMapping;
379 lock (mappedChannels)
380 if (!mappedChannels.TryGetValue(x, out channelMapping))
384 if (!providers.TryGetValue(channelMapping.ProviderId, out provider))
388 var callback = await provider.SendUpdateMessage(
391 estimatedCompletionTime,
394 channelMapping.ProviderChannelId,
399 callbacks.Add(callback);
405 "Error sending deploy message to provider {providerId}!",
406 channelMapping.ProviderId);
412 async Task CollateTasks(
string errorMessage,
string dreamMakerOutput)
422 return (errorMessage, dreamMakerOutput) =>
AddMessageTask(CollateTasks(errorMessage, dreamMakerOutput));
426 public async Task
StartAsync(CancellationToken cancellationToken)
428 foreach (var tgsCommand
in commandFactory.GenerateCommands())
429 builtinCommands.Add(tgsCommand.Name.ToUpperInvariant(), tgsCommand);
430 var initialChatBots = activeChatBots.ToList();
431 await Task.WhenAll(initialChatBots.Select(x => ChangeSettings(x, cancellationToken)));
432 initialProviderConnectionsTask = InitialConnection();
433 chatHandler = MonitorMessages(handlerCts.Token);
437 public async Task
StopAsync(CancellationToken cancellationToken)
440 if (chatHandler !=
null)
442 await Task.WhenAll(providers.Select(x => x.Key).Select(x => DeleteConnection(x, cancellationToken)));
443 await messageSendTask;
449 if (customCommandHandler ==
null)
450 throw new InvalidOperationException(
"RegisterCommandHandler() hasn't been called!");
453 lock (mappedChannels)
455 customCommandHandler,
456 mappedChannels.Select(y => y.Value.Channel),
460 lock (trackingContexts)
461 trackingContexts.Remove(context);
464 lock (trackingContexts)
465 trackingContexts.Add(context);
473 var logMessageSent = 0;
474 async Task UpdateTrackingContext(
IChatTrackingContext channelSink, IEnumerable<ChannelRepresentation> channels)
476 if (Interlocked.Exchange(ref logMessageSent, 1) == 0)
481 var waitingForInitialConnection = !initialProviderConnectionsTask.IsCompleted;
482 if (waitingForInitialConnection)
484 logger.LogTrace(
"Waiting for initial chat bot connections before updating tracking contexts...");
485 await initialProviderConnectionsTask.WithToken(cancellationToken);
489 lock (mappedChannels)
490 lock (trackingContexts)
491 tasks = trackingContexts.Select(x => UpdateTrackingContext(x, mappedChannels.Select(y => y.Value.Channel))).ToList();
493 if (waitingForInitialConnection)
495 logger.LogTrace(
"Updating chat tracking contexts...");
497 logger.LogTrace(
"No chat tracking contexts to update");
499 await Task.WhenAll(tasks);
505 if (this.customCommandHandler !=
null)
506 throw new InvalidOperationException(
"RegisterCommandHandler() already called!");
507 this.customCommandHandler = customCommandHandler ??
throw new ArgumentNullException(nameof(customCommandHandler));
513 logger.LogTrace(
"DeleteConnection {connectionId}", connectionId);
514 var provider = await RemoveProviderChannels(connectionId,
true, cancellationToken);
515 if (provider !=
null)
517 var startTime = DateTimeOffset.UtcNow;
520 await provider.Disconnect(cancellationToken);
524 logger.LogError(ex,
"Error disconnecting connection {connectionId}!", connectionId);
527 await provider.DisposeAsync();
528 var duration = DateTimeOffset.UtcNow - startTime;
529 if (duration.TotalSeconds > 3)
530 logger.LogWarning(
"Disconnecting a {providerType} took {totalSeconds}s!", provider.GetType().Name, duration.TotalSeconds);
533 logger.LogTrace(
"DeleteConnection: ID {connectionId} doesn't exist!", connectionId);
537 public Task
HandleRestart(Version updateVersion,
bool handlerMayDelayShutdownWithExtremelyLongRunningTasks, CancellationToken cancellationToken)
539 var message = updateVersion ==
null
540 ? $
"TGS: {(handlerMayDelayShutdownWithExtremelyLongRunningTasks ? "Graceful shutdown
" : "Going down
")}..."
541 : $
"TGS: Updating to version {updateVersion}...";
542 List<ulong> wdChannels;
543 lock (mappedChannels)
544 wdChannels = mappedChannels
545 .Where(x => !x.Value.IsSystemChannel)
566 async Task<IProvider>
RemoveProviderChannels(
long connectionId,
bool removeProvider, CancellationToken cancellationToken)
568 logger.LogTrace(
"RemoveProviderChannels {connectionId}...", connectionId);
572 if (!providers.TryGetValue(connectionId, out provider))
574 logger.LogTrace(
"Aborted, no such provider!");
579 providers.Remove(connectionId);
582 Task trackingContextsUpdateTask;
583 lock (mappedChannels)
585 foreach (var mappedConnectionChannel
in mappedChannels.Where(x => x.Value.ProviderId == connectionId).Select(x => x.Key).ToList())
586 mappedChannels.Remove(mappedConnectionChannel);
588 var newMappedChannels = mappedChannels.Select(y => y.Value.Channel).ToList();
591 lock (trackingContexts)
592 trackingContextsUpdateTask = Task.WhenAll(trackingContexts.Select(x => x.UpdateChannels(newMappedChannels, cancellationToken)));
594 trackingContextsUpdateTask = Task.CompletedTask;
597 await trackingContextsUpdateTask;
610 logger.LogTrace(
"Remapping channels for provider reconnection...");
611 IEnumerable<Models.ChatChannel> channelsToMap;
614 providerId = providers.Where(x => x.Value == provider).Select(x => x.Key).First();
616 lock (activeChatBots)
617 channelsToMap = activeChatBots.FirstOrDefault(x => x.Id == providerId)?.Channels;
619 if (channelsToMap?.Any() ??
false)
620 await ChangeChannels(providerId, channelsToMap, cancellationToken);
631#pragma warning disable CA1502
633#pragma warning restore CA1502
637 logger.LogTrace(
"Abort message processing because provider is disconnected!");
644 await RemapProvider(provider, cancellationToken);
650 KeyValuePair<ulong, ChannelMapping>? mappedChannel;
656 cancellationToken.ThrowIfCancellationRequested();
658 var providerIdNullable = providers
659 .Where(x => x.Value == provider)
660 .Select(x => (
long?)x.Key)
663 if (!providerIdNullable.HasValue)
666 logger.LogDebug(
"Unable to process command \"{command}\" due to provider disconnecting", message.
Content);
670 providerId = providerIdNullable.Value;
671 mappedChannel = mappedChannels
672 .Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == providerChannelId)
673 .Select(x => (KeyValuePair<ulong, ChannelMapping>?)x)
675 hasChannelZero = mappedChannels
676 .Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == 0)
682 logger.LogInformation(
"Receieved message from unmapped channel whose provider contains ID 0. Remapping...");
683 await RemapProvider(provider, cancellationToken);
684 logger.LogTrace(
"Resume processing original message...");
685 await ProcessMessage(provider, message,
true, cancellationToken);
690 lock (mappedChannels)
691 if (!mappedChannel.HasValue)
694 lock (synchronizationLock)
695 newId = channelIdCounter++;
697 "Mapping private channel {connectionName}:{channelFriendlyName} as {newId}",
704 ProviderId = providerId,
709 "Mapping DM {connectionName}:{userId} ({userFriendlyName}) as {newId}",
714 message.User.Channel.RealId = newId;
717 message.User.Channel.RealId = mappedChannel.Value.Key;
720 if (!mappedChannel.HasValue)
723 "Error mapping message: Provider ID: {providerId}, Channel Real ID: {realId}",
726 logger.LogTrace(
"message: {messageJson}", JsonConvert.SerializeObject(message));
727 lock (mappedChannels)
728 logger.LogTrace(
"mappedChannels: {mappedChannelsJson}", JsonConvert.SerializeObject(mappedChannels));
737 Text =
"TGS: Processing error, check logs!",
743 var mappingChannelRepresentation = mappedChannel.Value.Value.Channel;
745 message.User.Channel.Id = mappingChannelRepresentation.Id;
746 message.User.Channel.Tag = mappingChannelRepresentation.Tag;
747 message.User.Channel.IsAdminChannel = mappingChannelRepresentation.IsAdminChannel;
750 var trimmedMessage = message.
Content.Trim();
751 if (trimmedMessage.Length == 0)
754 var splits =
new List<string>(trimmedMessage.Split(
' ', StringSplitOptions.RemoveEmptyEntries));
755 var address = splits[0];
756 if (address.Length > 1 && (address.Last() ==
':' || address.Last() ==
','))
757 address = address[0..^1];
759 address = address.ToUpperInvariant();
762 address == CommonMention.ToUpperInvariant()
763 || address == provider.
BotMention.ToUpperInvariant();
770 "Start processing command: {message}. User (True provider Id): {profiderId}",
772 JsonConvert.SerializeObject(message.
User));
778 if (splits.Count == 0)
795 var command = splits[0].ToUpperInvariant();
797 var arguments = String.Join(
" ", splits);
799 ICommand GetCommand(
string commandName)
801 if (!builtinCommands.TryGetValue(commandName, out var handler))
803 handler = trackingContexts
804 .Where(x => x.CustomCommands !=
null)
805 .SelectMany(x => x.CustomCommands)
806 .Where(x => x.Name.ToUpperInvariant() == commandName)
813 const string UnknownCommandMessage =
"Unknown command! Type '?' or 'help' for available commands.";
815 if (command ==
"HELP" || command ==
"?")
818 if (splits.Count == 0)
820 var allCommands = builtinCommands.Select(x => x.Value).ToList();
821 allCommands.AddRange(
823 .Where(x => x.CustomCommands !=
null)
825 x => x.CustomCommands));
826 helpText = String.Format(CultureInfo.InvariantCulture,
"Available commands (Type '?' or 'help' and then a command name for more details): {0}", String.Join(
", ", allCommands.Select(x => x.Name)));
830 var helpHandler = GetCommand(splits[0].ToUpperInvariant());
831 if (helpHandler !=
default)
832 helpText = String.Format(CultureInfo.InvariantCulture,
"{0}: {1}{2}", helpHandler.Name, helpHandler.HelpText, helpHandler.AdminOnly ?
" - May only be used in admin channels" : String.Empty);
834 helpText = UnknownCommandMessage;
838 new List<ulong> { message.User.Channel.RealId },
848 var commandHandler = GetCommand(command);
850 if (commandHandler ==
default)
853 new List<ulong> { message.User.Channel.RealId },
857 Text = UnknownCommandMessage,
866 new List<ulong> { message.User.Channel.RealId },
870 Text =
"Use this command in an admin channel!",
876 var result = await commandHandler.Invoke(arguments, message.
User, cancellationToken);
878 await SendMessage(
new List<ulong> { message.User.Channel.RealId }, message, result, cancellationToken);
880 catch (OperationCanceledException ex)
882 logger.LogTrace(ex,
"Command processing canceled!");
887 logger.LogError(e,
"Error processing chat command");
889 new List<ulong> { message.User.Channel.RealId },
893 Text =
"TGS: Internal error processing command! Check server logs!",
899 logger.LogTrace(
"Done processing command.");
910 logger.LogTrace(
"Starting processing loop...");
911 var messageTasks =
new Dictionary<IProvider, Task<Message>>();
912 Task activeProcessingTask = Task.CompletedTask;
915 Task updatedTask =
null;
916 while (!cancellationToken.IsCancellationRequested)
918 if (updatedTask?.IsCompleted !=
false)
919 lock (synchronizationLock)
920 updatedTask = connectionsUpdated.Task;
923 foreach (var disposedProviderMessageTaskKvp
in messageTasks.Where(x => x.Key.Disposed).ToList())
924 messageTasks.Remove(disposedProviderMessageTaskKvp.Key);
928 foreach (var providerKvp
in providers)
929 if (!messageTasks.ContainsKey(providerKvp.Value))
930 messageTasks.Add(providerKvp.Value, providerKvp.Value.NextMessage(cancellationToken));
932 if (messageTasks.Count == 0)
934 logger.LogTrace(
"No providers active, pausing messsage monitoring...");
935 await updatedTask.WithToken(cancellationToken);
936 logger.LogTrace(
"Resuming message monitoring...");
941 await Task.WhenAny(updatedTask, Task.WhenAny(messageTasks.Select(x => x.Value)));
944 foreach (var completedMessageTaskKvp
in messageTasks.Where(x => x.Value.IsCompleted).ToList())
946 var provider = completedMessageTaskKvp.Key;
947 messageTasks.Remove(provider);
949 if (provider.Disposed)
952 var message = await completedMessageTaskKvp.Value;
953 var messageNumber = Interlocked.Increment(ref messagesProcessed);
955 async Task WrapProcessMessage()
957 var localActiveProcessingTask = activeProcessingTask;
961 await ProcessMessage(provider, message,
false, cancellationToken);
965 logger.LogError(ex,
"Error processing message {messageNumber}!", messageNumber);
968 await localActiveProcessingTask;
971 activeProcessingTask = WrapProcessMessage();
975 catch (OperationCanceledException ex)
977 logger.LogTrace(ex,
"Message processing loop cancelled!");
981 logger.LogError(e,
"Message loop crashed!");
985 await activeProcessingTask;
988 logger.LogTrace(
"Leaving message processing loop");
1001 channelIds = channelIds.ToList();
1004 "Chat send \"{message}\"{embed} to channels: [{channelIdsCommaSeperated}]",
1006 message.
Embed !=
null ?
" (with embed)" : String.Empty,
1007 String.Join(
", ", channelIds));
1009 if (!channelIds.Any())
1010 return Task.CompletedTask;
1012 return Task.WhenAll(
1013 channelIds.Select(x =>
1015 ChannelMapping channelMapping;
1016 lock (mappedChannels)
1017 if (!mappedChannels.TryGetValue(x, out channelMapping))
1018 return Task.CompletedTask;
1021 if (!providers.TryGetValue(channelMapping.ProviderId, out provider))
1022 return Task.CompletedTask;
1023 return provider.SendMessage(replyTo, message, channelMapping.ProviderChannelId, cancellationToken);
1033 await Task.WhenAll(providers.Select(x => x.Value.InitialConnectionJob));
1034 logger.LogTrace(
"Initial provider connection task completed");
1043 async Task Wrap(Task originalTask)
1050 catch (OperationCanceledException ex)
1052 logger.LogDebug(ex,
"Async chat message cancelled!");
1054 catch (Exception ex)
1056 logger.LogError(ex,
"Error in asynchronous chat message!");
1061 messageSendTask = Wrap(messageSendTask);
1072 async Task SendMessageTask()
1074 var cancellationToken = handlerCts.Token;
1075 if (waitForConnections)
1076 await initialProviderConnectionsTask.WithToken(cancellationToken);
1079 channelIdsFactory(),
1085 AddMessageTask(SendMessageTask());
Represents a mapping of a ChannelRepresentation.RealId.
string ConnectionName
The name of the connection the ChannelRepresentation belongs to.
bool IsAdminChannel
If this is considered a channel for admin commands.
ulong RealId
The Providers.IProvider channel Id.
bool IsPrivateChannel
If this is a 1-to-1 chat channel.
async Task ChangeSettings(Models.ChatBot newSettings, CancellationToken cancellationToken)
Change chat settings. If the Api.Models.EntityId.Id is not currently in use, a new connection will be...
async Task ProcessMessage(IProvider provider, Message message, bool recursed, CancellationToken cancellationToken)
Processes a message .
const string CommonMention
The common bot mention.
long messagesProcessed
The number of Messages processed.
readonly IProviderFactory providerFactory
The IProviderFactory for the ChatManager.
readonly object synchronizationLock
Used for various lock statements throughout this class.
ChatManager(IProviderFactory providerFactory, ICommandFactory commandFactory, IServerControl serverControl, ILoggerFactory loggerFactory, ILogger< ChatManager > logger, IEnumerable< Models.ChatBot > initialChatBots)
Initializes a new instance of the ChatManager class.
ICustomCommandHandler customCommandHandler
The ICustomCommandHandler for the ChangeChannels(long, IEnumerable<Models.ChatChannel>,...
void QueueMessageInternal(MessageContent message, Func< IEnumerable< ulong > > channelIdsFactory, bool waitForConnections)
Adds a given message to the send queue.
readonly IDictionary< long, IProvider > providers
Map of IProviders in use, keyed by ChatBotSettings Api.Models.EntityId.Id.
readonly List< Models.ChatBot > activeChatBots
The active Models.ChatBot for the ChatManager.
async Task InitialConnection()
Aggregate all IProvider.InitialConnectionJobs into one <sse cref="Task">.
Task initialProviderConnectionsTask
A Task that represents the IProviders initial connection.
Task SendMessage(IEnumerable< ulong > channelIds, Message replyTo, MessageContent message, CancellationToken cancellationToken)
Asynchronously send a given message to a set of channelIds .
async Task MonitorMessages(CancellationToken cancellationToken)
Monitors active providers for new Messages.
readonly IDictionary< ulong, ChannelMapping > mappedChannels
Map of ChannelRepresentation.RealIds to ChannelMappings.
Task HandleRestart(Version updateVersion, bool handlerMayDelayShutdownWithExtremelyLongRunningTasks, CancellationToken cancellationToken)
Handle a restart of the server. A Task representing the running operation.
void QueueMessage(MessageContent message, IEnumerable< ulong > channelIds)
Queue a chat message to a given set of channelIds .
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the ChatManager.
Task messageSendTask
A Task that represents all sent messages.
ulong channelIdCounter
Used for remapping ChannelRepresentation.RealIds.
readonly ICommandFactory commandFactory
The ICommandFactory for the ChatManager.
async Task RemapProvider(IProvider provider, CancellationToken cancellationToken)
Remap the channels for a given provider .
IChatTrackingContext CreateTrackingContext()
Start tracking Commands.CustomCommands and ChannelRepresentations. A new IChatTrackingContext.
void AddMessageTask(Task task)
Adds a given task to messageSendTask.
readonly ILogger< ChatManager > logger
The ILogger for the ChatManager.
async ValueTask DisposeAsync()
readonly IRestartRegistration restartRegistration
The IRestartRegistration for the ChatManager.
void QueueWatchdogMessage(string message)
Queue a chat message to configured watchdog channels.
Task chatHandler
The Task that monitors incoming chat messages.
readonly CancellationTokenSource handlerCts
The CancellationTokenSource for chatHandler.
async Task StartAsync(CancellationToken cancellationToken)
void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
Registers a customCommandHandler to use.
TaskCompletionSource connectionsUpdated
The TaskCompletionSource that completes when ChatBotSettingss change.
readonly IDictionary< string, ICommand > builtinCommands
Unchanging ICommands in the ChatManager mapped by ICommand.Name.
readonly IList< IChatTrackingContext > trackingContexts
The active IChatTrackingContexts for the ChatManager.
async Task DeleteConnection(long connectionId, CancellationToken cancellationToken)
Disconnects and deletes a given connection. A Task representing the running operation.
async Task ChangeChannels(long connectionId, IEnumerable< Models.ChatChannel > newChannels, CancellationToken cancellationToken)
Change chat channels. A Task representing the running operation.
async Task< IProvider > RemoveProviderChannels(long connectionId, bool removeProvider, CancellationToken cancellationToken)
Remove a IProvider from mappedChannels optionally removing the provider itself from providers and upd...
async Task StopAsync(CancellationToken cancellationToken)
async Task UpdateTrackingContexts(CancellationToken cancellationToken)
Force an update with the active channels on all active IChatTrackingContexts. A Task representing the...
Action< string, string > QueueDeploymentMessage(Models.RevisionInformation revisionInformation, Version byondVersion, DateTimeOffset? estimatedCompletionTime, string gitHubOwner, string gitHubRepo, bool localCommitPushed)
Send the message for a deployment to configured deployment channels. An Action<T1,...
string FriendlyName
The friendly name of the user.
ulong RealId
The internal user id.
ChannelRepresentation Channel
The ChannelRepresentation the user spoke from.
Represents a message recieved by a IProvider.
string Content
The text of the message.
ChatUser User
The ChatUser who sent the Message.
Represents a message to send to a chat provider.
ChatEmbed Embed
The ChatEmbed.
string Text
The message string.
Helpers for manipulating the Serilog.Context.LogContext.
const string ChatMessageIterationContextProperty
The Serilog.Context.LogContext property name for the ID of the chat message currently being processed...
Factory for built in ICommands.
Represents a command that can be invoked by talking to chat bots.
Task UpdateChannels(IEnumerable< ChannelRepresentation > newChannels, CancellationToken cancellationToken)
Called when newChannels are set.
For managing connected chat services.
Represents a tracking of dynamic chat json files.
Handles Commands.ICommands that map to those defined in a IChatTrackingContext.
IProvider CreateProvider(ChatBot settings)
Create a IProvider.
For interacting with a chat service.
string BotMention
The string that indicates the IProvider was mentioned.
bool Connected
If the IProvider is currently connected.
Handler for server restarts.
Represents the lifetime of a IRestartHandler registration.
Represents a service that may take an updated Host assembly and run it, stopping the current assembly...
IRestartRegistration RegisterForRestart(IRestartHandler handler)
Register a given handler to run before stopping the server for a restart.