From a99b5ff07fdcd925a2e06cb5ac61b24646a1acb4 Mon Sep 17 00:00:00 2001
From: "tgstation-server-ci[bot]"
<161980869+tgstation-server-ci[bot]@users.noreply.github.com>
Date: Sat, 5 Oct 2024 03:39:21 +0000
Subject: [PATCH] Deploy code docs to GitHub Pages for workflow run 3372
Commit: e82b1a6f3982383c51c63752ecfbffc54be4b046
---
_administration_controller_8cs_source.html | 2 +-
_application_8cs_source.html | 6 +-
_file_logging_configuration_8cs_source.html | 73 +-
..._connection_string_builder_8cs_source.html | 83 +-
_irc_password_type_8cs.html | 1 +
_irc_password_type_8cs_source.html | 7 +-
_irc_provider_8cs_source.html | 1285 +++++++--------
_provider_factory_8cs_source.html | 91 +-
changelog.yml | 21 +
...1_1_api_1_1_models_1_1_engine_version.html | 4 +-
...els_1_1_irc_connection_string_builder.html | 85 +-
..._1_providers_1_1_irc_provider-members.html | 82 +-
...1_chat_1_1_providers_1_1_irc_provider.html | 1381 ++++++++++-------
...roviders_1_1_irc_provider__coll__graph.map | 82 +-
...roviders_1_1_irc_provider__coll__graph.md5 | 2 +-
...roviders_1_1_irc_provider__coll__graph.png | Bin 134538 -> 159089 bytes
...6341a13aed727a56c3dfe047f8b22d_icgraph.map | 5 +
...6341a13aed727a56c3dfe047f8b22d_icgraph.md5 | 1 +
...6341a13aed727a56c3dfe047f8b22d_icgraph.png | Bin 0 -> 10672 bytes
...219ee47d3cdc106c66c7279c5d1473c_cgraph.map | 5 +
...219ee47d3cdc106c66c7279c5d1473c_cgraph.md5 | 1 +
...219ee47d3cdc106c66c7279c5d1473c_cgraph.png | Bin 0 -> 10717 bytes
...09ea88022cc7f1ed175e5f863e79c9_icgraph.map | 5 -
...09ea88022cc7f1ed175e5f863e79c9_icgraph.md5 | 1 -
...09ea88022cc7f1ed175e5f863e79c9_icgraph.png | Bin 10774 -> 0 bytes
...b2c312d6d9703edbeb60825cfb1602_icgraph.map | 5 -
...b2c312d6d9703edbeb60825cfb1602_icgraph.md5 | 1 -
...b2c312d6d9703edbeb60825cfb1602_icgraph.png | Bin 11175 -> 0 bytes
...b8ee20f302c136f9a254c34315950a4_cgraph.map | 7 -
...b8ee20f302c136f9a254c34315950a4_cgraph.md5 | 1 -
...b8ee20f302c136f9a254c34315950a4_cgraph.png | Bin 17995 -> 0 bytes
...s_1_1_chat_1_1_providers_1_1_provider.html | 4 +-
...roviders_1_1_provider_factory-members.html | 3 +-
...at_1_1_providers_1_1_provider_factory.html | 164 +-
...ders_1_1_provider_factory__coll__graph.map | 39 +-
...ders_1_1_provider_factory__coll__graph.md5 | 2 +-
...ders_1_1_provider_factory__coll__graph.png | Bin 100642 -> 110671 bytes
...nents_1_1_interop_1_1_message_content.html | 2 +-
..._1_file_logging_configuration-members.html | 3 +-
...ration_1_1_file_logging_configuration.html | 82 +-
...rollers_1_1_administration_controller.html | 6 +-
...ver_1_1_host_1_1_core_1_1_application.html | 8 +-
..._host_1_1_i_o_1_1_default_i_o_manager.html | 2 +-
..._1_1_host_1_1_utils_1_1_async_delayer.html | 2 +-
functions.html | 4 +-
functions_c.html | 2 +-
functions_d.html | 1 -
functions_func_i.html | 3 +-
functions_func_l.html | 2 +-
functions_func_p.html | 2 +-
functions_g.html | 4 +-
functions_h.html | 4 +-
functions_i.html | 5 +-
functions_l.html | 3 +-
functions_p.html | 7 +-
functions_prop_p.html | 1 +
functions_r.html | 3 +-
functions_s.html | 1 +
functions_vars.html | 1 +
functions_vars_c.html | 2 +-
functions_vars_d.html | 1 -
functions_vars_l.html | 1 +
functions_vars_s.html | 1 +
index.html | 1 +
...er_1_1_host_1_1_i_o_1_1_i_i_o_manager.html | 2 +-
...m_1_1_i_assembly_information_provider.html | 4 +-
...station_1_1_server_1_1_api_1_1_models.html | 8 +-
search/all_10.js | 43 +-
search/all_11.js | 71 +-
search/all_13.js | 10 +-
search/all_14.js | 267 ++--
search/all_15.js | 6 +-
search/all_16.js | 6 +-
search/all_18.js | 4 +-
search/all_2.js | 113 +-
search/all_3.js | 2 +-
search/all_4.js | 8 +-
search/all_5.js | 255 ++-
search/all_6.js | 2 +-
search/all_7.js | 6 +-
search/all_8.js | 4 +-
search/all_9.js | 4 +-
search/all_a.js | 395 ++---
search/all_d.js | 45 +-
search/enumvalues_e.js | 3 +-
search/functions_8.js | 43 +-
search/functions_b.js | 4 +-
search/functions_f.js | 2 +-
search/properties_f.js | 23 +-
search/variables_0.js | 19 +-
search/variables_11.js | 2 +-
search/variables_12.js | 55 +-
search/variables_2.js | 2 +-
search/variables_3.js | 47 +-
search/variables_b.js | 13 +-
95 files changed, 2731 insertions(+), 2340 deletions(-)
create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_a036341a13aed727a56c3dfe047f8b22d_icgraph.map
create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_a036341a13aed727a56c3dfe047f8b22d_icgraph.md5
create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_a036341a13aed727a56c3dfe047f8b22d_icgraph.png
create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_a4219ee47d3cdc106c66c7279c5d1473c_cgraph.map
create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_a4219ee47d3cdc106c66c7279c5d1473c_cgraph.md5
create mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_a4219ee47d3cdc106c66c7279c5d1473c_cgraph.png
delete mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_a6709ea88022cc7f1ed175e5f863e79c9_icgraph.map
delete mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_a6709ea88022cc7f1ed175e5f863e79c9_icgraph.md5
delete mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_a6709ea88022cc7f1ed175e5f863e79c9_icgraph.png
delete mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_ad5b2c312d6d9703edbeb60825cfb1602_icgraph.map
delete mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_ad5b2c312d6d9703edbeb60825cfb1602_icgraph.md5
delete mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_ad5b2c312d6d9703edbeb60825cfb1602_icgraph.png
delete mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_afb8ee20f302c136f9a254c34315950a4_cgraph.map
delete mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_afb8ee20f302c136f9a254c34315950a4_cgraph.md5
delete mode 100644 class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider_afb8ee20f302c136f9a254c34315950a4_cgraph.png
diff --git a/_administration_controller_8cs_source.html b/_administration_controller_8cs_source.html
index b662a723d5..adfd9c8216 100644
--- a/_administration_controller_8cs_source.html
+++ b/_administration_controller_8cs_source.html
@@ -461,7 +461,7 @@ $(document).ready(function() { init_codefold(0); });
const string Administration
The server administration controller.
const string Logs
The endpoint to download server logs.
File logging configuration options.
-string GetFullLogDirectory(IIOManager ioManager, IAssemblyInformationProvider assemblyInformationProvider, IPlatformIdentifier platformIdentifier)
Gets the evaluated log Directory.
+string GetFullLogDirectory(IIOManager ioManager, IAssemblyInformationProvider assemblyInformationProvider, IPlatformIdentifier platformIdentifier)
Gets the evaluated log Directory.
ApiController for TGS administration purposes.
ValueTask< IActionResult > ListLogs([FromQuery] int? page, [FromQuery] int? pageSize, CancellationToken cancellationToken)
List LogFileResponses present.
readonly FileLoggingConfiguration fileLoggingConfiguration
The FileLoggingConfiguration for the AdministrationController.
diff --git a/_application_8cs_source.html b/_application_8cs_source.html
index 55fcf866d1..fee9f87c03 100644
--- a/_application_8cs_source.html
+++ b/_application_8cs_source.html
@@ -851,9 +851,9 @@ $(document).ready(function() { init_codefold(0); });
Configuration options for the web control panel.
DatabaseType DatabaseType
The Configuration.DatabaseType to create.
-LogLevel MicrosoftLogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs for Microsoft library sources.
-string GetFullLogDirectory(IIOManager ioManager, IAssemblyInformationProvider assemblyInformationProvider, IPlatformIdentifier platformIdentifier)
Gets the evaluated log Directory.
-LogLevel LogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs.
+LogLevel MicrosoftLogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs for Microsoft library sources.
+string GetFullLogDirectory(IIOManager ioManager, IAssemblyInformationProvider assemblyInformationProvider, IPlatformIdentifier platformIdentifier)
Gets the evaluated log Directory.
+LogLevel LogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs.
bool Disable
If file logging is disabled.
General configuration options.
static readonly Version CurrentConfigVersion
The current ConfigVersion.
diff --git a/_file_logging_configuration_8cs_source.html b/_file_logging_configuration_8cs_source.html
index 6e93425aeb..3bd4cef776 100644
--- a/_file_logging_configuration_8cs_source.html
+++ b/_file_logging_configuration_8cs_source.html
@@ -102,47 +102,50 @@ $(document).ready(function() { init_codefold(0); });
-
-
-
-
-
-
-
-
-
-
-
-
-
56 ArgumentNullException.ThrowIfNull(ioManager);
-
57 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
-
58 ArgumentNullException.ThrowIfNull(platformIdentifier);
-
-
-
-
-
63 return platformIdentifier.IsWindows
-
-
65 Environment.GetFolderPath(
-
66 Environment.SpecialFolder.CommonApplicationData,
-
67 Environment.SpecialFolderOption.DoNotVerify),
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
61 ArgumentNullException.ThrowIfNull(ioManager);
+
62 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
+
63 ArgumentNullException.ThrowIfNull(platformIdentifier);
+
+
+
+
+
68 return platformIdentifier.IsWindows
+
+
70 Environment.GetFolderPath(
+
71 Environment.SpecialFolder.CommonApplicationData,
+
72 Environment.SpecialFolderOption.DoNotVerify),
+
+
+
+
+
+
-
+
-
+
File logging configuration options.
string? Directory
Where log files are stored.
-LogLevel MicrosoftLogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs for Microsoft library sources.
-string GetFullLogDirectory(IIOManager ioManager, IAssemblyInformationProvider assemblyInformationProvider, IPlatformIdentifier platformIdentifier)
Gets the evaluated log Directory.
-LogLevel LogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs.
+LogLevel MicrosoftLogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs for Microsoft library sources.
+string GetFullLogDirectory(IIOManager ioManager, IAssemblyInformationProvider assemblyInformationProvider, IPlatformIdentifier platformIdentifier)
Gets the evaluated log Directory.
+LogLevel LogLevel
The minimum Microsoft.Extensions.Logging.LogLevel to display in logs.
const string Section
The key for the Microsoft.Extensions.Configuration.IConfigurationSection the FileLoggingConfiguration...
+bool ProviderNetworkDebug
If Chat Providers should log their network traffic. Normally disabled because it is too noisy.
bool Disable
If file logging is disabled.
Interface for using filesystems.
string ConcatPath(params string[] paths)
Combines an array of strings into a path.
diff --git a/_irc_connection_string_builder_8cs_source.html b/_irc_connection_string_builder_8cs_source.html
index 86ce8a47b6..36c258b166 100644
--- a/_irc_connection_string_builder_8cs_source.html
+++ b/_irc_connection_string_builder_8cs_source.html
@@ -147,49 +147,50 @@ $(document).ready(function() { init_codefold(0); });
-
-
-
-
-
-
- 97 if (splits.Length < 6)
-
-
- 100 var rest =
new List<string>(splits);
- 101 rest.RemoveRange(0, 5);
-
-
+
+
+
+
+
+
+
+ 98 if (splits.Length < 6)
+
+
+ 101 var rest =
new List<string>(splits);
+ 102 rest.RemoveRange(0, 5);
+
+
-
-
-
-
-
108 var sb =
new StringBuilder();
-
-
-
-
-
-
-
-
-
117 sb.Append(Convert.ToInt32(
UseSsl.Value));
-
-
-
-
-
-
-
-
-
-
127 return sb.ToString();
-
+
+
+
+
+
109 var sb =
new StringBuilder();
+
+
+
+
+
+
+
+
+
118 sb.Append(Convert.ToInt32(
UseSsl.Value));
+
+
+
+
+
+
+
+
+
+
128 return sb.ToString();
+
-
+
-
+
Helper for building ChatBotSettings.ConnectionStrings.
ChatConnectionStringBuilder for ChatProvider.Irc.
@@ -200,7 +201,7 @@ $(document).ready(function() { init_codefold(0); });
string? Address
The IP address or URL of the IRC server.
bool? UseSsl
If the connection should be made using SSL.
IrcConnectionStringBuilder()
Initializes a new instance of the IrcConnectionStringBuilder class.
-override string ToString()
+override string ToString()
string? Nickname
The nickname for the bot to use.
diff --git a/_irc_password_type_8cs.html b/_irc_password_type_8cs.html
index 7dcb21f9e7..0753f8c5a2 100644
--- a/_irc_password_type_8cs.html
+++ b/_irc_password_type_8cs.html
@@ -99,6 +99,7 @@ Enumerations
| enum | Tgstation.Server.Api.Models.IrcPasswordType { Tgstation.Server.Api.Models.Server
, Tgstation.Server.Api.Models.Sasl
, Tgstation.Server.Api.Models.NickServ
+, Tgstation.Server.Api.Models.Oper
} |
| | Represents the type of a password for a ChatProvider.Irc. More...
|
| |
diff --git a/_irc_password_type_8cs_source.html b/_irc_password_type_8cs_source.html
index 6c12b300c6..6085a96276 100644
--- a/_irc_password_type_8cs_source.html
+++ b/_irc_password_type_8cs_source.html
@@ -92,11 +92,14 @@ $(document).ready(function() { init_codefold(0); });
-
+
+
+
-
+
IrcPasswordType
Represents the type of a password for a ChatProvider.Irc.
+@ Oper
Use OPER authentication.
@ NickServ
Use NickServ authentication.
@ Sasl
Use PLAIN sasl authentication.
@ Server
Use server authentication.
diff --git a/_irc_provider_8cs_source.html b/_irc_provider_8cs_source.html
index 67189975e2..8255d78021 100644
--- a/_irc_provider_8cs_source.html
+++ b/_irc_provider_8cs_source.html
@@ -97,321 +97,314 @@ $(document).ready(function() { init_codefold(0); });
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
111 ILogger<IrcProvider> logger,
-
-
113 Models.ChatBot chatBot)
-
114 : base(
jobManager, asyncDelayer, logger, chatBot)
-
-
116 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
-
-
118 var builder = chatBot.CreateConnectionStringBuilder();
-
-
120 throw new InvalidOperationException(
"Invalid ChatConnectionStringBuilder!");
-
-
-
123 port = ircBuilder.Port!.Value;
-
-
-
-
-
-
-
-
131 SupportNonRfc =
true,
-
132 CtcpUserInfo =
"You are going to play. And I am going to watch. And everything will be just fine...",
-
-
134 AutoRejoinOnKick =
true,
-
-
-
137 AutoReconnect =
false,
-
138 ActiveChannelSyncing =
true,
-
139 AutoNickHandling =
true,
-
-
141 UseSsl = ircBuilder.UseSsl!.Value,
-
-
143 if (ircBuilder.UseSsl.Value)
-
144 client.ValidateServerCertificate =
true;
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
123 ILogger<IrcProvider> logger,
+
+
125 Models.ChatBot chatBot,
+
+
127 : base(
jobManager, asyncDelayer, logger, chatBot)
+
+
129 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
+
+
+
132 var builder = chatBot.CreateConnectionStringBuilder();
+
+
134 throw new InvalidOperationException(
"Invalid ChatConnectionStringBuilder!");
+
+
+
137 port = ircBuilder.Port!.Value;
+
138 ssl = ircBuilder.UseSsl!.Value;
+
+
+
+
+
+
144 assemblyInfo = assemblyInformationProvider ??
throw new ArgumentNullException(nameof(assemblyInformationProvider));
+
+
+
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
160 await base.DisposeAsync();
-
-
-
-
+
+
+
+
+
157 await base.DisposeAsync();
+
+
+
+
-
-
-
-
-
169 ArgumentNullException.ThrowIfNull(message);
-
-
171 await Task.Factory.StartNew(
-
-
-
-
-
176 var messageText = message.
Text;
-
177 messageText ??= $
"Embed Only: {JsonConvert.SerializeObject(message.Embed)}";
-
-
179 messageText = String.Concat(
-
-
181 .Where(x => x !=
'\r')
-
182 .Select(x => x ==
'\n' ?
'|' : x));
-
-
-
-
186 if (channelName ==
null)
-
-
-
189 sendType = SendType.Notice;
-
-
-
192 sendType = SendType.Message;
-
-
194 var messageSize = Encoding.UTF8.GetByteCount(messageText) + Encoding.UTF8.GetByteCount(channelName) +
PreambleMessageLength;
-
-
-
197 messageText = $
"TGS: Could not send message to IRC. Line write exceeded protocol limit of {MessageBytesLimit}B.";
-
-
-
-
201 client.SendMessage(sendType, channelName, messageText);
-
-
-
-
205 Logger.LogWarning(e,
"Unable to send to channel {channelName}!", channelName);
-
-
-
-
-
-
211 "Failed to send to channel {channelId}: Message size ({messageSize}B) exceeds IRC limit of 512B",
-
-
-
-
-
-
217 TaskScheduler.Current);
-
+
+
+
+
+
166 ArgumentNullException.ThrowIfNull(message);
+
+
168 await Task.Factory.StartNew(
+
+
+
+
+
173 var messageText = message.
Text;
+
174 messageText ??= $
"Embed Only: {JsonConvert.SerializeObject(message.Embed)}";
+
+
176 messageText = String.Concat(
+
+
178 .Where(x => x !=
'\r')
+
179 .Select(x => x ==
'\n' ?
'|' : x));
+
+
+
+
183 if (channelName ==
null)
+
+
+
186 sendType = SendType.Notice;
+
+
+
189 sendType = SendType.Message;
+
+
191 var messageSize = Encoding.UTF8.GetByteCount(messageText) + Encoding.UTF8.GetByteCount(channelName) +
PreambleMessageLength;
+
+
+
194 messageText = $
"TGS: Could not send message to IRC. Line write exceeded protocol limit of {MessageBytesLimit}B.";
+
+
+
+
198 client.SendMessage(sendType, channelName, messageText);
+
+
+
+
202 Logger.LogWarning(e,
"Unable to send to channel {channelName}!", channelName);
+
+
+
+
+
+
208 "Failed to send to channel {channelId}: Message size ({messageSize}B) exceeds IRC limit of 512B",
+
+
+
+
+
+
214 TaskScheduler.Current);
+
-
-
-
221 public override async ValueTask<Func<string?, string, ValueTask<Func<bool, ValueTask>>>>
SendUpdateMessage(
-
222 Models.RevisionInformation revisionInformation,
-
-
224 DateTimeOffset? estimatedCompletionTime,
-
-
-
-
228 bool localCommitPushed,
-
229 CancellationToken cancellationToken)
-
-
231 ArgumentNullException.ThrowIfNull(revisionInformation);
-
232 ArgumentNullException.ThrowIfNull(engineVersion);
-
233 ArgumentNullException.ThrowIfNull(gitHubOwner);
-
234 ArgumentNullException.ThrowIfNull(gitHubRepo);
-
-
236 var commitInsert = revisionInformation.CommitSha![..7];
-
237 string remoteCommitInsert;
-
238 if (revisionInformation.CommitSha == revisionInformation.OriginCommitSha)
-
-
240 commitInsert = String.Format(CultureInfo.InvariantCulture, localCommitPushed ?
"^{0}" :
"{0}", commitInsert);
-
241 remoteCommitInsert = String.Empty;
-
-
-
244 remoteCommitInsert = String.Format(CultureInfo.InvariantCulture,
". Remote commit: ^{0}", revisionInformation.OriginCommitSha![..7]);
-
-
246 var testmergeInsert = (revisionInformation.ActiveTestMerges?.Count ?? 0) == 0
-
-
-
249 CultureInfo.InvariantCulture,
-
250 " (Test Merges: {0})",
-
-
-
-
-
255 .Select(x => x.TestMerge)
-
-
-
258 var result = String.Format(CultureInfo.InvariantCulture,
"#{0} at {1}", x.Number, x.TargetCommitSha![..7]);
-
259 if (x.Comment != null)
-
260 result += String.Format(CultureInfo.InvariantCulture,
" ({0})", x.Comment);
-
-
-
-
-
-
-
-
-
269 Text = String.Format(
-
270 CultureInfo.InvariantCulture,
-
271 $
"{prefix}: Deploying revision: {0}{1}{2} BYOND Version: {3}{4}",
-
-
-
-
-
276 estimatedCompletionTime.HasValue
-
277 ? $
" ETA: {estimatedCompletionTime - DateTimeOffset.UtcNow}"
-
-
-
-
-
-
283 return async (errorMessage, dreamMakerOutput) =>
-
-
-
-
-
-
289 Text = $
"{prefix}: Deployment {(errorMessage == null ? "complete
" : "failed
")}!",
-
-
-
-
-
294 return active => ValueTask.CompletedTask;
-
-
+
+
+
218 public override async ValueTask<Func<string?, string, ValueTask<Func<bool, ValueTask>>>>
SendUpdateMessage(
+
219 Models.RevisionInformation revisionInformation,
+
+
221 DateTimeOffset? estimatedCompletionTime,
+
+
+
+
225 bool localCommitPushed,
+
226 CancellationToken cancellationToken)
+
+
228 ArgumentNullException.ThrowIfNull(revisionInformation);
+
229 ArgumentNullException.ThrowIfNull(engineVersion);
+
230 ArgumentNullException.ThrowIfNull(gitHubOwner);
+
231 ArgumentNullException.ThrowIfNull(gitHubRepo);
+
+
233 var commitInsert = revisionInformation.CommitSha![..7];
+
234 string remoteCommitInsert;
+
235 if (revisionInformation.CommitSha == revisionInformation.OriginCommitSha)
+
+
237 commitInsert = String.Format(CultureInfo.InvariantCulture, localCommitPushed ?
"^{0}" :
"{0}", commitInsert);
+
238 remoteCommitInsert = String.Empty;
+
+
+
241 remoteCommitInsert = String.Format(CultureInfo.InvariantCulture,
". Remote commit: ^{0}", revisionInformation.OriginCommitSha![..7]);
+
+
243 var testmergeInsert = (revisionInformation.ActiveTestMerges?.Count ?? 0) == 0
+
+
+
246 CultureInfo.InvariantCulture,
+
247 " (Test Merges: {0})",
+
+
+
+
+
252 .Select(x => x.TestMerge)
+
+
+
255 var result = String.Format(CultureInfo.InvariantCulture,
"#{0} at {1}", x.Number, x.TargetCommitSha![..7]);
+
256 if (x.Comment != null)
+
257 result += String.Format(CultureInfo.InvariantCulture,
" ({0})", x.Comment);
+
+
+
+
+
+
+
+
+
266 Text = String.Format(
+
267 CultureInfo.InvariantCulture,
+
268 $
"{prefix}: Deploying revision: {0}{1}{2} BYOND Version: {3}{4}",
+
+
+
+
+
273 estimatedCompletionTime.HasValue
+
274 ? $
" ETA: {estimatedCompletionTime - DateTimeOffset.UtcNow}"
+
+
+
+
+
+
280 return async (errorMessage, dreamMakerOutput) =>
+
+
+
+
+
+
286 Text = $
"{prefix}: Deployment {(errorMessage == null ? "complete
" : "failed
")}!",
+
+
+
+
+
291 return active => ValueTask.CompletedTask;
+
+
-
-
299 protected override async ValueTask<Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>>
MapChannelsImpl(
-
300 IEnumerable<Models.ChatChannel> channels,
-
301 CancellationToken cancellationToken)
-
302 => await Task.Factory.StartNew(
-
-
-
305 if (channels.Any(x => x.IrcChannel ==
null))
-
306 throw new InvalidOperationException(
"ChatChannel missing IrcChannel!");
-
-
-
309 var channelsWithKeys =
new Dictionary<string, string>();
-
310 var hs =
new HashSet<string>();
-
311 foreach (var channel
in channels)
-
-
313 var name = channel.GetIrcChannelName();
-
314 var key = channel.GetIrcChannelKey();
-
315 if (hs.Add(name) && key !=
null)
-
316 channelsWithKeys.Add(name, key);
-
-
-
319 var toPart =
new List<string>();
-
320 foreach (var activeChannel
in client.JoinedChannels)
-
321 if (!hs.Remove(activeChannel))
-
322 toPart.Add(activeChannel);
-
-
324 foreach (var channelToLeave
in toPart)
-
325 client.RfcPart(channelToLeave,
"Pretty nice abscond!");
-
326 foreach (var channelToJoin
in hs)
-
327 if (channelsWithKeys.TryGetValue(channelToJoin, out var key))
-
328 client.RfcJoin(channelToJoin, key);
-
-
330 client.RfcJoin(channelToJoin);
-
-
332 return new Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
-
-
-
-
336 var channelName = dbChannel.GetIrcChannelName();
-
-
-
-
340 if (y.Value != channelName)
-
-
-
-
-
-
346 id = channelIdCounter++;
-
347 channelIdMap.Add(id.Value, channelName);
-
-
-
350 return new KeyValuePair<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
-
-
352 new List<ChannelRepresentation>
-
-
354 new(address, channelName, id!.Value)
-
-
-
357 IsAdminChannel = dbChannel.IsAdminChannel == true,
-
358 IsPrivateChannel = false,
-
359 EmbedsSupported = false,
-
-
-
-
-
-
-
-
367 TaskScheduler.Current);
-
-
-
370 protected override async ValueTask
Connect(CancellationToken cancellationToken)
-
-
372 disconnecting =
false;
-
373 cancellationToken.ThrowIfCancellationRequested();
-
-
-
376 await Task.Factory.StartNew(
-
377 () => client.Connect(address, port),
+
+
296 protected override async ValueTask<Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>>
MapChannelsImpl(
+
297 IEnumerable<Models.ChatChannel> channels,
+
298 CancellationToken cancellationToken)
+
299 => await Task.Factory.StartNew(
+
+
+
302 if (channels.Any(x => x.IrcChannel ==
null))
+
303 throw new InvalidOperationException(
"ChatChannel missing IrcChannel!");
+
+
+
306 var channelsWithKeys =
new Dictionary<string, string>();
+
307 var hs =
new HashSet<string>();
+
308 foreach (var channel
in channels)
+
+
310 var name = channel.GetIrcChannelName();
+
311 var key = channel.GetIrcChannelKey();
+
312 if (hs.Add(name) && key !=
null)
+
313 channelsWithKeys.Add(name, key);
+
+
+
316 var toPart =
new List<string>();
+
317 foreach (var activeChannel
in client.JoinedChannels)
+
318 if (!hs.Remove(activeChannel))
+
319 toPart.Add(activeChannel);
+
+
321 foreach (var channelToLeave
in toPart)
+
322 client.RfcPart(channelToLeave,
"Pretty nice abscond!");
+
323 foreach (var channelToJoin
in hs)
+
324 if (channelsWithKeys.TryGetValue(channelToJoin, out var key))
+
325 client.RfcJoin(channelToJoin, key);
+
+
327 client.RfcJoin(channelToJoin);
+
+
329 return new Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
+
+
+
+
333 var channelName = dbChannel.GetIrcChannelName();
+
+
+
+
337 if (y.Value != channelName)
+
+
+
+
+
+
343 id = channelIdCounter++;
+
344 channelIdMap.Add(id.Value, channelName);
+
+
+
347 return new KeyValuePair<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
+
+
349 new List<ChannelRepresentation>
+
+
351 new(address, channelName, id!.Value)
+
+
+
354 IsAdminChannel = dbChannel.IsAdminChannel == true,
+
355 IsPrivateChannel = false,
+
356 EmbedsSupported = false,
+
+
+
+
+
+
+
+
364 TaskScheduler.Current);
+
+
+
367 protected override async ValueTask
Connect(CancellationToken cancellationToken)
+
+
369 cancellationToken.ThrowIfCancellationRequested();
+
+
+
372 await Task.Factory.StartNew(
+
+
+
375 client = InstantiateClient();
+
376 client.Connect(address, port);
+
380 TaskScheduler.Current)
@@ -419,298 +412,351 @@ $(document).ready(function() { init_codefold(0); });
383 cancellationToken.ThrowIfCancellationRequested();
-
385 Logger.LogTrace(
"Authenticating ({passwordType})...", passwordType);
-
386 switch (passwordType)
-
-
-
389 client.Login(nickname, nickname, 0, nickname, password);
-
-
-
392 client.Login(nickname, nickname, 0, nickname);
-
393 cancellationToken.ThrowIfCancellationRequested();
-
394 client.SendMessage(SendType.Message,
"NickServ", String.Format(CultureInfo.InvariantCulture,
"IDENTIFY {0}", password));
-
-
-
397 await SaslAuthenticate(cancellationToken);
-
-
-
400 client.Login(nickname, nickname, 0, nickname);
-
-
-
403 throw new InvalidOperationException($
"Invalid IrcPasswordType: {passwordType.Value}");
-
-
-
406 cancellationToken.ThrowIfCancellationRequested();
-
407 Logger.LogTrace(
"Processing initial messages...");
-
408 await NonBlockingListen(cancellationToken);
-
-
410 var nickCheckCompleteTcs =
new TaskCompletionSource();
-
411 using (cancellationToken.Register(() => nickCheckCompleteTcs.TrySetCanceled(cancellationToken)))
-
-
413 listenTask = Task.Factory.StartNew(
-
-
-
416 Logger.LogTrace(
"Entering nick check loop");
-
417 while (!disconnecting && client.IsConnected && client.Nickname != nickname)
-
-
419 client.ListenOnce(
true);
-
420 if (disconnecting || !client.IsConnected)
-
-
422 await NonBlockingListen(cancellationToken);
-
-
-
425 if (client.GetIrcUser(nickname) ==
null)
-
426 client.RfcNick(nickname);
-
-
-
429 nickCheckCompleteTcs.TrySetResult();
+
385 listenTask = Task.Factory.StartNew(
+
+
+
388 Logger.LogTrace(
"Starting blocking listen...");
+
+
+
+
+
+
+
395 Logger.LogWarning(ex,
"IRC Main Listen Exception!");
+
+
+
398 Logger.LogTrace(
"Exiting listening task...");
+
+
+
+
402 TaskScheduler.Current);
+
+
404 Logger.LogTrace(
"Authenticating ({passwordType})...", passwordType);
+
405 switch (passwordType)
+
+
+
408 client.RfcPass(password);
+
409 await Login(client, nickname, cancellationToken);
+
+
+
412 await Login(client, nickname, cancellationToken);
+
413 cancellationToken.ThrowIfCancellationRequested();
+
414 client.SendMessage(SendType.Message,
"NickServ", String.Format(CultureInfo.InvariantCulture,
"IDENTIFY {0}", password));
+
+
+
417 await SaslAuthenticate(cancellationToken);
+
+
+
420 await Login(client, nickname, cancellationToken);
+
421 cancellationToken.ThrowIfCancellationRequested();
+
422 client.RfcOper(nickname, password, Priority.Critical);
+
+
+
425 await Login(client, nickname, cancellationToken);
+
+
+
428 throw new InvalidOperationException($
"Invalid IrcPasswordType: {passwordType.Value}");
+
-
431 Logger.LogTrace(
"Starting blocking listen...");
-
-
-
-
-
-
-
438 Logger.LogWarning(ex,
"IRC Main Listen Exception!");
-
+
431 cancellationToken.ThrowIfCancellationRequested();
+
+
433 Logger.LogTrace(
"Connection established!");
+
+
435 catch (
Exception e) when (e is not OperationCanceledException)
+
+
+
+
+
-
441 Logger.LogTrace(
"Exiting listening task...");
-
-
-
-
445 TaskScheduler.Current);
-
-
447 await nickCheckCompleteTcs.Task;
-
-
-
450 Logger.LogTrace(
"Connection established!");
-
-
452 catch (
Exception e) when (e is not OperationCanceledException)
-
-
-
-
+
+
442 protected override async ValueTask
DisconnectImpl(CancellationToken cancellationToken)
+
+
+
+
446 await Task.Factory.StartNew(
+
+
+
+
+
451 client.RfcQuit(
"Mr. Stark, I don't feel so good...", Priority.Critical);
+
+
+
+
455 Logger.LogWarning(e,
"Error quitting IRC!");
+
+
+
+
+
460 TaskScheduler.Current);
+
461 await HardDisconnect(cancellationToken);
+
+
463 catch (OperationCanceledException)
+
+
+
+
+
+
469 Logger.LogWarning(e,
"Error disconnecting from IRC!");
+
+
-
-
-
459 protected override async ValueTask
DisconnectImpl(CancellationToken cancellationToken)
-
-
-
-
463 await Task.Factory.StartNew(
-
-
-
-
-
468 client.RfcQuit(
"Mr. Stark, I don't feel so good...", Priority.Critical);
-
-
-
-
472 Logger.LogWarning(e,
"Error quitting IRC!");
-
-
-
-
-
477 TaskScheduler.Current);
-
478 await HardDisconnect(cancellationToken);
-
-
480 catch (OperationCanceledException)
-
-
-
-
-
-
486 Logger.LogWarning(e,
"Error disconnecting from IRC!");
-
-
-
-
-
-
-
-
497 if (e.Data.Nick.Equals(client.Nickname, StringComparison.OrdinalIgnoreCase))
-
-
-
500 var username = e.Data.Nick;
-
501 var channelName = isPrivate ? username : e.Data.Channel;
-
-
503 ulong MapAndGetChannelId(Dictionary<ulong, string?> dicToCheck)
+
+
+
481 async ValueTask
Login(IrcFeatures client,
string nickname, CancellationToken cancellationToken)
+
+
483 var promise =
new TaskCompletionSource<object>();
+
+
485 void Callback(
object? sender, EventArgs e)
+
+
487 Logger.LogTrace(
"IRC Registered.");
+
488 promise.TrySetResult(e);
+
+
+
491 client.OnRegistered += Callback;
+
+
493 client.Login(nickname, nickname, 0, nickname);
+
+
495 using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+
496 cts.CancelAfter(TimeSpan.FromSeconds(30));
+
+
+
+
500 await promise.Task.WaitAsync(cts.Token);
+
501 client.OnRegistered -= Callback;
+
+
503 catch (OperationCanceledException)
-
505 ulong? resultId =
null;
-
506 if (!dicToCheck.Any(x =>
-
-
508 if (x.Value != channelName)
-
-
-
-
-
-
514 resultId = channelIdCounter++;
-
515 dicToCheck.Add(resultId.Value, channelName);
-
516 if (dicToCheck == queryChannelIdMap)
-
517 channelIdMap.Add(resultId.Value,
null);
-
-
-
520 return resultId!.Value;
-
-
-
523 ulong userId, channelId;
-
+
505 if (client.IsConnected)
+
+
507 throw new JobException(
"Timed out waiting for IRC Registration");
+
+
+
+
+
+
+
+
518 if (e.Data.Nick.Equals(client.Nickname, StringComparison.OrdinalIgnoreCase))
+
+
+
521 var username = e.Data.Nick;
+
522 var channelName = isPrivate ? username : e.Data.Channel;
+
+
524 ulong MapAndGetChannelId(Dictionary<ulong, string?> dicToCheck)
-
526 userId = MapAndGetChannelId(
new Dictionary<ulong, string?>(queryChannelIdMap
-
527 .Cast<KeyValuePair<ulong, string?>>()));
-
528 channelId = isPrivate ? userId : MapAndGetChannelId(channelIdMap);
-
-
-
531 var channelFriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture,
"PM: {0}", channelName) : channelName;
-
-
-
-
-
536 IsPrivateChannel = isPrivate,
-
537 EmbedsSupported =
false,
-
-
-
-
-
-
-
-
-
546 EnqueueMessage(message);
-
+
526 ulong? resultId =
null;
+
527 if (!dicToCheck.Any(x =>
+
+
529 if (x.Value != channelName)
+
+
+
+
+
+
535 resultId = channelIdCounter++;
+
536 dicToCheck.Add(resultId.Value, channelName);
+
537 if (dicToCheck == queryChannelIdMap)
+
538 channelIdMap.Add(resultId.Value,
null);
+
+
+
541 return resultId!.Value;
+
+
+
544 ulong userId, channelId;
+
+
+
547 userId = MapAndGetChannelId(
new Dictionary<ulong, string?>(queryChannelIdMap
+
548 .Cast<KeyValuePair<ulong, string?>>()));
+
549 channelId = isPrivate ? userId : MapAndGetChannelId(channelIdMap);
+
+
+
552 var channelFriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture,
"PM: {0}", channelName) : channelName;
+
+
+
+
+
557 IsPrivateChannel = isPrivate,
+
558 EmbedsSupported =
false,
+
+
+
+
+
+
+
+
+
567 EnqueueMessage(message);
+
-
-
-
-
-
-
-
-
-
-
-
573 client.Listen(
false);
-
-
-
-
577 Logger.LogWarning(ex,
"IRC Non-Blocking Listen Exception!");
-
-
-
-
581 TaskCreationOptions.None,
-
582 TaskScheduler.Current)
-
583 .WaitAsync(cancellationToken);
-
-
-
-
-
592 client.WriteLine(
"CAP REQ :sasl", Priority.Critical);
-
593 cancellationToken.ThrowIfCancellationRequested();
-
-
595 Logger.LogTrace(
"Logging in...");
-
596 client.Login(nickname, nickname, 0, nickname);
-
597 cancellationToken.ThrowIfCancellationRequested();
-
-
-
600 var receivedAck =
false;
-
601 var receivedPlus =
false;
-
-
603 void AuthenticationDelegate(
object sender, ReadLineEventArgs e)
-
-
605 if (e.Line.Contains(
"ACK :sasl", StringComparison.Ordinal))
-
-
607 else if (e.Line.Contains(
"AUTHENTICATE +", StringComparison.Ordinal))
-
-
-
-
611 Logger.LogTrace(
"Performing handshake...");
-
612 client.OnReadLine += AuthenticationDelegate;
-
-
-
615 using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
-
616 timeoutCts.CancelAfter(TimeSpan.FromSeconds(25));
-
617 var timeoutToken = timeoutCts.Token;
-
-
619 var listenTimeSpan = TimeSpan.FromMilliseconds(10);
-
-
-
622 await NonBlockingListen(cancellationToken);
+
+
+
+
+
+
+
+
+
+
+
594 client.Listen(
false);
+
+
+
+
598 Logger.LogWarning(ex,
"IRC Non-Blocking Listen Exception!");
+
+
+
+
602 TaskCreationOptions.None,
+
603 TaskScheduler.Current)
+
604 .WaitAsync(cancellationToken);
+
+
+
+
+
613 client.WriteLine(
"CAP REQ :sasl", Priority.Critical);
+
614 cancellationToken.ThrowIfCancellationRequested();
+
+
616 Logger.LogTrace(
"Logging in...");
+
617 client.Login(nickname, nickname, 0, nickname);
+
618 cancellationToken.ThrowIfCancellationRequested();
+
+
+
621 var receivedAck =
false;
+
622 var receivedPlus =
false;
-
624 client.WriteLine(
"AUTHENTICATE PLAIN", Priority.Critical);
-
625 timeoutToken.ThrowIfCancellationRequested();
-
-
627 for (; !receivedPlus;
-
-
629 await NonBlockingListen(cancellationToken);
+
624 void AuthenticationDelegate(
object sender, ReadLineEventArgs e)
+
+
626 if (e.Line.Contains(
"ACK :sasl", StringComparison.Ordinal))
+
+
628 else if (e.Line.Contains(
"AUTHENTICATE +", StringComparison.Ordinal))
+
-
-
-
633 client.OnReadLine -= AuthenticationDelegate;
-
-
-
636 cancellationToken.ThrowIfCancellationRequested();
-
-
-
639 Logger.LogTrace(
"Sending credentials...");
-
640 var authString = String.Format(
-
641 CultureInfo.InvariantCulture,
-
-
-
-
-
646 var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString));
-
647 var authLine = $
"AUTHENTICATE {b64}";
-
648 client.WriteLine(authLine, Priority.Critical);
-
649 cancellationToken.ThrowIfCancellationRequested();
-
-
651 Logger.LogTrace(
"Finishing authentication...");
-
652 client.WriteLine(
"CAP END", Priority.Critical);
-
-
-
-
-
-
-
-
-
664 Logger.LogTrace(
"Not hard disconnecting, already offline");
-
-
-
-
668 Logger.LogTrace(
"Hard disconnect");
-
-
670 disconnecting =
true;
+
+
632 Logger.LogTrace(
"Performing handshake...");
+
633 client.OnReadLine += AuthenticationDelegate;
+
+
+
636 using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+
637 timeoutCts.CancelAfter(TimeSpan.FromSeconds(25));
+
638 var timeoutToken = timeoutCts.Token;
+
+
640 var listenTimeSpan = TimeSpan.FromMilliseconds(10);
+
+
+
643 await NonBlockingListen(cancellationToken);
+
+
645 client.WriteLine(
"AUTHENTICATE PLAIN", Priority.Critical);
+
646 timeoutToken.ThrowIfCancellationRequested();
+
+
648 for (; !receivedPlus;
+
+
650 await NonBlockingListen(cancellationToken);
+
+
+
+
654 client.OnReadLine -= AuthenticationDelegate;
+
+
+
657 cancellationToken.ThrowIfCancellationRequested();
+
+
+
660 Logger.LogTrace(
"Sending credentials...");
+
661 var authString = String.Format(
+
662 CultureInfo.InvariantCulture,
+
+
+
+
+
667 var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString));
+
668 var authLine = $
"AUTHENTICATE {b64}";
+
669 client.WriteLine(authLine, Priority.Critical);
+
670 cancellationToken.ThrowIfCancellationRequested();
-
-
-
674 var disconnectTask = Task.Factory.StartNew(
-
-
-
-
-
-
-
-
-
683 Logger.LogWarning(e,
"Error disconnecting IRC!");
-
-
-
-
-
688 TaskScheduler.Current);
-
-
-
-
-
693 listenTask ?? Task.CompletedTask),
-
-
+
672 Logger.LogTrace(
"Finishing authentication...");
+
673 client.WriteLine(
"CAP END", Priority.Critical);
+
-
+
+
+
+
+
+
+
685 Logger.LogTrace(
"Not hard disconnecting, already offline");
+
+
+
+
689 Logger.LogTrace(
"Hard disconnect");
+
+
+
+
693 var disconnectTask = Task.Factory.StartNew(
+
+
+
+
+
+
+
+
+
702 Logger.LogWarning(e,
"Error disconnecting IRC!");
+
+
+
+
+
707 TaskScheduler.Current);
+
+
+
+
+
712 listenTask ?? Task.CompletedTask),
+
+
-
+
+
+
+
+
723 var newClient =
new IrcFeatures
+
+
725 SupportNonRfc =
true,
+
726 CtcpUserInfo =
"You are going to play. And I am going to watch. And everything will be just fine...",
+
+
728 AutoRejoinOnKick =
true,
+
+
+
731 AutoReconnect =
false,
+
732 ActiveChannelSyncing =
true,
+
733 AutoNickHandling =
true,
+
734 CtcpVersion = assemblyInfo.VersionString,
+
+
736 EnableUTF8Recode =
true,
+
+
+
739 newClient.ValidateServerCertificate =
true;
+
+
741 newClient.OnChannelMessage += Client_OnChannelMessage;
+
742 newClient.OnQueryMessage += Client_OnQueryMessage;
+
+
744 if (loggingConfiguration.ProviderNetworkDebug)
+
+
746 newClient.OnReadLine += (sender, e) => Logger.LogTrace(
"READ: {line}", e.Line);
+
747 newClient.OnWriteLine += (sender, e) => Logger.LogTrace(
"WRITE: {line}", e.Line);
+
+
+
750 newClient.OnError += (sender, e) =>
+
+
752 Logger.LogError(
"IRC ERROR: {error}", e.ErrorMessage);
+
753 newClient.Disconnect();
+
+
+
+
+
+
+
+
Information about an engine installation.
override string ToString()
@@ -718,35 +764,39 @@ $(document).ready(function() { init_codefold(0); });
ChatConnectionStringBuilder for ChatProvider.Irc.
Represents a Providers.IProvider channel.
Represents a tgs_chat_user datum.
-
IProvider for internet relay chat.
-
readonly IrcFeatures client
The IrcFeatures client.
-
readonly ushort port
Port of the server to connect to.
-
readonly string password
Password which will used for authentication.
+
IProvider for internet relay chat.
+
IrcFeatures InstantiateClient()
Creates a new instance of the IRC client. Reusing the same client after a disconnection seems to caus...
+
readonly IAssemblyInformationProvider assemblyInfo
The IAssemblyInformationProvider obtained from constructor, used for the CTCP version string.
+
readonly ushort port
Port of the server to connect to.
+
readonly string password
Password which will used for authentication.
override async ValueTask< Dictionary< Models.ChatChannel, IEnumerable< ChannelRepresentation > > > MapChannelsImpl(IEnumerable< Models.ChatChannel > channels, CancellationToken cancellationToken)
-
override async ValueTask< Func< string?, string, ValueTask< Func< bool, ValueTask > > > > SendUpdateMessage(Models.RevisionInformation revisionInformation, EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, ulong channelId, bool localCommitPushed, CancellationToken cancellationToken)
Send the message for a deployment.A ValueTask<TResult> resulting in a Func<T1, T2,...
-
override string BotMention
The string that indicates the IProvider was mentioned.
-
override async ValueTask DisconnectImpl(CancellationToken cancellationToken)
-
ulong channelIdCounter
Id counter for channelIdMap.
-
async ValueTask HardDisconnect(CancellationToken cancellationToken)
Attempt to disconnect from IRC immediately.
-
const int PreambleMessageLength
Length of the preamble when writing a message to the server. Must be summed with the channel name to ...
-
override async ValueTask DisposeAsync()
-
async ValueTask SaslAuthenticate(CancellationToken cancellationToken)
Run SASL authentication on client.
-
readonly string address
Address of the server to connect to.
+
override async ValueTask< Func< string?, string, ValueTask< Func< bool, ValueTask > > > > SendUpdateMessage(Models.RevisionInformation revisionInformation, EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, ulong channelId, bool localCommitPushed, CancellationToken cancellationToken)
Send the message for a deployment.A ValueTask<TResult> resulting in a Func<T1, T2,...
+
override string BotMention
The string that indicates the IProvider was mentioned.
+
override async ValueTask DisconnectImpl(CancellationToken cancellationToken)
+
ulong channelIdCounter
Id counter for channelIdMap.
+
IrcProvider(IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger< IrcProvider > logger, IAssemblyInformationProvider assemblyInformationProvider, Models.ChatBot chatBot, FileLoggingConfiguration loggingConfiguration)
Initializes a new instance of the IrcProvider class.
+
async ValueTask HardDisconnect(CancellationToken cancellationToken)
Attempt to disconnect from IRC immediately.
+
const int PreambleMessageLength
Length of the preamble when writing a message to the server. Must be summed with the channel name to ...
+
override async ValueTask DisposeAsync()
+
async ValueTask SaslAuthenticate(CancellationToken cancellationToken)
Run SASL authentication on client.
+
readonly string address
Address of the server to connect to.
void Client_OnQueryMessage(object sender, IrcEventArgs e)
When a query message is received in IRC.
-
override async ValueTask Connect(CancellationToken cancellationToken)
-
readonly? IrcPasswordType passwordType
The IrcPasswordType of password.
-
readonly string nickname
IRC nickname.
-
bool disconnecting
If we are disconnecting.
-
void HandleMessage(IrcEventArgs e, bool isPrivate)
Handle an IRC message.
-
readonly Dictionary< ulong, string > queryChannelIdMap
Map of ChannelRepresentation.RealIds to query users.
-
override bool Connected
If the IProvider is currently connected.
-
override async ValueTask SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
Send a message to the IProvider.A ValueTask representing the running operation.
+
override async ValueTask Connect(CancellationToken cancellationToken)
+
readonly? IrcPasswordType passwordType
The IrcPasswordType of password.
+
async ValueTask Login(IrcFeatures client, string nickname, CancellationToken cancellationToken)
Register the client on the network.
+
readonly string nickname
IRC nickname.
+
void HandleMessage(IrcEventArgs e, bool isPrivate)
Handle an IRC message.
+
IrcFeatures client
The IrcFeatures client.
+
readonly Dictionary< ulong, string > queryChannelIdMap
Map of ChannelRepresentation.RealIds to query users.
+
override bool Connected
If the IProvider is currently connected.
+
readonly bool ssl
Wether or not this IRC client is to use ssl.
+
override async ValueTask SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
Send a message to the IProvider.A ValueTask representing the running operation.
+
readonly FileLoggingConfiguration loggingConfiguration
The FileLoggingConfiguration for the IrcProvider.
void Client_OnChannelMessage(object sender, IrcEventArgs e)
When a channel message is received in IRC.
-
const int MessageBytesLimit
Hard limit to sendable message size in bytes.
-
Task? listenTask
The ValueTask used for IrcConnection.Listen(bool).
+
const int MessageBytesLimit
Hard limit to sendable message size in bytes.
+
Task? listenTask
The ValueTask used for IrcConnection.Listen(bool).
Task NonBlockingListen(CancellationToken cancellationToken)
Perform a non-blocking IrcConnection.Listen(bool).
-
IrcProvider(IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger< IrcProvider > logger, IAssemblyInformationProvider assemblyInformationProvider, Models.ChatBot chatBot)
Initializes a new instance of the IrcProvider class.
-
readonly Dictionary< ulong, string?> channelIdMap
Map of ChannelRepresentation.RealIds to channel names.
+
readonly Dictionary< ulong, string?> channelIdMap
Map of ChannelRepresentation.RealIds to channel names.
Represents a message received by a IProvider.
static string GetEngineCompilerPrefix(Api.Models.EngineType engineType)
Get the prefix for messages about deployments.
@@ -754,6 +804,7 @@ $(document).ready(function() { init_codefold(0); });
ILogger< Provider > Logger
The ILogger for the Provider.
Represents a message to send to a chat provider.
string? Text
The message string.
+
File logging configuration options.
IIOManager that resolves paths to Environment.CurrentDirectory.
const TaskCreationOptions BlockingTaskCreationOptions
The TaskCreationOptions used to spawn Tasks for potentially long running, blocking operations.
Operation exceptions thrown from the context of a Models.Job.
@@ -761,13 +812,13 @@ $(document).ready(function() { init_codefold(0); });
Task Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .A Task representing the running operation.
Manages the runtime of Jobs.
-
For waiting asynchronously.
IrcPasswordType
Represents the type of a password for a ChatProvider.Irc.
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
+
diff --git a/_provider_factory_8cs_source.html b/_provider_factory_8cs_source.html
index a075d87242..3d5ce19819 100644
--- a/_provider_factory_8cs_source.html
+++ b/_provider_factory_8cs_source.html
@@ -109,57 +109,64 @@ $(document).ready(function() { init_codefold(0); });
-
-
-
-
-
-
-
56 IOptions<GeneralConfiguration> generalConfigurationOptions)
-
-
-
-
-
-
62 generalConfiguration = generalConfigurationOptions?.Value ??
throw new ArgumentNullException(nameof(generalConfigurationOptions));
-
+
+
+
+
+
+
+
+
+
62 IOptions<GeneralConfiguration> generalConfigurationOptions,
+
63 IOptions<FileLoggingConfiguration> loggingConfigurationOptions)
+
+
+
+
+
+
69 generalConfiguration = generalConfigurationOptions?.Value ??
throw new ArgumentNullException(nameof(generalConfigurationOptions));
+
70 loggingConfiguration = loggingConfigurationOptions?.Value ??
throw new ArgumentNullException(nameof(loggingConfigurationOptions));
+
-
-
-
-
-
68 ArgumentNullException.ThrowIfNull(settings);
-
69 return settings.Provider
switch
-
-
-
-
-
-
-
-
77 ChatProvider.Discord =>
new DiscordProvider(
-
-
-
-
-
-
-
84 _ =>
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
"Invalid ChatProvider: {0}", settings.Provider)),
-
-
+
+
+
+
+
76 ArgumentNullException.ThrowIfNull(settings);
+
77 return settings.Provider
switch
+
+
+
+
+
+
+
+
+
86 ChatProvider.Discord =>
new DiscordProvider(
+
+
+
+
+
+
+
93 _ =>
throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
"Invalid ChatProvider: {0}", settings.Provider)),
+
+
-
+
-
-
IProvider for internet relay chat.
+
+
IProvider for internet relay chat.
+
readonly FileLoggingConfiguration loggingConfiguration
The FileLoggingConfiguration for the ProviderFactory.
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the ProviderFactory.
-
IProvider CreateProvider(Models.ChatBot settings)
Create a IProvider.A new IProvider.
+
IProvider CreateProvider(Models.ChatBot settings)
Create a IProvider.A new IProvider.
readonly GeneralConfiguration generalConfiguration
The GeneralConfiguration for the ProviderFactory.
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the ProviderFactory.
readonly IJobManager jobManager
The IJobManager for the ProviderFactory.
-
ProviderFactory(IJobManager jobManager, IAssemblyInformationProvider assemblyInformationProvider, IAsyncDelayer asyncDelayer, ILoggerFactory loggerFactory, IOptions< GeneralConfiguration > generalConfigurationOptions)
Initializes a new instance of the ProviderFactory class.
+
ProviderFactory(IJobManager jobManager, IAssemblyInformationProvider assemblyInformationProvider, IAsyncDelayer asyncDelayer, ILoggerFactory loggerFactory, IOptions< GeneralConfiguration > generalConfigurationOptions, IOptions< FileLoggingConfiguration > loggingConfigurationOptions)
Initializes a new instance of the ProviderFactory class.
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the ProviderFactory.
+
File logging configuration options.
General configuration options.
For interacting with a chat service.
diff --git a/changelog.yml b/changelog.yml
index 3b92e0b082..a2a7addeb0 100644
--- a/changelog.yml
+++ b/changelog.yml
@@ -1,5 +1,12 @@
Components:
Configuration:
+ - Version: 5.3.0
+ Changes:
+ - Descriptions:
+ - Adds a new `ProviderNetworkDebug` flag in `FileLogging` to debug IRC traffic.
+ Author: craftxbox
+ PullRequest: 1935
+ Unreleased: true
- Version: 5.2.0
Changes:
- Descriptions:
@@ -174,6 +181,12 @@ Components:
- The swarm protocol is now versioned separately from the TGS core version. This allows for slightly differing servers to group together in a swarm. Updates will still synchronize all servers to the same version. **It is still recommended that all servers in the swarm run the same TGS version.**
Author: Cyberboss
PullRequest: 1929
+ - Descriptions:
+ - Refactors the IrcProvider to be a bit more resilient.
+ - Fixes the IrcProvider getting permanently stuck if the connection between TGS and IRC is ever severed.
+ - Adds the OPER Irc authentication mode, helpful for anyone running their own IRC servers.
+ Author: craftxbox
+ PullRequest: 1935
- Descriptions:
- Fixed some oddities in the authentication/authorization pipeline.
Author: Cyberboss
@@ -3850,6 +3863,10 @@ Components:
HttpApi:
- Version: 10.10.0
Changes:
+ - Descriptions:
+ - Adds new IRC authentication type with index 3 for /OPER authentication
+ Author: craftxbox
+ PullRequest: 1935
- Descriptions:
- Reserved error code 109 for a future change.
Author: Cyberboss
@@ -4670,6 +4687,10 @@ Components:
NugetApi:
- Version: 16.0.0
Changes:
+ - Descriptions:
+ - Adds IrcPasswordType.Oper for /OPER authentication
+ Author: craftxbox
+ PullRequest: 1935
- Descriptions:
- '**BREAKING:** Removed `IEquatable` implementations for `SwarmServer`s.'
- '**BREAKING:** `RightsHelper.RoleNames` now returns an `IEnumerable
` instead of a comma separated `string`.'
diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version.html
index ff8449fb39..11f914e7d4 100644
--- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version.html
+++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version.html
@@ -432,7 +432,7 @@ Here is the caller graph for this function:
References Tgstation.Server.Api.Models.EngineVersion.CustomIteration, Tgstation.Server.Api.Models.EngineVersion.Engine, Tgstation.Server.Api.Models.EngineVersion.SourceSHA, Tgstation.Server.Api.Models.EngineVersion.ToString(), and Tgstation.Server.Api.Models.EngineVersion.Version.
-
Referenced by Tgstation.Server.Host.Components.Engine.EngineManager.AssertAndLockVersion(), Tgstation.Server.Host.Components.Engine.EngineManager.ChangeVersion(), Tgstation.Server.Host.Components.Engine.EngineManager.DeleteVersion(), Tgstation.Server.Host.Components.Engine.EngineManager.InstallVersionFiles(), Tgstation.Server.Host.Components.Deployment.DreamMaker.RunCompileJob(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendUpdateMessage(), Tgstation.Server.Host.Components.Engine.EngineManager.StartAsync(), and Tgstation.Server.Api.Models.EngineVersion.ToString().
+
Referenced by Tgstation.Server.Host.Components.Engine.EngineManager.AssertAndLockVersion(), Tgstation.Server.Host.Components.Engine.EngineManager.ChangeVersion(), Tgstation.Server.Host.Components.Engine.EngineManager.DeleteVersion(), Tgstation.Server.Host.Components.Engine.EngineManager.InstallVersionFiles(), Tgstation.Server.Host.Components.Deployment.DreamMaker.RunCompileJob(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendUpdateMessage(), Tgstation.Server.Host.Components.Engine.EngineManager.StartAsync(), and Tgstation.Server.Api.Models.EngineVersion.ToString().
@@ -714,7 +714,7 @@ Here is the caller graph for this function:
Definition at line 24 of file EngineVersion.cs.
-
Referenced by Tgstation.Server.Host.Components.Watchdog.WindowsWatchdog.ApplyInitialDmb(), Tgstation.Server.Host.Components.Engine.ByondInstallation.ByondInstallation(), Tgstation.Server.Host.Components.Watchdog.AdvancedWatchdog.CanUseSwappableDmbProvider(), Tgstation.Server.Host.Components.Watchdog.WatchdogBase.ChangeSettings(), Tgstation.Server.Host.Components.Engine.EngineManager.CheckVersionParameter(), Tgstation.Server.Host.Components.Engine.EngineInstallerBase.CheckVersionValidity(), Tgstation.Server.Host.Components.Engine.DelegatingEngineInstaller.DelegateCall< TReturn >(), Tgstation.Server.Api.Models.EngineVersion.EngineVersion(), Tgstation.Server.Api.Models.EngineVersion.Equals(), Tgstation.Server.Host.Components.Chat.Commands.EngineCommand.Invoke(), Tgstation.Server.Host.Components.Session.SessionControllerFactory.LaunchNew(), Tgstation.Server.Host.Components.Engine.OpenDreamInstallation.OpenDreamInstallation(), Tgstation.Server.Host.Components.Session.SessionController.ProcessBridgeCommand(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendUpdateMessage(), Tgstation.Server.Api.Models.EngineVersion.ToString(), and Tgstation.Server.Api.Models.EngineVersion.TryParse().
+
Referenced by Tgstation.Server.Host.Components.Watchdog.WindowsWatchdog.ApplyInitialDmb(), Tgstation.Server.Host.Components.Engine.ByondInstallation.ByondInstallation(), Tgstation.Server.Host.Components.Watchdog.AdvancedWatchdog.CanUseSwappableDmbProvider(), Tgstation.Server.Host.Components.Watchdog.WatchdogBase.ChangeSettings(), Tgstation.Server.Host.Components.Engine.EngineManager.CheckVersionParameter(), Tgstation.Server.Host.Components.Engine.EngineInstallerBase.CheckVersionValidity(), Tgstation.Server.Host.Components.Engine.DelegatingEngineInstaller.DelegateCall< TReturn >(), Tgstation.Server.Api.Models.EngineVersion.EngineVersion(), Tgstation.Server.Api.Models.EngineVersion.Equals(), Tgstation.Server.Host.Components.Chat.Commands.EngineCommand.Invoke(), Tgstation.Server.Host.Components.Session.SessionControllerFactory.LaunchNew(), Tgstation.Server.Host.Components.Engine.OpenDreamInstallation.OpenDreamInstallation(), Tgstation.Server.Host.Components.Session.SessionController.ProcessBridgeCommand(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendUpdateMessage(), Tgstation.Server.Api.Models.EngineVersion.ToString(), and Tgstation.Server.Api.Models.EngineVersion.TryParse().
diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.html
index d875164fdf..0c8ebb6a3d 100644
--- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.html
+++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_irc_connection_string_builder.html
@@ -232,19 +232,20 @@ Properties
-
-
92 break;
-
93 default:
-
94 break;
-
95 }
-
96
-
97 if (splits.Length < 6)
-
98 return;
-
99
-
100 var rest = new List<string>(splits);
-
101 rest.RemoveRange(0, 5);
-
-
103 }
+
+
+
93 break;
+
94 default:
+
95 break;
+
96 }
+
97
+
98 if (splits.Length < 6)
+
99 return;
+
100
+
101 var rest = new List<string>(splits);
+
102 rest.RemoveRange(0, 5);
+
+
104 }
ushort? Port
The port the server runs on.
string? Password
The optional password to use.
IrcPasswordType? PasswordType
The optional IrcPasswordType to use.
@@ -275,29 +276,29 @@ Properties
-
Definition at line 106 of file IrcConnectionStringBuilder.cs.
-
@@ -358,7 +359,7 @@ Properties
Definition at line 30 of file IrcConnectionStringBuilder.cs.
-
Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
+
Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
@@ -387,7 +388,7 @@ Properties
Definition at line 45 of file IrcConnectionStringBuilder.cs.
-
Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
+
Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
@@ -416,7 +417,7 @@ Properties
Definition at line 40 of file IrcConnectionStringBuilder.cs.
-Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
+Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
@@ -445,7 +446,7 @@ Properties
Definition at line 25 of file IrcConnectionStringBuilder.cs.
-Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
+Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
@@ -474,7 +475,7 @@ Properties
Definition at line 35 of file IrcConnectionStringBuilder.cs.
-Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
+Referenced by Tgstation.Server.Api.Models.IrcConnectionStringBuilder.IrcConnectionStringBuilder(), and Tgstation.Server.Api.Models.IrcConnectionStringBuilder.ToString().
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider-members.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider-members.html
index 5c2bac1cf7..40728814cf 100644
--- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider-members.html
+++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider-members.html
@@ -81,19 +81,19 @@ $(function() {
This is the complete list of members for Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, including all inherited members.
| address | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | AsyncDelayer | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
- | BotMention | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
- | channelIdCounter | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | channelIdMap | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | ChatBot | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
- | client | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | Client_OnChannelMessage(object sender, IrcEventArgs e) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | Client_OnQueryMessage(object sender, IrcEventArgs e) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | Connect(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | protected |
- | Connected | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
- | Disconnect(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
- | DisconnectImpl(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | protected |
- | disconnecting | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | assemblyInfo | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | AsyncDelayer | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
+ | BotMention | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
+ | channelIdCounter | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | channelIdMap | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | ChatBot | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
+ | client | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | Client_OnChannelMessage(object sender, IrcEventArgs e) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | Client_OnQueryMessage(object sender, IrcEventArgs e) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | Connect(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | protected |
+ | Connected | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
+ | Disconnect(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
+ | DisconnectImpl(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | protected |
| DisposeAsync() | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | virtual |
| Disposed | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
| EnqueueMessage(Message? message) | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
@@ -103,33 +103,37 @@ $(function() {
| InitialConnectionJob | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
| initialConnectionTcs | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
| InitialMappingComplete() | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
- | IrcProvider(IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger< IrcProvider > logger, IAssemblyInformationProvider assemblyInformationProvider, Models.ChatBot chatBot) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
- | jobManager | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
- | listenTask | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | Logger | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
- | MapChannels(IEnumerable< ChatChannel > channels, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
- | MapChannelsImpl(IEnumerable< Models.ChatChannel > channels, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | protected |
- | Tgstation::Server::Host::Components::Chat::Providers::Provider.MapChannelsImpl(IEnumerable< ChatChannel > channels, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
- | MessageBytesLimit | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | privatestatic |
- | messageQueue | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
- | nextMessage | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
+ | InstantiateClient() | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | IrcProvider(IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger< IrcProvider > logger, IAssemblyInformationProvider assemblyInformationProvider, Models.ChatBot chatBot, FileLoggingConfiguration loggingConfiguration) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
+ | jobManager | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
+ | listenTask | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | Logger | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
+ | loggingConfiguration | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | Login(IrcFeatures client, string nickname, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | MapChannels(IEnumerable< ChatChannel > channels, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
+ | MapChannelsImpl(IEnumerable< Models.ChatChannel > channels, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | protected |
+ | Tgstation::Server::Host::Components::Chat::Providers::Provider.MapChannelsImpl(IEnumerable< ChatChannel > channels, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
+ | MessageBytesLimit | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | privatestatic |
+ | messageQueue | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
| NextMessage(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
- | nickname | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | NonBlockingListen(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | password | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | passwordType | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | port | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | PreambleMessageLength | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | privatestatic |
- | Provider(IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger< Provider > logger, ChatBot chatBot) | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
- | queryChannelIdMap | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | reconnectCts | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
- | ReconnectionLoop(uint reconnectInterval, bool connectNow, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
- | reconnectTask | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
- | reconnectTaskLock | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
- | SaslAuthenticate(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
- | SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
- | SendUpdateMessage(Models.RevisionInformation revisionInformation, EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, ulong channelId, bool localCommitPushed, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
- | SetReconnectInterval(uint reconnectInterval, bool connectNow) | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
+ | nextMessage | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
+ | nickname | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | NonBlockingListen(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | password | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | passwordType | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | port | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | PreambleMessageLength | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | privatestatic |
+ | Provider(IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger< Provider > logger, ChatBot chatBot) | Tgstation.Server.Host.Components.Chat.Providers.Provider | protected |
+ | queryChannelIdMap | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | reconnectCts | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
+ | ReconnectionLoop(uint reconnectInterval, bool connectNow, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
+ | reconnectTask | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
+ | reconnectTaskLock | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
+ | SaslAuthenticate(CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
+ | SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
+ | SendUpdateMessage(Models.RevisionInformation revisionInformation, EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, ulong channelId, bool localCommitPushed, CancellationToken cancellationToken) | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | |
+ | SetReconnectInterval(uint reconnectInterval, bool connectNow) | Tgstation.Server.Host.Components.Chat.Providers.Provider | |
+ | ssl | Tgstation.Server.Host.Components.Chat.Providers.IrcProvider | private |
| StopReconnectionTimer() | Tgstation.Server.Host.Components.Chat.Providers.Provider | private |
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider.html
index 2e6ee0b0bc..37993581d1 100644
--- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider.html
+++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider.html
@@ -107,52 +107,58 @@ Collaboration diagram for Tgstation.Server.Host.Components.Chat.Providers.IrcPro
[legend]
|
-| | IrcProvider (IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger< IrcProvider > logger, IAssemblyInformationProvider assemblyInformationProvider, Models.ChatBot chatBot) |
-| | Initializes a new instance of the IrcProvider class.
|
-| |
+| | IrcProvider (IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger< IrcProvider > logger, IAssemblyInformationProvider assemblyInformationProvider, Models.ChatBot chatBot, FileLoggingConfiguration loggingConfiguration) |
+| | Initializes a new instance of the IrcProvider class.
|
+| |
| override async ValueTask | DisposeAsync () |
| |
| override async ValueTask | SendMessage (Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken) |
@@ -321,6 +327,9 @@ Properties
|
+| async ValueTask | Login (IrcFeatures client, string nickname, CancellationToken cancellationToken) |
+| | Register the client on the network.
|
+| |
| void | HandleMessage (IrcEventArgs e, bool isPrivate) |
| | Handle an IRC message.
|
| |
@@ -339,18 +348,21 @@ Private Member Functions
| async ValueTask | HardDisconnect (CancellationToken cancellationToken) |
| | Attempt to disconnect from IRC immediately.
|
| |
+| IrcFeatures | InstantiateClient () |
+| | Creates a new instance of the IRC client. Reusing the same client after a disconnection seems to cause issues.
|
+| |
|
-| readonly IrcFeatures | client |
-| | The IrcFeatures client.
|
-| |
| readonly string | address |
| | Address of the server to connect to.
|
| |
| readonly ushort | port |
| | Port of the server to connect to.
|
| |
+| readonly bool | ssl |
+| | Wether or not this IRC client is to use ssl.
|
+| |
| readonly string | nickname |
| | IRC nickname.
|
| |
@@ -366,15 +378,21 @@ Private Attributes
| readonly Dictionary< ulong, string > | queryChannelIdMap |
| | Map of ChannelRepresentation.RealIds to query users.
|
| |
+| readonly IAssemblyInformationProvider | assemblyInfo |
+| | The IAssemblyInformationProvider obtained from constructor, used for the CTCP version string.
|
+| |
+| readonly FileLoggingConfiguration | loggingConfiguration |
+| | The FileLoggingConfiguration for the IrcProvider.
|
+| |
+| IrcFeatures | client |
+| | The IrcFeatures client.
|
+| |
| Task? | listenTask |
| | The ValueTask used for IrcConnection.Listen(bool).
|
| |
| ulong | channelIdCounter |
| | Id counter for channelIdMap.
|
| |
-| bool | disconnecting |
-| | If we are disconnecting.
|
-| |
|
@@ -395,10 +413,10 @@ Additional Inherited Members
IProvider for internet relay chat.
-
Definition at line 27 of file IrcProvider.cs.
+
Definition at line 28 of file IrcProvider.cs.
-
-◆ IrcProvider()
+
+◆ IrcProvider()
@@ -431,7 +449,13 @@ Additional Inherited Members
|
|
Models::ChatBot |
-
chatBot |
+
chatBot, |
+
+
+ |
+ |
+ FileLoggingConfiguration |
+ loggingConfiguration |
|
@@ -449,79 +473,63 @@ Additional Inherited Members
| logger | The ILogger for the Provider. |
| assemblyInformationProvider | The IAssemblyInformationProvider to get the IAssemblyInformationProvider.VersionString from. |
| chatBot | The Models.ChatBot for the Provider. |
+
| loggingConfiguration | The FileLoggingConfiguration for the Provider. |
-Definition at line 108 of file IrcProvider.cs.
- 114 : base(
jobManager, asyncDelayer, logger, chatBot)
-
115 {
-
116 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
-
117
-
118 var builder = chatBot.CreateConnectionStringBuilder();
-
-
120 throw new InvalidOperationException("Invalid ChatConnectionStringBuilder!");
-
121
-
-
123 port = ircBuilder.Port!.Value;
-
-
125
-
-
-
128
-
-
130 {
-
131 SupportNonRfc = true,
-
132 CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...",
-
133 AutoRejoin = true,
-
134 AutoRejoinOnKick = true,
-
135 AutoRelogin = true,
-
136 AutoRetry = false,
-
137 AutoReconnect = false,
-
138 ActiveChannelSyncing = true,
-
139 AutoNickHandling = true,
-
-
141 UseSsl = ircBuilder.UseSsl!.Value,
-
142 };
-
143 if (ircBuilder.UseSsl.Value)
-
144 client.ValidateServerCertificate = true;
-
145
-
-
+
Definition at line 120 of file IrcProvider.cs.
+
127 : base(
jobManager, asyncDelayer, logger, chatBot)
+
128 {
+
129 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
+
+
131
+
132 var builder = chatBot.CreateConnectionStringBuilder();
+
+
134 throw new InvalidOperationException("Invalid ChatConnectionStringBuilder!");
+
135
+
+
137 port = ircBuilder.Port!.Value;
+
138 ssl = ircBuilder.UseSsl!.Value;
+
+
140
+
+
+
143
+
144 assemblyInfo = assemblyInformationProvider ??
throw new ArgumentNullException(nameof(assemblyInformationProvider));
+
+
146
+
148
-
149
-
150
-
151
-
-
-
-
155 }
+
+
+
+
152 }
ChatConnectionStringBuilder for ChatProvider.Irc.
-
readonly IrcFeatures client
The IrcFeatures client.
-
readonly ushort port
Port of the server to connect to.
-
readonly string password
Password which will used for authentication.
-
ulong channelIdCounter
Id counter for channelIdMap.
-
readonly string address
Address of the server to connect to.
-
void Client_OnQueryMessage(object sender, IrcEventArgs e)
When a query message is received in IRC.
-
readonly? IrcPasswordType passwordType
The IrcPasswordType of password.
-
readonly string nickname
IRC nickname.
-
readonly Dictionary< ulong, string > queryChannelIdMap
Map of ChannelRepresentation.RealIds to query users.
-
void Client_OnChannelMessage(object sender, IrcEventArgs e)
When a channel message is received in IRC.
-
readonly Dictionary< ulong, string?> channelIdMap
Map of ChannelRepresentation.RealIds to channel names.
+
IrcFeatures InstantiateClient()
Creates a new instance of the IRC client. Reusing the same client after a disconnection seems to caus...
+
readonly IAssemblyInformationProvider assemblyInfo
The IAssemblyInformationProvider obtained from constructor, used for the CTCP version string.
+
readonly ushort port
Port of the server to connect to.
+
readonly string password
Password which will used for authentication.
+
ulong channelIdCounter
Id counter for channelIdMap.
+
readonly string address
Address of the server to connect to.
+
readonly? IrcPasswordType passwordType
The IrcPasswordType of password.
+
readonly string nickname
IRC nickname.
+
IrcFeatures client
The IrcFeatures client.
+
readonly Dictionary< ulong, string > queryChannelIdMap
Map of ChannelRepresentation.RealIds to query users.
+
readonly bool ssl
Wether or not this IRC client is to use ssl.
+
readonly FileLoggingConfiguration loggingConfiguration
The FileLoggingConfiguration for the IrcProvider.
+
readonly Dictionary< ulong, string?> channelIdMap
Map of ChannelRepresentation.RealIds to channel names.
readonly IJobManager jobManager
The IJobManager for the Provider.
-
-
References Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.address, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.channelIdCounter, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.channelIdMap, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.client, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.Client_OnChannelMessage(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.Client_OnQueryMessage(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.nickname, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.password, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.passwordType, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.port, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.queryChannelIdMap, and Tgstation.Server.Host.System.IAssemblyInformationProvider.VersionString.
+
References Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.address, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.assemblyInfo, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.channelIdCounter, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.channelIdMap, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.client, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.InstantiateClient(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.loggingConfiguration, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.nickname, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.password, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.passwordType, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.port, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.queryChannelIdMap, and Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.ssl.
@@ -571,18 +579,6 @@ Here is the call graph for this function:
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
-
-
-
@@ -628,18 +624,6 @@ Here is the caller graph for this function:
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
-
-
-
@@ -668,14 +652,17 @@ Here is the caller graph for this function:
-Definition at line 370 of file IrcProvider.cs.
- 371 {
-
-
373 cancellationToken.ThrowIfCancellationRequested();
-
374 try
-
375 {
-
376 await Task.Factory.StartNew(
-
+
Definition at line 367 of file IrcProvider.cs.
+
368 {
+
369 cancellationToken.ThrowIfCancellationRequested();
+
370 try
+
371 {
+
372 await Task.Factory.StartNew(
+
373 () =>
+
374 {
+
+
+
377 },
378 cancellationToken,
380 TaskScheduler.Current)
@@ -683,83 +670,65 @@ Here is the caller graph for this function:
382
383 cancellationToken.ThrowIfCancellationRequested();
384
-
-
-
387 {
-
-
-
390 break;
-
-
-
393 cancellationToken.ThrowIfCancellationRequested();
-
394 client.SendMessage(SendType.Message,
"NickServ", String.Format(CultureInfo.InvariantCulture,
"IDENTIFY {0}",
password));
-
395 break;
-
-
-
398 break;
-
399 case null:
-
-
401 break;
-
402 default:
-
403 throw new InvalidOperationException($"Invalid IrcPasswordType: {passwordType.Value}");
-
404 }
-
405
-
406 cancellationToken.ThrowIfCancellationRequested();
-
407 Logger.LogTrace(
"Processing initial messages...");
-
-
409
-
410 var nickCheckCompleteTcs = new TaskCompletionSource();
-
411 using (cancellationToken.Register(() => nickCheckCompleteTcs.TrySetCanceled(cancellationToken)))
-
412 {
-
-
414 async () =>
-
415 {
-
416 Logger.LogTrace(
"Entering nick check loop");
-
-
418 {
-
-
-
421 break;
-
-
423
-
424
-
-
-
427 }
-
428
-
429 nickCheckCompleteTcs.TrySetResult();
+
+
386 () =>
+
387 {
+
388 Logger.LogTrace(
"Starting blocking listen...");
+
389 try
+
390 {
+
+
392 }
+
+
394 {
+
395 Logger.LogWarning(ex,
"IRC Main Listen Exception!");
+
396 }
+
397
+
398 Logger.LogTrace(
"Exiting listening task...");
+
399 },
+
400 cancellationToken,
+
+
402 TaskScheduler.Current);
+
403
+
+
+
406 {
+
+
+
+
410 break;
+
+
+
413 cancellationToken.ThrowIfCancellationRequested();
+
414 client.SendMessage(SendType.Message,
"NickServ", String.Format(CultureInfo.InvariantCulture,
"IDENTIFY {0}",
password));
+
415 break;
+
+
+
418 break;
+
+
+
421 cancellationToken.ThrowIfCancellationRequested();
+
+
423 break;
+
424 case null:
+
+
426 break;
+
427 default:
+
428 throw new InvalidOperationException($"Invalid IrcPasswordType: {passwordType.Value}");
+
429 }
430
-
431 Logger.LogTrace(
"Starting blocking listen...");
-
432 try
-
433 {
-
-
435 }
-
-
437 {
-
438 Logger.LogWarning(ex,
"IRC Main Listen Exception!");
-
439 }
-
440
-
441 Logger.LogTrace(
"Exiting listening task...");
-
442 },
-
443 cancellationToken,
-
-
445 TaskScheduler.Current);
-
446
-
447 await nickCheckCompleteTcs.Task;
-
448 }
-
449
-
450 Logger.LogTrace(
"Connection established!");
-
451 }
-
452 catch (
Exception e) when (e is not OperationCanceledException)
-
453 {
-
-
455 }
-
456 }
+
431 cancellationToken.ThrowIfCancellationRequested();
+
432
+
433 Logger.LogTrace(
"Connection established!");
+
434 }
+
435 catch (
Exception e) when (e is not OperationCanceledException)
+
436 {
+
+
438 }
+
439 }
-
async ValueTask SaslAuthenticate(CancellationToken cancellationToken)
Run SASL authentication on client.
-
bool disconnecting
If we are disconnecting.
-
Task? listenTask
The ValueTask used for IrcConnection.Listen(bool).
-
Task NonBlockingListen(CancellationToken cancellationToken)
Perform a non-blocking IrcConnection.Listen(bool).
+
async ValueTask SaslAuthenticate(CancellationToken cancellationToken)
Run SASL authentication on client.
+
async ValueTask Login(IrcFeatures client, string nickname, CancellationToken cancellationToken)
Register the client on the network.
+
Task? listenTask
The ValueTask used for IrcConnection.Listen(bool).
ILogger< Provider > Logger
The ILogger for the Provider.
IIOManager that resolves paths to Environment.CurrentDirectory.
const TaskCreationOptions BlockingTaskCreationOptions
The TaskCreationOptions used to spawn Tasks for potentially long running, blocking operations.
@@ -797,37 +766,37 @@ Here is the caller graph for this function:
-Definition at line 459 of file IrcProvider.cs.
- 460 {
-
461 try
-
462 {
-
463 await Task.Factory.StartNew(
-
464 () =>
-
465 {
-
466 try
-
467 {
-
468 client.RfcQuit(
"Mr. Stark, I don't feel so good...", Priority.Critical);
-
469 }
-
-
471 {
-
472 Logger.LogWarning(e,
"Error quitting IRC!");
-
473 }
-
474 },
-
475 cancellationToken,
-
-
477 TaskScheduler.Current);
-
-
479 }
-
480 catch (OperationCanceledException)
-
481 {
-
482 throw;
-
483 }
-
-
485 {
-
486 Logger.LogWarning(e,
"Error disconnecting from IRC!");
-
487 }
-
488 }
-
async ValueTask HardDisconnect(CancellationToken cancellationToken)
Attempt to disconnect from IRC immediately.
+
Definition at line 442 of file IrcProvider.cs.
+
443 {
+
444 try
+
445 {
+
446 await Task.Factory.StartNew(
+
447 () =>
+
448 {
+
449 try
+
450 {
+
451 client.RfcQuit(
"Mr. Stark, I don't feel so good...", Priority.Critical);
+
452 }
+
+
454 {
+
455 Logger.LogWarning(e,
"Error quitting IRC!");
+
456 }
+
457 },
+
458 cancellationToken,
+
+
460 TaskScheduler.Current);
+
+
462 }
+
463 catch (OperationCanceledException)
+
464 {
+
465 throw;
+
466 }
+
+
468 {
+
469 Logger.LogWarning(e,
"Error disconnecting from IRC!");
+
470 }
+
471 }
+
async ValueTask HardDisconnect(CancellationToken cancellationToken)
Attempt to disconnect from IRC immediately.
References Tgstation.Server.Host.IO.DefaultIOManager.BlockingTaskCreationOptions.
@@ -860,15 +829,15 @@ Here is the caller graph for this function:
Reimplemented from Tgstation.Server.Host.Components.Chat.Providers.Provider.
-Definition at line 158 of file IrcProvider.cs.
- 159 {
-
160 await base.DisposeAsync();
-
161
-
162
-
-
164 }
+
Definition at line 155 of file IrcProvider.cs.
+
156 {
+
157 await base.DisposeAsync();
+
158
+
159
+
+
161 }
-
References Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.HardDisconnect().
+
References Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.HardDisconnect().
@@ -927,59 +896,59 @@ Here is the call graph for this function:
-
Definition at line 495 of file IrcProvider.cs.
-
496 {
-
497 if (e.Data.Nick.Equals(
client.Nickname, StringComparison.OrdinalIgnoreCase))
-
498 return;
-
499
-
500 var username = e.Data.Nick;
-
501 var channelName = isPrivate ? username : e.Data.Channel;
-
502
-
503 ulong MapAndGetChannelId(Dictionary<ulong, string?> dicToCheck)
-
504 {
-
505 ulong? resultId = null;
-
506 if (!dicToCheck.Any(x =>
-
507 {
-
508 if (x.Value != channelName)
-
509 return false;
-
510 resultId = x.Key;
-
511 return true;
-
512 }))
-
513 {
-
-
515 dicToCheck.Add(resultId.Value, channelName);
-
-
-
518 }
-
519
-
520 return resultId!.Value;
-
521 }
-
522
-
523 ulong userId, channelId;
-
+
Definition at line 516 of file IrcProvider.cs.
+
517 {
+
518 if (e.Data.Nick.Equals(
client.Nickname, StringComparison.OrdinalIgnoreCase))
+
519 return;
+
520
+
521 var username = e.Data.Nick;
+
522 var channelName = isPrivate ? username : e.Data.Channel;
+
523
+
524 ulong MapAndGetChannelId(Dictionary<ulong, string?> dicToCheck)
525 {
-
-
527 .Cast<KeyValuePair<ulong, string?>>()));
-
528 channelId = isPrivate ? userId : MapAndGetChannelId(
channelIdMap);
-
529 }
-
530
-
531 var channelFriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture, "PM: {0}", channelName) : channelName;
-
532 var message = new Message(
-
533 new ChatUser(
-
534 new ChannelRepresentation(
address, channelFriendlyName, channelId)
-
535 {
-
536 IsPrivateChannel = isPrivate,
-
537 EmbedsSupported = false,
-
538
-
539
-
540 },
-
541 username,
-
542 username,
-
543 userId),
-
544 e.Data.Message);
-
545
-
-
547 }
+
526 ulong? resultId = null;
+
527 if (!dicToCheck.Any(x =>
+
528 {
+
529 if (x.Value != channelName)
+
530 return false;
+
531 resultId = x.Key;
+
532 return true;
+
533 }))
+
534 {
+
+
536 dicToCheck.Add(resultId.Value, channelName);
+
+
+
539 }
+
540
+
541 return resultId!.Value;
+
542 }
+
543
+
544 ulong userId, channelId;
+
+
546 {
+
+
548 .Cast<KeyValuePair<ulong, string?>>()));
+
549 channelId = isPrivate ? userId : MapAndGetChannelId(
channelIdMap);
+
550 }
+
551
+
552 var channelFriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture, "PM: {0}", channelName) : channelName;
+
553 var message = new Message(
+
554 new ChatUser(
+
555 new ChannelRepresentation(
address, channelFriendlyName, channelId)
+
556 {
+
557 IsPrivateChannel = isPrivate,
+
558 EmbedsSupported = false,
+
559
+
560
+
561 },
+
562 username,
+
563 username,
+
564 userId),
+
565 e.Data.Message);
+
566
+
+
568 }
void EnqueueMessage(Message? message)
Queues a message for NextMessage(CancellationToken).
@@ -1017,49 +986,47 @@ Here is the call graph for this function:
- Returns
- A ValueTask representing the running operation.
-Definition at line 660 of file IrcProvider.cs.
- 661 {
-
-
663 {
-
664 Logger.LogTrace(
"Not hard disconnecting, already offline");
-
665 return;
-
666 }
-
667
-
668 Logger.LogTrace(
"Hard disconnect");
-
669
-
-
671
-
672
-
673
-
674 var disconnectTask = Task.Factory.StartNew(
-
675 () =>
-
676 {
-
677 try
-
678 {
-
-
680 }
-
-
682 {
-
683 Logger.LogWarning(e,
"Error disconnecting IRC!");
-
684 }
-
685 },
-
686 cancellationToken,
-
-
688 TaskScheduler.Current);
-
689
-
690 await Task.WhenAny(
-
691 Task.WhenAll(
-
692 disconnectTask,
-
-
-
695 }
-
override bool Connected
If the IProvider is currently connected.
+
Definition at line 681 of file IrcProvider.cs.
+
682 {
+
+
684 {
+
685 Logger.LogTrace(
"Not hard disconnecting, already offline");
+
686 return;
+
687 }
+
688
+
689 Logger.LogTrace(
"Hard disconnect");
+
690
+
691
+
692
+
693 var disconnectTask = Task.Factory.StartNew(
+
694 () =>
+
695 {
+
696 try
+
697 {
+
+
699 }
+
+
701 {
+
702 Logger.LogWarning(e,
"Error disconnecting IRC!");
+
703 }
+
704 },
+
705 cancellationToken,
+
+
707 TaskScheduler.Current);
+
708
+
709 await Task.WhenAny(
+
710 Task.WhenAll(
+
711 disconnectTask,
+
+
+
714 }
+
override bool Connected
If the IProvider is currently connected.
Task Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .A Task representing the running operation.
References Tgstation.Server.Host.IO.DefaultIOManager.BlockingTaskCreationOptions, and Tgstation.Server.Host.Utils.AsyncDelayer.Delay().
-
Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.DisposeAsync().
+
Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.DisposeAsync().
@@ -1081,6 +1048,177 @@ Here is the caller graph for this function:
+
+
+
+◆ InstantiateClient()
+
+
+
+
+
+
+
+
+ | IrcFeatures Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.InstantiateClient |
+ ( |
+ | ) |
+ |
+
+
+ |
+
+private |
+
+
+
+
+
Creates a new instance of the IRC client. Reusing the same client after a disconnection seems to cause issues.
+
- Returns
- The IrcFeatures client to use.
+
+
Definition at line 721 of file IrcProvider.cs.
+
722 {
+
723 var newClient = new IrcFeatures
+
724 {
+
725 SupportNonRfc = true,
+
726 CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...",
+
727 AutoRejoin = true,
+
728 AutoRejoinOnKick = true,
+
729 AutoRelogin = false,
+
730 AutoRetry = false,
+
731 AutoReconnect = false,
+
732 ActiveChannelSyncing = true,
+
733 AutoNickHandling = true,
+
+
+
736 EnableUTF8Recode = true,
+
737 };
+
+
739 newClient.ValidateServerCertificate = true;
+
740
+
+
+
743
+
+
745 {
+
746 newClient.OnReadLine += (sender, e) =>
Logger.LogTrace(
"READ: {line}", e.Line);
+
747 newClient.OnWriteLine += (sender, e) =>
Logger.LogTrace(
"WRITE: {line}", e.Line);
+
748 }
+
749
+
750 newClient.OnError += (sender, e) =>
+
751 {
+
752 Logger.LogError(
"IRC ERROR: {error}", e.ErrorMessage);
+
753 newClient.Disconnect();
+
754 };
+
755
+
756 return newClient;
+
757 }
+
void Client_OnQueryMessage(object sender, IrcEventArgs e)
When a query message is received in IRC.
+
void Client_OnChannelMessage(object sender, IrcEventArgs e)
When a channel message is received in IRC.
+
bool ProviderNetworkDebug
If Chat Providers should log their network traffic. Normally disabled because it is too noisy.
+
+
+
Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
+
+
+
+
+
+
+◆ Login()
+
+
+
+
+
+
+
+
+ | async ValueTask Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.Login |
+ ( |
+ IrcFeatures |
+ client, |
+
+
+ |
+ |
+ string |
+ nickname, |
+
+
+ |
+ |
+ CancellationToken |
+ cancellationToken |
+
+
+ |
+ ) |
+ | |
+
+
+ |
+
+private |
+
+
+
+
+
Register the client on the network.
+
- Parameters
-
+
+ | client | IRC client. |
+ | nickname | Nickname. |
+ | cancellationToken | Cancellation token. |
+
+
+
+
- Returns
- Task that resolves when registration has been completed.
+
- Exceptions
-
+
+ | TimeoutException | If the IRC server fails to respond. |
+
+
+
+
+
Definition at line 481 of file IrcProvider.cs.
+
482 {
+
483 var promise = new TaskCompletionSource<object>();
+
484
+
485 void Callback(object? sender, EventArgs e)
+
486 {
+
487 Logger.LogTrace(
"IRC Registered.");
+
488 promise.TrySetResult(e);
+
489 }
+
490
+
491 client.OnRegistered += Callback;
+
492
+
+
494
+
495 using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+
496 cts.CancelAfter(TimeSpan.FromSeconds(30));
+
497
+
498 try
+
499 {
+
500 await promise.Task.WaitAsync(cts.Token);
+
501 client.OnRegistered -= Callback;
+
502 }
+
503 catch (OperationCanceledException)
+
504 {
+
+
+
507 throw new JobException(
"Timed out waiting for IRC Registration");
+
508 }
+
509 }
+
@@ -1119,7 +1257,7 @@ Here is the caller graph for this function:
-References Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.channelIdMap, and Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.client.
+References Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.channelIdMap, and Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.client.
@@ -1191,70 +1329,71 @@ Here is the caller graph for this function:
- Returns
- A ValueTask representing the running operation.
-Definition at line 590 of file IrcProvider.cs.
- 591 {
-
592 client.WriteLine(
"CAP REQ :sasl", Priority.Critical);
-
593 cancellationToken.ThrowIfCancellationRequested();
-
594
-
595 Logger.LogTrace(
"Logging in...");
-
-
597 cancellationToken.ThrowIfCancellationRequested();
-
598
-
599
-
600 var receivedAck = false;
-
601 var receivedPlus = false;
-
602
-
603 void AuthenticationDelegate(object sender, ReadLineEventArgs e)
-
604 {
-
605 if (e.Line.Contains("ACK :sasl", StringComparison.Ordinal))
-
606 receivedAck = true;
-
607 else if (e.Line.Contains("AUTHENTICATE +", StringComparison.Ordinal))
-
608 receivedPlus = true;
-
609 }
-
610
-
611 Logger.LogTrace(
"Performing handshake...");
-
612 client.OnReadLine += AuthenticationDelegate;
-
613 try
-
614 {
-
615 using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
-
616 timeoutCts.CancelAfter(TimeSpan.FromSeconds(25));
-
617 var timeoutToken = timeoutCts.Token;
-
618
-
619 var listenTimeSpan = TimeSpan.FromMilliseconds(10);
-
620 for (; !receivedAck;
-
-
+
Definition at line 611 of file IrcProvider.cs.
+
612 {
+
613 client.WriteLine(
"CAP REQ :sasl", Priority.Critical);
+
614 cancellationToken.ThrowIfCancellationRequested();
+
615
+
616 Logger.LogTrace(
"Logging in...");
+
+
618 cancellationToken.ThrowIfCancellationRequested();
+
619
+
620
+
621 var receivedAck = false;
+
622 var receivedPlus = false;
623
-
624 client.WriteLine(
"AUTHENTICATE PLAIN", Priority.Critical);
-
625 timeoutToken.ThrowIfCancellationRequested();
-
626
-
627 for (; !receivedPlus;
-
-
+
624 void AuthenticationDelegate(object sender, ReadLineEventArgs e)
+
625 {
+
626 if (e.Line.Contains("ACK :sasl", StringComparison.Ordinal))
+
627 receivedAck = true;
+
628 else if (e.Line.Contains("AUTHENTICATE +", StringComparison.Ordinal))
+
629 receivedPlus = true;
630 }
-
631 finally
-
632 {
-
633 client.OnReadLine -= AuthenticationDelegate;
-
634 }
-
635
-
636 cancellationToken.ThrowIfCancellationRequested();
-
637
-
638
-
639 Logger.LogTrace(
"Sending credentials...");
-
640 var authString = String.Format(
-
641 CultureInfo.InvariantCulture,
-
642 "{0}{1}{0}{1}{2}",
-
-
644 '\0',
-
-
646 var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString));
-
647 var authLine = $"AUTHENTICATE {b64}";
-
648 client.WriteLine(authLine, Priority.Critical);
-
649 cancellationToken.ThrowIfCancellationRequested();
-
650
-
651 Logger.LogTrace(
"Finishing authentication...");
-
652 client.WriteLine(
"CAP END", Priority.Critical);
-
653 }
+
631
+
632 Logger.LogTrace(
"Performing handshake...");
+
633 client.OnReadLine += AuthenticationDelegate;
+
634 try
+
635 {
+
636 using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+
637 timeoutCts.CancelAfter(TimeSpan.FromSeconds(25));
+
638 var timeoutToken = timeoutCts.Token;
+
639
+
640 var listenTimeSpan = TimeSpan.FromMilliseconds(10);
+
641 for (; !receivedAck;
+
+
+
644
+
645 client.WriteLine(
"AUTHENTICATE PLAIN", Priority.Critical);
+
646 timeoutToken.ThrowIfCancellationRequested();
+
647
+
648 for (; !receivedPlus;
+
+
+
651 }
+
652 finally
+
653 {
+
654 client.OnReadLine -= AuthenticationDelegate;
+
655 }
+
656
+
657 cancellationToken.ThrowIfCancellationRequested();
+
658
+
659
+
660 Logger.LogTrace(
"Sending credentials...");
+
661 var authString = String.Format(
+
662 CultureInfo.InvariantCulture,
+
663 "{0}{1}{0}{1}{2}",
+
+
665 '\0',
+
+
667 var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString));
+
668 var authLine = $"AUTHENTICATE {b64}";
+
669 client.WriteLine(authLine, Priority.Critical);
+
670 cancellationToken.ThrowIfCancellationRequested();
+
671
+
672 Logger.LogTrace(
"Finishing authentication...");
+
673 client.WriteLine(
"CAP END", Priority.Critical);
+
674 }
+
Task NonBlockingListen(CancellationToken cancellationToken)
Perform a non-blocking IrcConnection.Listen(bool).
References Tgstation.Server.Host.Utils.AsyncDelayer.Delay().
Implements Tgstation.Server.Host.Components.Chat.Providers.IProvider.
-
Definition at line 167 of file IrcProvider.cs.
-
168 {
-
169 ArgumentNullException.ThrowIfNull(message);
-
170
-
171 await Task.Factory.StartNew(
-
172 () =>
-
173 {
-
174
-
175
-
176 var messageText = message.
Text;
-
177 messageText ??= $"Embed Only: {JsonConvert.SerializeObject(message.Embed)}";
-
178
-
179 messageText = String.Concat(
-
180 messageText
-
181 .Where(x => x != '\r')
-
182 .Select(x => x == '\n' ? '|' : x));
-
183
-
-
185 SendType sendType;
-
186 if (channelName == null)
-
187 {
-
-
189 sendType = SendType.Notice;
-
190 }
-
191 else
-
192 sendType = SendType.Message;
-
193
-
194 var messageSize = Encoding.UTF8.GetByteCount(messageText) + Encoding.UTF8.GetByteCount(channelName) +
PreambleMessageLength;
-
-
196 if (messageTooLong)
-
197 messageText = $"TGS: Could not send message to IRC. Line write exceeded protocol limit of {MessageBytesLimit}B.";
-
198
-
199 try
-
200 {
-
201 client.SendMessage(sendType, channelName, messageText);
-
202 }
-
-
204 {
-
205 Logger.LogWarning(e,
"Unable to send to channel {channelName}!", channelName);
-
206 return;
-
207 }
-
208
-
209 if (messageTooLong)
-
-
211 "Failed to send to channel {channelId}: Message size ({messageSize}B) exceeds IRC limit of 512B",
-
212 channelId,
-
213 messageSize);
-
214 },
-
215 cancellationToken,
-
-
217 TaskScheduler.Current);
-
218 }
-
const int PreambleMessageLength
Length of the preamble when writing a message to the server. Must be summed with the channel name to ...
-
const int MessageBytesLimit
Hard limit to sendable message size in bytes.
+
Definition at line 164 of file IrcProvider.cs.
+
165 {
+
166 ArgumentNullException.ThrowIfNull(message);
+
167
+
168 await Task.Factory.StartNew(
+
169 () =>
+
170 {
+
171
+
172
+
173 var messageText = message.
Text;
+
174 messageText ??= $"Embed Only: {JsonConvert.SerializeObject(message.Embed)}";
+
175
+
176 messageText = String.Concat(
+
177 messageText
+
178 .Where(x => x != '\r')
+
179 .Select(x => x == '\n' ? '|' : x));
+
180
+
+
182 SendType sendType;
+
183 if (channelName == null)
+
184 {
+
+
186 sendType = SendType.Notice;
+
187 }
+
188 else
+
189 sendType = SendType.Message;
+
190
+
191 var messageSize = Encoding.UTF8.GetByteCount(messageText) + Encoding.UTF8.GetByteCount(channelName) +
PreambleMessageLength;
+
+
193 if (messageTooLong)
+
194 messageText = $"TGS: Could not send message to IRC. Line write exceeded protocol limit of {MessageBytesLimit}B.";
+
195
+
196 try
+
197 {
+
198 client.SendMessage(sendType, channelName, messageText);
+
199 }
+
+
201 {
+
202 Logger.LogWarning(e,
"Unable to send to channel {channelName}!", channelName);
+
203 return;
+
204 }
+
205
+
206 if (messageTooLong)
+
+
208 "Failed to send to channel {channelId}: Message size ({messageSize}B) exceeds IRC limit of 512B",
+
209 channelId,
+
210 messageSize);
+
211 },
+
212 cancellationToken,
+
+
214 TaskScheduler.Current);
+
215 }
+
const int PreambleMessageLength
Length of the preamble when writing a message to the server. Must be summed with the channel name to ...
+
const int MessageBytesLimit
Hard limit to sendable message size in bytes.
string? Text
The message string.
-
References Tgstation.Server.Host.IO.DefaultIOManager.BlockingTaskCreationOptions, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.channelIdMap, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.client, Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.MessageBytesLimit, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.PreambleMessageLength, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.queryChannelIdMap, and Tgstation.Server.Host.Components.Interop.MessageContent.Text.
+
References Tgstation.Server.Host.IO.DefaultIOManager.BlockingTaskCreationOptions, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.channelIdMap, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.client, Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.MessageBytesLimit, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.PreambleMessageLength, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.queryChannelIdMap, and Tgstation.Server.Host.Components.Interop.MessageContent.Text.
-
Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendUpdateMessage().
+
Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendUpdateMessage().
@@ -1474,82 +1613,82 @@ Here is the caller graph for this function:
Implements Tgstation.Server.Host.Components.Chat.Providers.IProvider.
-
Definition at line 221 of file IrcProvider.cs.
-
230 {
-
231 ArgumentNullException.ThrowIfNull(revisionInformation);
-
232 ArgumentNullException.ThrowIfNull(engineVersion);
-
233 ArgumentNullException.ThrowIfNull(gitHubOwner);
-
234 ArgumentNullException.ThrowIfNull(gitHubRepo);
-
235
-
236 var commitInsert = revisionInformation.CommitSha![..7];
-
237 string remoteCommitInsert;
-
238 if (revisionInformation.CommitSha == revisionInformation.OriginCommitSha)
-
239 {
-
240 commitInsert = String.Format(CultureInfo.InvariantCulture, localCommitPushed ? "^{0}" : "{0}", commitInsert);
-
241 remoteCommitInsert = String.Empty;
-
242 }
-
243 else
-
244 remoteCommitInsert = String.Format(CultureInfo.InvariantCulture, ". Remote commit: ^{0}", revisionInformation.OriginCommitSha![..7]);
-
245
-
246 var testmergeInsert = (revisionInformation.ActiveTestMerges?.Count ?? 0) == 0
-
247 ? String.Empty
-
248 : String.Format(
-
249 CultureInfo.InvariantCulture,
-
250 " (Test Merges: {0})",
-
251 String.Join(
-
252 ", ",
-
253 revisionInformation
-
254 .ActiveTestMerges!
-
-
256 .Select(x =>
-
257 {
-
258 var result = String.Format(CultureInfo.InvariantCulture, "#{0} at {1}", x.Number, x.TargetCommitSha![..7]);
-
259 if (x.Comment != null)
-
260 result += String.Format(CultureInfo.InvariantCulture, " ({0})", x.Comment);
-
261 return result;
-
262 })));
-
263
-
-
-
266 null,
-
-
268 {
-
269 Text = String.Format(
-
270 CultureInfo.InvariantCulture,
-
271 $"{prefix}: Deploying revision: {0}{1}{2} BYOND Version: {3}{4}",
-
272 commitInsert,
-
273 testmergeInsert,
-
274 remoteCommitInsert,
-
-
276 estimatedCompletionTime.HasValue
-
277 ? $" ETA: {estimatedCompletionTime - DateTimeOffset.UtcNow}"
-
278 : String.Empty),
-
279 },
-
280 channelId,
-
281 cancellationToken);
-
282
-
283 return async (errorMessage, dreamMakerOutput) =>
-
284 {
-
-
286 null,
-
-
288 {
-
289 Text = $"{prefix}: Deployment {(errorMessage == null ? "complete" : "failed")}!",
-
290 },
-
291 channelId,
-
292 cancellationToken);
-
293
-
294 return active => ValueTask.CompletedTask;
-
295 };
-
296 }
+
Definition at line 218 of file IrcProvider.cs.
+
227 {
+
228 ArgumentNullException.ThrowIfNull(revisionInformation);
+
229 ArgumentNullException.ThrowIfNull(engineVersion);
+
230 ArgumentNullException.ThrowIfNull(gitHubOwner);
+
231 ArgumentNullException.ThrowIfNull(gitHubRepo);
+
232
+
233 var commitInsert = revisionInformation.CommitSha![..7];
+
234 string remoteCommitInsert;
+
235 if (revisionInformation.CommitSha == revisionInformation.OriginCommitSha)
+
236 {
+
237 commitInsert = String.Format(CultureInfo.InvariantCulture, localCommitPushed ? "^{0}" : "{0}", commitInsert);
+
238 remoteCommitInsert = String.Empty;
+
239 }
+
240 else
+
241 remoteCommitInsert = String.Format(CultureInfo.InvariantCulture, ". Remote commit: ^{0}", revisionInformation.OriginCommitSha![..7]);
+
242
+
243 var testmergeInsert = (revisionInformation.ActiveTestMerges?.Count ?? 0) == 0
+
244 ? String.Empty
+
245 : String.Format(
+
246 CultureInfo.InvariantCulture,
+
247 " (Test Merges: {0})",
+
248 String.Join(
+
249 ", ",
+
250 revisionInformation
+
251 .ActiveTestMerges!
+
+
253 .Select(x =>
+
254 {
+
255 var result = String.Format(CultureInfo.InvariantCulture, "#{0} at {1}", x.Number, x.TargetCommitSha![..7]);
+
256 if (x.Comment != null)
+
257 result += String.Format(CultureInfo.InvariantCulture, " ({0})", x.Comment);
+
258 return result;
+
259 })));
+
260
+
+
+
263 null,
+
+
265 {
+
266 Text = String.Format(
+
267 CultureInfo.InvariantCulture,
+
268 $"{prefix}: Deploying revision: {0}{1}{2} BYOND Version: {3}{4}",
+
269 commitInsert,
+
270 testmergeInsert,
+
271 remoteCommitInsert,
+
+
273 estimatedCompletionTime.HasValue
+
274 ? $" ETA: {estimatedCompletionTime - DateTimeOffset.UtcNow}"
+
275 : String.Empty),
+
276 },
+
277 channelId,
+
278 cancellationToken);
+
279
+
280 return async (errorMessage, dreamMakerOutput) =>
+
281 {
+
+
283 null,
+
+
285 {
+
286 Text = $"{prefix}: Deployment {(errorMessage == null ? "complete" : "failed")}!",
+
287 },
+
288 channelId,
+
289 cancellationToken);
+
290
+
291 return active => ValueTask.CompletedTask;
+
292 };
+
293 }
override string ToString()
EngineType? Engine
The EngineType.
-
override async ValueTask SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
Send a message to the IProvider.A ValueTask representing the running operation.
+
override async ValueTask SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
Send a message to the IProvider.A ValueTask representing the running operation.
static string GetEngineCompilerPrefix(Api.Models.EngineType engineType)
Get the prefix for messages about deployments.
Represents a message to send to a chat provider.
-
References Tgstation.Server.Api.Models.EngineVersion.Engine, Tgstation.Server.Host.Components.Chat.Providers.Provider.GetEngineCompilerPrefix(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage(), and Tgstation.Server.Api.Models.EngineVersion.ToString().
+
References Tgstation.Server.Api.Models.EngineVersion.Engine, Tgstation.Server.Host.Components.Chat.Providers.Provider.GetEngineCompilerPrefix(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage(), and Tgstation.Server.Api.Models.EngineVersion.ToString().
@@ -1591,9 +1730,37 @@ Here is the call graph for this function:
Address of the server to connect to.
-
Definition at line 53 of file IrcProvider.cs.
+
Definition at line 49 of file IrcProvider.cs.
-
Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
+
Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
+
+
+
+
+
◆ assemblyInfo
+
+
@@ -1619,9 +1786,9 @@ Here is the call graph for this function:
Id counter for channelIdMap.
-Definition at line 93 of file IrcProvider.cs.
+Definition at line 109 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
+Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
@@ -1647,14 +1814,14 @@ Here is the call graph for this function:
Map of ChannelRepresentation.RealIds to channel names.
-Definition at line 78 of file IrcProvider.cs.
+Definition at line 79 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.MapChannelsImpl(), and Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage().
+Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.MapChannelsImpl(), and Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage().
-
-◆ client
+
+◆ client
@@ -1663,7 +1830,7 @@ Here is the call graph for this function:
- | readonly IrcFeatures Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.client |
+ IrcFeatures Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.client |
|
@@ -1675,35 +1842,9 @@ Here is the call graph for this function:
The IrcFeatures client.
-Definition at line 48 of file IrcProvider.cs.
+Definition at line 99 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.MapChannelsImpl(), and Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage().
-
-
-
-
-◆ disconnecting
-
-
-
-
-
-
-
-
- | bool Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.disconnecting |
-
-
- |
-
-private |
-
-
-
@@ -1729,7 +1870,35 @@ Here is the call graph for this function:
The ValueTask used for IrcConnection.Listen(bool).
-Definition at line 88 of file IrcProvider.cs.
+Definition at line 104 of file IrcProvider.cs.
+
+
+
+
+◆ loggingConfiguration
+
+
+
+
+
+
+
+
+ | readonly FileLoggingConfiguration Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.loggingConfiguration |
+
+
+ |
+
+private |
+
+
+
@@ -1755,9 +1924,9 @@ Here is the call graph for this function:
Hard limit to sendable message size in bytes.
-Definition at line 37 of file IrcProvider.cs.
+Definition at line 38 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage().
+Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage().
@@ -1783,9 +1952,9 @@ Here is the call graph for this function:
IRC nickname.
-Definition at line 63 of file IrcProvider.cs.
+Definition at line 64 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
+Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
@@ -1811,9 +1980,9 @@ Here is the call graph for this function:
Password which will used for authentication.
-Definition at line 68 of file IrcProvider.cs.
+Definition at line 69 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
+Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
@@ -1839,9 +2008,9 @@ Here is the call graph for this function:
The IrcPasswordType of password.
-Definition at line 73 of file IrcProvider.cs.
+Definition at line 74 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
+Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
@@ -1867,9 +2036,9 @@ Here is the call graph for this function:
Port of the server to connect to.
-Definition at line 58 of file IrcProvider.cs.
+Definition at line 54 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
+Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider().
@@ -1895,9 +2064,9 @@ Here is the call graph for this function:
Length of the preamble when writing a message to the server. Must be summed with the channel name to get the true value.
-Definition at line 32 of file IrcProvider.cs.
+Definition at line 33 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage().
+Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage().
@@ -1923,9 +2092,37 @@ Here is the call graph for this function:
Map of ChannelRepresentation.RealIds to query users.
-Definition at line 83 of file IrcProvider.cs.
+Definition at line 84 of file IrcProvider.cs.
-Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider(), and Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage().
+Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.IrcProvider(), and Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage().
+
+
+
+
+◆ ssl
+
+
+
+
+
+
+
+
+ | readonly bool Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.ssl |
+
+
+ |
+
+private |
+
+
+
@@ -1954,7 +2151,7 @@ Here is the call graph for this function:
Implements Tgstation.Server.Host.Components.Chat.Providers.IProvider.
-Definition at line 43 of file IrcProvider.cs.
+Definition at line 44 of file IrcProvider.cs.
@@ -1982,7 +2179,7 @@ Here is the call graph for this function:
Implements Tgstation.Server.Host.Components.Chat.Providers.IProvider.
-Definition at line 40 of file IrcProvider.cs.
+Definition at line 41 of file IrcProvider.cs.
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.map b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.map
index 0e29a45876..73151a8dda 100644
--- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.map
+++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.map
@@ -1,40 +1,46 @@
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.md5 b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.md5
index e424fd2da6..3003c8ff34 100644
--- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.md5
+++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.md5
@@ -1 +1 @@
-65216ed67c021d6aea6b99a9f9ed2836
\ No newline at end of file
+6f9a28f53080275c781638b3022fc846
\ No newline at end of file
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.png b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_irc_provider__coll__graph.png
index 56724a553f7052069a57d7b1810af4fcc05ec3e4..63680cc01ae1c930bebd46a170ac9fc06676b69d 100644
GIT binary patch
literal 159089
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