From 5c4f55850c9036cfee63fb5ed3dc5124d3b225ff Mon Sep 17 00:00:00 2001 From: craftxbox Date: Fri, 20 Sep 2024 04:02:11 -0230 Subject: [PATCH 1/6] Refactor IrcProvider just a little --- .../Models/IrcConnectionStringBuilder.cs | 1 + .../Models/IrcPasswordType.cs | 5 + .../Components/Chat/Providers/IrcProvider.cs | 198 +++++++++++------- .../Chat/Providers/TestIrcProvider.cs | 11 + 4 files changed, 135 insertions(+), 80 deletions(-) diff --git a/src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs b/src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs index b4cb64a811..18e8a14df6 100644 --- a/src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs +++ b/src/Tgstation.Server.Api/Models/IrcConnectionStringBuilder.cs @@ -88,6 +88,7 @@ namespace Tgstation.Server.Api.Models case IrcPasswordType.NickServ: case IrcPasswordType.Sasl: case IrcPasswordType.Server: + case IrcPasswordType.Oper: PasswordType = passwordType; break; default: diff --git a/src/Tgstation.Server.Api/Models/IrcPasswordType.cs b/src/Tgstation.Server.Api/Models/IrcPasswordType.cs index add54b1565..9a0fca38c4 100644 --- a/src/Tgstation.Server.Api/Models/IrcPasswordType.cs +++ b/src/Tgstation.Server.Api/Models/IrcPasswordType.cs @@ -19,5 +19,10 @@ /// Use NickServ authentication. /// NickServ, + + /// + /// Use OPER authentication. + /// + Oper, } } diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs index ad83a80b18..3a532f6b42 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -42,11 +42,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// public override string BotMention => client.Nickname; - /// - /// The client. - /// - readonly IrcFeatures client; - /// /// Address of the server to connect to. /// @@ -57,6 +52,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// readonly ushort port; + /// + /// Wether or not this IRC client is to use ssl. + /// + readonly bool ssl; + /// /// IRC nickname. /// @@ -82,6 +82,16 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// readonly Dictionary queryChannelIdMap; + /// + /// The version string obtained from . + /// + readonly string versionString; + + /// + /// The client. + /// + IrcFeatures client; + /// /// The used for . /// @@ -92,11 +102,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// ulong channelIdCounter; - /// - /// If we are disconnecting. - /// - bool disconnecting; - /// /// Initializes a new instance of the class. /// @@ -121,33 +126,15 @@ namespace Tgstation.Server.Host.Components.Chat.Providers address = ircBuilder.Address!; port = ircBuilder.Port!.Value; + ssl = ircBuilder.UseSsl!.Value; nickname = ircBuilder.Nickname!; password = ircBuilder.Password!; passwordType = ircBuilder.PasswordType; - client = new IrcFeatures - { - SupportNonRfc = true, - CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...", - AutoRejoin = true, - AutoRejoinOnKick = true, - AutoRelogin = true, - AutoRetry = false, - AutoReconnect = false, - ActiveChannelSyncing = true, - AutoNickHandling = true, - CtcpVersion = assemblyInformationProvider.VersionString, - UseSsl = ircBuilder.UseSsl!.Value, - }; - if (ircBuilder.UseSsl.Value) - client.ValidateServerCertificate = true; // dunno if it defaults to that or what + versionString = assemblyInformationProvider.VersionString; - client.OnChannelMessage += Client_OnChannelMessage; - client.OnQueryMessage += Client_OnQueryMessage; - - /*client.OnReadLine += (sender, e) => Logger.LogTrace("READ: {line}", e.Line); - client.OnWriteLine += (sender, e) => Logger.LogTrace("WRITE: {line}", e.Line);*/ + client = InstantiateClient(); channelIdMap = new Dictionary(); queryChannelIdMap = new Dictionary(); @@ -369,12 +356,15 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// protected override async ValueTask Connect(CancellationToken cancellationToken) { - disconnecting = false; cancellationToken.ThrowIfCancellationRequested(); try { await Task.Factory.StartNew( - () => client.Connect(address, port), + () => + { + client = InstantiateClient(); + client.Connect(address, port); + }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, TaskScheduler.Current) @@ -382,70 +372,50 @@ namespace Tgstation.Server.Host.Components.Chat.Providers cancellationToken.ThrowIfCancellationRequested(); + listenTask = Task.Factory.StartNew( + () => + { + Logger.LogTrace("Starting blocking listen..."); + try + { + client.Listen(); + } + catch (Exception ex) + { + Logger.LogWarning(ex, "IRC Main Listen Exception!"); + } + + Logger.LogTrace("Exiting listening task..."); + }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, TaskScheduler.Current); + Logger.LogTrace("Authenticating ({passwordType})...", passwordType); switch (passwordType) { case IrcPasswordType.Server: - client.Login(nickname, nickname, 0, nickname, password); + client.RfcPass(password); + await Login(client, nickname); break; case IrcPasswordType.NickServ: - client.Login(nickname, nickname, 0, nickname); + await Login(client, nickname); cancellationToken.ThrowIfCancellationRequested(); client.SendMessage(SendType.Message, "NickServ", String.Format(CultureInfo.InvariantCulture, "IDENTIFY {0}", password)); break; case IrcPasswordType.Sasl: await SaslAuthenticate(cancellationToken); break; + case IrcPasswordType.Oper: + await Login(client, nickname); + cancellationToken.ThrowIfCancellationRequested(); + client.RfcOper(nickname, password, Priority.Critical); + break; case null: - client.Login(nickname, nickname, 0, nickname); + await Login(client, nickname); break; default: throw new InvalidOperationException($"Invalid IrcPasswordType: {passwordType.Value}"); } cancellationToken.ThrowIfCancellationRequested(); - Logger.LogTrace("Processing initial messages..."); - await NonBlockingListen(cancellationToken); - - var nickCheckCompleteTcs = new TaskCompletionSource(); - using (cancellationToken.Register(() => nickCheckCompleteTcs.TrySetCanceled(cancellationToken))) - { - listenTask = Task.Factory.StartNew( - async () => - { - Logger.LogTrace("Entering nick check loop"); - while (!disconnecting && client.IsConnected && client.Nickname != nickname) - { - client.ListenOnce(true); - if (disconnecting || !client.IsConnected) - break; - await NonBlockingListen(cancellationToken); - - // ensure we have the correct nick - if (client.GetIrcUser(nickname) == null) - client.RfcNick(nickname); - } - - nickCheckCompleteTcs.TrySetResult(); - - Logger.LogTrace("Starting blocking listen..."); - try - { - client.Listen(); - } - catch (Exception ex) - { - Logger.LogWarning(ex, "IRC Main Listen Exception!"); - } - - Logger.LogTrace("Exiting listening task..."); - }, - cancellationToken, - DefaultIOManager.BlockingTaskCreationOptions, - TaskScheduler.Current); - - await nickCheckCompleteTcs.Task; - } Logger.LogTrace("Connection established!"); } @@ -487,6 +457,37 @@ namespace Tgstation.Server.Host.Components.Chat.Providers } } + /// + /// Register the client on the network. + /// + /// IRC client. + /// Nickname. + /// that resolves when registration has been completed. + /// If the IRC server fails to respond. + private async Task Login(IrcFeatures client, string nickname) + { + var promise = new TaskCompletionSource(); + + void Callback(object? sender, EventArgs e) + { + Logger.LogTrace("IRC Registered."); + promise.TrySetResult(e); + } + + client.OnRegistered += Callback; + + client.Login(nickname, nickname, 0, nickname); + + var completed = await Task.WhenAny(promise.Task, Task.Delay(30 * 1000)); + if (completed == promise.Task) + { + client.OnRegistered -= Callback; + return; + } + + throw new TimeoutException("Timed out waiting for IRC registration."); + } + /// /// Handle an IRC message. /// @@ -667,8 +668,6 @@ namespace Tgstation.Server.Host.Components.Chat.Providers Logger.LogTrace("Hard disconnect"); - disconnecting = true; - // This call blocks permanently randomly sometimes // Frankly I don't give a shit var disconnectTask = Task.Factory.StartNew( @@ -693,5 +692,44 @@ namespace Tgstation.Server.Host.Components.Chat.Providers listenTask ?? Task.CompletedTask), AsyncDelayer.Delay(TimeSpan.FromSeconds(5), cancellationToken)); } + + /// + /// Creates a new instance of the IRC client. + /// Reusing the same client after a disconnection seems to cause issues. + /// + /// The client to use. + private IrcFeatures InstantiateClient() + { + IrcFeatures newClient = new IrcFeatures + { + SupportNonRfc = true, + CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...", + AutoRejoin = true, + AutoRejoinOnKick = true, + AutoRelogin = false, + AutoRetry = false, + AutoReconnect = false, + ActiveChannelSyncing = true, + AutoNickHandling = true, + CtcpVersion = versionString, + UseSsl = ssl, + EnableUTF8Recode = true, + }; + if (ssl) + newClient.ValidateServerCertificate = true; // dunno if it defaults to that or what + + newClient.OnChannelMessage += Client_OnChannelMessage; + newClient.OnQueryMessage += Client_OnQueryMessage; + + newClient.OnReadLine += (sender, e) => Logger.LogTrace("READ: {line}", e.Line); + newClient.OnWriteLine += (sender, e) => Logger.LogTrace("WRITE: {line}", e.Line); + newClient.OnError += (sender, e) => + { + Logger.LogError("IRC ERROR: {error}", e.ErrorMessage); + newClient.Disconnect(); + }; + + return newClient; + } } } diff --git a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs index 3f1e050219..6a343b40a0 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs @@ -88,6 +88,17 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests await InvokeConnect(provider); Assert.IsTrue(provider.Connected); + await Task.Delay(2000); // IRC servers do not like it when you connect and disconnect in rapid succession + + await provider.Disconnect(default); + Assert.IsFalse(provider.Connected); + + await Task.Delay(2000); // same as above + + await InvokeConnect(provider); + await Task.Delay(2000); // make sure it stays connected after a reconnect attempt + Assert.IsTrue(provider.Connected); + await provider.Disconnect(default); Assert.IsFalse(provider.Connected); } From 8e9cb0eb3aae86cafe07a46106ae94f4c170dd1d Mon Sep 17 00:00:00 2001 From: craftxbox Date: Fri, 20 Sep 2024 17:13:13 -0230 Subject: [PATCH 2/6] Changes from review --- .../Components/Chat/Providers/IrcProvider.cs | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs index 3a532f6b42..c383c75da8 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -83,9 +83,9 @@ namespace Tgstation.Server.Host.Components.Chat.Providers readonly Dictionary queryChannelIdMap; /// - /// The version string obtained from . + /// The obtained from constructor, used for the CTCP version string. /// - readonly string versionString; + readonly IAssemblyInformationProvider assemblyInfo; /// /// The client. @@ -132,7 +132,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers password = ircBuilder.Password!; passwordType = ircBuilder.PasswordType; - versionString = assemblyInformationProvider.VersionString; + assemblyInfo = assemblyInformationProvider; client = InstantiateClient(); @@ -386,17 +386,20 @@ namespace Tgstation.Server.Host.Components.Chat.Providers } Logger.LogTrace("Exiting listening task..."); - }, cancellationToken, DefaultIOManager.BlockingTaskCreationOptions, TaskScheduler.Current); + }, + cancellationToken, + DefaultIOManager.BlockingTaskCreationOptions, + TaskScheduler.Current); Logger.LogTrace("Authenticating ({passwordType})...", passwordType); switch (passwordType) { case IrcPasswordType.Server: client.RfcPass(password); - await Login(client, nickname); + await Login(client, nickname, cancellationToken); break; case IrcPasswordType.NickServ: - await Login(client, nickname); + await Login(client, nickname, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); client.SendMessage(SendType.Message, "NickServ", String.Format(CultureInfo.InvariantCulture, "IDENTIFY {0}", password)); break; @@ -404,12 +407,12 @@ namespace Tgstation.Server.Host.Components.Chat.Providers await SaslAuthenticate(cancellationToken); break; case IrcPasswordType.Oper: - await Login(client, nickname); + await Login(client, nickname, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); client.RfcOper(nickname, password, Priority.Critical); break; case null: - await Login(client, nickname); + await Login(client, nickname, cancellationToken); break; default: throw new InvalidOperationException($"Invalid IrcPasswordType: {passwordType.Value}"); @@ -462,9 +465,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// /// IRC client. /// Nickname. + /// Cancellation token. /// that resolves when registration has been completed. /// If the IRC server fails to respond. - private async Task Login(IrcFeatures client, string nickname) + async ValueTask Login(IrcFeatures client, string nickname, CancellationToken cancellationToken) { var promise = new TaskCompletionSource(); @@ -478,14 +482,20 @@ namespace Tgstation.Server.Host.Components.Chat.Providers client.Login(nickname, nickname, 0, nickname); - var completed = await Task.WhenAny(promise.Task, Task.Delay(30 * 1000)); - if (completed == promise.Task) - { - client.OnRegistered -= Callback; - return; - } + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + cts.CancelAfter(TimeSpan.FromSeconds(30)); - throw new TimeoutException("Timed out waiting for IRC registration."); + try + { + await promise.Task.WaitAsync(cts.Token); + client.OnRegistered -= Callback; + } + catch (OperationCanceledException) + { + if (client.IsConnected) + client.Disconnect(); + throw new JobException("Timed out waiting for IRC Registration"); + } } /// @@ -698,9 +708,9 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// Reusing the same client after a disconnection seems to cause issues. /// /// The client to use. - private IrcFeatures InstantiateClient() + IrcFeatures InstantiateClient() { - IrcFeatures newClient = new IrcFeatures + var newClient = new IrcFeatures { SupportNonRfc = true, CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...", @@ -711,7 +721,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers AutoReconnect = false, ActiveChannelSyncing = true, AutoNickHandling = true, - CtcpVersion = versionString, + CtcpVersion = assemblyInfo.VersionString, UseSsl = ssl, EnableUTF8Recode = true, }; @@ -721,8 +731,9 @@ namespace Tgstation.Server.Host.Components.Chat.Providers newClient.OnChannelMessage += Client_OnChannelMessage; newClient.OnQueryMessage += Client_OnQueryMessage; - newClient.OnReadLine += (sender, e) => Logger.LogTrace("READ: {line}", e.Line); - newClient.OnWriteLine += (sender, e) => Logger.LogTrace("WRITE: {line}", e.Line); + /* newClient.OnReadLine += (sender, e) => Logger.LogTrace("READ: {line}", e.Line); + newClient.OnWriteLine += (sender, e) => Logger.LogTrace("WRITE: {line}", e.Line); */ + newClient.OnError += (sender, e) => { Logger.LogError("IRC ERROR: {error}", e.ErrorMessage); From 0efd4c400f2ed6998dde6ea2c46417b30661be6d Mon Sep 17 00:00:00 2001 From: craftxbox Date: Wed, 2 Oct 2024 18:56:56 -0230 Subject: [PATCH 3/6] debug var for irc provider --- .../Components/Chat/Providers/IrcProvider.cs | 28 +++++++++++++------ .../Chat/Providers/ProviderFactory.cs | 13 +++++++-- .../Configuration/FileLoggingConfiguration.cs | 5 ++++ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs index c383c75da8..f32698a3b0 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -13,6 +13,7 @@ using Newtonsoft.Json; using Tgstation.Server.Api.Models; using Tgstation.Server.Host.Components.Interop; +using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Extensions; using Tgstation.Server.Host.IO; using Tgstation.Server.Host.Jobs; @@ -87,6 +88,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// readonly IAssemblyInformationProvider assemblyInfo; + /// + /// The for the . + /// + readonly FileLoggingConfiguration loggingConfiguration; + /// /// The client. /// @@ -110,12 +116,14 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// The for the . /// The to get the from. /// The for the . + /// The for the . public IrcProvider( IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger logger, IAssemblyInformationProvider assemblyInformationProvider, - Models.ChatBot chatBot) + Models.ChatBot chatBot, + FileLoggingConfiguration loggingConfiguration) : base(jobManager, asyncDelayer, logger, chatBot) { ArgumentNullException.ThrowIfNull(assemblyInformationProvider); @@ -132,7 +140,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers password = ircBuilder.Password!; passwordType = ircBuilder.PasswordType; - assemblyInfo = assemblyInformationProvider; + assemblyInfo = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); + this.loggingConfiguration = loggingConfiguration ?? throw new ArgumentNullException(nameof(loggingConfiguration)); client = InstantiateClient(); @@ -731,14 +740,17 @@ namespace Tgstation.Server.Host.Components.Chat.Providers newClient.OnChannelMessage += Client_OnChannelMessage; newClient.OnQueryMessage += Client_OnQueryMessage; - /* newClient.OnReadLine += (sender, e) => Logger.LogTrace("READ: {line}", e.Line); - newClient.OnWriteLine += (sender, e) => Logger.LogTrace("WRITE: {line}", e.Line); */ + if (loggingConfiguration.ProviderNetworkDebug) + { + newClient.OnReadLine += (sender, e) => Logger.LogTrace("READ: {line}", e.Line); + newClient.OnWriteLine += (sender, e) => Logger.LogTrace("WRITE: {line}", e.Line); + } newClient.OnError += (sender, e) => - { - Logger.LogError("IRC ERROR: {error}", e.ErrorMessage); - newClient.Disconnect(); - }; + { + Logger.LogError("IRC ERROR: {error}", e.ErrorMessage); + newClient.Disconnect(); + }; return newClient; } diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/ProviderFactory.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/ProviderFactory.cs index ed3608b92d..41113d196f 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/ProviderFactory.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/ProviderFactory.cs @@ -40,6 +40,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// readonly GeneralConfiguration generalConfiguration; + /// + /// The for the . + /// + readonly FileLoggingConfiguration loggingConfiguration; + /// /// Initializes a new instance of the class. /// @@ -48,18 +53,21 @@ namespace Tgstation.Server.Host.Components.Chat.Providers /// The value of . /// The value of . /// The containing the value of . + /// The containing the value of . public ProviderFactory( IJobManager jobManager, IAssemblyInformationProvider assemblyInformationProvider, IAsyncDelayer asyncDelayer, ILoggerFactory loggerFactory, - IOptions generalConfigurationOptions) + IOptions generalConfigurationOptions, + IOptions loggingConfigurationOptions) { this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer)); this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider)); generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions)); + loggingConfiguration = loggingConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(loggingConfigurationOptions)); } /// @@ -73,7 +81,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers asyncDelayer, loggerFactory.CreateLogger(), assemblyInformationProvider, - settings), + settings, + loggingConfiguration), ChatProvider.Discord => new DiscordProvider( jobManager, asyncDelayer, diff --git a/src/Tgstation.Server.Host/Configuration/FileLoggingConfiguration.cs b/src/Tgstation.Server.Host/Configuration/FileLoggingConfiguration.cs index 534daf8ebb..6bd98f2039 100644 --- a/src/Tgstation.Server.Host/Configuration/FileLoggingConfiguration.cs +++ b/src/Tgstation.Server.Host/Configuration/FileLoggingConfiguration.cs @@ -29,6 +29,11 @@ namespace Tgstation.Server.Host.Configuration /// public bool Disable { get; set; } + /// + /// If Chat Providers should log their network traffic. Normally disabled because it is too noisy. + /// + public bool ProviderNetworkDebug { get; set; } + /// /// The minimum to display in logs. /// From 16e035955f5e03708b82e0b6bfaa88d5c0722487 Mon Sep 17 00:00:00 2001 From: craftxbox Date: Wed, 2 Oct 2024 19:31:13 -0230 Subject: [PATCH 4/6] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ed43da7fb1..a03445fd6b 100644 --- a/README.md +++ b/README.md @@ -251,6 +251,8 @@ Create an `appsettings.Production.yml` file next to `appsettings.yml`. This will - `FileLogging:LogLevel`: Can be one of `Trace`, `Debug`, `Information`, `Warning`, `Error`, or `Critical`. Restricts what is put into the log files. Currently `Debug` is reccommended for help with error reporting. +- `FileLogging:ProviderNetworkDebug`: Boolean controlling whether or not Chat bot providers should log their raw network traffic. Currently only applies to IrcProvider. + - `Kestrel:Endpoints:Http:Url`: The URL (i.e. interface and ports) your application should listen on. General use case should be `http://localhost:` for restricted local connections. See the Remote Access section for configuring public access to the World Wide Web. This doesn't need to be changed using the docker setup and should be mapped with the `-p` option instead - `Database:DatabaseType`: Can be one of `SqlServer`, `MariaDB`, `MySql`, `PostgresSql`, or `Sqlite`. From 4123991fe5cea6ac2119b2558eecaa791494f8c0 Mon Sep 17 00:00:00 2001 From: craftxbox Date: Thu, 3 Oct 2024 21:36:58 -0230 Subject: [PATCH 5/6] bump the config version also style fixes --- build/Version.props | 2 +- .../Components/Chat/Providers/IrcProvider.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/Version.props b/build/Version.props index 9c784ed4d7..e1726021b4 100644 --- a/build/Version.props +++ b/build/Version.props @@ -4,7 +4,7 @@ 6.10.0 - 5.2.0 + 5.3.0 10.10.0 7.0.0 16.0.0 diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs index f32698a3b0..ca526f725b 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -747,10 +747,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers } newClient.OnError += (sender, e) => - { - Logger.LogError("IRC ERROR: {error}", e.ErrorMessage); - newClient.Disconnect(); - }; + { + Logger.LogError("IRC ERROR: {error}", e.ErrorMessage); + newClient.Disconnect(); + }; return newClient; } From 94aafc1f9ad01a75f0962664a88534c003c6360e Mon Sep 17 00:00:00 2001 From: craftxbox Date: Fri, 4 Oct 2024 01:11:25 -0230 Subject: [PATCH 6/6] fix the tests (i forgot about them) --- .../Components/Chat/Providers/IrcProvider.cs | 1 + .../Chat/Providers/TestIrcProvider.cs | 24 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs index ca526f725b..0eedc7c949 100644 --- a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -127,6 +127,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers : base(jobManager, asyncDelayer, logger, chatBot) { ArgumentNullException.ThrowIfNull(assemblyInformationProvider); + ArgumentNullException.ThrowIfNull(loggingConfiguration); var builder = chatBot.CreateConnectionStringBuilder(); if (builder == null || !builder.Valid || builder is not IrcConnectionStringBuilder ircBuilder) diff --git a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs index 6a343b40a0..bc1ecbf0a4 100644 --- a/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs +++ b/tests/Tgstation.Server.Host.Tests/Components/Chat/Providers/TestIrcProvider.cs @@ -9,6 +9,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Tgstation.Server.Api.Models; +using Tgstation.Server.Host.Configuration; using Tgstation.Server.Host.Jobs; using Tgstation.Server.Host.Models; using Tgstation.Server.Host.System; @@ -22,15 +23,15 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests [TestMethod] public async Task TestConstructionAndDisposal() { - Assert.ThrowsException(() => new IrcProvider(null, null, null, null, null)); + Assert.ThrowsException(() => new IrcProvider(null, null, null, null, null, null)); var mockJobManager = new Mock(); - Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, null, null, null, null)); + Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, null, null, null, null, null)); var mockAsyncDelayer = new Mock(); - Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, null, null, null)); + Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, null, null, null, null)); var mockLogger = new Mock>(); - Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, mockLogger.Object, null, null)); + Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, mockLogger.Object, null, null, null)); var mockAss = new Mock(); - Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, mockLogger.Object, mockAss.Object, null)); + Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, mockLogger.Object, mockAss.Object, null, null)); var mockBot = new ChatBot { @@ -38,8 +39,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests Instance = new Models.Instance(), Provider = ChatProvider.Irc }; + Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, mockLogger.Object, mockAss.Object, mockBot, null)); - Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, mockLogger.Object, mockAss.Object, mockBot)); + var mockLogConf = new FileLoggingConfiguration(); + Assert.ThrowsException(() => new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, mockLogger.Object, mockAss.Object, mockBot, mockLogConf)); mockBot.ConnectionString = new IrcConnectionStringBuilder { @@ -49,7 +52,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests Port = 6667 }.ToString(); - await new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, mockLogger.Object, mockAss.Object, mockBot).DisposeAsync(); + await new IrcProvider(mockJobManager.Object, mockAsyncDelayer.Object, mockLogger.Object, mockAss.Object, mockBot, mockLogConf).DisposeAsync(); } static ValueTask InvokeConnect(IProvider provider, CancellationToken cancellationToken = default) => (ValueTask)provider.GetType().GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(provider, new object[] { cancellationToken }); @@ -78,12 +81,15 @@ namespace Tgstation.Server.Host.Components.Chat.Providers.Tests .Setup(x => x.WaitForJobCompletion(It.IsNotNull(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(ValueTask.FromResult(true)); var mockJobManager = mockSetup.Object; - await using var provider = new IrcProvider(mockJobManager, new AsyncDelayer(), loggerFactory.CreateLogger(), Mock.Of(), new ChatBot + + var chatBot = new ChatBot { ConnectionString = actualToken, Provider = ChatProvider.Irc, Instance = new Models.Instance(), - }); + }; + + await using var provider = new IrcProvider(mockJobManager, new AsyncDelayer(), loggerFactory.CreateLogger(), Mock.Of(), chatBot, new FileLoggingConfiguration()); Assert.IsFalse(provider.Connected); await InvokeConnect(provider); Assert.IsTrue(provider.Connected);