diff --git a/_base_remote_deployment_manager_8cs_source.html b/_base_remote_deployment_manager_8cs_source.html index a0dd4c8139..d16d5a27da 100644 --- a/_base_remote_deployment_manager_8cs_source.html +++ b/_base_remote_deployment_manager_8cs_source.html @@ -136,176 +136,168 @@ $(document).ready(function() { init_codefold(0); });
66 if (repositorySettings.AccessToken == null)
67 return;
68
-
69 var deployedRevisionInformation = compileJob.RevisionInformation;
-
70 if ((previousRevisionInformation != null && previousRevisionInformation.CommitSha == deployedRevisionInformation.CommitSha)
+
69 var revisionInformation = compileJob.RevisionInformation;
+
70 if ((previousRevisionInformation != null && previousRevisionInformation.CommitSha == revisionInformation.CommitSha)
71 || !repositorySettings.PostTestMergeComment!.Value)
72 return;
73
-
74 previousRevisionInformation ??= new RevisionInformation();
-
75 previousRevisionInformation.ActiveTestMerges ??= new List<RevInfoTestMerge>();
+
74 var previousTestMerges = (IEnumerable<RevInfoTestMerge>?)previousRevisionInformation?.ActiveTestMerges ?? Enumerable.Empty<RevInfoTestMerge>();
+
75 var currentTestMerges = (IEnumerable<RevInfoTestMerge>?)revisionInformation.ActiveTestMerges ?? Enumerable.Empty<RevInfoTestMerge>();
76
-
77 deployedRevisionInformation.ActiveTestMerges ??= new List<RevInfoTestMerge>();
-
78
-
79 // added prs
-
80 var addedTestMerges = deployedRevisionInformation
-
81 .ActiveTestMerges
-
82 .Select(x => x.TestMerge)
-
83 .Where(x => !previousRevisionInformation
-
84 .ActiveTestMerges
-
85 .Any(y => y.TestMerge.Number == x.Number))
-
86 .ToList();
-
87 var removedTestMerges = previousRevisionInformation
-
88 .ActiveTestMerges
+
77 // determine what TMs were changed and how
+
78 var addedTestMerges = currentTestMerges
+
79 .Select(x => x.TestMerge)
+
80 .Where(x => !previousTestMerges
+
81 .Any(y => y.TestMerge.Number == x.Number))
+
82 .ToList();
+
83 var removedTestMerges = previousTestMerges
+
84 .Select(x => x.TestMerge)
+
85 .Where(x => !currentTestMerges
+
86 .Any(y => y.TestMerge.Number == x.Number))
+
87 .ToList();
+
88 var updatedTestMerges = currentTestMerges
89 .Select(x => x.TestMerge)
-
90 .Where(x => !deployedRevisionInformation
-
91 .ActiveTestMerges
-
92 .Any(y => y.TestMerge.Number == x.Number))
-
93 .ToList();
-
94 var updatedTestMerges = deployedRevisionInformation
-
95 .ActiveTestMerges
-
96 .Select(x => x.TestMerge)
-
97 .Where(x => previousRevisionInformation
-
98 .ActiveTestMerges
-
99 .Any(y => y.TestMerge.Number == x.Number))
-
100 .ToList();
-
101
-
102 if (addedTestMerges.Count == 0 && removedTestMerges.Count == 0 && updatedTestMerges.Count == 0)
-
103 return;
-
104
-
105 Logger.LogTrace(
-
106 "Commenting on {addedCount} added, {removedCount} removed, and {updatedCount} updated test merge sources...",
-
107 addedTestMerges.Count,
-
108 removedTestMerges.Count,
-
109 updatedTestMerges.Count);
-
110
-
111 var tasks = new List<ValueTask>(addedTestMerges.Count + updatedTestMerges.Count + removedTestMerges.Count);
-
112 foreach (var addedTestMerge in addedTestMerges)
-
113 {
-
114 var addCommentTask = CommentOnTestMergeSource(
-
115 repositorySettings,
-
116 repoOwner,
-
117 repoName,
-
118 FormatTestMerge(
-
119 repositorySettings,
-
120 compileJob,
-
121 addedTestMerge,
-
122 repoOwner,
-
123 repoName,
-
124 false),
-
125 addedTestMerge.Number,
-
126 cancellationToken);
-
127 tasks.Add(addCommentTask);
-
128 }
-
129
-
130 foreach (var removedTestMerge in removedTestMerges)
-
131 {
-
132 var removeCommentTask = CommentOnTestMergeSource(
-
133 repositorySettings,
-
134 repoOwner,
-
135 repoName,
-
136 "#### Test Merge Removed",
-
137 removedTestMerge.Number,
-
138 cancellationToken);
-
139 tasks.Add(removeCommentTask);
-
140 }
-
141
-
142 foreach (var updatedTestMerge in updatedTestMerges)
-
143 {
-
144 var updateCommentTask = CommentOnTestMergeSource(
-
145 repositorySettings,
-
146 repoOwner,
-
147 repoName,
-
148 FormatTestMerge(
-
149 repositorySettings,
-
150 compileJob,
-
151 updatedTestMerge,
-
152 repoOwner,
-
153 repoName,
-
154 true),
-
155 updatedTestMerge.Number,
-
156 cancellationToken);
-
157 tasks.Add(updateCommentTask);
-
158 }
-
159
-
160 if (tasks.Count > 0)
-
161 await ValueTaskExtensions.WhenAll(tasks);
-
162 }
+
90 .Where(x => previousTestMerges
+
91 .Any(y => y.TestMerge.Number == x.Number))
+
92 .ToList();
+
93
+
94 if (addedTestMerges.Count == 0 && removedTestMerges.Count == 0 && updatedTestMerges.Count == 0)
+
95 return;
+
96
+
97 Logger.LogTrace(
+
98 "Commenting on {addedCount} added, {removedCount} removed, and {updatedCount} updated test merge sources...",
+
99 addedTestMerges.Count,
+
100 removedTestMerges.Count,
+
101 updatedTestMerges.Count);
+
102
+
103 var tasks = new List<ValueTask>(addedTestMerges.Count + updatedTestMerges.Count + removedTestMerges.Count);
+
104 foreach (var addedTestMerge in addedTestMerges)
+
105 {
+
106 var addCommentTask = CommentOnTestMergeSource(
+
107 repositorySettings,
+
108 repoOwner,
+
109 repoName,
+
110 FormatTestMerge(
+
111 repositorySettings,
+
112 compileJob,
+
113 addedTestMerge,
+
114 repoOwner,
+
115 repoName,
+
116 false),
+
117 addedTestMerge.Number,
+
118 cancellationToken);
+
119 tasks.Add(addCommentTask);
+
120 }
+
121
+
122 foreach (var removedTestMerge in removedTestMerges)
+
123 {
+
124 var removeCommentTask = CommentOnTestMergeSource(
+
125 repositorySettings,
+
126 repoOwner,
+
127 repoName,
+
128 "#### Test Merge Removed",
+
129 removedTestMerge.Number,
+
130 cancellationToken);
+
131 tasks.Add(removeCommentTask);
+
132 }
+
133
+
134 foreach (var updatedTestMerge in updatedTestMerges)
+
135 {
+
136 var updateCommentTask = CommentOnTestMergeSource(
+
137 repositorySettings,
+
138 repoOwner,
+
139 repoName,
+
140 FormatTestMerge(
+
141 repositorySettings,
+
142 compileJob,
+
143 updatedTestMerge,
+
144 repoOwner,
+
145 repoName,
+
146 true),
+
147 updatedTestMerge.Number,
+
148 cancellationToken);
+
149 tasks.Add(updateCommentTask);
+
150 }
+
151
+
152 if (tasks.Count > 0)
+
153 await ValueTaskExtensions.WhenAll(tasks);
+
154 }
+
155
+
+
157 public ValueTask ApplyDeployment(CompileJob compileJob, CancellationToken cancellationToken)
+
158 {
+
159 ArgumentNullException.ThrowIfNull(compileJob);
+
160
+
161 if (activationCallbacks.TryGetValue(compileJob.Require(x => x.Id), out var activationCallback))
+
162 activationCallback(true);
163
-
-
165 public ValueTask ApplyDeployment(CompileJob compileJob, CancellationToken cancellationToken)
-
166 {
-
167 ArgumentNullException.ThrowIfNull(compileJob);
-
168
-
169 if (activationCallbacks.TryGetValue(compileJob.Require(x => x.Id), out var activationCallback))
-
170 activationCallback(true);
-
171
-
172 return ApplyDeploymentImpl(compileJob, cancellationToken);
-
173 }
+
164 return ApplyDeploymentImpl(compileJob, cancellationToken);
+
165 }
+
166
+
168 public abstract ValueTask FailDeployment(CompileJob compileJob, string errorMessage, CancellationToken cancellationToken);
+
169
+
+
171 public ValueTask MarkInactive(CompileJob compileJob, CancellationToken cancellationToken)
+
172 {
+
173 ArgumentNullException.ThrowIfNull(compileJob);
174
-
176 public abstract ValueTask FailDeployment(CompileJob compileJob, string errorMessage, CancellationToken cancellationToken);
+
175 if (activationCallbacks.TryRemove(compileJob.Require(x => x.Id), out var activationCallback))
+
176 activationCallback(false);
177
-
-
179 public ValueTask MarkInactive(CompileJob compileJob, CancellationToken cancellationToken)
-
180 {
-
181 ArgumentNullException.ThrowIfNull(compileJob);
-
182
-
183 if (activationCallbacks.TryRemove(compileJob.Require(x => x.Id), out var activationCallback))
-
184 activationCallback(false);
-
185
-
186 return MarkInactiveImpl(compileJob, cancellationToken);
-
187 }
+
178 return MarkInactiveImpl(compileJob, cancellationToken);
+
179 }
-
188
-
190 public abstract ValueTask<IReadOnlyCollection<TestMerge>> RemoveMergedTestMerges(
-
191 IRepository repository,
-
192 RepositorySettings repositorySettings,
-
193 RevisionInformation revisionInformation,
-
194 CancellationToken cancellationToken);
-
195
-
-
197 public ValueTask StageDeployment(CompileJob compileJob, Action<bool>? activationCallback, CancellationToken cancellationToken)
-
198 {
-
199 ArgumentNullException.ThrowIfNull(compileJob);
-
200
-
201 var compileJobId = compileJob.Require(x => x.Id);
-
202 if (activationCallback != null && !activationCallbacks.TryAdd(compileJobId, activationCallback))
-
203 Logger.LogError("activationCallbacks conflicted on CompileJob #{id}!", compileJobId);
-
204
-
205 return StageDeploymentImpl(compileJob, cancellationToken);
-
206 }
+
180
+
182 public abstract ValueTask<IReadOnlyCollection<TestMerge>> RemoveMergedTestMerges(
+
183 IRepository repository,
+
184 RepositorySettings repositorySettings,
+
185 RevisionInformation revisionInformation,
+
186 CancellationToken cancellationToken);
+
187
+
+
189 public ValueTask StageDeployment(CompileJob compileJob, Action<bool>? activationCallback, CancellationToken cancellationToken)
+
190 {
+
191 ArgumentNullException.ThrowIfNull(compileJob);
+
192
+
193 var compileJobId = compileJob.Require(x => x.Id);
+
194 if (activationCallback != null && !activationCallbacks.TryAdd(compileJobId, activationCallback))
+
195 Logger.LogError("activationCallbacks conflicted on CompileJob #{id}!", compileJobId);
+
196
+
197 return StageDeploymentImpl(compileJob, cancellationToken);
+
198 }
-
207
-
209 public abstract ValueTask StartDeployment(
-
210 Api.Models.Internal.IGitRemoteInformation remoteInformation,
-
211 CompileJob compileJob,
-
212 CancellationToken cancellationToken);
+
199
+
201 public abstract ValueTask StartDeployment(
+
202 Api.Models.Internal.IGitRemoteInformation remoteInformation,
+
203 CompileJob compileJob,
+
204 CancellationToken cancellationToken);
+
205
+
212 protected abstract ValueTask StageDeploymentImpl(CompileJob compileJob, CancellationToken cancellationToken);
213
-
220 protected abstract ValueTask StageDeploymentImpl(CompileJob compileJob, CancellationToken cancellationToken);
+
220 protected abstract ValueTask ApplyDeploymentImpl(CompileJob compileJob, CancellationToken cancellationToken);
221
-
228 protected abstract ValueTask ApplyDeploymentImpl(CompileJob compileJob, CancellationToken cancellationToken);
+
228 protected abstract ValueTask MarkInactiveImpl(CompileJob compileJob, CancellationToken cancellationToken);
229
-
236 protected abstract ValueTask MarkInactiveImpl(CompileJob compileJob, CancellationToken cancellationToken);
-
237
-
248 protected abstract string FormatTestMerge(
-
249 RepositorySettings repositorySettings,
-
250 CompileJob compileJob,
-
251 TestMerge testMerge,
-
252 string remoteRepositoryOwner,
-
253 string remoteRepositoryName,
-
254 bool updated);
-
255
-
266 protected abstract ValueTask CommentOnTestMergeSource(
-
267 RepositorySettings repositorySettings,
-
268 string remoteRepositoryOwner,
-
269 string remoteRepositoryName,
-
270 string comment,
-
271 int testMergeNumber,
-
272 CancellationToken cancellationToken);
-
273 }
+
240 protected abstract string FormatTestMerge(
+
241 RepositorySettings repositorySettings,
+
242 CompileJob compileJob,
+
243 TestMerge testMerge,
+
244 string remoteRepositoryOwner,
+
245 string remoteRepositoryName,
+
246 bool updated);
+
247
+
258 protected abstract ValueTask CommentOnTestMergeSource(
+
259 RepositorySettings repositorySettings,
+
260 string remoteRepositoryOwner,
+
261 string remoteRepositoryName,
+
262 string comment,
+
263 int testMergeNumber,
+
264 CancellationToken cancellationToken);
+
265 }
-
274}
+
266}
Metadata about a server instance.
Definition Instance.cs:9
string? CommitSha
The revision SHA.
@@ -318,23 +310,23 @@ $(document).ready(function() { init_codefold(0); });
ValueTask ApplyDeploymentImpl(CompileJob compileJob, CancellationToken cancellationToken)
Implementation of ApplyDeployment(CompileJob, CancellationToken).
Api.Models.Instance Metadata
The Api.Models.Instance for the BaseRemoteDeploymentManager.
ValueTask FailDeployment(CompileJob compileJob, string errorMessage, CancellationToken cancellationToken)
Fail a deployment for a given compileJob .A ValueTask representing the running operation.
-
ValueTask StageDeployment(CompileJob compileJob, Action< bool >? activationCallback, CancellationToken cancellationToken)
Stage a given compileJob 's deployment.A ValueTask representing the running operation.
+
ValueTask StageDeployment(CompileJob compileJob, Action< bool >? activationCallback, CancellationToken cancellationToken)
Stage a given compileJob 's deployment.A ValueTask representing the running operation.
ValueTask MarkInactiveImpl(CompileJob compileJob, CancellationToken cancellationToken)
Implementation of MarkInactive(CompileJob, CancellationToken).
-
ValueTask MarkInactive(CompileJob compileJob, CancellationToken cancellationToken)
Mark the deplotment for a given compileJob as inactive.A Task representing the running operation.
+
ValueTask MarkInactive(CompileJob compileJob, CancellationToken cancellationToken)
Mark the deplotment for a given compileJob as inactive.A Task representing the running operation.
ValueTask CommentOnTestMergeSource(RepositorySettings repositorySettings, string remoteRepositoryOwner, string remoteRepositoryName, string comment, int testMergeNumber, CancellationToken cancellationToken)
Create a comment of a given testMergeNumber 's source.
string FormatTestMerge(RepositorySettings repositorySettings, CompileJob compileJob, TestMerge testMerge, string remoteRepositoryOwner, string remoteRepositoryName, bool updated)
Formats a comment for a given testMerge .
ValueTask StageDeploymentImpl(CompileJob compileJob, CancellationToken cancellationToken)
Implementation of StageDeployment(CompileJob, Action<bool>, CancellationToken).
readonly ConcurrentDictionary< long, Action< bool > > activationCallbacks
A map of CompileJob Api.Models.EntityId.Ids to activation callback Action<T1>s.
BaseRemoteDeploymentManager(ILogger< BaseRemoteDeploymentManager > logger, Api.Models.Instance metadata, ConcurrentDictionary< long, Action< bool > > activationCallbacks)
Initializes a new instance of the BaseRemoteDeploymentManager class.
async ValueTask PostDeploymentComments(CompileJob compileJob, RevisionInformation? previousRevisionInformation, RepositorySettings repositorySettings, string? repoOwner, string? repoName, CancellationToken cancellationToken)
Post deployment comments to the test merge ticket.A ValueTask representing the running operation.
-
ValueTask ApplyDeployment(CompileJob compileJob, CancellationToken cancellationToken)
Stage a given compileJob 's deployment.A ValueTask representing the running operation.
+
ValueTask ApplyDeployment(CompileJob compileJob, CancellationToken cancellationToken)
Stage a given compileJob 's deployment.A ValueTask representing the running operation.
ValueTask< IReadOnlyCollection< TestMerge > > RemoveMergedTestMerges(IRepository repository, RepositorySettings repositorySettings, RevisionInformation revisionInformation, CancellationToken cancellationToken)
Get the updated list of TestMerges for an origin merge.A ValueTask<TResult> resulting in the IReadOnl...
ILogger< BaseRemoteDeploymentManager > Logger
The ILogger for the BaseRemoteDeploymentManager.
Definition CompileJob.cs:11
RevisionInformation RevisionInformation
See CompileJobResponse.RevisionInformation.
Definition CompileJob.cs:27
+
Many to many relationship for Models.RevisionInformation and Models.TestMerge.
-
ICollection< RevInfoTestMerge >? ActiveTestMerges
See Api.Models.RevisionInformation.ActiveTestMerges.
Definition TestMerge.cs:9
Creates and updates remote deployments.
Represents an on-disk git repository.
diff --git a/_chat_manager_8cs_source.html b/_chat_manager_8cs_source.html index f3dccede3c..f19ff38e75 100644 --- a/_chat_manager_8cs_source.html +++ b/_chat_manager_8cs_source.html @@ -394,719 +394,721 @@ $(document).ready(function() { init_codefold(0); });
376
-
378 public Func<string?, string, Action<bool>> QueueDeploymentMessage(
+
378 public Func<string?, string, Action<bool>> QueueDeploymentMessage(
379 Models.RevisionInformation revisionInformation,
-
380 EngineVersion engineVersion,
-
381 DateTimeOffset? estimatedCompletionTime,
-
382 string? gitHubOwner,
-
383 string? gitHubRepo,
-
384 bool localCommitPushed)
-
385 {
-
386 List<ulong> wdChannels;
-
387 lock (mappedChannels) // so it doesn't change while we're using it
-
388 wdChannels = mappedChannels.Where(x => x.Value.IsUpdatesChannel).Select(x => x.Key).ToList();
-
389
-
390 logger.LogTrace("Sending deployment message for RevisionInformation: {revisionInfoId}", revisionInformation.Id);
-
391
-
392 var callbacks = new List<Func<string?, string, ValueTask<Func<bool, ValueTask>>>>();
-
393
-
394 var task = Task.WhenAll(
-
395 wdChannels.Select(
-
396 async x =>
-
397 {
-
398 ChannelMapping? channelMapping;
-
399 lock (mappedChannels)
-
400 if (!mappedChannels.TryGetValue(x, out channelMapping))
-
401 return;
-
402 IProvider? provider;
-
403 lock (providers)
-
404 if (!providers.TryGetValue(channelMapping.ProviderId, out provider))
-
405 return;
-
406 try
-
407 {
-
408 var callback = await provider.SendUpdateMessage(
-
409 revisionInformation,
-
410 engineVersion,
-
411 estimatedCompletionTime,
-
412 gitHubOwner,
-
413 gitHubRepo,
-
414 channelMapping.ProviderChannelId,
-
415 localCommitPushed,
-
416 handlerCts.Token);
-
417
-
418 lock (callbacks)
-
419 callbacks.Add(callback);
-
420 }
-
421 catch (Exception ex)
-
422 {
-
423 logger.LogWarning(
-
424 ex,
-
425 "Error sending deploy message to provider {providerId}!",
-
426 channelMapping.ProviderId);
-
427 }
-
428 }));
-
429
-
430 AddMessageTask(task);
+
380 Models.RevisionInformation? previousRevisionInformation,
+
381 EngineVersion engineVersion,
+
382 DateTimeOffset? estimatedCompletionTime,
+
383 string? gitHubOwner,
+
384 string? gitHubRepo,
+
385 bool localCommitPushed)
+
386 {
+
387 List<ulong> wdChannels;
+
388 lock (mappedChannels) // so it doesn't change while we're using it
+
389 wdChannels = mappedChannels.Where(x => x.Value.IsUpdatesChannel).Select(x => x.Key).ToList();
+
390
+
391 logger.LogTrace("Sending deployment message for RevisionInformation: {revisionInfoId}", revisionInformation.Id);
+
392
+
393 var callbacks = new List<Func<string?, string, ValueTask<Func<bool, ValueTask>>>>();
+
394
+
395 var task = Task.WhenAll(
+
396 wdChannels.Select(
+
397 async x =>
+
398 {
+
399 ChannelMapping? channelMapping;
+
400 lock (mappedChannels)
+
401 if (!mappedChannels.TryGetValue(x, out channelMapping))
+
402 return;
+
403 IProvider? provider;
+
404 lock (providers)
+
405 if (!providers.TryGetValue(channelMapping.ProviderId, out provider))
+
406 return;
+
407 try
+
408 {
+
409 var callback = await provider.SendUpdateMessage(
+
410 revisionInformation,
+
411 previousRevisionInformation,
+
412 engineVersion,
+
413 estimatedCompletionTime,
+
414 gitHubOwner,
+
415 gitHubRepo,
+
416 channelMapping.ProviderChannelId,
+
417 localCommitPushed,
+
418 handlerCts.Token);
+
419
+
420 lock (callbacks)
+
421 callbacks.Add(callback);
+
422 }
+
423 catch (Exception ex)
+
424 {
+
425 logger.LogWarning(
+
426 ex,
+
427 "Error sending deploy message to provider {providerId}!",
+
428 channelMapping.ProviderId);
+
429 }
+
430 }));
431
-
432 Task callbackTask;
-
433 Func<bool, Task>? finalUpdateAction = null;
-
434 async Task CallbackTask(string? errorMessage, string dreamMakerOutput)
-
435 {
-
436 await task;
-
437 var callbackResults = await ValueTaskExtensions.WhenAll(
-
438 callbacks.Select(
-
439 x => x(
-
440 errorMessage,
-
441 dreamMakerOutput)),
-
442 callbacks.Count);
-
443
-
444 finalUpdateAction = active => ValueTaskExtensions.WhenAll(callbackResults.Select(finalizerCallback => finalizerCallback(active))).AsTask();
-
445 }
-
446
-
447 async Task CompletionTask(bool active)
-
448 {
-
449 try
-
450 {
-
451 await callbackTask;
-
452 }
-
453 catch
-
454 {
-
455 // Handled in AddMessageTask
-
456 return;
-
457 }
-
458
-
459 AddMessageTask(finalUpdateAction!(active));
-
460 }
-
461
-
462 return (errorMessage, dreamMakerOutput) =>
-
463 {
-
464 callbackTask = CallbackTask(errorMessage, dreamMakerOutput);
-
465 AddMessageTask(callbackTask);
-
466 return active => AddMessageTask(CompletionTask(active));
-
467 };
-
468 }
+
432 AddMessageTask(task);
+
433
+
434 Task callbackTask;
+
435 Func<bool, Task>? finalUpdateAction = null;
+
436 async Task CallbackTask(string? errorMessage, string dreamMakerOutput)
+
437 {
+
438 await task;
+
439 var callbackResults = await ValueTaskExtensions.WhenAll(
+
440 callbacks.Select(
+
441 x => x(
+
442 errorMessage,
+
443 dreamMakerOutput)),
+
444 callbacks.Count);
+
445
+
446 finalUpdateAction = active => ValueTaskExtensions.WhenAll(callbackResults.Select(finalizerCallback => finalizerCallback(active))).AsTask();
+
447 }
+
448
+
449 async Task CompletionTask(bool active)
+
450 {
+
451 try
+
452 {
+
453 await callbackTask;
+
454 }
+
455 catch
+
456 {
+
457 // Handled in AddMessageTask
+
458 return;
+
459 }
+
460
+
461 AddMessageTask(finalUpdateAction!(active));
+
462 }
+
463
+
464 return (errorMessage, dreamMakerOutput) =>
+
465 {
+
466 callbackTask = CallbackTask(errorMessage, dreamMakerOutput);
+
467 AddMessageTask(callbackTask);
+
468 return active => AddMessageTask(CompletionTask(active));
+
469 };
+
470 }
-
469
-
-
471 public async Task StartAsync(CancellationToken cancellationToken)
-
472 {
-
473 foreach (var tgsCommand in commandFactory.GenerateCommands())
-
474 builtinCommands.Add(tgsCommand.Name.ToUpperInvariant(), tgsCommand);
-
475 var initialChatBots = activeChatBots.ToList();
-
476 await ValueTaskExtensions.WhenAll(initialChatBots.Select(x => ChangeSettings(x, cancellationToken)));
-
477 initialProviderConnectionsTask = InitialConnection();
-
478 chatHandler = MonitorMessages(handlerCts.Token);
-
479 }
+
471
+
+
473 public async Task StartAsync(CancellationToken cancellationToken)
+
474 {
+
475 foreach (var tgsCommand in commandFactory.GenerateCommands())
+
476 builtinCommands.Add(tgsCommand.Name.ToUpperInvariant(), tgsCommand);
+
477 var initialChatBots = activeChatBots.ToList();
+
478 await ValueTaskExtensions.WhenAll(initialChatBots.Select(x => ChangeSettings(x, cancellationToken)));
+
479 initialProviderConnectionsTask = InitialConnection();
+
480 chatHandler = MonitorMessages(handlerCts.Token);
+
481 }
-
480
-
-
482 public async Task StopAsync(CancellationToken cancellationToken)
-
483 {
-
484 handlerCts.Cancel();
-
485 if (chatHandler != null)
-
486 await chatHandler;
-
487 await Task.WhenAll(providers.Select(x => x.Key).Select(x => DeleteConnection(x, cancellationToken)));
-
488 await messageSendTask;
-
489 }
+
482
+
+
484 public async Task StopAsync(CancellationToken cancellationToken)
+
485 {
+
486 handlerCts.Cancel();
+
487 if (chatHandler != null)
+
488 await chatHandler;
+
489 await Task.WhenAll(providers.Select(x => x.Key).Select(x => DeleteConnection(x, cancellationToken)));
+
490 await messageSendTask;
+
491 }
-
490
-
- -
493 {
-
494 if (customCommandHandler == null)
-
495 throw new InvalidOperationException("RegisterCommandHandler() hasn't been called!");
-
496
-
497 IChatTrackingContext context = null!;
-
498 lock (mappedChannels)
-
499 context = new ChatTrackingContext(
-
500 customCommandHandler,
-
501 mappedChannels.Select(y => y.Value.Channel),
-
502 loggerFactory.CreateLogger<ChatTrackingContext>(),
-
503 () =>
-
504 {
-
505 lock (trackingContexts)
-
506 trackingContexts.Remove(context);
-
507 });
-
508
-
509 lock (trackingContexts)
-
510 trackingContexts.Add(context);
-
511
-
512 return context;
-
513 }
+
492
+
+ +
495 {
+
496 if (customCommandHandler == null)
+
497 throw new InvalidOperationException("RegisterCommandHandler() hasn't been called!");
+
498
+
499 IChatTrackingContext context = null!;
+
500 lock (mappedChannels)
+
501 context = new ChatTrackingContext(
+
502 customCommandHandler,
+
503 mappedChannels.Select(y => y.Value.Channel),
+
504 loggerFactory.CreateLogger<ChatTrackingContext>(),
+
505 () =>
+
506 {
+
507 lock (trackingContexts)
+
508 trackingContexts.Remove(context);
+
509 });
+
510
+
511 lock (trackingContexts)
+
512 trackingContexts.Add(context);
+
513
+
514 return context;
+
515 }
-
514
-
-
516 public async ValueTask UpdateTrackingContexts(CancellationToken cancellationToken)
-
517 {
-
518 var logMessageSent = 0;
-
519 async Task UpdateTrackingContext(IChatTrackingContext channelSink, IEnumerable<ChannelRepresentation> channels)
-
520 {
-
521 if (Interlocked.Exchange(ref logMessageSent, 1) == 0)
-
522
-
523 await channelSink.UpdateChannels(channels, cancellationToken);
-
524 }
-
525
-
526 var waitingForInitialConnection = !initialProviderConnectionsTask!.IsCompleted;
-
527 if (waitingForInitialConnection)
-
528 {
-
529 logger.LogTrace("Waiting for initial chat bot connections before updating tracking contexts...");
-
530 await initialProviderConnectionsTask.WaitAsync(cancellationToken);
-
531 }
-
532
-
533 List<Task> tasks;
-
534 lock (mappedChannels)
-
535 lock (trackingContexts)
-
536 tasks = trackingContexts.Select(x => UpdateTrackingContext(x, mappedChannels.Select(y => y.Value.Channel))).ToList();
-
537
-
538 if (waitingForInitialConnection)
-
539 if (tasks.Count > 0)
-
540 logger.LogTrace("Updating chat tracking contexts...");
-
541 else
-
542 logger.LogTrace("No chat tracking contexts to update");
-
543
-
544 await Task.WhenAll(tasks);
-
545 }
+
516
+
+
518 public async ValueTask UpdateTrackingContexts(CancellationToken cancellationToken)
+
519 {
+
520 var logMessageSent = 0;
+
521 async Task UpdateTrackingContext(IChatTrackingContext channelSink, IEnumerable<ChannelRepresentation> channels)
+
522 {
+
523 if (Interlocked.Exchange(ref logMessageSent, 1) == 0)
+
524
+
525 await channelSink.UpdateChannels(channels, cancellationToken);
+
526 }
+
527
+
528 var waitingForInitialConnection = !initialProviderConnectionsTask!.IsCompleted;
+
529 if (waitingForInitialConnection)
+
530 {
+
531 logger.LogTrace("Waiting for initial chat bot connections before updating tracking contexts...");
+
532 await initialProviderConnectionsTask.WaitAsync(cancellationToken);
+
533 }
+
534
+
535 List<Task> tasks;
+
536 lock (mappedChannels)
+
537 lock (trackingContexts)
+
538 tasks = trackingContexts.Select(x => UpdateTrackingContext(x, mappedChannels.Select(y => y.Value.Channel))).ToList();
+
539
+
540 if (waitingForInitialConnection)
+
541 if (tasks.Count > 0)
+
542 logger.LogTrace("Updating chat tracking contexts...");
+
543 else
+
544 logger.LogTrace("No chat tracking contexts to update");
+
545
+
546 await Task.WhenAll(tasks);
+
547 }
-
546
-
-
548 public void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
-
549 {
-
550 if (this.customCommandHandler != null)
-
551 throw new InvalidOperationException("RegisterCommandHandler() already called!");
-
552 this.customCommandHandler = customCommandHandler ?? throw new ArgumentNullException(nameof(customCommandHandler));
-
553 }
+
548
+
+
550 public void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
+
551 {
+
552 if (this.customCommandHandler != null)
+
553 throw new InvalidOperationException("RegisterCommandHandler() already called!");
+
554 this.customCommandHandler = customCommandHandler ?? throw new ArgumentNullException(nameof(customCommandHandler));
+
555 }
-
554
-
-
556 public async Task DeleteConnection(long connectionId, CancellationToken cancellationToken)
-
557 {
-
558 logger.LogTrace("DeleteConnection {connectionId}", connectionId);
-
559 var hasSemaphore = changeChannelSemaphores.TryRemove(connectionId, out var semaphore);
-
560 using (hasSemaphore
-
561 ? semaphore
-
562 : null)
-
563 using (hasSemaphore
-
564 ? await SemaphoreSlimContext.Lock(semaphore!, cancellationToken)
-
565 : null)
-
566 {
-
567 var provider = await RemoveProviderChannels(connectionId, true, cancellationToken);
-
568 if (provider != null)
-
569 {
-
570 var startTime = DateTimeOffset.UtcNow;
-
571 try
-
572 {
-
573 await provider.Disconnect(cancellationToken);
-
574 }
-
575 catch (Exception ex)
-
576 {
-
577 logger.LogError(ex, "Error disconnecting connection {connectionId}!", connectionId);
-
578 }
-
579
-
580 await provider.DisposeAsync();
-
581 var duration = DateTimeOffset.UtcNow - startTime;
-
582 if (duration.TotalSeconds > 3)
-
583 logger.LogWarning("Disconnecting a {providerType} took {totalSeconds}s!", provider.GetType().Name, duration.TotalSeconds);
-
584 }
-
585 else
-
586 logger.LogTrace("DeleteConnection: ID {connectionId} doesn't exist!", connectionId);
-
587 }
-
588 }
+
556
+
+
558 public async Task DeleteConnection(long connectionId, CancellationToken cancellationToken)
+
559 {
+
560 logger.LogTrace("DeleteConnection {connectionId}", connectionId);
+
561 var hasSemaphore = changeChannelSemaphores.TryRemove(connectionId, out var semaphore);
+
562 using (hasSemaphore
+
563 ? semaphore
+
564 : null)
+
565 using (hasSemaphore
+
566 ? await SemaphoreSlimContext.Lock(semaphore!, cancellationToken)
+
567 : null)
+
568 {
+
569 var provider = await RemoveProviderChannels(connectionId, true, cancellationToken);
+
570 if (provider != null)
+
571 {
+
572 var startTime = DateTimeOffset.UtcNow;
+
573 try
+
574 {
+
575 await provider.Disconnect(cancellationToken);
+
576 }
+
577 catch (Exception ex)
+
578 {
+
579 logger.LogError(ex, "Error disconnecting connection {connectionId}!", connectionId);
+
580 }
+
581
+
582 await provider.DisposeAsync();
+
583 var duration = DateTimeOffset.UtcNow - startTime;
+
584 if (duration.TotalSeconds > 3)
+
585 logger.LogWarning("Disconnecting a {providerType} took {totalSeconds}s!", provider.GetType().Name, duration.TotalSeconds);
+
586 }
+
587 else
+
588 logger.LogTrace("DeleteConnection: ID {connectionId} doesn't exist!", connectionId);
+
589 }
+
590 }
-
589
-
-
591 public ValueTask HandleRestart(Version? updateVersion, bool handlerMayDelayShutdownWithExtremelyLongRunningTasks, CancellationToken cancellationToken)
-
592 {
-
593 var message = updateVersion == null
-
594 ? $"TGS: {(handlerMayDelayShutdownWithExtremelyLongRunningTasks ? "Graceful shutdown" : "Going down")}..."
-
595 : $"TGS: Updating to version {updateVersion}...";
-
596 List<ulong> systemChannels;
-
597 lock (mappedChannels) // so it doesn't change while we're using it
-
598 systemChannels = mappedChannels
-
599 .Where(x => x.Value.IsSystemChannel)
-
600 .Select(x => x.Key)
-
601 .ToList();
-
602
-
603 return SendMessage(
-
604 systemChannels,
-
605 null,
- -
607 {
-
608 Text = message,
-
609 },
-
610 cancellationToken);
-
611 }
+
591
+
+
593 public ValueTask HandleRestart(Version? updateVersion, bool handlerMayDelayShutdownWithExtremelyLongRunningTasks, CancellationToken cancellationToken)
+
594 {
+
595 var message = updateVersion == null
+
596 ? $"TGS: {(handlerMayDelayShutdownWithExtremelyLongRunningTasks ? "Graceful shutdown" : "Going down")}..."
+
597 : $"TGS: Updating to version {updateVersion}...";
+
598 List<ulong> systemChannels;
+
599 lock (mappedChannels) // so it doesn't change while we're using it
+
600 systemChannels = mappedChannels
+
601 .Where(x => x.Value.IsSystemChannel)
+
602 .Select(x => x.Key)
+
603 .ToList();
+
604
+
605 return SendMessage(
+
606 systemChannels,
+
607 null,
+ +
609 {
+
610 Text = message,
+
611 },
+
612 cancellationToken);
+
613 }
-
612
-
-
620 async ValueTask<IProvider?> RemoveProviderChannels(long connectionId, bool removeProvider, CancellationToken cancellationToken)
-
621 {
-
622 logger.LogTrace("RemoveProviderChannels {connectionId}...", connectionId);
-
623 IProvider? provider;
-
624 lock (providers)
-
625 {
-
626 if (!providers.TryGetValue(connectionId, out provider))
-
627 {
-
628 logger.LogTrace("Aborted, no such provider!");
-
629 return null;
-
630 }
-
631
-
632 if (removeProvider)
-
633 providers.Remove(connectionId);
-
634 }
-
635
-
636 ValueTask trackingContextsUpdateTask;
-
637 lock (mappedChannels)
-
638 {
-
639 foreach (var mappedConnectionChannel in mappedChannels.Where(x => x.Value.ProviderId == connectionId).Select(x => x.Key).ToList())
-
640 mappedChannels.Remove(mappedConnectionChannel);
-
641
-
642 var newMappedChannels = mappedChannels.Select(y => y.Value.Channel).ToList();
+
614
+
+
622 async ValueTask<IProvider?> RemoveProviderChannels(long connectionId, bool removeProvider, CancellationToken cancellationToken)
+
623 {
+
624 logger.LogTrace("RemoveProviderChannels {connectionId}...", connectionId);
+
625 IProvider? provider;
+
626 lock (providers)
+
627 {
+
628 if (!providers.TryGetValue(connectionId, out provider))
+
629 {
+
630 logger.LogTrace("Aborted, no such provider!");
+
631 return null;
+
632 }
+
633
+
634 if (removeProvider)
+
635 providers.Remove(connectionId);
+
636 }
+
637
+
638 ValueTask trackingContextsUpdateTask;
+
639 lock (mappedChannels)
+
640 {
+
641 foreach (var mappedConnectionChannel in mappedChannels.Where(x => x.Value.ProviderId == connectionId).Select(x => x.Key).ToList())
+
642 mappedChannels.Remove(mappedConnectionChannel);
643
-
644 if (removeProvider)
-
645 lock (trackingContexts)
-
646 trackingContextsUpdateTask = ValueTaskExtensions.WhenAll(trackingContexts.Select(x => x.UpdateChannels(newMappedChannels, cancellationToken)));
-
647 else
-
648 trackingContextsUpdateTask = ValueTask.CompletedTask;
-
649 }
-
650
-
651 await trackingContextsUpdateTask;
+
644 var newMappedChannels = mappedChannels.Select(y => y.Value.Channel).ToList();
+
645
+
646 if (removeProvider)
+
647 lock (trackingContexts)
+
648 trackingContextsUpdateTask = ValueTaskExtensions.WhenAll(trackingContexts.Select(x => x.UpdateChannels(newMappedChannels, cancellationToken)));
+
649 else
+
650 trackingContextsUpdateTask = ValueTask.CompletedTask;
+
651 }
652
-
653 return provider;
-
654 }
+
653 await trackingContextsUpdateTask;
+
654
+
655 return provider;
+
656 }
-
655
-
-
662 async ValueTask RemapProvider(IProvider provider, CancellationToken cancellationToken)
-
663 {
-
664 logger.LogTrace("Remapping channels for provider reconnection...");
-
665 IEnumerable<Models.ChatChannel>? channelsToMap;
-
666 long providerId;
-
667 lock (providers)
-
668 providerId = providers.Where(x => x.Value == provider).Select(x => x.Key).First();
-
669
-
670 lock (activeChatBots)
-
671 channelsToMap = activeChatBots.FirstOrDefault(x => x.Id == providerId)?.Channels;
-
672
-
673 if (channelsToMap?.Any() ?? false)
-
674 await ChangeChannels(providerId, channelsToMap, cancellationToken);
-
675 }
+
657
+
+
664 async ValueTask RemapProvider(IProvider provider, CancellationToken cancellationToken)
+
665 {
+
666 logger.LogTrace("Remapping channels for provider reconnection...");
+
667 IEnumerable<Models.ChatChannel>? channelsToMap;
+
668 long providerId;
+
669 lock (providers)
+
670 providerId = providers.Where(x => x.Value == provider).Select(x => x.Key).First();
+
671
+
672 lock (activeChatBots)
+
673 channelsToMap = activeChatBots.FirstOrDefault(x => x.Id == providerId)?.Channels;
+
674
+
675 if (channelsToMap?.Any() ?? false)
+
676 await ChangeChannels(providerId, channelsToMap, cancellationToken);
+
677 }
-
676
-
685#pragma warning disable CA1502
-
-
686 async ValueTask ProcessMessage(IProvider provider, Message? message, bool recursed, CancellationToken cancellationToken)
-
687#pragma warning restore CA1502
-
688 {
-
689 if (!provider.Connected)
-
690 {
-
691 logger.LogTrace("Abort message processing because provider is disconnected!");
-
692 return;
-
693 }
-
694
-
695 // provider reconnected, remap channels.
-
696 if (message == null)
-
697 {
-
698 await RemapProvider(provider, cancellationToken);
-
699 return;
-
700 }
-
701
-
702 // map the channel if it's private and we haven't seen it
-
703 var providerChannelId = message.User.Channel.RealId;
-
704 KeyValuePair<ulong, ChannelMapping>? mappedChannel;
-
705 long providerId;
-
706 bool hasChannelZero;
-
707 lock (providers)
-
708 {
-
709 // important, otherwise we could end up processing during shutdown
-
710 cancellationToken.ThrowIfCancellationRequested();
-
711
-
712 var providerIdNullable = providers
-
713 .Where(x => x.Value == provider)
-
714 .Select(x => (long?)x.Key)
-
715 .FirstOrDefault();
-
716
-
717 if (!providerIdNullable.HasValue)
-
718 {
-
719 // possible to have a message queued and then the provider immediately disconnects
-
720 logger.LogDebug("Unable to process command \"{command}\" due to provider disconnecting", message.Content);
-
721 return;
-
722 }
-
723
-
724 providerId = providerIdNullable.Value;
-
725 mappedChannel = mappedChannels
-
726 .Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == providerChannelId)
-
727 .Select(x => (KeyValuePair<ulong, ChannelMapping>?)x)
-
728 .FirstOrDefault();
-
729 hasChannelZero = mappedChannels
-
730 .Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == 0)
-
731 .Any();
-
732 }
-
733
-
734 if (!recursed && !mappedChannel.HasValue && !message.User.Channel.IsPrivateChannel && hasChannelZero)
-
735 {
-
736 logger.LogInformation("Receieved message from unmapped channel whose provider contains ID 0. Remapping...");
-
737 await RemapProvider(provider, cancellationToken);
-
738 logger.LogTrace("Resume processing original message...");
-
739 await ProcessMessage(provider, message, true, cancellationToken);
-
740 return;
-
741 }
-
742
-
743 ValueTask TextReply(string reply) => SendMessage(
-
744 new List<ulong>
-
745 {
-
746 message.User.Channel.RealId,
-
747 },
-
748 message,
- -
750 {
-
751 Text = reply,
-
752 },
-
753 cancellationToken);
-
754
-
755 if (message.User.Channel.IsPrivateChannel)
-
756 lock (mappedChannels)
-
757 if (!mappedChannel.HasValue)
-
758 {
-
759 ulong newId;
-
760 lock (synchronizationLock)
-
761 newId = channelIdCounter++;
-
762 logger.LogTrace(
-
763 "Mapping private channel {connectionName}:{channelFriendlyName} as {newId}",
- -
765 message.User.FriendlyName,
-
766 newId);
-
767 mappedChannels.Add(newId, new ChannelMapping(message.User.Channel)
-
768 {
-
769 ProviderChannelId = message.User.Channel.RealId,
-
770 ProviderId = providerId,
-
771 });
-
772
-
773 logger.LogTrace(
-
774 "Mapping DM {connectionName}:{userId} ({userFriendlyName}) as {newId}",
- -
776 message.User.RealId,
-
777 message.User.FriendlyName,
-
778 newId);
-
779 message.User.Channel.RealId = newId;
-
780 }
-
781 else
-
782 message.User.Channel.RealId = mappedChannel.Value.Key;
-
783 else
-
784 {
-
785 if (!mappedChannel.HasValue)
-
786 {
-
787 logger.LogError(
-
788 "Error mapping message: Provider ID: {providerId}, Channel Real ID: {realId}",
-
789 providerId,
-
790 message.User.Channel.RealId);
-
791 logger.LogTrace("message: {messageJson}", JsonConvert.SerializeObject(message));
-
792 lock (mappedChannels)
-
793 logger.LogTrace("mappedChannels: {mappedChannelsJson}", JsonConvert.SerializeObject(mappedChannels));
-
794 await TextReply("TGS: Processing error, check logs!");
-
795 return;
-
796 }
-
797
-
798 var mappingChannelRepresentation = mappedChannel.Value.Value.Channel;
+
678
+
687#pragma warning disable CA1502
+
+
688 async ValueTask ProcessMessage(IProvider provider, Message? message, bool recursed, CancellationToken cancellationToken)
+
689#pragma warning restore CA1502
+
690 {
+
691 if (!provider.Connected)
+
692 {
+
693 logger.LogTrace("Abort message processing because provider is disconnected!");
+
694 return;
+
695 }
+
696
+
697 // provider reconnected, remap channels.
+
698 if (message == null)
+
699 {
+
700 await RemapProvider(provider, cancellationToken);
+
701 return;
+
702 }
+
703
+
704 // map the channel if it's private and we haven't seen it
+
705 var providerChannelId = message.User.Channel.RealId;
+
706 KeyValuePair<ulong, ChannelMapping>? mappedChannel;
+
707 long providerId;
+
708 bool hasChannelZero;
+
709 lock (providers)
+
710 {
+
711 // important, otherwise we could end up processing during shutdown
+
712 cancellationToken.ThrowIfCancellationRequested();
+
713
+
714 var providerIdNullable = providers
+
715 .Where(x => x.Value == provider)
+
716 .Select(x => (long?)x.Key)
+
717 .FirstOrDefault();
+
718
+
719 if (!providerIdNullable.HasValue)
+
720 {
+
721 // possible to have a message queued and then the provider immediately disconnects
+
722 logger.LogDebug("Unable to process command \"{command}\" due to provider disconnecting", message.Content);
+
723 return;
+
724 }
+
725
+
726 providerId = providerIdNullable.Value;
+
727 mappedChannel = mappedChannels
+
728 .Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == providerChannelId)
+
729 .Select(x => (KeyValuePair<ulong, ChannelMapping>?)x)
+
730 .FirstOrDefault();
+
731 hasChannelZero = mappedChannels
+
732 .Where(x => x.Value.ProviderId == providerId && x.Value.ProviderChannelId == 0)
+
733 .Any();
+
734 }
+
735
+
736 if (!recursed && !mappedChannel.HasValue && !message.User.Channel.IsPrivateChannel && hasChannelZero)
+
737 {
+
738 logger.LogInformation("Receieved message from unmapped channel whose provider contains ID 0. Remapping...");
+
739 await RemapProvider(provider, cancellationToken);
+
740 logger.LogTrace("Resume processing original message...");
+
741 await ProcessMessage(provider, message, true, cancellationToken);
+
742 return;
+
743 }
+
744
+
745 ValueTask TextReply(string reply) => SendMessage(
+
746 new List<ulong>
+
747 {
+
748 message.User.Channel.RealId,
+
749 },
+
750 message,
+ +
752 {
+
753 Text = reply,
+
754 },
+
755 cancellationToken);
+
756
+
757 if (message.User.Channel.IsPrivateChannel)
+
758 lock (mappedChannels)
+
759 if (!mappedChannel.HasValue)
+
760 {
+
761 ulong newId;
+
762 lock (synchronizationLock)
+
763 newId = channelIdCounter++;
+
764 logger.LogTrace(
+
765 "Mapping private channel {connectionName}:{channelFriendlyName} as {newId}",
+ +
767 message.User.FriendlyName,
+
768 newId);
+
769 mappedChannels.Add(newId, new ChannelMapping(message.User.Channel)
+
770 {
+
771 ProviderChannelId = message.User.Channel.RealId,
+
772 ProviderId = providerId,
+
773 });
+
774
+
775 logger.LogTrace(
+
776 "Mapping DM {connectionName}:{userId} ({userFriendlyName}) as {newId}",
+ +
778 message.User.RealId,
+
779 message.User.FriendlyName,
+
780 newId);
+
781 message.User.Channel.RealId = newId;
+
782 }
+
783 else
+
784 message.User.Channel.RealId = mappedChannel.Value.Key;
+
785 else
+
786 {
+
787 if (!mappedChannel.HasValue)
+
788 {
+
789 logger.LogError(
+
790 "Error mapping message: Provider ID: {providerId}, Channel Real ID: {realId}",
+
791 providerId,
+
792 message.User.Channel.RealId);
+
793 logger.LogTrace("message: {messageJson}", JsonConvert.SerializeObject(message));
+
794 lock (mappedChannels)
+
795 logger.LogTrace("mappedChannels: {mappedChannelsJson}", JsonConvert.SerializeObject(mappedChannels));
+
796 await TextReply("TGS: Processing error, check logs!");
+
797 return;
+
798 }
799
-
800 message.User.Channel.RealId = mappingChannelRepresentation.RealId;
-
801 message.User.Channel.Tag = mappingChannelRepresentation.Tag;
-
802 message.User.Channel.IsAdminChannel = mappingChannelRepresentation.IsAdminChannel;
-
803 }
-
804
-
805 var trimmedMessage = message.Content.Trim();
-
806 if (trimmedMessage.Length == 0)
-
807 return;
-
808
-
809 var splits = new List<string>(trimmedMessage.Split(' ', StringSplitOptions.RemoveEmptyEntries));
-
810 var address = splits[0];
-
811 if (address.Length > 1 && (address.Last() == ':' || address.Last() == ','))
-
812 address = address[0..^1];
-
813
-
814 var addressed =
-
815 address.Equals(CommonMention, StringComparison.OrdinalIgnoreCase)
-
816 || address.Equals(provider.BotMention, StringComparison.OrdinalIgnoreCase);
-
817
-
818 // no mention
-
819 if (!addressed && !message.User.Channel.IsPrivateChannel)
-
820 return;
-
821
-
822 logger.LogTrace(
-
823 "Start processing command: {message}. User (True provider Id): {profiderId}",
-
824 message.Content,
-
825 JsonConvert.SerializeObject(message.User));
-
826 try
-
827 {
-
828 if (addressed)
-
829 splits.RemoveAt(0);
-
830
-
831 if (splits.Count == 0)
-
832 {
-
833 // just a mention
-
834 await TextReply("Hi!");
-
835 return;
-
836 }
-
837
-
838 var command = splits[0];
-
839 splits.RemoveAt(0);
-
840 var arguments = String.Join(" ", splits);
-
841
-
842 Tuple<ICommand, IChatTrackingContext?>? GetCommand(string command)
-
843 {
-
844 if (!builtinCommands.TryGetValue(command, out var handler))
-
845 return trackingContexts
-
846 .Where(trackingContext => trackingContext.Active)
-
847 .SelectMany(trackingContext => trackingContext.CustomCommands.Select(customCommand => Tuple.Create<ICommand, IChatTrackingContext?>(customCommand, trackingContext)))
-
848 .Where(tuple => tuple.Item1.Name.Equals(command, StringComparison.OrdinalIgnoreCase))
-
849 .FirstOrDefault();
-
850
-
851 return Tuple.Create<ICommand, IChatTrackingContext?>(handler, null);
-
852 }
-
853
-
854 const string UnknownCommandMessage = "TGS: Unknown command! Type '?' or 'help' for available commands.";
+
800 var mappingChannelRepresentation = mappedChannel.Value.Value.Channel;
+
801
+
802 message.User.Channel.RealId = mappingChannelRepresentation.RealId;
+
803 message.User.Channel.Tag = mappingChannelRepresentation.Tag;
+
804 message.User.Channel.IsAdminChannel = mappingChannelRepresentation.IsAdminChannel;
+
805 }
+
806
+
807 var trimmedMessage = message.Content.Trim();
+
808 if (trimmedMessage.Length == 0)
+
809 return;
+
810
+
811 var splits = new List<string>(trimmedMessage.Split(' ', StringSplitOptions.RemoveEmptyEntries));
+
812 var address = splits[0];
+
813 if (address.Length > 1 && (address.Last() == ':' || address.Last() == ','))
+
814 address = address[0..^1];
+
815
+
816 var addressed =
+
817 address.Equals(CommonMention, StringComparison.OrdinalIgnoreCase)
+
818 || address.Equals(provider.BotMention, StringComparison.OrdinalIgnoreCase);
+
819
+
820 // no mention
+
821 if (!addressed && !message.User.Channel.IsPrivateChannel)
+
822 return;
+
823
+
824 logger.LogTrace(
+
825 "Start processing command: {message}. User (True provider Id): {profiderId}",
+
826 message.Content,
+
827 JsonConvert.SerializeObject(message.User));
+
828 try
+
829 {
+
830 if (addressed)
+
831 splits.RemoveAt(0);
+
832
+
833 if (splits.Count == 0)
+
834 {
+
835 // just a mention
+
836 await TextReply("Hi!");
+
837 return;
+
838 }
+
839
+
840 var command = splits[0];
+
841 splits.RemoveAt(0);
+
842 var arguments = String.Join(" ", splits);
+
843
+
844 Tuple<ICommand, IChatTrackingContext?>? GetCommand(string command)
+
845 {
+
846 if (!builtinCommands.TryGetValue(command, out var handler))
+
847 return trackingContexts
+
848 .Where(trackingContext => trackingContext.Active)
+
849 .SelectMany(trackingContext => trackingContext.CustomCommands.Select(customCommand => Tuple.Create<ICommand, IChatTrackingContext?>(customCommand, trackingContext)))
+
850 .Where(tuple => tuple.Item1.Name.Equals(command, StringComparison.OrdinalIgnoreCase))
+
851 .FirstOrDefault();
+
852
+
853 return Tuple.Create<ICommand, IChatTrackingContext?>(handler, null);
+
854 }
855
-
856 if (command.Equals("help", StringComparison.OrdinalIgnoreCase) || command == "?")
-
857 {
-
858 string helpText;
-
859 if (splits.Count == 0)
-
860 {
-
861 var allCommands = builtinCommands.Select(x => x.Value).ToList();
-
862 allCommands.AddRange(
-
863 trackingContexts
-
864 .SelectMany(
-
865 x => x.CustomCommands));
-
866 helpText = String.Format(CultureInfo.InvariantCulture, "Available commands (Type '?' or 'help' and then a command name for more details): {0}", String.Join(", ", allCommands.Select(x => x.Name)));
-
867 }
-
868 else
-
869 {
-
870 var helpTuple = GetCommand(splits[0]);
-
871 if (helpTuple != default)
-
872 {
-
873 var (helpHandler, _) = helpTuple;
-
874 helpText = String.Format(CultureInfo.InvariantCulture, "{0}: {1}{2}", helpHandler.Name, helpHandler.HelpText, helpHandler.AdminOnly ? " - May only be used in admin channels" : String.Empty);
-
875 }
-
876 else
-
877 helpText = UnknownCommandMessage;
-
878 }
-
879
-
880 await TextReply(helpText);
-
881 return;
-
882 }
-
883
-
884 var tuple = GetCommand(command);
+
856 const string UnknownCommandMessage = "TGS: Unknown command! Type '?' or 'help' for available commands.";
+
857
+
858 if (command.Equals("help", StringComparison.OrdinalIgnoreCase) || command == "?")
+
859 {
+
860 string helpText;
+
861 if (splits.Count == 0)
+
862 {
+
863 var allCommands = builtinCommands.Select(x => x.Value).ToList();
+
864 allCommands.AddRange(
+
865 trackingContexts
+
866 .SelectMany(
+
867 x => x.CustomCommands));
+
868 helpText = String.Format(CultureInfo.InvariantCulture, "Available commands (Type '?' or 'help' and then a command name for more details): {0}", String.Join(", ", allCommands.Select(x => x.Name)));
+
869 }
+
870 else
+
871 {
+
872 var helpTuple = GetCommand(splits[0]);
+
873 if (helpTuple != default)
+
874 {
+
875 var (helpHandler, _) = helpTuple;
+
876 helpText = String.Format(CultureInfo.InvariantCulture, "{0}: {1}{2}", helpHandler.Name, helpHandler.HelpText, helpHandler.AdminOnly ? " - May only be used in admin channels" : String.Empty);
+
877 }
+
878 else
+
879 helpText = UnknownCommandMessage;
+
880 }
+
881
+
882 await TextReply(helpText);
+
883 return;
+
884 }
885
-
886 if (tuple == default)
-
887 {
-
888 await TextReply(UnknownCommandMessage);
-
889 return;
-
890 }
-
891
-
892 var (commandHandler, trackingContext) = tuple;
+
886 var tuple = GetCommand(command);
+
887
+
888 if (tuple == default)
+
889 {
+
890 await TextReply(UnknownCommandMessage);
+
891 return;
+
892 }
893
-
894 if (trackingContext?.Active == false)
-
895 {
-
896 await TextReply("TGS: The server is rebooting, please try again later");
-
897 return;
-
898 }
-
899
-
900 if (commandHandler.AdminOnly && !message.User.Channel.IsAdminChannel)
-
901 {
-
902 await TextReply("TGS: Use this command in an admin channel!");
-
903 return;
-
904 }
-
905
-
906 var result = await commandHandler.Invoke(arguments, message.User, cancellationToken);
-
907 if (result != null)
-
908 await SendMessage(new List<ulong> { message.User.Channel.RealId }, message, result, cancellationToken);
-
909 }
-
910 catch (OperationCanceledException ex)
-
911 {
-
912 logger.LogTrace(ex, "Command processing canceled!");
-
913 }
-
914 catch (Exception e)
-
915 {
-
916 // error bc custom commands should reply about why it failed
-
917 logger.LogError(e, "Error processing chat command");
-
918 await TextReply("TGS: Internal error processing command! Check server logs!");
-
919 }
-
920 finally
-
921 {
-
922 logger.LogTrace("Done processing command.");
-
923 }
-
924 }
+
894 var (commandHandler, trackingContext) = tuple;
+
895
+
896 if (trackingContext?.Active == false)
+
897 {
+
898 await TextReply("TGS: The server is rebooting, please try again later");
+
899 return;
+
900 }
+
901
+
902 if (commandHandler.AdminOnly && !message.User.Channel.IsAdminChannel)
+
903 {
+
904 await TextReply("TGS: Use this command in an admin channel!");
+
905 return;
+
906 }
+
907
+
908 var result = await commandHandler.Invoke(arguments, message.User, cancellationToken);
+
909 if (result != null)
+
910 await SendMessage(new List<ulong> { message.User.Channel.RealId }, message, result, cancellationToken);
+
911 }
+
912 catch (OperationCanceledException ex)
+
913 {
+
914 logger.LogTrace(ex, "Command processing canceled!");
+
915 }
+
916 catch (Exception e)
+
917 {
+
918 // error bc custom commands should reply about why it failed
+
919 logger.LogError(e, "Error processing chat command");
+
920 await TextReply("TGS: Internal error processing command! Check server logs!");
+
921 }
+
922 finally
+
923 {
+
924 logger.LogTrace("Done processing command.");
+
925 }
+
926 }
-
925
-
-
931 async Task MonitorMessages(CancellationToken cancellationToken)
-
932 {
-
933 logger.LogTrace("Starting processing loop...");
-
934 var messageTasks = new Dictionary<IProvider, Task<Message?>>();
-
935 ValueTask activeProcessingTask = ValueTask.CompletedTask;
-
936 try
-
937 {
-
938 Task? updatedTask = null;
-
939 while (!cancellationToken.IsCancellationRequested)
-
940 {
-
941 if (updatedTask?.IsCompleted != false)
-
942 lock (synchronizationLock)
-
943 updatedTask = connectionsUpdated.Task;
-
944
-
945 // prune disconnected providers
-
946 foreach (var disposedProviderMessageTaskKvp in messageTasks.Where(x => x.Key.Disposed).ToList())
-
947 messageTasks.Remove(disposedProviderMessageTaskKvp.Key);
-
948
-
949 // add new ones
-
950 lock (providers)
-
951 foreach (var providerKvp in providers)
-
952 if (!messageTasks.ContainsKey(providerKvp.Value))
-
953 messageTasks.Add(
-
954 providerKvp.Value,
-
955 providerKvp.Value.NextMessage(cancellationToken));
-
956
-
957 if (messageTasks.Count == 0)
-
958 {
-
959 logger.LogTrace("No providers active, pausing messsage monitoring...");
-
960 await updatedTask.WaitAsync(cancellationToken);
-
961 logger.LogTrace("Resuming message monitoring...");
-
962 continue;
-
963 }
-
964
-
965 // wait for a message
-
966 await Task.WhenAny(updatedTask, Task.WhenAny(messageTasks.Select(x => x.Value)));
-
967
-
968 // process completed ones
-
969 foreach (var completedMessageTaskKvp in messageTasks.Where(x => x.Value.IsCompleted).ToList())
-
970 {
-
971 var provider = completedMessageTaskKvp.Key;
-
972 messageTasks.Remove(provider);
-
973
-
974 if (provider.Disposed) // valid to receive one, but don't process it
-
975 continue;
-
976
-
977 var message = await completedMessageTaskKvp.Value;
-
978 var messageNumber = Interlocked.Increment(ref messagesProcessed);
-
979
-
980 async ValueTask WrapProcessMessage()
-
981 {
-
982 var localActiveProcessingTask = activeProcessingTask;
-
983 using (LogContext.PushProperty(SerilogContextHelper.ChatMessageIterationContextProperty, messageNumber))
-
984 try
-
985 {
-
986 await ProcessMessage(provider, message, false, cancellationToken);
-
987 }
-
988 catch (Exception ex)
-
989 {
-
990 logger.LogError(ex, "Error processing message {messageNumber}!", messageNumber);
-
991 }
-
992
-
993 await localActiveProcessingTask;
-
994 }
-
995
-
996 activeProcessingTask = WrapProcessMessage();
-
997 }
-
998 }
-
999 }
-
1000 catch (OperationCanceledException ex)
-
1001 {
-
1002 logger.LogTrace(ex, "Message processing loop cancelled!");
-
1003 }
-
1004 catch (Exception e)
-
1005 {
-
1006 logger.LogError(e, "Message loop crashed!");
-
1007 }
-
1008 finally
-
1009 {
-
1010 await activeProcessingTask;
-
1011 }
-
1012
-
1013 logger.LogTrace("Leaving message processing loop");
-
1014 }
+
927
+
+
933 async Task MonitorMessages(CancellationToken cancellationToken)
+
934 {
+
935 logger.LogTrace("Starting processing loop...");
+
936 var messageTasks = new Dictionary<IProvider, Task<Message?>>();
+
937 ValueTask activeProcessingTask = ValueTask.CompletedTask;
+
938 try
+
939 {
+
940 Task? updatedTask = null;
+
941 while (!cancellationToken.IsCancellationRequested)
+
942 {
+
943 if (updatedTask?.IsCompleted != false)
+
944 lock (synchronizationLock)
+
945 updatedTask = connectionsUpdated.Task;
+
946
+
947 // prune disconnected providers
+
948 foreach (var disposedProviderMessageTaskKvp in messageTasks.Where(x => x.Key.Disposed).ToList())
+
949 messageTasks.Remove(disposedProviderMessageTaskKvp.Key);
+
950
+
951 // add new ones
+
952 lock (providers)
+
953 foreach (var providerKvp in providers)
+
954 if (!messageTasks.ContainsKey(providerKvp.Value))
+
955 messageTasks.Add(
+
956 providerKvp.Value,
+
957 providerKvp.Value.NextMessage(cancellationToken));
+
958
+
959 if (messageTasks.Count == 0)
+
960 {
+
961 logger.LogTrace("No providers active, pausing messsage monitoring...");
+
962 await updatedTask.WaitAsync(cancellationToken);
+
963 logger.LogTrace("Resuming message monitoring...");
+
964 continue;
+
965 }
+
966
+
967 // wait for a message
+
968 await Task.WhenAny(updatedTask, Task.WhenAny(messageTasks.Select(x => x.Value)));
+
969
+
970 // process completed ones
+
971 foreach (var completedMessageTaskKvp in messageTasks.Where(x => x.Value.IsCompleted).ToList())
+
972 {
+
973 var provider = completedMessageTaskKvp.Key;
+
974 messageTasks.Remove(provider);
+
975
+
976 if (provider.Disposed) // valid to receive one, but don't process it
+
977 continue;
+
978
+
979 var message = await completedMessageTaskKvp.Value;
+
980 var messageNumber = Interlocked.Increment(ref messagesProcessed);
+
981
+
982 async ValueTask WrapProcessMessage()
+
983 {
+
984 var localActiveProcessingTask = activeProcessingTask;
+
985 using (LogContext.PushProperty(SerilogContextHelper.ChatMessageIterationContextProperty, messageNumber))
+
986 try
+
987 {
+
988 await ProcessMessage(provider, message, false, cancellationToken);
+
989 }
+
990 catch (Exception ex)
+
991 {
+
992 logger.LogError(ex, "Error processing message {messageNumber}!", messageNumber);
+
993 }
+
994
+
995 await localActiveProcessingTask;
+
996 }
+
997
+
998 activeProcessingTask = WrapProcessMessage();
+
999 }
+
1000 }
+
1001 }
+
1002 catch (OperationCanceledException ex)
+
1003 {
+
1004 logger.LogTrace(ex, "Message processing loop cancelled!");
+
1005 }
+
1006 catch (Exception e)
+
1007 {
+
1008 logger.LogError(e, "Message loop crashed!");
+
1009 }
+
1010 finally
+
1011 {
+
1012 await activeProcessingTask;
+
1013 }
+
1014
+
1015 logger.LogTrace("Leaving message processing loop");
+
1016 }
-
1015
-
-
1024 ValueTask SendMessage(IEnumerable<ulong> channelIds, Message? replyTo, MessageContent message, CancellationToken cancellationToken)
-
1025 {
-
1026 var channelIdsList = channelIds.ToList();
-
1027
-
1028 logger.LogTrace(
-
1029 "Chat send \"{message}\"{embed} to channels: [{channelIdsCommaSeperated}]",
-
1030 message.Text,
-
1031 message.Embed != null ? " (with embed)" : String.Empty,
-
1032 String.Join(", ", channelIdsList));
-
1033
-
1034 if (channelIdsList.Count == 0)
-
1035 return ValueTask.CompletedTask;
-
1036
- -
1038 channelIdsList.Select(x =>
-
1039 {
-
1040 ChannelMapping? channelMapping;
-
1041 lock (mappedChannels)
-
1042 if (!mappedChannels.TryGetValue(x, out channelMapping))
-
1043 return ValueTask.CompletedTask;
-
1044 IProvider? provider;
-
1045 lock (providers)
-
1046 if (!providers.TryGetValue(channelMapping.ProviderId, out provider))
-
1047 return ValueTask.CompletedTask;
-
1048 return provider.SendMessage(replyTo, message, channelMapping.ProviderChannelId, cancellationToken);
-
1049 }));
-
1050 }
+
1017
+
+
1026 ValueTask SendMessage(IEnumerable<ulong> channelIds, Message? replyTo, MessageContent message, CancellationToken cancellationToken)
+
1027 {
+
1028 var channelIdsList = channelIds.ToList();
+
1029
+
1030 logger.LogTrace(
+
1031 "Chat send \"{message}\"{embed} to channels: [{channelIdsCommaSeperated}]",
+
1032 message.Text,
+
1033 message.Embed != null ? " (with embed)" : String.Empty,
+
1034 String.Join(", ", channelIdsList));
+
1035
+
1036 if (channelIdsList.Count == 0)
+
1037 return ValueTask.CompletedTask;
+
1038
+ +
1040 channelIdsList.Select(x =>
+
1041 {
+
1042 ChannelMapping? channelMapping;
+
1043 lock (mappedChannels)
+
1044 if (!mappedChannels.TryGetValue(x, out channelMapping))
+
1045 return ValueTask.CompletedTask;
+
1046 IProvider? provider;
+
1047 lock (providers)
+
1048 if (!providers.TryGetValue(channelMapping.ProviderId, out provider))
+
1049 return ValueTask.CompletedTask;
+
1050 return provider.SendMessage(replyTo, message, channelMapping.ProviderChannelId, cancellationToken);
+
1051 }));
+
1052 }
-
1051
-
- -
1057 {
-
1058 await Task.WhenAll(providers.Select(x => x.Value.InitialConnectionJob));
-
1059 logger.LogTrace("Initial provider connection task completed");
-
1060 }
+
1053
+
+ +
1059 {
+
1060 await Task.WhenAll(providers.Select(x => x.Value.InitialConnectionJob));
+
1061 logger.LogTrace("Initial provider connection task completed");
+
1062 }
-
1061
-
-
1066 void AddMessageTask(Task task)
-
1067 {
-
1068 async Task Wrap(Task originalTask)
-
1069 {
-
1070 await originalTask;
-
1071 try
-
1072 {
-
1073 await task;
-
1074 }
-
1075 catch (OperationCanceledException ex)
-
1076 {
-
1077 logger.LogDebug(ex, "Async chat message cancelled!");
-
1078 }
-
1079 catch (Exception ex)
-
1080 {
-
1081 logger.LogError(ex, "Error in asynchronous chat message!");
-
1082 }
-
1083 }
-
1084
-
1085 lock (handlerCts)
-
1086 messageSendTask = Wrap(messageSendTask);
-
1087 }
+
1063
+
+
1068 void AddMessageTask(Task task)
+
1069 {
+
1070 async Task Wrap(Task originalTask)
+
1071 {
+
1072 await originalTask;
+
1073 try
+
1074 {
+
1075 await task;
+
1076 }
+
1077 catch (OperationCanceledException ex)
+
1078 {
+
1079 logger.LogDebug(ex, "Async chat message cancelled!");
+
1080 }
+
1081 catch (Exception ex)
+
1082 {
+
1083 logger.LogError(ex, "Error in asynchronous chat message!");
+
1084 }
+
1085 }
+
1086
+
1087 lock (handlerCts)
+
1088 messageSendTask = Wrap(messageSendTask);
+
1089 }
-
1088
-
-
1095 void QueueMessageInternal(MessageContent message, Func<IEnumerable<ulong>> channelIdsFactory, bool waitForConnections)
-
1096 {
-
1097 async Task SendMessageTask()
-
1098 {
-
1099 var cancellationToken = handlerCts.Token;
-
1100 if (waitForConnections)
-
1101 await initialProviderConnectionsTask!.WaitAsync(cancellationToken);
-
1102
-
1103 await SendMessage(
-
1104 channelIdsFactory(),
-
1105 null,
-
1106 message,
-
1107 cancellationToken);
-
1108 }
-
1109
-
1110 AddMessageTask(SendMessageTask());
-
1111 }
+
1090
+
+
1097 void QueueMessageInternal(MessageContent message, Func<IEnumerable<ulong>> channelIdsFactory, bool waitForConnections)
+
1098 {
+
1099 async Task SendMessageTask()
+
1100 {
+
1101 var cancellationToken = handlerCts.Token;
+
1102 if (waitForConnections)
+
1103 await initialProviderConnectionsTask!.WaitAsync(cancellationToken);
+
1104
+
1105 await SendMessage(
+
1106 channelIdsFactory(),
+
1107 null,
+
1108 message,
+
1109 cancellationToken);
+
1110 }
+
1111
+
1112 AddMessageTask(SendMessageTask());
+
1113 }
-
1112 }
+
1114 }
-
1113}
+
1115}
Information about an engine installation.
Extension methods for the ValueTask and ValueTask<TResult> classes.
@@ -1120,44 +1122,44 @@ $(document).ready(function() { init_codefold(0); });
const string CommonMention
The common bot mention.
long messagesProcessed
The number of Messages processed.
readonly IProviderFactory providerFactory
The IProviderFactory for the ChatManager.
-
ValueTask SendMessage(IEnumerable< ulong > channelIds, Message? replyTo, MessageContent message, CancellationToken cancellationToken)
Asynchronously send a given message to a set of channelIds .
+
ValueTask SendMessage(IEnumerable< ulong > channelIds, Message? replyTo, MessageContent message, CancellationToken cancellationToken)
Asynchronously send a given message to a set of channelIds .
readonly object synchronizationLock
Used for various lock statements throughout this class.
ChatManager(IProviderFactory providerFactory, ICommandFactory commandFactory, IServerControl serverControl, ILoggerFactory loggerFactory, ILogger< ChatManager > logger, IEnumerable< Models.ChatBot > initialChatBots)
Initializes a new instance of the ChatManager class.
Task? initialProviderConnectionsTask
A Task that represents the IProviders initial connection.
-
ValueTask HandleRestart(Version? updateVersion, bool handlerMayDelayShutdownWithExtremelyLongRunningTasks, CancellationToken cancellationToken)
Handle a restart of the server.A ValueTask representing the running operation.
-
void QueueMessageInternal(MessageContent message, Func< IEnumerable< ulong > > channelIdsFactory, bool waitForConnections)
Adds a given message to the send queue.
+
ValueTask HandleRestart(Version? updateVersion, bool handlerMayDelayShutdownWithExtremelyLongRunningTasks, CancellationToken cancellationToken)
Handle a restart of the server.A ValueTask representing the running operation.
+
void QueueMessageInternal(MessageContent message, Func< IEnumerable< ulong > > channelIdsFactory, bool waitForConnections)
Adds a given message to the send queue.
readonly List< Models.ChatBot > activeChatBots
The active Models.ChatBot for the ChatManager.
-
async Task InitialConnection()
Aggregate all IProvider.InitialConnectionJobs into one <sse cref="Task">.
-
async ValueTask RemapProvider(IProvider provider, CancellationToken cancellationToken)
Remap the channels for a given provider .
-
async ValueTask ProcessMessage(IProvider provider, Message? message, bool recursed, CancellationToken cancellationToken)
Processes a message .
+
async Task InitialConnection()
Aggregate all IProvider.InitialConnectionJobs into one <sse cref="Task">.
+
async ValueTask RemapProvider(IProvider provider, CancellationToken cancellationToken)
Remap the channels for a given provider .
+
async ValueTask ProcessMessage(IProvider provider, Message? message, bool recursed, CancellationToken cancellationToken)
Processes a message .
readonly Dictionary< long, IProvider > providers
Map of IProviders in use, keyed by ChatBotSettings EntityId.Id.
-
async Task MonitorMessages(CancellationToken cancellationToken)
Monitors active providers for new Messages.
+
async Task MonitorMessages(CancellationToken cancellationToken)
Monitors active providers for new Messages.
ICustomCommandHandler? customCommandHandler
The ICustomCommandHandler for the ChangeChannels(long, IEnumerable<Models.ChatChannel>,...
async ValueTask ChangeChannels(long connectionId, IEnumerable< Models.ChatChannel > newChannels, CancellationToken cancellationToken)
readonly Dictionary< string, ICommand > builtinCommands
Unchanging ICommands in the ChatManager mapped by ICommand.Name.
void QueueMessage(MessageContent message, IEnumerable< ulong > channelIds)
Queue a chat message to a given set of channelIds .
readonly ILoggerFactory loggerFactory
The ILoggerFactory for the ChatManager.
-
Func< string?, string, Action< bool > > QueueDeploymentMessage(Models.RevisionInformation revisionInformation, EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, bool localCommitPushed)
Send the message for a deployment to configured deployment channels.A Func<T1, T2,...
Task messageSendTask
A Task that represents all sent messages.
Task? chatHandler
The Task that monitors incoming chat messages.
async ValueTask ChangeSettings(Models.ChatBot newSettings, CancellationToken cancellationToken)
Change chat settings. If the Api.Models.EntityId.Id is not currently in use, a new connection will be...
ulong channelIdCounter
Used for remapping ChannelRepresentation.RealIds.
readonly ICommandFactory commandFactory
The ICommandFactory for the ChatManager.
-
IChatTrackingContext CreateTrackingContext()
Start tracking Commands.CustomCommands and ChannelRepresentations.A new IChatTrackingContext.
-
void AddMessageTask(Task task)
Adds a given task to messageSendTask.
+
IChatTrackingContext CreateTrackingContext()
Start tracking Commands.CustomCommands and ChannelRepresentations.A new IChatTrackingContext.
+
void AddMessageTask(Task task)
Adds a given task to messageSendTask.
readonly ILogger< ChatManager > logger
The ILogger for the ChatManager.
readonly ConcurrentDictionary< long, SemaphoreSlim > changeChannelSemaphores
Map of SemaphoreSlims used to guard concurrent access to ChangeChannels(long, IEnumerable<Models....
readonly IRestartRegistration restartRegistration
The IRestartRegistration for the ChatManager.
void QueueWatchdogMessage(string message)
Queue a chat message to configured watchdog channels.
readonly CancellationTokenSource handlerCts
The CancellationTokenSource for chatHandler.
-
async Task StartAsync(CancellationToken cancellationToken)
-
void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
Registers a customCommandHandler to use.
+
async Task StartAsync(CancellationToken cancellationToken)
+
void RegisterCommandHandler(ICustomCommandHandler customCommandHandler)
Registers a customCommandHandler to use.
TaskCompletionSource connectionsUpdated
The TaskCompletionSource that completes when ChatBotSettingss change.
-
async ValueTask UpdateTrackingContexts(CancellationToken cancellationToken)
Force an update with the active channels on all active IChatTrackingContexts.A ValueTask representing...
-
async Task DeleteConnection(long connectionId, CancellationToken cancellationToken)
Disconnects and deletes a given connection.A Task representing the running operation.
-
async ValueTask< IProvider?> RemoveProviderChannels(long connectionId, bool removeProvider, CancellationToken cancellationToken)
Remove a IProvider from mappedChannels optionally removing the provider itself from providers and upd...
-
async Task StopAsync(CancellationToken cancellationToken)
+
async ValueTask UpdateTrackingContexts(CancellationToken cancellationToken)
Force an update with the active channels on all active IChatTrackingContexts.A ValueTask representing...
+
async Task DeleteConnection(long connectionId, CancellationToken cancellationToken)
Disconnects and deletes a given connection.A Task representing the running operation.
+
Func< string?, string, Action< bool > > QueueDeploymentMessage(Models.RevisionInformation revisionInformation, Models.RevisionInformation? previousRevisionInformation, EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, bool localCommitPushed)
Send the message for a deployment to configured deployment channels.A Func<T1, T2,...
+
async ValueTask< IProvider?> RemoveProviderChannels(long connectionId, bool removeProvider, CancellationToken cancellationToken)
Remove a IProvider from mappedChannels optionally removing the provider itself from providers and upd...
+
async Task StopAsync(CancellationToken cancellationToken)
readonly List< IChatTrackingContext > trackingContexts
The active IChatTrackingContexts for the ChatManager.
readonly Dictionary< ulong, ChannelMapping > mappedChannels
Map of ChannelRepresentation.RealIds to ChannelMappings.
diff --git a/_discord_provider_8cs.html b/_discord_provider_8cs.html index 2e37d36288..25468f7041 100644 --- a/_discord_provider_8cs.html +++ b/_discord_provider_8cs.html @@ -74,11 +74,263 @@ $(function() {
+
DiscordProvider.cs File Reference

Go to the source code of this file.

+ + + + + +

+Classes

class  Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
 IProvider for the Discord app. More...
 
+ + + + + + + + + + + + + +

+Namespaces

namespace  Tgstation
 
namespace  Tgstation.Server
 
namespace  Tgstation.Server.Host
 
namespace  Tgstation.Server.Host.Components
 
namespace  Tgstation.Server.Host.Components.Chat
 
namespace  Tgstation.Server.Host.Components.Chat.Providers
 
+ + + + + + + + +

+Functions

 Tgstation.Server.Host.Components.Chat.Providers.if (removedTestMerges.Count !=0) fields.Add(new EmbedField("Removed
 
 Tgstation.Server.Host.Components.Chat.Providers.[instance initializer]
 
Optional< IReadOnlyList< IEmbed > > ConvertEmbed (ChatEmbed? embed)
 Convert a ChatEmbed to an IEmbed parameters.
 
+ + + +

+Variables

 Tgstation.Server.Host.Components.Chat.Providers.false
 
+

Function Documentation

+ +

◆ ConvertEmbed()

+ +
+
+ + + + + + + + +
Optional< IReadOnlyList< IEmbed > > ConvertEmbed (ChatEmbedembed)
+
+ +

Convert a ChatEmbed to an IEmbed parameters.

+
Parameters
+ + +
embedThe ChatEmbed to convert.
+
+
+
Returns
The parameter for sending a single IEmbed.
+ +

Definition at line 1019 of file DiscordProvider.cs.

+
1020 {
+
1021 if (embed == null)
+
1022 return default;
+
1023
+
1024 var embedErrors = new List<string>();
+
1025 Optional<Color> colour = default;
+
1026 if (embed.Colour != null)
+
1027 if (Int32.TryParse(embed.Colour[1..], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var argb))
+
1028 colour = Color.FromArgb(argb);
+
1029 else
+
1030 embedErrors.Add(
+
1031 String.Format(
+
1032 CultureInfo.InvariantCulture,
+
1033 "Invalid embed colour: {0}",
+
1034 embed.Colour));
+
1035
+
1036 if (embed.Author != null && String.IsNullOrWhiteSpace(embed.Author.Name))
+
1037 {
+
1038 embedErrors.Add("Null or whitespace embed author name!");
+
1039 embed.Author = null;
+
1040 }
+
1041
+
1042 List<IEmbedField>? fields = null;
+
1043 if (embed.Fields != null)
+
1044 {
+
1045 fields = new List<IEmbedField>();
+
1046 var i = -1;
+
1047 foreach (var field in embed.Fields)
+
1048 {
+
1049 ++i;
+
1050 var invalid = false;
+
1051 if (String.IsNullOrWhiteSpace(field.Name))
+
1052 {
+
1053 embedErrors.Add(
+
1054 String.Format(
+
1055 CultureInfo.InvariantCulture,
+
1056 "Null or whitespace field name at index {0}!",
+
1057 i));
+
1058 invalid = true;
+
1059 }
+
1060
+
1061 if (String.IsNullOrWhiteSpace(field.Value))
+
1062 {
+
1063 embedErrors.Add(
+
1064 String.Format(
+
1065 CultureInfo.InvariantCulture,
+
1066 "Null or whitespace field value at index {0}!",
+
1067 i));
+
1068 invalid = true;
+
1069 }
+
1070
+
1071 if (invalid)
+
1072 continue;
+
1073
+
1074 fields.Add(new EmbedField(field.Name!, field.Value!)
+
1075 {
+
1076 IsInline = field.IsInline ?? default(Optional<bool>),
+
1077 });
+
1078 }
+
1079 }
+
1080
+
1081 if (embed.Footer != null && String.IsNullOrWhiteSpace(embed.Footer.Text))
+
1082 {
+
1083 embedErrors.Add("Null or whitespace embed footer text!");
+
1084 embed.Footer = null;
+
1085 }
+
1086
+
1087 if (embed.Image != null && String.IsNullOrWhiteSpace(embed.Image.Url))
+
1088 {
+
1089 embedErrors.Add("Null or whitespace embed image url!");
+
1090 embed.Image = null;
+
1091 }
+
1092
+
1093 if (embed.Thumbnail != null && String.IsNullOrWhiteSpace(embed.Thumbnail.Url))
+
1094 {
+
1095 embedErrors.Add("Null or whitespace embed thumbnail url!");
+
1096 embed.Thumbnail = null;
+
1097 }
+
1098
+
1099 Optional<DateTimeOffset> timestampOptional = default;
+
1100 if (embed.Timestamp != null)
+
1101 if (DateTimeOffset.TryParse(embed.Timestamp, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var timestamp))
+
1102 timestampOptional = timestamp.ToUniversalTime();
+
1103 else
+
1104 embedErrors.Add(
+
1105 String.Format(
+
1106 CultureInfo.InvariantCulture,
+
1107 "Invalid embed timestamp: {0}",
+
1108 embed.Timestamp));
+
1109
+
1110 var discordEmbed = new Embed
+
1111 {
+
1112 Author = embed.Author != null
+
1113 ? new EmbedAuthor(embed.Author.Name!)
+
1114 {
+
1115 IconUrl = embed.Author.IconUrl ?? default(Optional<string>),
+
1116 ProxyIconUrl = embed.Author.ProxyIconUrl ?? default(Optional<string>),
+
1117 Url = embed.Author.Url ?? default(Optional<string>),
+
1118 }
+
1119 : default(Optional<IEmbedAuthor>),
+
1120 Colour = colour,
+
1121 Description = embed.Description ?? default(Optional<string>),
+
1122 Fields = fields ?? default(Optional<IReadOnlyList<IEmbedField>>),
+
1123 Footer = embed.Footer != null
+
1124 ? (Optional<IEmbedFooter>)new EmbedFooter(embed.Footer.Text!)
+
1125 {
+
1126 IconUrl = embed.Footer.IconUrl ?? default(Optional<string>),
+
1127 ProxyIconUrl = embed.Footer.ProxyIconUrl ?? default(Optional<string>),
+
1128 }
+
1129 : default,
+
1130 Image = embed.Image != null
+
1131 ? new EmbedImage(embed.Image.Url!)
+
1132 {
+
1133 Width = embed.Image.Width ?? default(Optional<int>),
+
1134 Height = embed.Image.Height ?? default(Optional<int>),
+
1135 ProxyUrl = embed.Image.ProxyUrl ?? default(Optional<string>),
+
1136 }
+
1137 : default(Optional<IEmbedImage>),
+
1138 Provider = embed.Provider != null
+
1139 ? new EmbedProvider
+
1140 {
+
1141 Name = embed.Provider.Name ?? default(Optional<string>),
+
1142 Url = embed.Provider.Url ?? default(Optional<string>),
+
1143 }
+
1144 : default(Optional<IEmbedProvider>),
+
1145 Thumbnail = embed.Thumbnail != null
+
1146 ? new EmbedThumbnail(embed.Thumbnail.Url!)
+
1147 {
+
1148 Width = embed.Thumbnail.Width ?? default(Optional<int>),
+
1149 Height = embed.Thumbnail.Height ?? default(Optional<int>),
+
1150 ProxyUrl = embed.Thumbnail.ProxyUrl ?? default(Optional<string>),
+
1151 }
+
1152 : default(Optional<IEmbedThumbnail>),
+
1153 Timestamp = timestampOptional,
+
1154 Title = embed.Title ?? default(Optional<string>),
+
1155 Url = embed.Url ?? default(Optional<string>),
+
1156 Video = embed.Video != null
+
1157 ? new EmbedVideo
+
1158 {
+
1159 Url = embed.Video.Url ?? default(Optional<string>),
+
1160 Width = embed.Video.Width ?? default(Optional<int>),
+
1161 Height = embed.Video.Height ?? default(Optional<int>),
+
1162 ProxyUrl = embed.Video.ProxyUrl ?? default(Optional<string>),
+
1163 }
+
1164 : default(Optional<IEmbedVideo>),
+
1165 };
+
1166
+
1167 var result = new List<IEmbed> { discordEmbed };
+
1168
+
1169 if (embedErrors.Count > 0)
+
1170 {
+
1171 var joinedErrors = String.Join(Environment.NewLine, embedErrors);
+
1172 Logger.LogError("Embed description contains errors:{newLine}{issues}", Environment.NewLine, joinedErrors);
+
1173 result.Add(new Embed
+
1174 {
+
1175 Title = "TGS Embed Errors",
+
1176 Description = joinedErrors,
+
1177 Colour = Color.Red,
+
1178 Footer = new EmbedFooter("Please report this to your codebase's maintainers."),
+
1179 Timestamp = DateTimeOffset.UtcNow,
+
1180 });
+
1181 }
+
1182
+
1183 return result;
+
1184 }
+
@ Optional
DMAPI validation is performed but not required for the deployment to succeed.
+
+

References Tgstation.Server.Host.Components.Interop.ChatEmbed.Author, Tgstation.Server.Host.Components.Interop.ChatEmbed.Colour, Tgstation.Server.Host.Components.Interop.ChatEmbed.Fields, Tgstation.Server.Host.Components.Interop.ChatEmbed.Footer, Tgstation.Server.Host.Components.Interop.ChatEmbed.Image, Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger, Tgstation.Server.Host.Components.Interop.ChatEmbedProvider.Name, Tgstation.Server.Api.Models.Optional, Tgstation.Server.Host.Components.Interop.ChatEmbedFooter.Text, Tgstation.Server.Host.Components.Interop.ChatEmbed.Thumbnail, Tgstation.Server.Host.Components.Interop.ChatEmbed.Timestamp, and Tgstation.Server.Host.Components.Interop.ChatEmbedMedia.Url.

+ +

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendMessage().

+
+Here is the caller graph for this function:
+
+
+ + + + + +
+ +
+
-
443#pragma warning restore CA1506
-
444
-
-
451 async ValueTask<TimeSpan?> CalculateExpectedDeploymentTime(IDatabaseContext databaseContext, CancellationToken cancellationToken)
-
452 {
-
453 var previousCompileJobs = await databaseContext
- -
455 .AsQueryable()
-
456 .Where(x => x.Job.Instance!.Id == metadata.Id)
-
457 .OrderByDescending(x => x.Job.StoppedAt)
-
458 .Take(10)
-
459 .Select(x => new
-
460 {
-
461 StoppedAt = x.Job.StoppedAt!.Value,
-
462 StartedAt = x.Job.StartedAt!.Value,
-
463 })
-
464 .ToListAsync(cancellationToken);
-
465
-
466 TimeSpan? averageSpan = null;
-
467 if (previousCompileJobs.Count != 0)
-
468 {
-
469 var totalSpan = TimeSpan.Zero;
-
470 foreach (var previousCompileJob in previousCompileJobs)
-
471 totalSpan += previousCompileJob.StoppedAt - previousCompileJob.StartedAt;
-
472 averageSpan = totalSpan / previousCompileJobs.Count;
-
473 }
-
474
-
475 return averageSpan;
-
476 }
+
447#pragma warning restore CA1506
+
448
+
+
455 async ValueTask<TimeSpan?> CalculateExpectedDeploymentTime(IDatabaseContext databaseContext, CancellationToken cancellationToken)
+
456 {
+
457 var previousCompileJobs = await databaseContext
+ +
459 .AsQueryable()
+
460 .Where(x => x.Job.Instance!.Id == metadata.Id)
+
461 .OrderByDescending(x => x.Job.StoppedAt)
+
462 .Take(10)
+
463 .Select(x => new
+
464 {
+
465 StoppedAt = x.Job.StoppedAt!.Value,
+
466 StartedAt = x.Job.StartedAt!.Value,
+
467 })
+
468 .ToListAsync(cancellationToken);
+
469
+
470 TimeSpan? averageSpan = null;
+
471 if (previousCompileJobs.Count != 0)
+
472 {
+
473 var totalSpan = TimeSpan.Zero;
+
474 foreach (var previousCompileJob in previousCompileJobs)
+
475 totalSpan += previousCompileJob.StoppedAt - previousCompileJob.StartedAt;
+
476 averageSpan = totalSpan / previousCompileJobs.Count;
+
477 }
+
478
+
479 return averageSpan;
+
480 }
-
477
-
-
492 async ValueTask<Models.CompileJob> Compile(
-
493 Models.Job job,
-
494 Models.RevisionInformation revisionInformation,
-
495 Api.Models.Internal.DreamMakerSettings dreamMakerSettings,
-
496 DreamDaemonLaunchParameters launchParameters,
-
497 IRepository repository,
-
498 IRemoteDeploymentManager remoteDeploymentManager,
-
499 JobProgressReporter progressReporter,
-
500 TimeSpan? estimatedDuration,
-
501 bool localCommitExistsOnRemote,
-
502 CancellationToken cancellationToken)
-
503 {
-
504 logger.LogTrace("Begin Compile");
-
505
-
506 using var progressCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
-
507
-
508 progressReporter.StageName = "Reserving BYOND version";
-
509 var progressTask = ProgressTask(progressReporter, estimatedDuration, progressCts.Token);
-
510 try
-
511 {
-
512 using var engineLock = await engineManager.UseExecutables(null, null, cancellationToken);
- -
514 revisionInformation,
-
515 engineLock.Version,
-
516 DateTimeOffset.UtcNow + estimatedDuration,
-
517 repository.RemoteRepositoryOwner,
-
518 repository.RemoteRepositoryName,
-
519 localCommitExistsOnRemote);
-
520
-
521 var compileJob = new Models.CompileJob(job, revisionInformation, engineLock.Version.ToString())
-
522 {
-
523 DirectoryName = Guid.NewGuid(),
-
524 DmeName = dreamMakerSettings.ProjectName,
-
525 RepositoryOrigin = repository.Origin.ToString(),
-
526 };
+
481
+
+
497 async ValueTask<Models.CompileJob> Compile(
+
498 Models.Job job,
+
499 Models.CompileJob? oldCompileJob,
+
500 Models.RevisionInformation revisionInformation,
+
501 Api.Models.Internal.DreamMakerSettings dreamMakerSettings,
+
502 DreamDaemonLaunchParameters launchParameters,
+
503 IRepository repository,
+
504 IRemoteDeploymentManager remoteDeploymentManager,
+
505 JobProgressReporter progressReporter,
+
506 TimeSpan? estimatedDuration,
+
507 bool localCommitExistsOnRemote,
+
508 CancellationToken cancellationToken)
+
509 {
+
510 logger.LogTrace("Begin Compile");
+
511
+
512 using var progressCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+
513
+
514 progressReporter.StageName = "Reserving BYOND version";
+
515 var progressTask = ProgressTask(progressReporter, estimatedDuration, progressCts.Token);
+
516 try
+
517 {
+
518 using var engineLock = await engineManager.UseExecutables(null, null, cancellationToken);
+ +
520 revisionInformation,
+
521 oldCompileJob?.RevisionInformation,
+
522 engineLock.Version,
+
523 DateTimeOffset.UtcNow + estimatedDuration,
+
524 repository.RemoteRepositoryOwner,
+
525 repository.RemoteRepositoryName,
+
526 localCommitExistsOnRemote);
527
-
528 progressReporter.StageName = "Creating remote deployment notification";
-
529 await remoteDeploymentManager.StartDeployment(
-
530 repository,
-
531 compileJob,
-
532 cancellationToken);
-
533
-
534 logger.LogTrace("Deployment will timeout at {timeoutTime}", DateTimeOffset.UtcNow + dreamMakerSettings.Timeout!.Value);
-
535 using var timeoutTokenSource = new CancellationTokenSource(dreamMakerSettings.Timeout.Value);
-
536 var timeoutToken = timeoutTokenSource.Token;
-
537 using (timeoutToken.Register(() => logger.LogWarning("Deployment timed out!")))
-
538 {
-
539 using var combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken, cancellationToken);
-
540 try
-
541 {
-
542 await RunCompileJob(
-
543 progressReporter,
-
544 compileJob,
-
545 dreamMakerSettings,
-
546 launchParameters,
-
547 engineLock,
-
548 repository,
-
549 remoteDeploymentManager,
-
550 combinedTokenSource.Token);
-
551 }
-
552 catch (OperationCanceledException) when (timeoutToken.IsCancellationRequested)
-
553 {
-
554 throw new JobException(ErrorCode.DeploymentTimeout);
-
555 }
-
556 }
-
557
-
558 return compileJob;
-
559 }
-
560 catch (OperationCanceledException)
-
561 {
-
562 // DCT: Cancellation token is for job, delaying here is fine
-
563 progressReporter.StageName = "Running CompileCancelled event";
-
564 await eventConsumer.HandleEvent(EventType.CompileCancelled, Enumerable.Empty<string>(), true, CancellationToken.None);
-
565 throw;
+
528 var compileJob = new Models.CompileJob(job, revisionInformation, engineLock.Version.ToString())
+
529 {
+
530 DirectoryName = Guid.NewGuid(),
+
531 DmeName = dreamMakerSettings.ProjectName,
+
532 RepositoryOrigin = repository.Origin.ToString(),
+
533 };
+
534
+
535 progressReporter.StageName = "Creating remote deployment notification";
+
536 await remoteDeploymentManager.StartDeployment(
+
537 repository,
+
538 compileJob,
+
539 cancellationToken);
+
540
+
541 logger.LogTrace("Deployment will timeout at {timeoutTime}", DateTimeOffset.UtcNow + dreamMakerSettings.Timeout!.Value);
+
542 using var timeoutTokenSource = new CancellationTokenSource(dreamMakerSettings.Timeout.Value);
+
543 var timeoutToken = timeoutTokenSource.Token;
+
544 using (timeoutToken.Register(() => logger.LogWarning("Deployment timed out!")))
+
545 {
+
546 using var combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutToken, cancellationToken);
+
547 try
+
548 {
+
549 await RunCompileJob(
+
550 progressReporter,
+
551 compileJob,
+
552 dreamMakerSettings,
+
553 launchParameters,
+
554 engineLock,
+
555 repository,
+
556 remoteDeploymentManager,
+
557 combinedTokenSource.Token);
+
558 }
+
559 catch (OperationCanceledException) when (timeoutToken.IsCancellationRequested)
+
560 {
+
561 throw new JobException(ErrorCode.DeploymentTimeout);
+
562 }
+
563 }
+
564
+
565 return compileJob;
566 }
-
567 finally
+
567 catch (OperationCanceledException)
568 {
-
569 progressCts.Cancel();
-
570 await progressTask;
-
571 }
-
572 }
+
569 // DCT: Cancellation token is for job, delaying here is fine
+
570 progressReporter.StageName = "Running CompileCancelled event";
+
571 await eventConsumer.HandleEvent(EventType.CompileCancelled, Enumerable.Empty<string>(), true, CancellationToken.None);
+
572 throw;
+
573 }
+
574 finally
+
575 {
+
576 progressCts.Cancel();
+
577 await progressTask;
+
578 }
+
579 }
-
573
-
-
586 async ValueTask RunCompileJob(
-
587 JobProgressReporter progressReporter,
-
588 Models.CompileJob job,
-
589 Api.Models.Internal.DreamMakerSettings dreamMakerSettings,
-
590 DreamDaemonLaunchParameters launchParameters,
-
591 IEngineExecutableLock engineLock,
-
592 IRepository repository,
-
593 IRemoteDeploymentManager remoteDeploymentManager,
-
594 CancellationToken cancellationToken)
-
595 {
-
596 var outputDirectory = job.DirectoryName!.Value.ToString();
-
597 logger.LogTrace("Compile output GUID: {dirGuid}", outputDirectory);
-
598
-
599 try
-
600 {
-
601 // copy the repository
-
602 logger.LogTrace("Copying repository to game directory");
-
603 progressReporter.StageName = "Copying repository";
-
604 var resolvedOutputDirectory = ioManager.ResolvePath(outputDirectory);
-
605 var repoOrigin = repository.Origin;
-
606 var repoReference = repository.Reference;
-
607 using (repository)
-
608 await repository.CopyTo(resolvedOutputDirectory, cancellationToken);
-
609
-
610 // repository closed now
-
611
-
612 // run precompile scripts
-
613 progressReporter.StageName = "Running PreCompile event";
- -
615 EventType.CompileStart,
-
616 new List<string>
-
617 {
-
618 resolvedOutputDirectory,
-
619 repoOrigin.ToString(),
-
620 engineLock.Version.ToString(),
-
621 repoReference,
-
622 },
-
623 true,
-
624 cancellationToken);
-
625
-
626 // determine the dme
-
627 progressReporter.StageName = "Determining .dme";
-
628 if (job.DmeName == null)
-
629 {
-
630 logger.LogTrace("Searching for available .dmes");
-
631 var foundPaths = await ioManager.GetFilesWithExtension(resolvedOutputDirectory, DmeExtension, true, cancellationToken);
-
632 var foundPath = foundPaths.FirstOrDefault();
-
633 if (foundPath == default)
-
634 throw new JobException(ErrorCode.DeploymentNoDme);
-
635 job.DmeName = foundPath.Substring(
-
636 resolvedOutputDirectory.Length + 1,
-
637 foundPath.Length - resolvedOutputDirectory.Length - DmeExtension.Length - 2); // +1 for . in extension
-
638 }
-
639 else
-
640 {
-
641 var targetDme = ioManager.ConcatPath(outputDirectory, String.Join('.', job.DmeName, DmeExtension));
-
642 if (!await ioManager.PathIsChildOf(outputDirectory, targetDme, cancellationToken))
-
643 throw new JobException(ErrorCode.DeploymentWrongDme);
-
644
-
645 var targetDmeExists = await ioManager.FileExists(targetDme, cancellationToken);
-
646 if (!targetDmeExists)
-
647 throw new JobException(ErrorCode.DeploymentMissingDme);
-
648 }
-
649
-
650 logger.LogDebug("Selected \"{dmeName}.dme\" for compilation!", job.DmeName);
+
580
+
+
593 async ValueTask RunCompileJob(
+
594 JobProgressReporter progressReporter,
+
595 Models.CompileJob job,
+
596 Api.Models.Internal.DreamMakerSettings dreamMakerSettings,
+
597 DreamDaemonLaunchParameters launchParameters,
+
598 IEngineExecutableLock engineLock,
+
599 IRepository repository,
+
600 IRemoteDeploymentManager remoteDeploymentManager,
+
601 CancellationToken cancellationToken)
+
602 {
+
603 var outputDirectory = job.DirectoryName!.Value.ToString();
+
604 logger.LogTrace("Compile output GUID: {dirGuid}", outputDirectory);
+
605
+
606 try
+
607 {
+
608 // copy the repository
+
609 logger.LogTrace("Copying repository to game directory");
+
610 progressReporter.StageName = "Copying repository";
+
611 var resolvedOutputDirectory = ioManager.ResolvePath(outputDirectory);
+
612 var repoOrigin = repository.Origin;
+
613 var repoReference = repository.Reference;
+
614 using (repository)
+
615 await repository.CopyTo(resolvedOutputDirectory, cancellationToken);
+
616
+
617 // repository closed now
+
618
+
619 // run precompile scripts
+
620 progressReporter.StageName = "Running PreCompile event";
+ +
622 EventType.CompileStart,
+
623 new List<string>
+
624 {
+
625 resolvedOutputDirectory,
+
626 repoOrigin.ToString(),
+
627 engineLock.Version.ToString(),
+
628 repoReference,
+
629 },
+
630 true,
+
631 cancellationToken);
+
632
+
633 // determine the dme
+
634 progressReporter.StageName = "Determining .dme";
+
635 if (job.DmeName == null)
+
636 {
+
637 logger.LogTrace("Searching for available .dmes");
+
638 var foundPaths = await ioManager.GetFilesWithExtension(resolvedOutputDirectory, DmeExtension, true, cancellationToken);
+
639 var foundPath = foundPaths.FirstOrDefault();
+
640 if (foundPath == default)
+
641 throw new JobException(ErrorCode.DeploymentNoDme);
+
642 job.DmeName = foundPath.Substring(
+
643 resolvedOutputDirectory.Length + 1,
+
644 foundPath.Length - resolvedOutputDirectory.Length - DmeExtension.Length - 2); // +1 for . in extension
+
645 }
+
646 else
+
647 {
+
648 var targetDme = ioManager.ConcatPath(outputDirectory, String.Join('.', job.DmeName, DmeExtension));
+
649 if (!await ioManager.PathIsChildOf(outputDirectory, targetDme, cancellationToken))
+
650 throw new JobException(ErrorCode.DeploymentWrongDme);
651
-
652 progressReporter.StageName = "Modifying .dme";
-
653 await ModifyDme(job, cancellationToken);
-
654
-
655 // run precompile scripts
-
656 progressReporter.StageName = "Running PreDreamMaker event";
- -
658 EventType.PreDreamMaker,
-
659 new List<string>
-
660 {
-
661 resolvedOutputDirectory,
-
662 repoOrigin.ToString(),
-
663 engineLock.Version.ToString(),
-
664 },
-
665 true,
-
666 cancellationToken);
-
667
-
668 // run compiler
-
669 progressReporter.StageName = "Running Compiler";
-
670 var compileSuceeded = await RunDreamMaker(engineLock, job, dreamMakerSettings.CompilerAdditionalArguments, cancellationToken);
-
671
-
672 // Session takes ownership of the lock and Disposes it so save this for later
-
673 var engineVersion = engineLock.Version;
+
652 var targetDmeExists = await ioManager.FileExists(targetDme, cancellationToken);
+
653 if (!targetDmeExists)
+
654 throw new JobException(ErrorCode.DeploymentMissingDme);
+
655 }
+
656
+
657 logger.LogDebug("Selected \"{dmeName}.dme\" for compilation!", job.DmeName);
+
658
+
659 progressReporter.StageName = "Modifying .dme";
+
660 await ModifyDme(job, cancellationToken);
+
661
+
662 // run precompile scripts
+
663 progressReporter.StageName = "Running PreDreamMaker event";
+ +
665 EventType.PreDreamMaker,
+
666 new List<string>
+
667 {
+
668 resolvedOutputDirectory,
+
669 repoOrigin.ToString(),
+
670 engineLock.Version.ToString(),
+
671 },
+
672 true,
+
673 cancellationToken);
674
-
675 // verify api
-
676 try
-
677 {
-
678 if (!compileSuceeded)
-
679 throw new JobException(
-
680 ErrorCode.DeploymentExitCode,
-
681 new JobException($"Compilation failed:{Environment.NewLine}{Environment.NewLine}{job.Output}"));
-
682
-
683 await VerifyApi(
-
684 launchParameters.StartupTimeout!.Value,
-
685 dreamMakerSettings.ApiValidationSecurityLevel!.Value,
-
686 job,
-
687 progressReporter,
-
688 engineLock,
-
689 dreamMakerSettings.ApiValidationPort!.Value,
-
690 dreamMakerSettings.DMApiValidationMode!.Value,
-
691 launchParameters.LogOutput!.Value,
-
692 cancellationToken);
-
693 }
-
694 catch (JobException)
-
695 {
-
696 // DD never validated or compile failed
-
697 progressReporter.StageName = "Running CompileFailure event";
- -
699 EventType.CompileFailure,
-
700 new List<string>
-
701 {
-
702 resolvedOutputDirectory,
-
703 compileSuceeded ? "1" : "0",
-
704 engineVersion.ToString(),
-
705 },
-
706 true,
-
707 cancellationToken);
-
708 throw;
-
709 }
-
710
-
711 progressReporter.StageName = "Running CompileComplete event";
- -
713 EventType.CompileComplete,
-
714 new List<string>
-
715 {
-
716 resolvedOutputDirectory,
-
717 engineVersion.ToString(),
-
718 },
-
719 true,
-
720 cancellationToken);
-
721
-
722 logger.LogTrace("Applying static game file symlinks...");
-
723 progressReporter.StageName = "Symlinking GameStaticFiles";
-
724
-
725 // symlink in the static data
-
726 await configuration.SymlinkStaticFilesTo(resolvedOutputDirectory, cancellationToken);
-
727
-
728 logger.LogDebug("Compile complete!");
-
729 }
-
730 catch (Exception ex)
-
731 {
-
732 progressReporter.StageName = "Cleaning output directory";
-
733 await CleanupFailedCompile(job, remoteDeploymentManager, ex);
-
734 throw;
-
735 }
-
736 }
+
675 // run compiler
+
676 progressReporter.StageName = "Running Compiler";
+
677 var compileSuceeded = await RunDreamMaker(engineLock, job, dreamMakerSettings.CompilerAdditionalArguments, cancellationToken);
+
678
+
679 // Session takes ownership of the lock and Disposes it so save this for later
+
680 var engineVersion = engineLock.Version;
+
681
+
682 // verify api
+
683 try
+
684 {
+
685 if (!compileSuceeded)
+
686 throw new JobException(
+
687 ErrorCode.DeploymentExitCode,
+
688 new JobException($"Compilation failed:{Environment.NewLine}{Environment.NewLine}{job.Output}"));
+
689
+
690 await VerifyApi(
+
691 launchParameters.StartupTimeout!.Value,
+
692 dreamMakerSettings.ApiValidationSecurityLevel!.Value,
+
693 job,
+
694 progressReporter,
+
695 engineLock,
+
696 dreamMakerSettings.ApiValidationPort!.Value,
+
697 dreamMakerSettings.DMApiValidationMode!.Value,
+
698 launchParameters.LogOutput!.Value,
+
699 cancellationToken);
+
700 }
+
701 catch (JobException)
+
702 {
+
703 // DD never validated or compile failed
+
704 progressReporter.StageName = "Running CompileFailure event";
+ +
706 EventType.CompileFailure,
+
707 new List<string>
+
708 {
+
709 resolvedOutputDirectory,
+
710 compileSuceeded ? "1" : "0",
+
711 engineVersion.ToString(),
+
712 },
+
713 true,
+
714 cancellationToken);
+
715 throw;
+
716 }
+
717
+
718 progressReporter.StageName = "Running CompileComplete event";
+ +
720 EventType.CompileComplete,
+
721 new List<string>
+
722 {
+
723 resolvedOutputDirectory,
+
724 engineVersion.ToString(),
+
725 },
+
726 true,
+
727 cancellationToken);
+
728
+
729 logger.LogTrace("Applying static game file symlinks...");
+
730 progressReporter.StageName = "Symlinking GameStaticFiles";
+
731
+
732 // symlink in the static data
+
733 await configuration.SymlinkStaticFilesTo(resolvedOutputDirectory, cancellationToken);
+
734
+
735 logger.LogDebug("Compile complete!");
+
736 }
+
737 catch (Exception ex)
+
738 {
+
739 progressReporter.StageName = "Cleaning output directory";
+
740 await CleanupFailedCompile(job, remoteDeploymentManager, ex);
+
741 throw;
+
742 }
+
743 }
-
737
-
-
745 async ValueTask ProgressTask(JobProgressReporter progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken)
-
746 {
-
747 double? lastReport = estimatedDuration.HasValue ? 0 : null;
-
748 progressReporter.ReportProgress(lastReport);
-
749
-
750 var minimumSleepInterval = TimeSpan.FromMilliseconds(250);
-
751 var sleepInterval = estimatedDuration.HasValue ? estimatedDuration.Value / 100 : minimumSleepInterval;
-
752
-
753 if (estimatedDuration.HasValue)
-
754 {
-
755 logger.LogDebug("Compile is expected to take: {estimatedDuration}", estimatedDuration);
-
756 }
-
757 else
-
758 {
-
759 logger.LogTrace("No metric to estimate compile time.");
-
760 }
-
761
-
762 try
-
763 {
-
764 for (var iteration = 0; iteration < (estimatedDuration.HasValue ? 99 : Int32.MaxValue); ++iteration)
-
765 {
-
766 if (estimatedDuration.HasValue)
-
767 {
-
768 var nextInterval = DateTimeOffset.UtcNow + sleepInterval;
-
769 do
-
770 {
-
771 var remainingSleepThisInterval = nextInterval - DateTimeOffset.UtcNow;
-
772 var nextSleepSpan = remainingSleepThisInterval < minimumSleepInterval ? minimumSleepInterval : remainingSleepThisInterval;
-
773
-
774 await asyncDelayer.Delay(nextSleepSpan, cancellationToken);
-
775 progressReporter.ReportProgress(lastReport);
-
776 }
-
777 while (DateTimeOffset.UtcNow < nextInterval);
-
778 }
-
779 else
-
780 await asyncDelayer.Delay(minimumSleepInterval, cancellationToken);
-
781
-
782 lastReport = estimatedDuration.HasValue ? sleepInterval * (iteration + 1) / estimatedDuration.Value : null;
-
783 progressReporter.ReportProgress(lastReport);
-
784 }
-
785 }
-
786 catch (OperationCanceledException ex)
-
787 {
-
788 logger.LogTrace(ex, "ProgressTask aborted.");
-
789 }
-
790 catch (Exception ex)
-
791 {
-
792 logger.LogError(ex, "ProgressTask crashed!");
-
793 }
-
794 }
+
744
+
+
752 async ValueTask ProgressTask(JobProgressReporter progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken)
+
753 {
+
754 double? lastReport = estimatedDuration.HasValue ? 0 : null;
+
755 progressReporter.ReportProgress(lastReport);
+
756
+
757 var minimumSleepInterval = TimeSpan.FromMilliseconds(250);
+
758 var sleepInterval = estimatedDuration.HasValue ? estimatedDuration.Value / 100 : minimumSleepInterval;
+
759
+
760 if (estimatedDuration.HasValue)
+
761 {
+
762 logger.LogDebug("Compile is expected to take: {estimatedDuration}", estimatedDuration);
+
763 }
+
764 else
+
765 {
+
766 logger.LogTrace("No metric to estimate compile time.");
+
767 }
+
768
+
769 try
+
770 {
+
771 for (var iteration = 0; iteration < (estimatedDuration.HasValue ? 99 : Int32.MaxValue); ++iteration)
+
772 {
+
773 if (estimatedDuration.HasValue)
+
774 {
+
775 var nextInterval = DateTimeOffset.UtcNow + sleepInterval;
+
776 do
+
777 {
+
778 var remainingSleepThisInterval = nextInterval - DateTimeOffset.UtcNow;
+
779 var nextSleepSpan = remainingSleepThisInterval < minimumSleepInterval ? minimumSleepInterval : remainingSleepThisInterval;
+
780
+
781 await asyncDelayer.Delay(nextSleepSpan, cancellationToken);
+
782 progressReporter.ReportProgress(lastReport);
+
783 }
+
784 while (DateTimeOffset.UtcNow < nextInterval);
+
785 }
+
786 else
+
787 await asyncDelayer.Delay(minimumSleepInterval, cancellationToken);
+
788
+
789 lastReport = estimatedDuration.HasValue ? sleepInterval * (iteration + 1) / estimatedDuration.Value : null;
+
790 progressReporter.ReportProgress(lastReport);
+
791 }
+
792 }
+
793 catch (OperationCanceledException ex)
+
794 {
+
795 logger.LogTrace(ex, "ProgressTask aborted.");
+
796 }
+
797 catch (Exception ex)
+
798 {
+
799 logger.LogError(ex, "ProgressTask crashed!");
+
800 }
+
801 }
-
795
-
-
809 async ValueTask VerifyApi(
-
810 uint timeout,
-
811 DreamDaemonSecurity securityLevel,
-
812 Models.CompileJob job,
-
813 JobProgressReporter progressReporter,
-
814 IEngineExecutableLock engineLock,
-
815 ushort portToUse,
-
816 DMApiValidationMode validationMode,
-
817 bool logOutput,
-
818 CancellationToken cancellationToken)
-
819 {
-
820 if (validationMode == DMApiValidationMode.Skipped)
-
821 {
-
822 logger.LogDebug("Skipping DMAPI validation");
-
823 job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
-
824 return;
-
825 }
-
826
-
827 progressReporter.StageName = "Validating DMAPI";
-
828
-
829 var requireValidate = validationMode == DMApiValidationMode.Required;
-
830 logger.LogTrace("Verifying {possiblyRequired}DMAPI...", requireValidate ? "required " : String.Empty);
-
831 var launchParameters = new DreamDaemonLaunchParameters
-
832 {
-
833 AllowWebClient = false,
-
834 Port = portToUse,
-
835 OpenDreamTopicPort = 0,
-
836 SecurityLevel = securityLevel,
-
837 Visibility = DreamDaemonVisibility.Invisible,
-
838 StartupTimeout = timeout,
-
839 TopicRequestTimeout = 0, // not used
-
840 HealthCheckSeconds = 0, // not used
-
841 StartProfiler = false,
-
842 LogOutput = logOutput,
-
843 MapThreads = 1, // lowest possible amount
-
844 };
-
845
-
846 job.MinimumSecurityLevel = securityLevel; // needed for the TempDmbProvider
-
847
-
848 ApiValidationStatus validationStatus;
-
849 await using (var provider = new TemporaryDmbProvider(
-
850 ioManager.ResolvePath(job.DirectoryName!.Value.ToString()),
-
851 job,
-
852 engineLock.Version))
-
853 await using (var controller = await sessionControllerFactory.LaunchNew(provider, engineLock, launchParameters, true, cancellationToken))
-
854 {
-
855 var launchResult = await controller.LaunchResult.WaitAsync(cancellationToken);
-
856
-
857 if (launchResult.StartupTime.HasValue)
-
858 await controller.Lifetime.WaitAsync(cancellationToken);
-
859
-
860 if (!controller.Lifetime.IsCompleted)
-
861 await controller.DisposeAsync();
-
862
-
863 validationStatus = controller.ApiValidationStatus;
-
864
-
865 logger.LogTrace("API validation status: {validationStatus}", validationStatus);
+
802
+
+
816 async ValueTask VerifyApi(
+
817 uint timeout,
+
818 DreamDaemonSecurity securityLevel,
+
819 Models.CompileJob job,
+
820 JobProgressReporter progressReporter,
+
821 IEngineExecutableLock engineLock,
+
822 ushort portToUse,
+
823 DMApiValidationMode validationMode,
+
824 bool logOutput,
+
825 CancellationToken cancellationToken)
+
826 {
+
827 if (validationMode == DMApiValidationMode.Skipped)
+
828 {
+
829 logger.LogDebug("Skipping DMAPI validation");
+
830 job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
+
831 return;
+
832 }
+
833
+
834 progressReporter.StageName = "Validating DMAPI";
+
835
+
836 var requireValidate = validationMode == DMApiValidationMode.Required;
+
837 logger.LogTrace("Verifying {possiblyRequired}DMAPI...", requireValidate ? "required " : String.Empty);
+
838 var launchParameters = new DreamDaemonLaunchParameters
+
839 {
+
840 AllowWebClient = false,
+
841 Port = portToUse,
+
842 OpenDreamTopicPort = 0,
+
843 SecurityLevel = securityLevel,
+
844 Visibility = DreamDaemonVisibility.Invisible,
+
845 StartupTimeout = timeout,
+
846 TopicRequestTimeout = 0, // not used
+
847 HealthCheckSeconds = 0, // not used
+
848 StartProfiler = false,
+
849 LogOutput = logOutput,
+
850 MapThreads = 1, // lowest possible amount
+
851 };
+
852
+
853 job.MinimumSecurityLevel = securityLevel; // needed for the TempDmbProvider
+
854
+
855 ApiValidationStatus validationStatus;
+
856 await using (var provider = new TemporaryDmbProvider(
+
857 ioManager.ResolvePath(job.DirectoryName!.Value.ToString()),
+
858 job,
+
859 engineLock.Version))
+
860 await using (var controller = await sessionControllerFactory.LaunchNew(provider, engineLock, launchParameters, true, cancellationToken))
+
861 {
+
862 var launchResult = await controller.LaunchResult.WaitAsync(cancellationToken);
+
863
+
864 if (launchResult.StartupTime.HasValue)
+
865 await controller.Lifetime.WaitAsync(cancellationToken);
866
-
867 job.DMApiVersion = controller.DMApiVersion;
-
868 }
+
867 if (!controller.Lifetime.IsCompleted)
+
868 await controller.DisposeAsync();
869
-
870 switch (validationStatus)
-
871 {
-
872 case ApiValidationStatus.RequiresUltrasafe:
-
873 job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
-
874 return;
-
875 case ApiValidationStatus.RequiresSafe:
-
876 job.MinimumSecurityLevel = DreamDaemonSecurity.Safe;
-
877 return;
-
878 case ApiValidationStatus.RequiresTrusted:
-
879 job.MinimumSecurityLevel = DreamDaemonSecurity.Trusted;
-
880 return;
-
881 case ApiValidationStatus.NeverValidated:
-
882 if (requireValidate)
-
883 throw new JobException(ErrorCode.DeploymentNeverValidated);
-
884 job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
-
885 break;
-
886 case ApiValidationStatus.BadValidationRequest:
-
887 case ApiValidationStatus.Incompatible:
-
888 throw new JobException(ErrorCode.DeploymentInvalidValidation);
-
889 case ApiValidationStatus.UnaskedValidationRequest:
-
890 default:
-
891 throw new InvalidOperationException(
-
892 $"Session controller returned unexpected ApiValidationStatus: {validationStatus}");
-
893 }
-
894 }
+
870 validationStatus = controller.ApiValidationStatus;
+
871
+
872 logger.LogTrace("API validation status: {validationStatus}", validationStatus);
+
873
+
874 job.DMApiVersion = controller.DMApiVersion;
+
875 }
+
876
+
877 switch (validationStatus)
+
878 {
+
879 case ApiValidationStatus.RequiresUltrasafe:
+
880 job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
+
881 return;
+
882 case ApiValidationStatus.RequiresSafe:
+
883 job.MinimumSecurityLevel = DreamDaemonSecurity.Safe;
+
884 return;
+
885 case ApiValidationStatus.RequiresTrusted:
+
886 job.MinimumSecurityLevel = DreamDaemonSecurity.Trusted;
+
887 return;
+
888 case ApiValidationStatus.NeverValidated:
+
889 if (requireValidate)
+
890 throw new JobException(ErrorCode.DeploymentNeverValidated);
+
891 job.MinimumSecurityLevel = DreamDaemonSecurity.Ultrasafe;
+
892 break;
+
893 case ApiValidationStatus.BadValidationRequest:
+
894 case ApiValidationStatus.Incompatible:
+
895 throw new JobException(ErrorCode.DeploymentInvalidValidation);
+
896 case ApiValidationStatus.UnaskedValidationRequest:
+
897 default:
+
898 throw new InvalidOperationException(
+
899 $"Session controller returned unexpected ApiValidationStatus: {validationStatus}");
+
900 }
+
901 }
-
895
-
-
904 async ValueTask<bool> RunDreamMaker(
-
905 IEngineExecutableLock engineLock,
-
906 Models.CompileJob job,
-
907 string? additionalCompilerArguments,
-
908 CancellationToken cancellationToken)
-
909 {
-
910 var environment = await engineLock.LoadEnv(logger, true, cancellationToken);
-
911 var arguments = engineLock.FormatCompilerArguments($"{job.DmeName}.{DmeExtension}", additionalCompilerArguments);
-
912
-
913 await using var dm = await processExecutor.LaunchProcess(
-
914 engineLock.CompilerExePath,
-
915 ioManager.ResolvePath(
-
916 job.DirectoryName!.Value.ToString()),
-
917 arguments,
-
918 cancellationToken,
-
919 environment,
-
920 readStandardHandles: true,
-
921 noShellExecute: true);
-
922
-
923 if (sessionConfiguration.LowPriorityDeploymentProcesses)
-
924 dm.AdjustPriority(false);
-
925
-
926 int exitCode;
-
927 using (cancellationToken.Register(() => dm.Terminate()))
-
928 exitCode = (await dm.Lifetime).Value;
-
929 cancellationToken.ThrowIfCancellationRequested();
-
930
-
931 logger.LogDebug("DreamMaker exit code: {exitCode}", exitCode);
-
932 job.Output = $"{await dm.GetCombinedOutput(cancellationToken)}{Environment.NewLine}{Environment.NewLine}Exit Code: {exitCode}";
-
933 logger.LogDebug("DreamMaker output: {newLine}{output}", Environment.NewLine, job.Output);
-
934
-
935 currentDreamMakerOutput = job.Output;
-
936 return exitCode == 0;
-
937 }
+
902
+
+
911 async ValueTask<bool> RunDreamMaker(
+
912 IEngineExecutableLock engineLock,
+
913 Models.CompileJob job,
+
914 string? additionalCompilerArguments,
+
915 CancellationToken cancellationToken)
+
916 {
+
917 var environment = await engineLock.LoadEnv(logger, true, cancellationToken);
+
918 var arguments = engineLock.FormatCompilerArguments($"{job.DmeName}.{DmeExtension}", additionalCompilerArguments);
+
919
+
920 await using var dm = await processExecutor.LaunchProcess(
+
921 engineLock.CompilerExePath,
+
922 ioManager.ResolvePath(
+
923 job.DirectoryName!.Value.ToString()),
+
924 arguments,
+
925 cancellationToken,
+
926 environment,
+
927 readStandardHandles: true,
+
928 noShellExecute: true);
+
929
+
930 if (sessionConfiguration.LowPriorityDeploymentProcesses)
+
931 dm.AdjustPriority(false);
+
932
+
933 int exitCode;
+
934 using (cancellationToken.Register(() => dm.Terminate()))
+
935 exitCode = (await dm.Lifetime).Value;
+
936 cancellationToken.ThrowIfCancellationRequested();
+
937
+
938 logger.LogDebug("DreamMaker exit code: {exitCode}", exitCode);
+
939 job.Output = $"{await dm.GetCombinedOutput(cancellationToken)}{Environment.NewLine}{Environment.NewLine}Exit Code: {exitCode}";
+
940 logger.LogDebug("DreamMaker output: {newLine}{output}", Environment.NewLine, job.Output);
+
941
+
942 currentDreamMakerOutput = job.Output;
+
943 return exitCode == 0;
+
944 }
-
938
-
-
945 async ValueTask ModifyDme(Models.CompileJob job, CancellationToken cancellationToken)
-
946 {
-
947 var dmeFileName = String.Join('.', job.DmeName, DmeExtension);
-
948 var stringDirectoryName = job.DirectoryName!.Value.ToString();
-
949 var dmePath = ioManager.ConcatPath(stringDirectoryName, dmeFileName);
-
950 var dmeReadTask = ioManager.ReadAllBytes(dmePath, cancellationToken);
-
951
-
952 var dmeModificationsTask = configuration.CopyDMFilesTo(
-
953 dmeFileName,
-
954 ioManager.ResolvePath(
-
955 ioManager.ConcatPath(
-
956 stringDirectoryName,
-
957 ioManager.GetDirectoryName(dmeFileName))),
-
958 cancellationToken);
-
959
-
960 var dmeBytes = await dmeReadTask;
-
961 var dme = Encoding.UTF8.GetString(dmeBytes);
-
962
-
963 var dmeModifications = await dmeModificationsTask;
-
964
-
965 if (dmeModifications == null || dmeModifications.TotalDmeOverwrite)
-
966 {
-
967 if (dmeModifications != null)
-
968 logger.LogDebug(".dme replacement configured!");
-
969 else
-
970 logger.LogTrace("No .dme modifications required.");
-
971 return;
-
972 }
-
973
-
974 var dmeLines = new List<string>(dme.Split('\n', StringSplitOptions.None));
-
975 for (var dmeLineIndex = 0; dmeLineIndex < dmeLines.Count; ++dmeLineIndex)
-
976 {
-
977 var line = dmeLines[dmeLineIndex];
-
978 if (line.Contains("BEGIN_INCLUDE", StringComparison.Ordinal) && dmeModifications.HeadIncludeLine != null)
-
979 {
-
980 var headIncludeLineNumber = dmeLineIndex + 1;
-
981 logger.LogDebug(
-
982 "Inserting HeadInclude.dm at line {lineNumber}: {includeLine}",
-
983 headIncludeLineNumber,
-
984 dmeModifications.HeadIncludeLine);
-
985 dmeLines.Insert(headIncludeLineNumber, dmeModifications.HeadIncludeLine);
-
986 ++dmeLineIndex;
-
987 }
-
988 else if (line.Contains("END_INCLUDE", StringComparison.Ordinal) && dmeModifications.TailIncludeLine != null)
-
989 {
-
990 logger.LogDebug(
-
991 "Inserting TailInclude.dm at line {lineNumber}: {includeLine}",
-
992 dmeLineIndex,
-
993 dmeModifications.TailIncludeLine);
-
994 dmeLines.Insert(dmeLineIndex, dmeModifications.TailIncludeLine);
-
995 break;
-
996 }
-
997 }
-
998
-
999 dmeBytes = Encoding.UTF8.GetBytes(String.Join('\n', dmeLines));
-
1000 await ioManager.WriteAllBytes(dmePath, dmeBytes, cancellationToken);
-
1001 }
+
945
+
+
952 async ValueTask ModifyDme(Models.CompileJob job, CancellationToken cancellationToken)
+
953 {
+
954 var dmeFileName = String.Join('.', job.DmeName, DmeExtension);
+
955 var stringDirectoryName = job.DirectoryName!.Value.ToString();
+
956 var dmePath = ioManager.ConcatPath(stringDirectoryName, dmeFileName);
+
957 var dmeReadTask = ioManager.ReadAllBytes(dmePath, cancellationToken);
+
958
+
959 var dmeModificationsTask = configuration.CopyDMFilesTo(
+
960 dmeFileName,
+
961 ioManager.ResolvePath(
+
962 ioManager.ConcatPath(
+
963 stringDirectoryName,
+
964 ioManager.GetDirectoryName(dmeFileName))),
+
965 cancellationToken);
+
966
+
967 var dmeBytes = await dmeReadTask;
+
968 var dme = Encoding.UTF8.GetString(dmeBytes);
+
969
+
970 var dmeModifications = await dmeModificationsTask;
+
971
+
972 if (dmeModifications == null || dmeModifications.TotalDmeOverwrite)
+
973 {
+
974 if (dmeModifications != null)
+
975 logger.LogDebug(".dme replacement configured!");
+
976 else
+
977 logger.LogTrace("No .dme modifications required.");
+
978 return;
+
979 }
+
980
+
981 var dmeLines = new List<string>(dme.Split('\n', StringSplitOptions.None));
+
982 for (var dmeLineIndex = 0; dmeLineIndex < dmeLines.Count; ++dmeLineIndex)
+
983 {
+
984 var line = dmeLines[dmeLineIndex];
+
985 if (line.Contains("BEGIN_INCLUDE", StringComparison.Ordinal) && dmeModifications.HeadIncludeLine != null)
+
986 {
+
987 var headIncludeLineNumber = dmeLineIndex + 1;
+
988 logger.LogDebug(
+
989 "Inserting HeadInclude.dm at line {lineNumber}: {includeLine}",
+
990 headIncludeLineNumber,
+
991 dmeModifications.HeadIncludeLine);
+
992 dmeLines.Insert(headIncludeLineNumber, dmeModifications.HeadIncludeLine);
+
993 ++dmeLineIndex;
+
994 }
+
995 else if (line.Contains("END_INCLUDE", StringComparison.Ordinal) && dmeModifications.TailIncludeLine != null)
+
996 {
+
997 logger.LogDebug(
+
998 "Inserting TailInclude.dm at line {lineNumber}: {includeLine}",
+
999 dmeLineIndex,
+
1000 dmeModifications.TailIncludeLine);
+
1001 dmeLines.Insert(dmeLineIndex, dmeModifications.TailIncludeLine);
+
1002 break;
+
1003 }
+
1004 }
+
1005
+
1006 dmeBytes = Encoding.UTF8.GetBytes(String.Join('\n', dmeLines));
+
1007 await ioManager.WriteAllBytes(dmePath, dmeBytes, cancellationToken);
+
1008 }
-
1002
-
-
1010 ValueTask CleanupFailedCompile(Models.CompileJob job, IRemoteDeploymentManager remoteDeploymentManager, Exception exception)
-
1011 {
-
1012 async ValueTask CleanDir()
-
1013 {
-
1014 if (sessionConfiguration.DelayCleaningFailedDeployments)
-
1015 {
-
1016 logger.LogDebug("Not cleaning up errored deployment directory {guid} due to config.", job.DirectoryName);
-
1017 return;
-
1018 }
-
1019
-
1020 logger.LogTrace("Cleaning compile directory...");
-
1021 var jobPath = job.DirectoryName!.Value.ToString();
-
1022 try
-
1023 {
-
1024 // DCT: None available
-
1025 await eventConsumer.HandleEvent(EventType.DeploymentCleanup, new List<string> { jobPath }, true, CancellationToken.None);
-
1026 await ioManager.DeleteDirectory(jobPath, CancellationToken.None);
-
1027 }
-
1028 catch (Exception e)
-
1029 {
-
1030 logger.LogWarning(e, "Error cleaning up compile directory {path}!", ioManager.ResolvePath(jobPath));
-
1031 }
-
1032 }
-
1033
-
1034 var dirCleanTask = CleanDir();
-
1035
-
1036 var failRemoteDeployTask = remoteDeploymentManager.FailDeployment(
-
1037 job,
-
1038 FormatExceptionForUsers(exception),
-
1039 CancellationToken.None); // DCT: None available
+
1009
+
+
1017 ValueTask CleanupFailedCompile(Models.CompileJob job, IRemoteDeploymentManager remoteDeploymentManager, Exception exception)
+
1018 {
+
1019 async ValueTask CleanDir()
+
1020 {
+
1021 if (sessionConfiguration.DelayCleaningFailedDeployments)
+
1022 {
+
1023 logger.LogDebug("Not cleaning up errored deployment directory {guid} due to config.", job.DirectoryName);
+
1024 return;
+
1025 }
+
1026
+
1027 logger.LogTrace("Cleaning compile directory...");
+
1028 var jobPath = job.DirectoryName!.Value.ToString();
+
1029 try
+
1030 {
+
1031 // DCT: None available
+
1032 await eventConsumer.HandleEvent(EventType.DeploymentCleanup, new List<string> { jobPath }, true, CancellationToken.None);
+
1033 await ioManager.DeleteDirectory(jobPath, CancellationToken.None);
+
1034 }
+
1035 catch (Exception e)
+
1036 {
+
1037 logger.LogWarning(e, "Error cleaning up compile directory {path}!", ioManager.ResolvePath(jobPath));
+
1038 }
+
1039 }
1040
- -
1042 dirCleanTask,
-
1043 failRemoteDeployTask);
-
1044 }
+
1041 var dirCleanTask = CleanDir();
+
1042
+
1043 var failRemoteDeployTask = remoteDeploymentManager.FailDeployment(
+
1044 job,
+
1045 FormatExceptionForUsers(exception),
+
1046 CancellationToken.None); // DCT: None available
+
1047
+ +
1049 dirCleanTask,
+
1050 failRemoteDeployTask);
+
1051 }
-
1045 }
+
1052 }
-
1046}
+
1053}
Metadata about a server instance.
Definition Instance.cs:9
@@ -997,19 +1003,20 @@ $(document).ready(function() { init_codefold(0); });
static async ValueTask WhenAll(IEnumerable< ValueTask > tasks)
Fully await a given list of tasks .
Func< string?, string, Action< bool > >? currentChatCallback
The active callback from IChatManager.QueueDeploymentMessage.
+
async ValueTask< Models.CompileJob > Compile(Models.Job job, Models.CompileJob? oldCompileJob, Models.RevisionInformation revisionInformation, Api.Models.Internal.DreamMakerSettings dreamMakerSettings, DreamDaemonLaunchParameters launchParameters, IRepository repository, IRemoteDeploymentManager remoteDeploymentManager, JobProgressReporter progressReporter, TimeSpan? estimatedDuration, bool localCommitExistsOnRemote, CancellationToken cancellationToken)
Run the compile implementation.
readonly IRepositoryManager repositoryManager
The IRepositoryManager for DreamMaker.
Definition DreamMaker.cs:78
-
ValueTask CleanupFailedCompile(Models.CompileJob job, IRemoteDeploymentManager remoteDeploymentManager, Exception exception)
Cleans up a failed compile job .
-
async ValueTask ProgressTask(JobProgressReporter progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken)
Gradually triggers a given progressReporter over a given estimatedDuration .
-
async ValueTask VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, JobProgressReporter progressReporter, IEngineExecutableLock engineLock, ushort portToUse, DMApiValidationMode validationMode, bool logOutput, CancellationToken cancellationToken)
Run a quick DD instance to test the DMAPI is installed on the target code.
+
ValueTask CleanupFailedCompile(Models.CompileJob job, IRemoteDeploymentManager remoteDeploymentManager, Exception exception)
Cleans up a failed compile job .
+
async ValueTask ProgressTask(JobProgressReporter progressReporter, TimeSpan? estimatedDuration, CancellationToken cancellationToken)
Gradually triggers a given progressReporter over a given estimatedDuration .
+
async ValueTask VerifyApi(uint timeout, DreamDaemonSecurity securityLevel, Models.CompileJob job, JobProgressReporter progressReporter, IEngineExecutableLock engineLock, ushort portToUse, DMApiValidationMode validationMode, bool logOutput, CancellationToken cancellationToken)
Run a quick DD instance to test the DMAPI is installed on the target code.
DreamMaker(IEngineManager engineManager, IIOManager ioManager, StaticFiles.IConfiguration configuration, ISessionControllerFactory sessionControllerFactory, IEventConsumer eventConsumer, IChatManager chatManager, IProcessExecutor processExecutor, ICompileJobSink compileJobConsumer, IRepositoryManager repositoryManager, IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory, IAsyncDelayer asyncDelayer, IMetricFactory metricFactory, ILogger< DreamMaker > logger, SessionConfiguration sessionConfiguration, Api.Models.Instance metadata)
Initializes a new instance of the DreamMaker class.
readonly ILogger< DreamMaker > logger
The ILogger for DreamMaker.
Definition DreamMaker.cs:98
readonly SessionConfiguration sessionConfiguration
The SessionConfiguration for DreamMaker.
-
async ValueTask RunCompileJob(JobProgressReporter progressReporter, Models.CompileJob job, Api.Models.Internal.DreamMakerSettings dreamMakerSettings, DreamDaemonLaunchParameters launchParameters, IEngineExecutableLock engineLock, IRepository repository, IRemoteDeploymentManager remoteDeploymentManager, CancellationToken cancellationToken)
Executes and populate a given job .
+
async ValueTask RunCompileJob(JobProgressReporter progressReporter, Models.CompileJob job, Api.Models.Internal.DreamMakerSettings dreamMakerSettings, DreamDaemonLaunchParameters launchParameters, IEngineExecutableLock engineLock, IRepository repository, IRemoteDeploymentManager remoteDeploymentManager, CancellationToken cancellationToken)
Executes and populate a given job .
readonly StaticFiles.IConfiguration configuration
The StaticFiles.IConfiguration for DreamMaker.
Definition DreamMaker.cs:53
const string DmeExtension
Extension for .dmes.
Definition DreamMaker.cs:38
async ValueTask DeploymentProcess(Models.Job job, IDatabaseContextFactory databaseContextFactory, JobProgressReporter progressReporter, CancellationToken cancellationToken)
Create and a compile job and insert it into the database. Meant to be called by a IJobManager....
string? currentDreamMakerOutput
Cached for currentChatCallback.
-
async ValueTask< TimeSpan?> CalculateExpectedDeploymentTime(IDatabaseContext databaseContext, CancellationToken cancellationToken)
Calculate the average length of a deployment using a given databaseContext .
+
async ValueTask< TimeSpan?> CalculateExpectedDeploymentTime(IDatabaseContext databaseContext, CancellationToken cancellationToken)
Calculate the average length of a deployment using a given databaseContext .
readonly IIOManager ioManager
The IIOManager for DreamMaker.
Definition DreamMaker.cs:48
readonly IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory
The IRemoteDeploymentManagerFactory for DreamMaker.
Definition DreamMaker.cs:88
readonly Counter successfulDeployments
The number of successful deployments.
@@ -1023,10 +1030,9 @@ $(document).ready(function() { init_codefold(0); });
readonly ICompileJobSink compileJobConsumer
The ICompileJobSink for DreamMaker.
Definition DreamMaker.cs:83
readonly IEventConsumer eventConsumer
The IEventConsumer for DreamMaker.
Definition DreamMaker.cs:63
readonly Api.Models.Instance metadata
The Instance DreamMaker belongs to.
-
async ValueTask ModifyDme(Models.CompileJob job, CancellationToken cancellationToken)
Adds server side includes to the .dme being compiled.
-
async ValueTask< Models.CompileJob > Compile(Models.Job job, Models.RevisionInformation revisionInformation, Api.Models.Internal.DreamMakerSettings dreamMakerSettings, DreamDaemonLaunchParameters launchParameters, IRepository repository, IRemoteDeploymentManager remoteDeploymentManager, JobProgressReporter progressReporter, TimeSpan? estimatedDuration, bool localCommitExistsOnRemote, CancellationToken cancellationToken)
Run the compile implementation.
+
async ValueTask ModifyDme(Models.CompileJob job, CancellationToken cancellationToken)
Adds server side includes to the .dme being compiled.
readonly IChatManager chatManager
The IChatManager for DreamMaker.
Definition DreamMaker.cs:68
-
async ValueTask< bool > RunDreamMaker(IEngineExecutableLock engineLock, Models.CompileJob job, string? additionalCompilerArguments, CancellationToken cancellationToken)
Compiles a .dme with DreamMaker.
+
async ValueTask< bool > RunDreamMaker(IEngineExecutableLock engineLock, Models.CompileJob job, string? additionalCompilerArguments, CancellationToken cancellationToken)
Compiles a .dme with DreamMaker.
readonly object deploymentLock
lock object for deploying.
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for DreamMaker.
Definition DreamMaker.cs:93
@@ -1043,7 +1049,7 @@ $(document).ready(function() { init_codefold(0); });
RemoteGitProvider? RemoteGitProvider
The Models.RemoteGitProvider in use by the repository.
string? RemoteRepositoryOwner
If RemoteGitProvider is not RemoteGitProvider.Unknown this will be set with the owner of the reposito...
For managing connected chat services.
-
Func< string?, string, Action< bool > > QueueDeploymentMessage(Models.RevisionInformation revisionInformation, Api.Models.EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, bool localCommitPushed)
Send the message for a deployment to configured deployment channels.
+
Func< string?, string, Action< bool > > QueueDeploymentMessage(Models.RevisionInformation revisionInformation, Models.RevisionInformation? previousRevisionInformation, Api.Models.EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, bool localCommitPushed)
Send the message for a deployment to configured deployment channels.
ValueTask LoadCompileJob(CompileJob job, Action< bool >? activationAction, CancellationToken cancellationToken)
Load a new job into the ICompileJobSink.
diff --git a/_i_chat_manager_8cs_source.html b/_i_chat_manager_8cs_source.html index ae621e87df..499e8ab51f 100644 --- a/_i_chat_manager_8cs_source.html +++ b/_i_chat_manager_8cs_source.html @@ -107,20 +107,21 @@ $(document).ready(function() { init_codefold(0); });
53
58 void QueueWatchdogMessage(string message);
59
-
70 Func<string?, string, Action<bool>> QueueDeploymentMessage(
-
71 Models.RevisionInformation revisionInformation,
-
72 Api.Models.EngineVersion engineVersion,
-
73 DateTimeOffset? estimatedCompletionTime,
-
74 string? gitHubOwner,
-
75 string? gitHubRepo,
-
76 bool localCommitPushed);
-
77
- -
83
-
89 ValueTask UpdateTrackingContexts(CancellationToken cancellationToken);
-
90 }
+
71 Func<string?, string, Action<bool>> QueueDeploymentMessage(
+
72 Models.RevisionInformation revisionInformation,
+
73 Models.RevisionInformation? previousRevisionInformation,
+
74 Api.Models.EngineVersion engineVersion,
+
75 DateTimeOffset? estimatedCompletionTime,
+
76 string? gitHubOwner,
+
77 string? gitHubRepo,
+
78 bool localCommitPushed);
+
79
+ +
85
+
91 ValueTask UpdateTrackingContexts(CancellationToken cancellationToken);
+
92 }
-
91}
+
93}
Represents a message to send to a chat provider.
For managing connected chat services.
@@ -130,8 +131,8 @@ $(document).ready(function() { init_codefold(0); });
ValueTask ChangeChannels(long connectionId, IEnumerable< Models.ChatChannel > newChannels, CancellationToken cancellationToken)
Change chat channels.
IChatTrackingContext CreateTrackingContext()
Start tracking Commands.CustomCommands and ChannelRepresentations.
ValueTask UpdateTrackingContexts(CancellationToken cancellationToken)
Force an update with the active channels on all active IChatTrackingContexts.
-
Func< string?, string, Action< bool > > QueueDeploymentMessage(Models.RevisionInformation revisionInformation, Api.Models.EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, bool localCommitPushed)
Send the message for a deployment to configured deployment channels.
ValueTask ChangeSettings(Models.ChatBot newSettings, CancellationToken cancellationToken)
Change chat settings. If the Api.Models.EntityId.Id is not currently in use, a new connection will be...
+
Func< string?, string, Action< bool > > QueueDeploymentMessage(Models.RevisionInformation revisionInformation, Models.RevisionInformation? previousRevisionInformation, Api.Models.EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, bool localCommitPushed)
Send the message for a deployment to configured deployment channels.
void QueueMessage(MessageContent message, IEnumerable< ulong > channelIds)
Queue a chat message to a given set of channelIds .
Represents a tracking of dynamic chat json files.
Handles Commands.ICommands that map to those defined in a IChatTrackingContext.
diff --git a/_i_provider_8cs_source.html b/_i_provider_8cs_source.html index 8fdedc8726..361965df14 100644 --- a/_i_provider_8cs_source.html +++ b/_i_provider_8cs_source.html @@ -116,22 +116,24 @@ $(document).ready(function() { init_codefold(0); });
74
81 Task SetReconnectInterval(uint reconnectInterval, bool connectNow);
82
-
95 ValueTask<Func<string?, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(
-
96 Models.RevisionInformation revisionInformation,
-
97 Api.Models.EngineVersion engineVersion,
-
98 DateTimeOffset? estimatedCompletionTime,
-
99 string? gitHubOwner,
-
100 string? gitHubRepo,
-
101 ulong channelId,
-
102 bool localCommitPushed,
-
103 CancellationToken cancellationToken);
-
104 }
+
96 ValueTask<Func<string?, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(
+
97 Models.RevisionInformation revisionInformation,
+
98 Models.RevisionInformation? previousRevisionInformation,
+
99 Api.Models.EngineVersion engineVersion,
+
100 DateTimeOffset? estimatedCompletionTime,
+
101 string? gitHubOwner,
+
102 string? gitHubRepo,
+
103 ulong channelId,
+
104 bool localCommitPushed,
+
105 CancellationToken cancellationToken);
+
106 }
-
105}
+
107}
Represents a message received by a IProvider.
Definition Message.cs:9
Represents a message to send to a chat provider.
+
ValueTask< Func< string?, string, ValueTask< Func< bool, ValueTask > > > > SendUpdateMessage(Models.RevisionInformation revisionInformation, Models.RevisionInformation? previousRevisionInformation, Api.Models.EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, ulong channelId, bool localCommitPushed, CancellationToken cancellationToken)
Send the message for a deployment.
ValueTask Disconnect(CancellationToken cancellationToken)
Gracefully disconnects the provider. Permanently stops the reconnection timer.
ValueTask SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
Send a message to the IProvider.
string BotMention
The string that indicates the IProvider was mentioned.
Definition IProvider.cs:30
@@ -140,7 +142,6 @@ $(document).ready(function() { init_codefold(0); });
Task InitialConnectionJob
A Task that completes once the IProvider finishes it's first connection attempt regardless of success...
Definition IProvider.cs:35
bool Connected
If the IProvider is currently connected.
Definition IProvider.cs:20
ValueTask< Dictionary< ChatChannel, IEnumerable< ChannelRepresentation > > > MapChannels(IEnumerable< ChatChannel > channels, CancellationToken cancellationToken)
Get the ChannelRepresentations for given channels .
-
ValueTask< Func< string?, string, ValueTask< Func< bool, ValueTask > > > > SendUpdateMessage(Models.RevisionInformation revisionInformation, Api.Models.EngineVersion engineVersion, DateTimeOffset? estimatedCompletionTime, string? gitHubOwner, string? gitHubRepo, ulong channelId, bool localCommitPushed, CancellationToken cancellationToken)
Send the message for a deployment.
void InitialMappingComplete()
Indicate to the provider that at least one MapChannels(IEnumerable<ChatChannel>, CancellationToken) c...
bool Disposed
If the IProvider was disposed.
Definition IProvider.cs:25
diff --git a/_instance_controller_8cs_source.html b/_instance_controller_8cs_source.html index 2d67af448c..f147a70b18 100644 --- a/_instance_controller_8cs_source.html +++ b/_instance_controller_8cs_source.html @@ -160,7 +160,7 @@ $(document).ready(function() { init_codefold(0); });
116 logger,
118 apiHeaders,
-
119 false)
+
119 false)
120 {
diff --git a/_irc_provider_8cs_source.html b/_irc_provider_8cs_source.html index 1f044241e5..bdd99b7cfb 100644 --- a/_irc_provider_8cs_source.html +++ b/_irc_provider_8cs_source.html @@ -101,700 +101,717 @@ $(document).ready(function() { init_codefold(0); }); - - -
22
- -
24{
-
-
28 sealed class IrcProvider : Provider
-
29 {
-
33 const int PreambleMessageLength = 12;
-
34
-
38 const int MessageBytesLimit = 512;
-
39
-
41 public override bool Connected => client.IsConnected;
-
42
-
44 public override string BotMention => client.Nickname;
-
45
-
49 readonly string address;
-
50
-
54 readonly ushort port;
-
55
-
59 readonly bool ssl;
-
60
-
64 readonly string nickname;
-
65
-
69 readonly string password;
-
70
- -
75
-
79 readonly Dictionary<ulong, string?> channelIdMap;
-
80
-
84 readonly Dictionary<ulong, string> queryChannelIdMap;
-
85
- -
90
- -
95
-
99 IrcFeatures client;
-
100
- -
105
- -
110
-
- - -
122 IAsyncDelayer asyncDelayer,
-
123 ILogger<IrcProvider> logger,
-
124 IAssemblyInformationProvider assemblyInformationProvider,
-
125 Models.ChatBot chatBot,
- -
127 : base(jobManager, asyncDelayer, logger, chatBot)
-
128 {
-
129 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
-
130 ArgumentNullException.ThrowIfNull(loggingConfiguration);
-
131
-
132 var builder = chatBot.CreateConnectionStringBuilder();
-
133 if (builder == null || !builder.Valid || builder is not IrcConnectionStringBuilder ircBuilder)
-
134 throw new InvalidOperationException("Invalid ChatConnectionStringBuilder!");
-
135
-
136 address = ircBuilder.Address!;
-
137 port = ircBuilder.Port!.Value;
-
138 ssl = ircBuilder.UseSsl!.Value;
-
139 nickname = ircBuilder.Nickname!;
-
140
-
141 password = ircBuilder.Password!;
-
142 passwordType = ircBuilder.PasswordType;
-
143
-
144 assemblyInfo = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
-
145 this.loggingConfiguration = loggingConfiguration ?? throw new ArgumentNullException(nameof(loggingConfiguration));
-
146
- -
148
-
149 channelIdMap = new Dictionary<ulong, string?>();
-
150 queryChannelIdMap = new Dictionary<ulong, string>();
- -
152 }
+ + + +
23
+ +
25{
+
+
29 sealed class IrcProvider : Provider
+
30 {
+
34 const int PreambleMessageLength = 12;
+
35
+
39 const int MessageBytesLimit = 512;
+
40
+
42 public override bool Connected => client.IsConnected;
+
43
+
45 public override string BotMention => client.Nickname;
+
46
+
50 readonly string address;
+
51
+
55 readonly ushort port;
+
56
+
60 readonly bool ssl;
+
61
+
65 readonly string nickname;
+
66
+
70 readonly string password;
+
71
+ +
76
+
80 readonly Dictionary<ulong, string?> channelIdMap;
+
81
+
85 readonly Dictionary<ulong, string> queryChannelIdMap;
+
86
+ +
91
+ +
96
+
100 IrcFeatures client;
+
101
+ +
106
+ +
111
+
+ + +
123 IAsyncDelayer asyncDelayer,
+
124 ILogger<IrcProvider> logger,
+
125 IAssemblyInformationProvider assemblyInformationProvider,
+
126 Models.ChatBot chatBot,
+ +
128 : base(jobManager, asyncDelayer, logger, chatBot)
+
129 {
+
130 ArgumentNullException.ThrowIfNull(assemblyInformationProvider);
+
131 ArgumentNullException.ThrowIfNull(loggingConfiguration);
+
132
+
133 var builder = chatBot.CreateConnectionStringBuilder();
+
134 if (builder == null || !builder.Valid || builder is not IrcConnectionStringBuilder ircBuilder)
+
135 throw new InvalidOperationException("Invalid ChatConnectionStringBuilder!");
+
136
+
137 address = ircBuilder.Address!;
+
138 port = ircBuilder.Port!.Value;
+
139 ssl = ircBuilder.UseSsl!.Value;
+
140 nickname = ircBuilder.Nickname!;
+
141
+
142 password = ircBuilder.Password!;
+
143 passwordType = ircBuilder.PasswordType;
+
144
+
145 assemblyInfo = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
+
146 this.loggingConfiguration = loggingConfiguration ?? throw new ArgumentNullException(nameof(loggingConfiguration));
+
147
+ +
149
+
150 channelIdMap = new Dictionary<ulong, string?>();
+
151 queryChannelIdMap = new Dictionary<ulong, string>();
+ +
153 }
-
153
-
-
155 public override async ValueTask DisposeAsync()
-
156 {
-
157 await base.DisposeAsync();
-
158
-
159 // DCT: None available
-
160 await HardDisconnect(CancellationToken.None);
-
161 }
+
154
+
+
156 public override async ValueTask DisposeAsync()
+
157 {
+
158 await base.DisposeAsync();
+
159
+
160 // DCT: None available
+
161 await HardDisconnect(CancellationToken.None);
+
162 }
-
162
-
-
164 public override async ValueTask SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
-
165 {
-
166 ArgumentNullException.ThrowIfNull(message);
-
167
-
168 await Task.Factory.StartNew(
-
169 () =>
-
170 {
-
171 // IRC doesn't allow newlines
-
172 // Explicitly ignore embeds
-
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
-
181 var channelName = channelIdMap[channelId];
-
182 SendType sendType;
-
183 if (channelName == null)
-
184 {
-
185 channelName = queryChannelIdMap[channelId];
-
186 sendType = SendType.Notice;
-
187 }
-
188 else
-
189 sendType = SendType.Message;
-
190
-
191 var messageSize = Encoding.UTF8.GetByteCount(messageText) + Encoding.UTF8.GetByteCount(channelName) + PreambleMessageLength;
-
192 var messageTooLong = messageSize > MessageBytesLimit;
-
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 }
-
200 catch (Exception e)
-
201 {
-
202 Logger.LogWarning(e, "Unable to send to channel {channelName}!", channelName);
-
203 return;
-
204 }
-
205
-
206 if (messageTooLong)
-
207 Logger.LogWarning(
-
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 }
+
163
+
+
165 public override async ValueTask SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
+
166 {
+
167 ArgumentNullException.ThrowIfNull(message);
+
168
+
169 await Task.Factory.StartNew(
+
170 () =>
+
171 {
+
172 // IRC doesn't allow newlines
+
173 // Explicitly ignore embeds
+
174 var messageText = message.Text;
+
175 messageText ??= $"Embed Only: {JsonConvert.SerializeObject(message.Embed)}";
+
176
+
177 messageText = String.Concat(
+
178 messageText
+
179 .Where(x => x != '\r')
+
180 .Select(x => x == '\n' ? '|' : x));
+
181
+
182 var channelName = channelIdMap[channelId];
+
183 SendType sendType;
+
184 if (channelName == null)
+
185 {
+
186 channelName = queryChannelIdMap[channelId];
+
187 sendType = SendType.Notice;
+
188 }
+
189 else
+
190 sendType = SendType.Message;
+
191
+
192 var messageSize = Encoding.UTF8.GetByteCount(messageText) + Encoding.UTF8.GetByteCount(channelName) + PreambleMessageLength;
+
193 var messageTooLong = messageSize > MessageBytesLimit;
+
194 if (messageTooLong)
+
195 messageText = $"TGS: Could not send message to IRC. Line write exceeded protocol limit of {MessageBytesLimit}B.";
+
196
+
197 try
+
198 {
+
199 client.SendMessage(sendType, channelName, messageText);
+
200 }
+
201 catch (Exception e)
+
202 {
+
203 Logger.LogWarning(e, "Unable to send to channel {channelName}!", channelName);
+
204 return;
+
205 }
+
206
+
207 if (messageTooLong)
+
208 Logger.LogWarning(
+
209 "Failed to send to channel {channelId}: Message size ({messageSize}B) exceeds IRC limit of 512B",
+
210 channelId,
+
211 messageSize);
+
212 },
+
213 cancellationToken,
+ +
215 TaskScheduler.Current);
+
216 }
-
216
-
-
218 public override async ValueTask<Func<string?, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(
-
219 Models.RevisionInformation revisionInformation,
-
220 EngineVersion engineVersion,
-
221 DateTimeOffset? estimatedCompletionTime,
-
222 string? gitHubOwner,
-
223 string? gitHubRepo,
-
224 ulong channelId,
-
225 bool localCommitPushed,
-
226 CancellationToken cancellationToken)
-
227 {
-
228 ArgumentNullException.ThrowIfNull(revisionInformation);
-
229 ArgumentNullException.ThrowIfNull(engineVersion);
-
230
-
231 var commitInsert = revisionInformation.CommitSha![..7];
-
232 string remoteCommitInsert;
-
233 if (revisionInformation.CommitSha == revisionInformation.OriginCommitSha)
-
234 {
-
235 commitInsert = String.Format(CultureInfo.InvariantCulture, localCommitPushed ? "^{0}" : "{0}", commitInsert);
-
236 remoteCommitInsert = String.Empty;
-
237 }
-
238 else
-
239 remoteCommitInsert = String.Format(CultureInfo.InvariantCulture, ". Remote commit: ^{0}", revisionInformation.OriginCommitSha![..7]);
-
240
-
241 var testmergeInsert = (revisionInformation.ActiveTestMerges?.Count ?? 0) == 0
-
242 ? String.Empty
-
243 : String.Format(
-
244 CultureInfo.InvariantCulture,
-
245 " (Test Merges: {0})",
-
246 String.Join(
-
247 ", ",
-
248 revisionInformation
-
249 .ActiveTestMerges!
-
250 .Select(x => x.TestMerge)
-
251 .Select(x =>
-
252 {
-
253 var result = String.Format(CultureInfo.InvariantCulture, "#{0} at {1}", x.Number, x.TargetCommitSha![..7]);
-
254 if (x.Comment != null)
-
255 result += String.Format(CultureInfo.InvariantCulture, " ({0})", x.Comment);
-
256 return result;
-
257 })));
-
258
-
259 var prefix = GetEngineCompilerPrefix(engineVersion.Engine!.Value);
-
260 await SendMessage(
-
261 null,
- -
263 {
-
264 Text = String.Format(
-
265 CultureInfo.InvariantCulture,
-
266 $"{prefix}: Deploying revision: {0}{1}{2} BYOND Version: {3}{4}",
-
267 commitInsert,
-
268 testmergeInsert,
-
269 remoteCommitInsert,
-
270 engineVersion.ToString(),
-
271 estimatedCompletionTime.HasValue
-
272 ? $" ETA: {estimatedCompletionTime - DateTimeOffset.UtcNow}"
-
273 : String.Empty),
-
274 },
-
275 channelId,
-
276 cancellationToken);
+
217
+
+
219 public override async ValueTask<Func<string?, string, ValueTask<Func<bool, ValueTask>>>> SendUpdateMessage(
+
220 Models.RevisionInformation revisionInformation,
+
221 Models.RevisionInformation? previousRevisionInformation,
+
222 EngineVersion engineVersion,
+
223 DateTimeOffset? estimatedCompletionTime,
+
224 string? gitHubOwner,
+
225 string? gitHubRepo,
+
226 ulong channelId,
+
227 bool localCommitPushed,
+
228 CancellationToken cancellationToken)
+
229 {
+
230 ArgumentNullException.ThrowIfNull(revisionInformation);
+
231 ArgumentNullException.ThrowIfNull(engineVersion);
+
232
+
233 var previousTestMerges = (IEnumerable<RevInfoTestMerge>?)previousRevisionInformation?.ActiveTestMerges ?? Enumerable.Empty<RevInfoTestMerge>();
+
234 var currentTestMerges = (IEnumerable<RevInfoTestMerge>?)revisionInformation.ActiveTestMerges ?? Enumerable.Empty<RevInfoTestMerge>();
+
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 = !currentTestMerges.Any()
+
247 ? String.Empty
+
248 : String.Format(
+
249 CultureInfo.InvariantCulture,
+
250 " (Test Merges: {0})",
+
251 String.Join(
+
252 ", ",
+
253 currentTestMerges
+
254 .Select(x => x.TestMerge)
+
255 .Select(x =>
+
256 {
+
257 var status = string.Empty;
+
258 if (!previousTestMerges.Any(y => y.TestMerge.Number == x.Number))
+
259 status = "Added";
+
260 else if (previousTestMerges.Any(y => y.TestMerge.Number == x.Number && y.TestMerge.TargetCommitSha != x.TargetCommitSha))
+
261 status = "Updated";
+
262
+
263 var result = $"#{x.Number} at {x.TargetCommitSha![..7]}";
+
264
+
265 if (!string.IsNullOrEmpty(x.Comment))
+
266 {
+
267 if (!string.IsNullOrEmpty(status))
+
268 result += $" ({status} - {x.Comment})";
+
269 else
+
270 result += $" ({x.Comment})";
+
271 }
+
272 else if (!string.IsNullOrEmpty(status))
+
273 result += $" ({status})";
+
274
+
275 return result;
+
276 })));
277
-
278 return async (errorMessage, dreamMakerOutput) =>
-
279 {
-
280 await SendMessage(
-
281 null,
- -
283 {
-
284 Text = $"{prefix}: Deployment {(errorMessage == null ? "complete" : "failed")}!",
-
285 },
-
286 channelId,
-
287 cancellationToken);
-
288
-
289 return active => ValueTask.CompletedTask;
-
290 };
-
291 }
+
278 var prefix = GetEngineCompilerPrefix(engineVersion.Engine!.Value);
+
279 await SendMessage(
+
280 null,
+ +
282 {
+
283 Text = String.Format(
+
284 CultureInfo.InvariantCulture,
+
285 $"{prefix}: Deploying revision: {0}{1}{2} BYOND Version: {3}{4}",
+
286 commitInsert,
+
287 testmergeInsert,
+
288 remoteCommitInsert,
+
289 engineVersion.ToString(),
+
290 estimatedCompletionTime.HasValue
+
291 ? $" ETA: {estimatedCompletionTime - DateTimeOffset.UtcNow}"
+
292 : String.Empty),
+
293 },
+
294 channelId,
+
295 cancellationToken);
+
296
+
297 return async (errorMessage, dreamMakerOutput) =>
+
298 {
+
299 await SendMessage(
+
300 null,
+ +
302 {
+
303 Text = $"{prefix}: Deployment {(errorMessage == null ? "complete" : "failed")}!",
+
304 },
+
305 channelId,
+
306 cancellationToken);
+
307
+
308 return active => ValueTask.CompletedTask;
+
309 };
+
310 }
-
292
-
294 protected override async ValueTask<Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>> MapChannelsImpl(
-
295 IEnumerable<Models.ChatChannel> channels,
-
296 CancellationToken cancellationToken)
-
297 => await Task.Factory.StartNew(
-
298 () =>
-
299 {
-
300 if (channels.Any(x => x.IrcChannel == null))
-
301 throw new InvalidOperationException("ChatChannel missing IrcChannel!");
-
302 lock (client)
-
303 {
-
304 var channelsWithKeys = new Dictionary<string, string>();
-
305 var hs = new HashSet<string>(); // for unique inserts
-
306 foreach (var channel in channels)
-
307 {
-
308 var name = channel.GetIrcChannelName();
-
309 var key = channel.GetIrcChannelKey();
-
310 if (hs.Add(name) && key != null)
-
311 channelsWithKeys.Add(name, key);
-
312 }
-
313
-
314 var toPart = new List<string>();
-
315 foreach (var activeChannel in client.JoinedChannels)
-
316 if (!hs.Remove(activeChannel))
-
317 toPart.Add(activeChannel);
-
318
-
319 foreach (var channelToLeave in toPart)
-
320 client.RfcPart(channelToLeave, "Pretty nice abscond!");
-
321 foreach (var channelToJoin in hs)
-
322 if (channelsWithKeys.TryGetValue(channelToJoin, out var key))
-
323 client.RfcJoin(channelToJoin, key);
-
324 else
-
325 client.RfcJoin(channelToJoin);
-
326
-
327 return new Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
-
328 channels
-
329 .Select(dbChannel =>
-
330 {
-
331 var channelName = dbChannel.GetIrcChannelName();
-
332 ulong? id = null;
-
333 if (!channelIdMap.Any(y =>
-
334 {
-
335 if (y.Value != channelName)
-
336 return false;
-
337 id = y.Key;
-
338 return true;
-
339 }))
-
340 {
-
341 id = channelIdCounter++;
-
342 channelIdMap.Add(id.Value, channelName);
-
343 }
-
344
-
345 return new KeyValuePair<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
-
346 dbChannel,
-
347 new List<ChannelRepresentation>
-
348 {
-
349 new(address, channelName, id!.Value)
-
350 {
-
351 Tag = dbChannel.Tag,
-
352 IsAdminChannel = dbChannel.IsAdminChannel == true,
-
353 IsPrivateChannel = false,
-
354 EmbedsSupported = false,
-
355 },
-
356 });
-
357 }));
-
358 }
-
359 },
-
360 cancellationToken,
- -
362 TaskScheduler.Current);
+
311
+
313 protected override async ValueTask<Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>> MapChannelsImpl(
+
314 IEnumerable<Models.ChatChannel> channels,
+
315 CancellationToken cancellationToken)
+
316 => await Task.Factory.StartNew(
+
317 () =>
+
318 {
+
319 if (channels.Any(x => x.IrcChannel == null))
+
320 throw new InvalidOperationException("ChatChannel missing IrcChannel!");
+
321 lock (client)
+
322 {
+
323 var channelsWithKeys = new Dictionary<string, string>();
+
324 var hs = new HashSet<string>(); // for unique inserts
+
325 foreach (var channel in channels)
+
326 {
+
327 var name = channel.GetIrcChannelName();
+
328 var key = channel.GetIrcChannelKey();
+
329 if (hs.Add(name) && key != null)
+
330 channelsWithKeys.Add(name, key);
+
331 }
+
332
+
333 var toPart = new List<string>();
+
334 foreach (var activeChannel in client.JoinedChannels)
+
335 if (!hs.Remove(activeChannel))
+
336 toPart.Add(activeChannel);
+
337
+
338 foreach (var channelToLeave in toPart)
+
339 client.RfcPart(channelToLeave, "Pretty nice abscond!");
+
340 foreach (var channelToJoin in hs)
+
341 if (channelsWithKeys.TryGetValue(channelToJoin, out var key))
+
342 client.RfcJoin(channelToJoin, key);
+
343 else
+
344 client.RfcJoin(channelToJoin);
+
345
+
346 return new Dictionary<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
+
347 channels
+
348 .Select(dbChannel =>
+
349 {
+
350 var channelName = dbChannel.GetIrcChannelName();
+
351 ulong? id = null;
+
352 if (!channelIdMap.Any(y =>
+
353 {
+
354 if (y.Value != channelName)
+
355 return false;
+
356 id = y.Key;
+
357 return true;
+
358 }))
+
359 {
+
360 id = channelIdCounter++;
+
361 channelIdMap.Add(id.Value, channelName);
+
362 }
363
-
-
365 protected override async ValueTask Connect(CancellationToken cancellationToken)
-
366 {
-
367 cancellationToken.ThrowIfCancellationRequested();
-
368 try
-
369 {
-
370 await Task.Factory.StartNew(
-
371 () =>
-
372 {
-
373 client = InstantiateClient();
-
374 client.Connect(address, port);
-
375 },
-
376 cancellationToken,
- -
378 TaskScheduler.Current)
-
379 .WaitAsync(cancellationToken);
-
380
-
381 cancellationToken.ThrowIfCancellationRequested();
+
364 return new KeyValuePair<Models.ChatChannel, IEnumerable<ChannelRepresentation>>(
+
365 dbChannel,
+
366 new List<ChannelRepresentation>
+
367 {
+
368 new(address, channelName, id!.Value)
+
369 {
+
370 Tag = dbChannel.Tag,
+
371 IsAdminChannel = dbChannel.IsAdminChannel == true,
+
372 IsPrivateChannel = false,
+
373 EmbedsSupported = false,
+
374 },
+
375 });
+
376 }));
+
377 }
+
378 },
+
379 cancellationToken,
+ +
381 TaskScheduler.Current);
382
-
383 listenTask = Task.Factory.StartNew(
-
384 () =>
-
385 {
-
386 Logger.LogTrace("Starting blocking listen...");
-
387 try
-
388 {
-
389 client.Listen();
-
390 }
-
391 catch (Exception ex)
-
392 {
-
393 Logger.LogWarning(ex, "IRC Main Listen Exception!");
-
394 }
-
395
-
396 Logger.LogTrace("Exiting listening task...");
-
397 },
-
398 cancellationToken,
- -
400 TaskScheduler.Current);
+
+
384 protected override async ValueTask Connect(CancellationToken cancellationToken)
+
385 {
+
386 cancellationToken.ThrowIfCancellationRequested();
+
387 try
+
388 {
+
389 await Task.Factory.StartNew(
+
390 () =>
+
391 {
+
392 client = InstantiateClient();
+
393 client.Connect(address, port);
+
394 },
+
395 cancellationToken,
+ +
397 TaskScheduler.Current)
+
398 .WaitAsync(cancellationToken);
+
399
+
400 cancellationToken.ThrowIfCancellationRequested();
401
-
402 Logger.LogTrace("Authenticating ({passwordType})...", passwordType);
-
403 switch (passwordType)
-
404 {
-
405 case IrcPasswordType.Server:
-
406 client.RfcPass(password);
-
407 await Login(client, nickname, cancellationToken);
-
408 break;
-
409 case IrcPasswordType.NickServ:
-
410 await Login(client, nickname, cancellationToken);
-
411 cancellationToken.ThrowIfCancellationRequested();
-
412 client.SendMessage(SendType.Message, "NickServ", String.Format(CultureInfo.InvariantCulture, "IDENTIFY {0}", password));
-
413 break;
-
414 case IrcPasswordType.Sasl:
-
415 await SaslAuthenticate(cancellationToken);
-
416 break;
-
417 case IrcPasswordType.Oper:
-
418 await Login(client, nickname, cancellationToken);
-
419 cancellationToken.ThrowIfCancellationRequested();
-
420 client.RfcOper(nickname, password, Priority.Critical);
-
421 break;
-
422 case null:
-
423 await Login(client, nickname, cancellationToken);
-
424 break;
-
425 default:
-
426 throw new InvalidOperationException($"Invalid IrcPasswordType: {passwordType.Value}");
-
427 }
-
428
-
429 cancellationToken.ThrowIfCancellationRequested();
-
430
-
431 Logger.LogTrace("Connection established!");
-
432 }
-
433 catch (Exception e) when (e is not OperationCanceledException)
-
434 {
-
435 throw new JobException(ErrorCode.ChatCannotConnectProvider, e);
-
436 }
-
437 }
+
402 listenTask = Task.Factory.StartNew(
+
403 () =>
+
404 {
+
405 Logger.LogTrace("Starting blocking listen...");
+
406 try
+
407 {
+
408 client.Listen();
+
409 }
+
410 catch (Exception ex)
+
411 {
+
412 Logger.LogWarning(ex, "IRC Main Listen Exception!");
+
413 }
+
414
+
415 Logger.LogTrace("Exiting listening task...");
+
416 },
+
417 cancellationToken,
+ +
419 TaskScheduler.Current);
+
420
+
421 Logger.LogTrace("Authenticating ({passwordType})...", passwordType);
+
422 switch (passwordType)
+
423 {
+
424 case IrcPasswordType.Server:
+
425 client.RfcPass(password);
+
426 await Login(client, nickname, cancellationToken);
+
427 break;
+
428 case IrcPasswordType.NickServ:
+
429 await Login(client, nickname, cancellationToken);
+
430 cancellationToken.ThrowIfCancellationRequested();
+
431 client.SendMessage(SendType.Message, "NickServ", String.Format(CultureInfo.InvariantCulture, "IDENTIFY {0}", password));
+
432 break;
+
433 case IrcPasswordType.Sasl:
+
434 await SaslAuthenticate(cancellationToken);
+
435 break;
+
436 case IrcPasswordType.Oper:
+
437 await Login(client, nickname, cancellationToken);
+
438 cancellationToken.ThrowIfCancellationRequested();
+
439 client.RfcOper(nickname, password, Priority.Critical);
+
440 break;
+
441 case null:
+
442 await Login(client, nickname, cancellationToken);
+
443 break;
+
444 default:
+
445 throw new InvalidOperationException($"Invalid IrcPasswordType: {passwordType.Value}");
+
446 }
+
447
+
448 cancellationToken.ThrowIfCancellationRequested();
+
449
+
450 Logger.LogTrace("Connection established!");
+
451 }
+
452 catch (Exception e) when (e is not OperationCanceledException)
+
453 {
+
454 throw new JobException(ErrorCode.ChatCannotConnectProvider, e);
+
455 }
+
456 }
-
438
-
-
440 protected override async ValueTask DisconnectImpl(CancellationToken cancellationToken)
-
441 {
-
442 try
-
443 {
-
444 await Task.Factory.StartNew(
-
445 () =>
-
446 {
-
447 try
-
448 {
-
449 client.RfcQuit("Mr. Stark, I don't feel so good...", Priority.Critical); // priocritical otherwise it wont go through
-
450 }
-
451 catch (Exception e)
-
452 {
-
453 Logger.LogWarning(e, "Error quitting IRC!");
-
454 }
-
455 },
-
456 cancellationToken,
- -
458 TaskScheduler.Current);
-
459 await HardDisconnect(cancellationToken);
-
460 }
-
461 catch (OperationCanceledException)
+
457
+
+
459 protected override async ValueTask DisconnectImpl(CancellationToken cancellationToken)
+
460 {
+
461 try
462 {
-
463 throw;
-
464 }
-
465 catch (Exception e)
-
466 {
-
467 Logger.LogWarning(e, "Error disconnecting from IRC!");
-
468 }
-
469 }
-
-
470
-
-
479 async ValueTask Login(IrcFeatures client, string nickname, CancellationToken cancellationToken)
-
480 {
-
481 var promise = new TaskCompletionSource<object>();
-
482
-
483 void Callback(object? sender, EventArgs e)
-
484 {
-
485 Logger.LogTrace("IRC Registered.");
-
486 promise.TrySetResult(e);
+
463 await Task.Factory.StartNew(
+
464 () =>
+
465 {
+
466 try
+
467 {
+
468 client.RfcQuit("Mr. Stark, I don't feel so good...", Priority.Critical); // priocritical otherwise it wont go through
+
469 }
+
470 catch (Exception e)
+
471 {
+
472 Logger.LogWarning(e, "Error quitting IRC!");
+
473 }
+
474 },
+
475 cancellationToken,
+ +
477 TaskScheduler.Current);
+
478 await HardDisconnect(cancellationToken);
+
479 }
+
480 catch (OperationCanceledException)
+
481 {
+
482 throw;
+
483 }
+
484 catch (Exception e)
+
485 {
+
486 Logger.LogWarning(e, "Error disconnecting from IRC!");
487 }
-
488
-
489 client.OnRegistered += Callback;
-
490
-
491 client.Login(nickname, nickname, 0, nickname);
-
492
-
493 using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
-
494 cts.CancelAfter(TimeSpan.FromSeconds(30));
-
495
-
496 try
-
497 {
-
498 await promise.Task.WaitAsync(cts.Token);
-
499 client.OnRegistered -= Callback;
-
500 }
-
501 catch (OperationCanceledException)
-
502 {
-
503 if (client.IsConnected)
-
504 client.Disconnect();
-
505 throw new JobException("Timed out waiting for IRC Registration");
+
488 }
+
+
489
+
+
498 async ValueTask Login(IrcFeatures client, string nickname, CancellationToken cancellationToken)
+
499 {
+
500 var promise = new TaskCompletionSource<object>();
+
501
+
502 void Callback(object? sender, EventArgs e)
+
503 {
+
504 Logger.LogTrace("IRC Registered.");
+
505 promise.TrySetResult(e);
506 }
-
507 }
+
507
+
508 client.OnRegistered += Callback;
+
509
+
510 client.Login(nickname, nickname, 0, nickname);
+
511
+
512 using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+
513 cts.CancelAfter(TimeSpan.FromSeconds(30));
+
514
+
515 try
+
516 {
+
517 await promise.Task.WaitAsync(cts.Token);
+
518 client.OnRegistered -= Callback;
+
519 }
+
520 catch (OperationCanceledException)
+
521 {
+
522 if (client.IsConnected)
+
523 client.Disconnect();
+
524 throw new JobException("Timed out waiting for IRC Registration");
+
525 }
+
526 }
-
508
-
-
514 void HandleMessage(IrcEventArgs e, bool isPrivate)
-
515 {
-
516 if (e.Data.Nick.Equals(client.Nickname, StringComparison.OrdinalIgnoreCase))
-
517 return;
-
518
-
519 var username = e.Data.Nick;
-
520 var channelName = isPrivate ? username : e.Data.Channel;
-
521
-
522 ulong MapAndGetChannelId(Dictionary<ulong, string?> dicToCheck)
-
523 {
-
524 ulong? resultId = null;
-
525 if (!dicToCheck.Any(x =>
-
526 {
-
527 if (x.Value != channelName)
-
528 return false;
-
529 resultId = x.Key;
-
530 return true;
-
531 }))
-
532 {
-
533 resultId = channelIdCounter++;
-
534 dicToCheck.Add(resultId.Value, channelName);
-
535 if (dicToCheck == queryChannelIdMap)
-
536 channelIdMap.Add(resultId.Value, null);
-
537 }
-
538
-
539 return resultId!.Value;
-
540 }
-
541
-
542 ulong userId, channelId;
-
543 lock (client)
-
544 {
-
545 userId = MapAndGetChannelId(new Dictionary<ulong, string?>(queryChannelIdMap
-
546 .Cast<KeyValuePair<ulong, string?>>())); // NRT my beloathed
-
547 channelId = isPrivate ? userId : MapAndGetChannelId(channelIdMap);
-
548 }
-
549
-
550 var channelFriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture, "PM: {0}", channelName) : channelName;
-
551 var message = new Message(
-
552 new ChatUser(
-
553 new ChannelRepresentation(address, channelFriendlyName, channelId)
-
554 {
-
555 IsPrivateChannel = isPrivate,
-
556 EmbedsSupported = false,
+
527
+
+
533 void HandleMessage(IrcEventArgs e, bool isPrivate)
+
534 {
+
535 if (e.Data.Nick.Equals(client.Nickname, StringComparison.OrdinalIgnoreCase))
+
536 return;
+
537
+
538 var username = e.Data.Nick;
+
539 var channelName = isPrivate ? username : e.Data.Channel;
+
540
+
541 ulong MapAndGetChannelId(Dictionary<ulong, string?> dicToCheck)
+
542 {
+
543 ulong? resultId = null;
+
544 if (!dicToCheck.Any(x =>
+
545 {
+
546 if (x.Value != channelName)
+
547 return false;
+
548 resultId = x.Key;
+
549 return true;
+
550 }))
+
551 {
+
552 resultId = channelIdCounter++;
+
553 dicToCheck.Add(resultId.Value, channelName);
+
554 if (dicToCheck == queryChannelIdMap)
+
555 channelIdMap.Add(resultId.Value, null);
+
556 }
557
-
558 // isAdmin and Tag populated by manager
-
559 },
-
560 username,
-
561 username,
-
562 userId),
-
563 e.Data.Message);
-
564
-
565 EnqueueMessage(message);
-
566 }
+
558 return resultId!.Value;
+
559 }
+
560
+
561 ulong userId, channelId;
+
562 lock (client)
+
563 {
+
564 userId = MapAndGetChannelId(new Dictionary<ulong, string?>(queryChannelIdMap
+
565 .Cast<KeyValuePair<ulong, string?>>())); // NRT my beloathed
+
566 channelId = isPrivate ? userId : MapAndGetChannelId(channelIdMap);
+
567 }
+
568
+
569 var channelFriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture, "PM: {0}", channelName) : channelName;
+
570 var message = new Message(
+
571 new ChatUser(
+
572 new ChannelRepresentation(address, channelFriendlyName, channelId)
+
573 {
+
574 IsPrivateChannel = isPrivate,
+
575 EmbedsSupported = false,
+
576
+
577 // isAdmin and Tag populated by manager
+
578 },
+
579 username,
+
580 username,
+
581 userId),
+
582 e.Data.Message);
+
583
+
584 EnqueueMessage(message);
+
585 }
-
567
-
573 void Client_OnQueryMessage(object sender, IrcEventArgs e) => HandleMessage(e, true);
-
574
-
580 void Client_OnChannelMessage(object sender, IrcEventArgs e) => HandleMessage(e, false);
-
581
-
587 Task NonBlockingListen(CancellationToken cancellationToken) => Task.Factory.StartNew(
-
588 () =>
-
589 {
-
590 try
-
591 {
-
592 client.Listen(false);
-
593 }
-
594 catch (Exception ex)
-
595 {
-
596 Logger.LogWarning(ex, "IRC Non-Blocking Listen Exception!");
-
597 }
-
598 },
-
599 cancellationToken,
-
600 TaskCreationOptions.None,
-
601 TaskScheduler.Current)
-
602 .WaitAsync(cancellationToken);
-
603
-
-
609 async ValueTask SaslAuthenticate(CancellationToken cancellationToken)
-
610 {
-
611 client.WriteLine("CAP REQ :sasl", Priority.Critical); // needs to be put in the buffer before anything else
-
612 cancellationToken.ThrowIfCancellationRequested();
-
613
-
614 Logger.LogTrace("Logging in...");
-
615 client.Login(nickname, nickname, 0, nickname);
-
616 cancellationToken.ThrowIfCancellationRequested();
-
617
-
618 // wait for the SASL ack or timeout
-
619 var receivedAck = false;
-
620 var receivedPlus = false;
-
621
-
622 void AuthenticationDelegate(object sender, ReadLineEventArgs e)
-
623 {
-
624 if (e.Line.Contains("ACK :sasl", StringComparison.Ordinal))
-
625 receivedAck = true;
-
626 else if (e.Line.Contains("AUTHENTICATE +", StringComparison.Ordinal))
-
627 receivedPlus = true;
-
628 }
-
629
-
630 Logger.LogTrace("Performing handshake...");
-
631 client.OnReadLine += AuthenticationDelegate;
-
632 try
-
633 {
-
634 using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
-
635 timeoutCts.CancelAfter(TimeSpan.FromSeconds(25));
-
636 var timeoutToken = timeoutCts.Token;
-
637
-
638 var listenTimeSpan = TimeSpan.FromMilliseconds(10);
-
639 for (; !receivedAck;
-
640 await AsyncDelayer.Delay(listenTimeSpan, timeoutToken))
-
641 await NonBlockingListen(cancellationToken);
-
642
-
643 client.WriteLine("AUTHENTICATE PLAIN", Priority.Critical);
-
644 timeoutToken.ThrowIfCancellationRequested();
-
645
-
646 for (; !receivedPlus;
-
647 await AsyncDelayer.Delay(listenTimeSpan, timeoutToken))
-
648 await NonBlockingListen(cancellationToken);
-
649 }
-
650 finally
-
651 {
-
652 client.OnReadLine -= AuthenticationDelegate;
-
653 }
-
654
-
655 cancellationToken.ThrowIfCancellationRequested();
+
586
+
592 void Client_OnQueryMessage(object sender, IrcEventArgs e) => HandleMessage(e, true);
+
593
+
599 void Client_OnChannelMessage(object sender, IrcEventArgs e) => HandleMessage(e, false);
+
600
+
606 Task NonBlockingListen(CancellationToken cancellationToken) => Task.Factory.StartNew(
+
607 () =>
+
608 {
+
609 try
+
610 {
+
611 client.Listen(false);
+
612 }
+
613 catch (Exception ex)
+
614 {
+
615 Logger.LogWarning(ex, "IRC Non-Blocking Listen Exception!");
+
616 }
+
617 },
+
618 cancellationToken,
+
619 TaskCreationOptions.None,
+
620 TaskScheduler.Current)
+
621 .WaitAsync(cancellationToken);
+
622
+
+
628 async ValueTask SaslAuthenticate(CancellationToken cancellationToken)
+
629 {
+
630 client.WriteLine("CAP REQ :sasl", Priority.Critical); // needs to be put in the buffer before anything else
+
631 cancellationToken.ThrowIfCancellationRequested();
+
632
+
633 Logger.LogTrace("Logging in...");
+
634 client.Login(nickname, nickname, 0, nickname);
+
635 cancellationToken.ThrowIfCancellationRequested();
+
636
+
637 // wait for the SASL ack or timeout
+
638 var receivedAck = false;
+
639 var receivedPlus = false;
+
640
+
641 void AuthenticationDelegate(object sender, ReadLineEventArgs e)
+
642 {
+
643 if (e.Line.Contains("ACK :sasl", StringComparison.Ordinal))
+
644 receivedAck = true;
+
645 else if (e.Line.Contains("AUTHENTICATE +", StringComparison.Ordinal))
+
646 receivedPlus = true;
+
647 }
+
648
+
649 Logger.LogTrace("Performing handshake...");
+
650 client.OnReadLine += AuthenticationDelegate;
+
651 try
+
652 {
+
653 using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
+
654 timeoutCts.CancelAfter(TimeSpan.FromSeconds(25));
+
655 var timeoutToken = timeoutCts.Token;
656
-
657 // Stolen! https://github.com/znc/znc/blob/1e697580155d5a38f8b5a377f3b1d94aaa979539/modules/sasl.cpp#L196
-
658 Logger.LogTrace("Sending credentials...");
-
659 var authString = String.Format(
-
660 CultureInfo.InvariantCulture,
-
661 "{0}{1}{0}{1}{2}",
-
662 nickname,
-
663 '\0',
-
664 password);
-
665 var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString));
-
666 var authLine = $"AUTHENTICATE {b64}";
-
667 client.WriteLine(authLine, Priority.Critical);
-
668 cancellationToken.ThrowIfCancellationRequested();
-
669
-
670 Logger.LogTrace("Finishing authentication...");
-
671 client.WriteLine("CAP END", Priority.Critical);
-
672 }
-
+
657 var listenTimeSpan = TimeSpan.FromMilliseconds(10);
+
658 for (; !receivedAck;
+
659 await AsyncDelayer.Delay(listenTimeSpan, timeoutToken))
+
660 await NonBlockingListen(cancellationToken);
+
661
+
662 client.WriteLine("AUTHENTICATE PLAIN", Priority.Critical);
+
663 timeoutToken.ThrowIfCancellationRequested();
+
664
+
665 for (; !receivedPlus;
+
666 await AsyncDelayer.Delay(listenTimeSpan, timeoutToken))
+
667 await NonBlockingListen(cancellationToken);
+
668 }
+
669 finally
+
670 {
+
671 client.OnReadLine -= AuthenticationDelegate;
+
672 }
673
-
-
679 async ValueTask HardDisconnect(CancellationToken cancellationToken)
-
680 {
-
681 if (!Connected)
-
682 {
-
683 Logger.LogTrace("Not hard disconnecting, already offline");
-
684 return;
-
685 }
-
686
-
687 Logger.LogTrace("Hard disconnect");
+
674 cancellationToken.ThrowIfCancellationRequested();
+
675
+
676 // Stolen! https://github.com/znc/znc/blob/1e697580155d5a38f8b5a377f3b1d94aaa979539/modules/sasl.cpp#L196
+
677 Logger.LogTrace("Sending credentials...");
+
678 var authString = String.Format(
+
679 CultureInfo.InvariantCulture,
+
680 "{0}{1}{0}{1}{2}",
+
681 nickname,
+
682 '\0',
+
683 password);
+
684 var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString));
+
685 var authLine = $"AUTHENTICATE {b64}";
+
686 client.WriteLine(authLine, Priority.Critical);
+
687 cancellationToken.ThrowIfCancellationRequested();
688
-
689 // This call blocks permanently randomly sometimes
-
690 // Frankly I don't give a shit
-
691 var disconnectTask = Task.Factory.StartNew(
-
692 () =>
-
693 {
-
694 try
-
695 {
-
696 client.Disconnect();
-
697 }
-
698 catch (Exception e)
-
699 {
-
700 Logger.LogWarning(e, "Error disconnecting IRC!");
-
701 }
-
702 },
-
703 cancellationToken,
- -
705 TaskScheduler.Current);
-
706
-
707 await Task.WhenAny(
-
708 Task.WhenAll(
-
709 disconnectTask,
-
710 listenTask ?? Task.CompletedTask),
-
711 AsyncDelayer.Delay(TimeSpan.FromSeconds(5), cancellationToken).AsTask());
-
712 }
+
689 Logger.LogTrace("Finishing authentication...");
+
690 client.WriteLine("CAP END", Priority.Critical);
+
691 }
-
713
-
-
719 IrcFeatures InstantiateClient()
-
720 {
-
721 var newClient = new IrcFeatures
-
722 {
-
723 SupportNonRfc = true,
-
724 CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...",
-
725 AutoRejoin = true,
-
726 AutoRejoinOnKick = true,
-
727 AutoRelogin = false,
-
728 AutoRetry = false,
-
729 AutoReconnect = false,
-
730 ActiveChannelSyncing = true,
-
731 AutoNickHandling = true,
-
732 CtcpVersion = assemblyInfo.VersionString,
-
733 UseSsl = ssl,
-
734 EnableUTF8Recode = true,
-
735 };
-
736 if (ssl)
-
737 newClient.ValidateServerCertificate = true; // dunno if it defaults to that or what
-
738
-
739 newClient.OnChannelMessage += Client_OnChannelMessage;
-
740 newClient.OnQueryMessage += Client_OnQueryMessage;
-
741
-
742 if (loggingConfiguration.ProviderNetworkDebug)
-
743 {
-
744 newClient.OnReadLine += (sender, e) => Logger.LogTrace("READ: {line}", e.Line);
-
745 newClient.OnWriteLine += (sender, e) => Logger.LogTrace("WRITE: {line}", e.Line);
-
746 }
-
747
-
748 newClient.OnError += (sender, e) =>
-
749 {
-
750 Logger.LogError("IRC ERROR: {error}", e.ErrorMessage);
-
751 newClient.Disconnect();
-
752 };
-
753
-
754 return newClient;
-
755 }
+
692
+
+
698 async ValueTask HardDisconnect(CancellationToken cancellationToken)
+
699 {
+
700 if (!Connected)
+
701 {
+
702 Logger.LogTrace("Not hard disconnecting, already offline");
+
703 return;
+
704 }
+
705
+
706 Logger.LogTrace("Hard disconnect");
+
707
+
708 // This call blocks permanently randomly sometimes
+
709 // Frankly I don't give a shit
+
710 var disconnectTask = Task.Factory.StartNew(
+
711 () =>
+
712 {
+
713 try
+
714 {
+
715 client.Disconnect();
+
716 }
+
717 catch (Exception e)
+
718 {
+
719 Logger.LogWarning(e, "Error disconnecting IRC!");
+
720 }
+
721 },
+
722 cancellationToken,
+ +
724 TaskScheduler.Current);
+
725
+
726 await Task.WhenAny(
+
727 Task.WhenAll(
+
728 disconnectTask,
+
729 listenTask ?? Task.CompletedTask),
+
730 AsyncDelayer.Delay(TimeSpan.FromSeconds(5), cancellationToken).AsTask());
+
731 }
-
756 }
+
732
+
+
738 IrcFeatures InstantiateClient()
+
739 {
+
740 var newClient = new IrcFeatures
+
741 {
+
742 SupportNonRfc = true,
+
743 CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...",
+
744 AutoRejoin = true,
+
745 AutoRejoinOnKick = true,
+
746 AutoRelogin = false,
+
747 AutoRetry = false,
+
748 AutoReconnect = false,
+
749 ActiveChannelSyncing = true,
+
750 AutoNickHandling = true,
+
751 CtcpVersion = assemblyInfo.VersionString,
+
752 UseSsl = ssl,
+
753 EnableUTF8Recode = true,
+
754 };
+
755 if (ssl)
+
756 newClient.ValidateServerCertificate = true; // dunno if it defaults to that or what
+
757
+
758 newClient.OnChannelMessage += Client_OnChannelMessage;
+
759 newClient.OnQueryMessage += Client_OnQueryMessage;
+
760
+
761 if (loggingConfiguration.ProviderNetworkDebug)
+
762 {
+
763 newClient.OnReadLine += (sender, e) => Logger.LogTrace("READ: {line}", e.Line);
+
764 newClient.OnWriteLine += (sender, e) => Logger.LogTrace("WRITE: {line}", e.Line);
+
765 }
+
766
+
767 newClient.OnError += (sender, e) =>
+
768 {
+
769 Logger.LogError("IRC ERROR: {error}", e.ErrorMessage);
+
770 newClient.Disconnect();
+
771 };
+
772
+
773 return newClient;
+
774 }
-
757}
+
775 }
+
+
776}
Information about an engine installation.
- -
ChatConnectionStringBuilder for ChatProvider.Irc.
Represents a tgs_chat_user datum.
Definition ChatUser.cs:12
- -
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.
+ +
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)
- -
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 ...
- -
async ValueTask SaslAuthenticate(CancellationToken cancellationToken)
Run SASL authentication on client.
-
readonly string address
Address of the server to connect to.
+
override string BotMention
The string that indicates the IProvider was mentioned.
+
override async ValueTask DisconnectImpl(CancellationToken cancellationToken)
+ +
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 ...
+ +
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.
-
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.
- -
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.
+
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.
+ +
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.
+
readonly bool ssl
Wether or not this IRC client is to use ssl.
+
override async ValueTask< Func< string?, string, ValueTask< Func< bool, ValueTask > > > > SendUpdateMessage(Models.RevisionInformation revisionInformation, Models.RevisionInformation? previousRevisionInformation, 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 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).
-
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.
Definition Message.cs:9
static string GetEngineCompilerPrefix(Api.Models.EngineType engineType)
Get the prefix for messages about deployments.
@@ -806,6 +823,7 @@ $(document).ready(function() { init_codefold(0); });
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.
+
Many to many relationship for Models.RevisionInformation and Models.TestMerge.
async ValueTask Delay(TimeSpan timeSpan, CancellationToken cancellationToken)
Create a Task that completes after a given timeSpan .A ValueTask representing the running operation.
Manages the runtime of Jobs.
@@ -815,11 +833,13 @@ $(document).ready(function() { init_codefold(0); });
IrcPasswordType
Represents the type of a password for a ChatProvider.Irc.
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition ErrorCode.cs:12
+
if(removedTestMerges.Count !=0) fields.Add(new EmbedField("Removed
+
diff --git a/_provider_8cs_source.html b/_provider_8cs_source.html index fca30f6e88..16d62342f6 100644 --- a/_provider_8cs_source.html +++ b/_provider_8cs_source.html @@ -241,135 +241,136 @@ $(document).ready(function() { init_codefold(0); });
195
198
- + -
202 Api.Models.EngineVersion engineVersion,
-
203 DateTimeOffset? estimatedCompletionTime,
-
204 string? gitHubOwner,
-
205 string? gitHubRepo,
-
206 ulong channelId,
- -
208 CancellationToken cancellationToken);
-
209
-
215 protected abstract ValueTask Connect(CancellationToken cancellationToken);
-
216
-
222 protected abstract ValueTask DisconnectImpl(CancellationToken cancellationToken);
-
223
- - -
232 CancellationToken cancellationToken);
-
233
-
- -
239 {
-
240 if (message == null)
-
241 Logger.LogTrace("Requesting channel remap...");
-
242
- -
244 {
-
245 messageQueue.Enqueue(message);
-
246 nextMessage.TrySetResult();
-
247 }
-
248 }
+ +
203 Api.Models.EngineVersion engineVersion,
+
204 DateTimeOffset? estimatedCompletionTime,
+
205 string? gitHubOwner,
+
206 string? gitHubRepo,
+
207 ulong channelId,
+ +
209 CancellationToken cancellationToken);
+
210
+
216 protected abstract ValueTask Connect(CancellationToken cancellationToken);
+
217
+
223 protected abstract ValueTask DisconnectImpl(CancellationToken cancellationToken);
+
224
+ + +
233 CancellationToken cancellationToken);
+
234
+
+ +
240 {
+
241 if (message == null)
+
242 Logger.LogTrace("Requesting channel remap...");
+
243
+ +
245 {
+
246 messageQueue.Enqueue(message);
+
247 nextMessage.TrySetResult();
+
248 }
+
249 }
-
249
-
- -
255 {
-
256 Logger.LogTrace("StopReconnectionTimer");
- -
258 if (reconnectCts != null)
-
259 {
-
260 reconnectCts.Cancel();
-
261 reconnectCts.Dispose();
-
262 reconnectCts = null;
-
263 var reconnectTask = this.reconnectTask!;
-
264 this.reconnectTask = null;
-
265 return reconnectTask;
-
266 }
-
267 else
-
268 Logger.LogTrace("Timer wasn't running");
-
269
-
270 return Task.CompletedTask;
-
271 }
+
250
+
+ +
256 {
+
257 Logger.LogTrace("StopReconnectionTimer");
+ +
259 if (reconnectCts != null)
+
260 {
+
261 reconnectCts.Cancel();
+
262 reconnectCts.Dispose();
+
263 reconnectCts = null;
+
264 var reconnectTask = this.reconnectTask!;
+
265 this.reconnectTask = null;
+
266 return reconnectTask;
+
267 }
+
268 else
+
269 Logger.LogTrace("Timer wasn't running");
+
270
+
271 return Task.CompletedTask;
+
272 }
-
272
-
- -
281 {
-
282 do
-
283 {
-
284 try
-
285 {
-
286 if (!connectNow)
- -
288 else
-
289 connectNow = false;
-
290 if (!Connected)
-
291 {
-
292 var job = Job.Create(Api.Models.JobCode.ReconnectChatBot, null, ChatBot.Instance!, ChatBotRights.WriteEnabled);
-
293 job.Description += $": {ChatBot.Name}";
-
294
- -
296 job,
-
297 async (core, databaseContextFactory, paramJob, progressReporter, jobCancellationToken) =>
-
298 {
-
299 try
-
300 {
-
301 if (Connected)
-
302 {
-
303 Logger.LogTrace("Disconnecting...");
- -
305 }
-
306 else
-
307 Logger.LogTrace("Already disconnected not doing disconnection attempt!");
-
308
-
309 Logger.LogTrace("Connecting...");
- -
311 Logger.LogTrace("Connected successfully");
-
312 EnqueueMessage(null);
-
313 }
-
314 catch
-
315 {
-
316 // we set this here because otherwise there could be stuff waiting on to connect us forever
-
317 initialConnectionTcs.TrySetResult();
-
318 throw;
-
319 }
-
320 },
- -
322
- -
324 }
-
325 }
- -
327 {
-
328 Logger.LogTrace(e, "ReconnectionLoop cancelled");
-
329 }
-
330 catch (Exception e)
-
331 {
-
332 Logger.LogError(e, "Error reconnecting!");
-
333 }
-
334 }
-
335 while (!cancellationToken.IsCancellationRequested);
-
336
-
337 Logger.LogTrace("ReconnectionLoop exiting...");
-
338 }
+
273
+
+ +
282 {
+
283 do
+
284 {
+
285 try
+
286 {
+
287 if (!connectNow)
+ +
289 else
+
290 connectNow = false;
+
291 if (!Connected)
+
292 {
+
293 var job = Job.Create(Api.Models.JobCode.ReconnectChatBot, null, ChatBot.Instance!, ChatBotRights.WriteEnabled);
+
294 job.Description += $": {ChatBot.Name}";
+
295
+ +
297 job,
+
298 async (core, databaseContextFactory, paramJob, progressReporter, jobCancellationToken) =>
+
299 {
+
300 try
+
301 {
+
302 if (Connected)
+
303 {
+
304 Logger.LogTrace("Disconnecting...");
+ +
306 }
+
307 else
+
308 Logger.LogTrace("Already disconnected not doing disconnection attempt!");
+
309
+
310 Logger.LogTrace("Connecting...");
+ +
312 Logger.LogTrace("Connected successfully");
+
313 EnqueueMessage(null);
+
314 }
+
315 catch
+
316 {
+
317 // we set this here because otherwise there could be stuff waiting on to connect us forever
+
318 initialConnectionTcs.TrySetResult();
+
319 throw;
+
320 }
+
321 },
+ +
323
+ +
325 }
+
326 }
+ +
328 {
+
329 Logger.LogTrace(e, "ReconnectionLoop cancelled");
+
330 }
+
331 catch (Exception e)
+
332 {
+
333 Logger.LogError(e, "Error reconnecting!");
+
334 }
+
335 }
+
336 while (!cancellationToken.IsCancellationRequested);
+
337
+
338 Logger.LogTrace("ReconnectionLoop exiting...");
+
339 }
-
339 }
+
340 }
-
340}
+
341}
Represents a message received by a IProvider.
Definition Message.cs:9
ValueTask Connect(CancellationToken cancellationToken)
Attempt to connect the Provider.
bool Disposed
If the IProvider was disposed.
Definition Provider.cs:117
-
Task StopReconnectionTimer()
Stops and awaits the reconnectTask.
Definition Provider.cs:254
+
Task StopReconnectionTimer()
Stops and awaits the reconnectTask.
Definition Provider.cs:255
async ValueTask Disconnect(CancellationToken cancellationToken)
Gracefully disconnects the provider. Permanently stops the reconnection timer.A ValueTask representin...
Definition Provider.cs:131
ValueTask DisconnectImpl(CancellationToken cancellationToken)
Gracefully disconnects the provider.
Provider(IJobManager jobManager, IAsyncDelayer asyncDelayer, ILogger< Provider > logger, ChatBot chatBot)
Initializes a new instance of the Provider class.
Definition Provider.cs:92
ValueTask< Dictionary< ChatChannel, IEnumerable< ChannelRepresentation > > > MapChannelsImpl(IEnumerable< ChatChannel > channels, CancellationToken cancellationToken)
Implementation of MapChannels(IEnumerable<ChatChannel>, CancellationToken).
async Task< Message?> NextMessage(CancellationToken cancellationToken)
Get a Task<TResult> resulting in the next Message the IProvider receives or null on a disconnect....
Definition Provider.cs:163
-
void EnqueueMessage(Message? message)
Queues a message for NextMessage(CancellationToken).
Definition Provider.cs:238
+
void EnqueueMessage(Message? message)
Queues a message for NextMessage(CancellationToken).
Definition Provider.cs:239
bool Connected
If the IProvider is currently connected.
Definition Provider.cs:111
readonly Queue< Message?> messageQueue
Queue<T> of received Messages.
Definition Provider.cs:45
readonly TaskCompletionSource initialConnectionTcs
The backing TaskCompletionSource for InitialConnectionJob.
Definition Provider.cs:50
@@ -382,10 +383,10 @@ $(document).ready(function() { init_codefold(0); });
ILogger< Provider > Logger
The ILogger for the Provider.
Definition Provider.cs:35
readonly object reconnectTaskLock
Used for synchronizing access to reconnectCts and reconnectTask.
Definition Provider.cs:55
Task SetReconnectInterval(uint reconnectInterval, bool connectNow)
Set the interval at which the provider starts jobs to try to reconnect.A Task representing the runnin...
Definition Provider.cs:180
-
async Task ReconnectionLoop(uint reconnectInterval, bool connectNow, CancellationToken cancellationToken)
Creates a Task that will attempt to reconnect the Provider every reconnectInterval minutes.
Definition Provider.cs:280
+
async Task ReconnectionLoop(uint reconnectInterval, bool connectNow, CancellationToken cancellationToken)
Creates a Task that will attempt to reconnect the Provider every reconnectInterval minutes.
Definition Provider.cs:281
string BotMention
The string that indicates the IProvider was mentioned.
Definition Provider.cs:114
-
ValueTask< Func< string?, string, ValueTask< Func< bool, ValueTask > > > > SendUpdateMessage(RevisionInformation revisionInformation, Api.Models.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,...
+
ValueTask< Func< string?, string, ValueTask< Func< bool, ValueTask > > > > SendUpdateMessage(RevisionInformation revisionInformation, RevisionInformation? previousRevisionInformation, Api.Models.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,...
void InitialMappingComplete()
Indicate to the provider that at least one MapChannels(IEnumerable<ChatChannel>, CancellationToken) c...
ValueTask SendMessage(Message? replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
Send a message to the IProvider.A ValueTask representing the running operation.
diff --git a/_provider_factory_8cs_source.html b/_provider_factory_8cs_source.html index 2852aa815f..247db05d24 100644 --- a/_provider_factory_8cs_source.html +++ b/_provider_factory_8cs_source.html @@ -142,10 +142,10 @@ $(document).ready(function() { init_codefold(0); });
84 settings,
-
86 ChatProvider.Discord => new DiscordProvider(
+
86 ChatProvider.Discord => new DiscordProvider(
-
89 loggerFactory.CreateLogger<DiscordProvider>(),
+
89 loggerFactory.CreateLogger<DiscordProvider>(),
91 settings,
@@ -156,7 +156,8 @@ $(document).ready(function() { init_codefold(0); });
96 }
97}
- + +
readonly FileLoggingConfiguration loggingConfiguration
The FileLoggingConfiguration for the ProviderFactory.
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the ProviderFactory.
diff --git a/_repository_update_service_8cs_source.html b/_repository_update_service_8cs_source.html index 7f321f0d5d..772d70ad9e 100644 --- a/_repository_update_service_8cs_source.html +++ b/_repository_update_service_8cs_source.html @@ -629,7 +629,7 @@ $(document).ready(function() { init_codefold(0); });
587 currentModel.AccessUser,
588 currentModel.AccessToken,
589 true,
-
590 false,
+
590 false,
591 checkoutReporter,
592 default);
593
@@ -744,6 +744,7 @@ $(document).ready(function() { init_codefold(0); });
@ CompileJobs
User may list and read all Models.Internal.CompileJobs.
@ Id
Lookup the Api.Models.EntityId.Id of the Models.PermissionSet.
+ diff --git a/_watchdog_base_8cs_source.html b/_watchdog_base_8cs_source.html index 9a773aae6d..139221f3a3 100644 --- a/_watchdog_base_8cs_source.html +++ b/_watchdog_base_8cs_source.html @@ -1179,7 +1179,7 @@ $(document).ready(function() { init_codefold(0); });
1261 response.ChannelIds!
1262 .Select(channelIdString =>
1263 {
- +
1264 if (UInt64.TryParse(channelIdString, out var channelId))
1265 return (ulong?)channelId;
1266 else
1267 Logger.LogWarning("Could not parse chat response channel ID: {channelID}", channelIdString);
diff --git a/annotated.html b/annotated.html index 0c2bf48f0a..1a005a9126 100644 --- a/annotated.html +++ b/annotated.html @@ -299,13 +299,14 @@ $(function() {  NProviders  CDiscordForwardingResponderAn IResponder<TGatewayEvent> that forwards to another targetResponder  CDiscordMessageA Message containing the source IMessageReference - CIDiscordRespondersCombined interface for the IResponder types used by TGS - CIProviderFor interacting with a chat service - CIProviderFactoryFactory for IProviders - CIrcProviderIProvider for internet relay chat - CMessageRepresents a message received by a IProvider - CProvider - CProviderFactory + CDiscordProviderIProvider for the Discord app + CIDiscordRespondersCombined interface for the IResponder types used by TGS + CIProviderFor interacting with a chat service + CIProviderFactoryFactory for IProviders + CIrcProviderIProvider for internet relay chat + CMessageRepresents a message received by a IProvider + CProvider + CProviderFactory  CChannelMappingRepresents a mapping of a ChannelRepresentation.RealId  CChannelRepresentationRepresents a Providers.IProvider channel  CChatManager diff --git a/changelog.yml b/changelog.yml index 5d82858e6f..26f48fa8a8 100644 --- a/changelog.yml +++ b/changelog.yml @@ -181,7 +181,6 @@ Components: Core: - Version: 6.15.0 ComponentVersions: - Core: 6.14.1 HttpApi: 10.12.1 GraphQLApi: 0.5.0 DreamMakerApi: 7.3.1 @@ -192,7 +191,12 @@ Components: NugetApi: 17.0.1 NugetClient: 20.0.0 WebControlPanel: 6.7.3 - Changes: [] + Changes: + - Descriptions: + - Discord and IRC chatbot now will indicate either Added or Updated next to TM entries (this also changes sorting) in deployment messages + - Discord chatbot now will list removed TMs in deployment messages + Author: Drulikar + PullRequest: 2134 Unreleased: true - Version: 6.14.2 ComponentVersions: {} diff --git a/class_i_async_disposable.html b/class_i_async_disposable.html index 99a1813796..df8a325297 100644 --- a/class_i_async_disposable.html +++ b/class_i_async_disposable.html @@ -132,8 +132,10 @@ Inheritance diagram for IAsyncDisposable:
- - + + + + @@ -161,8 +163,6 @@ Inheritance diagram for IAsyncDisposable:
- - diff --git a/class_i_async_disposable__inherit__graph.map b/class_i_async_disposable__inherit__graph.map index 53410fd5d2..04dcc65468 100644 --- a/class_i_async_disposable__inherit__graph.map +++ b/class_i_async_disposable__inherit__graph.map @@ -10,34 +10,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -53,49 +53,49 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/class_i_async_disposable__inherit__graph.md5 b/class_i_async_disposable__inherit__graph.md5 index 55d01ad4ea..717dd083a8 100644 --- a/class_i_async_disposable__inherit__graph.md5 +++ b/class_i_async_disposable__inherit__graph.md5 @@ -1 +1 @@ -86ca00e2a5da75ebd2ac91fc60cd235a \ No newline at end of file +fc586586a21a66eea64c920078b1fbc6 \ No newline at end of file diff --git a/class_i_async_disposable__inherit__graph.png b/class_i_async_disposable__inherit__graph.png index 8545cd2a31..e7150b2deb 100644 Binary files a/class_i_async_disposable__inherit__graph.png and b/class_i_async_disposable__inherit__graph.png differ diff --git a/class_i_responder.html b/class_i_responder.html index 5b524c09b1..850d975ccb 100644 --- a/class_i_responder.html +++ b/class_i_responder.html @@ -83,9 +83,12 @@ Inheritance diagram for IResponder:
- - - + + + + + +
[legend]

The documentation for this class was generated from the following file:
    diff --git a/class_i_responder__inherit__graph.map b/class_i_responder__inherit__graph.map index 2186b32b38..6f68327559 100644 --- a/class_i_responder__inherit__graph.map +++ b/class_i_responder__inherit__graph.map @@ -2,9 +2,12 @@ - - - - - + + + + + + + + diff --git a/class_i_responder__inherit__graph.md5 b/class_i_responder__inherit__graph.md5 index 95c08cb759..62a84027e3 100644 --- a/class_i_responder__inherit__graph.md5 +++ b/class_i_responder__inherit__graph.md5 @@ -1 +1 @@ -7d79b9b9813eb3d6161566d71b440840 \ No newline at end of file +73348c534d91eb87a021dec4112bf4e6 \ No newline at end of file diff --git a/class_i_responder__inherit__graph.png b/class_i_responder__inherit__graph.png index 290ac3513c..abcc04b425 100644 Binary files a/class_i_responder__inherit__graph.png and b/class_i_responder__inherit__graph.png differ 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 a86a9e8e12..fea4a81989 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.Engine.EngineManager.StartAsync(), and Tgstation.Server.Api.Models.EngineVersion.ToString().

Here is the call graph for this function:
@@ -447,39 +447,37 @@ Here is the caller graph for this function:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -685,7 +683,7 @@ Here is the caller graph for this function:

Definition at line 45 of file EngineVersion.cs.

45{ get; set; }
-

Referenced by Tgstation.Server.Host.Components.Engine.EngineManager.AssertAndLockVersion(), Tgstation.Server.Host.Components.Engine.EngineManager.CheckVersionParameter(), Tgstation.Server.Api.Models.EngineVersion.EngineVersion(), Tgstation.Server.Api.Models.EngineVersion.Equals(), Tgstation.Server.Host.Components.Chat.Commands.EngineCommand.Invoke(), Tgstation.Server.Api.Models.EngineVersion.ToString(), and Tgstation.Server.Api.Models.EngineVersion.TryParse().

+

Referenced by Tgstation.Server.Host.Components.Engine.EngineManager.AssertAndLockVersion(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.BuildUpdateEmbedFields(), Tgstation.Server.Host.Components.Engine.EngineManager.CheckVersionParameter(), Tgstation.Server.Api.Models.EngineVersion.EngineVersion(), Tgstation.Server.Api.Models.EngineVersion.Equals(), Tgstation.Server.Host.Components.Chat.Commands.EngineCommand.Invoke(), Tgstation.Server.Api.Models.EngineVersion.ToString(), and Tgstation.Server.Api.Models.EngineVersion.TryParse().

@@ -714,7 +712,7 @@ Here is the caller graph for this function:

Definition at line 24 of file EngineVersion.cs.

24{ get; set; }
-

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.Chat.Providers.DiscordProvider.BuildUpdateEmbedFields(), 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.DiscordProvider.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_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.map b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.map index ce597d8881..edbe4368b9 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.map +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.map @@ -1,35 +1,33 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.md5 b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.md5 index 3aaeb3ac88..0cbfff5517 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.md5 +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.md5 @@ -1 +1 @@ -026b1bb99fa097c410b289a108de9223 \ No newline at end of file +7e9e36f4a88458eb00d9f178c5beb889 \ No newline at end of file diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.png b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.png index 17ccedfedc..bc6cf8aa87 100644 Binary files a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.png and b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_engine_version_a5576a2503f8e4371139e9abe80df44d2_icgraph.png differ diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_bot_settings.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_bot_settings.html index 2bd2a84f6c..3d3a5357a4 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_bot_settings.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_bot_settings.html @@ -294,7 +294,7 @@ Here is the call graph for this function:

Definition at line 46 of file ChatBotSettings.cs.

46{ get; set; }
-

Referenced by Tgstation.Server.Api.Models.Internal.ChatBotSettings.CreateConnectionStringBuilder(), Tgstation.Server.Host.Database.MySqlDatabaseContext.OnModelCreating(), Tgstation.Server.Api.Models.Internal.ChatBotSettings.SetConnectionStringBuilder(), and Tgstation.Server.Host.Models.ChatBot.ToApi().

+

Referenced by Tgstation.Server.Api.Models.Internal.ChatBotSettings.CreateConnectionStringBuilder(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), Tgstation.Server.Host.Database.MySqlDatabaseContext.OnModelCreating(), Tgstation.Server.Api.Models.Internal.ChatBotSettings.SetConnectionStringBuilder(), and Tgstation.Server.Host.Models.ChatBot.ToApi().

diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_channel_base.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_channel_base.html index e5bfc88000..87c330b5bb 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_channel_base.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_chat_channel_base.html @@ -145,7 +145,7 @@ Properties

Definition at line 15 of file ChatChannelBase.cs.

15{ get; set; }
-

Referenced by Tgstation.Server.Host.Models.ChatChannel.ToApi().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), and Tgstation.Server.Host.Models.ChatChannel.ToApi().

diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_dream_daemon_launch_parameters.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_dream_daemon_launch_parameters.html index f52b5c4825..56114c24fa 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_dream_daemon_launch_parameters.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_internal_1_1_dream_daemon_launch_parameters.html @@ -371,7 +371,7 @@ Properties

Definition at line 107 of file DreamDaemonLaunchParameters.cs.

107{ get; set; }
-

Referenced by Tgstation.Server.Api.Models.Internal.DreamDaemonLaunchParameters.CanApplyWithoutReboot(), Tgstation.Server.Host.Components.Session.SessionControllerFactory.LaunchNew(), and Tgstation.Server.Host.Components.Deployment.DreamMaker.RunCompileJob().

+

Referenced by Tgstation.Server.Api.Models.Internal.DreamDaemonLaunchParameters.CanApplyWithoutReboot(), Tgstation.Server.Host.Components.Session.SessionControllerFactory.LaunchNew(), and Tgstation.Server.Host.Components.Deployment.DreamMaker.RunCompileJob().

@@ -576,7 +576,7 @@ Properties

Definition at line 68 of file DreamDaemonLaunchParameters.cs.

68{ get; set; }
-

Referenced by Tgstation.Server.Host.Components.Watchdog.AdvancedWatchdog.HandleNormalReboot(), Tgstation.Server.Host.Components.Session.SessionControllerFactory.LaunchNew(), and Tgstation.Server.Host.Components.Deployment.DreamMaker.RunCompileJob().

+

Referenced by Tgstation.Server.Host.Components.Watchdog.AdvancedWatchdog.HandleNormalReboot(), Tgstation.Server.Host.Components.Session.SessionControllerFactory.LaunchNew(), and Tgstation.Server.Host.Components.Deployment.DreamMaker.RunCompileJob().

diff --git a/class_tgstation_1_1_server_1_1_client_1_1_rest_server_client_factory.html b/class_tgstation_1_1_server_1_1_client_1_1_rest_server_client_factory.html index 3832fd2daa..1923e6620d 100644 --- a/class_tgstation_1_1_server_1_1_client_1_1_rest_server_client_factory.html +++ b/class_tgstation_1_1_server_1_1_client_1_1_rest_server_client_factory.html @@ -635,7 +635,7 @@ Here is the call graph for this function:
178 host,
179 apiHeaders,
180 attemptLoginRefresh ? loginHeaders : null,
-
181 false));
+
181 false));
182 if (timeout.HasValue)
183 client.Timeout = timeout.Value;
184
@@ -647,6 +647,7 @@ Here is the call graph for this function:
Represents a JWT returned by the API.
Routes to a server actions.
Definition Routes.cs:9
const string ApiRoot
The root of API methods.
Definition Routes.cs:13
+

References Tgstation.Server.Api.Routes.ApiRoot, Tgstation.Server.Client.ApiClientFactory.CreateApiClient(), and Tgstation.Server.Client.RestServerClientFactory.productHeaderValue.

diff --git a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions.html b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions.html index e62d7874f3..e1f83278ae 100644 --- a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions.html +++ b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions.html @@ -165,115 +165,116 @@ Static Public Member Functions
118 }
-

Referenced by Tgstation.Server.Host.Components.Deployment.DmbFactory.CleanRegisteredCompileJob(), Tgstation.Server.Host.Components.Deployment.DmbFactory.CleanUnusedCompileJobs(), Tgstation.Server.Host.Components.Deployment.DreamMaker.CleanupFailedCompile(), Tgstation.Server.Host.Swarm.SwarmService.CommitUpdate(), Tgstation.Server.Host.Components.Deployment.DreamMaker.DeploymentProcess(), Tgstation.Server.Client.ApiClient.DisposeAsync(), Tgstation.Server.Host.Security.IdentityCache.DisposeAsync(), Tgstation.Server.Host.Components.StaticFiles.Configuration.EnsureDirectories(), Tgstation.Server.Host.Components.StaticFiles.Configuration.ExecuteEventScripts(), Tgstation.Server.Host.Components.Watchdog.WatchdogBase.HandleEventImpl(), Tgstation.Server.Host.Swarm.SwarmService.HealthCheckNodes(), Tgstation.Server.Host.Components.Engine.OpenDreamInstaller.Install(), Tgstation.Server.Host.Components.Engine.WindowsOpenDreamInstaller.Install(), Tgstation.Server.Host.Components.Engine.PosixByondInstaller.Install(), Tgstation.Server.Host.Components.Engine.WindowsByondInstaller.Install(), Tgstation.Server.Host.Components.InstanceManager.OfflineInstance(), Tgstation.Server.Host.Components.Deployment.Remote.BaseRemoteDeploymentManager.PostDeploymentComments(), Tgstation.Server.Host.Swarm.SwarmService.RemoteAbortUpdate(), Tgstation.Server.Host.Components.Chat.ChatManager.RemoveProviderChannels(), Tgstation.Server.Host.Server.RestartImpl(), Tgstation.Server.Client.ApiClient.RunRequest< TResult >(), Tgstation.Server.Host.Components.Chat.ChatManager.SendMessage(), Tgstation.Server.Host.Swarm.SwarmService.SendUpdatedServerListToNodes(), Tgstation.Server.Host.Swarm.SwarmService.Shutdown(), Tgstation.Server.Host.Components.Chat.ChatManager.StartAsync(), Tgstation.Server.Host.Components.Engine.EngineManager.StartAsync(), Tgstation.Server.Host.Components.InstanceManager.StopAsync(), Tgstation.Server.Host.Jobs.JobService.StopAsync(), Tgstation.Server.Host.Components.StaticFiles.Configuration.SymlinkStaticFilesTo(), and Tgstation.Server.Host.Controllers.InstanceController.Update().

+

Referenced by Tgstation.Server.Host.Components.Deployment.DmbFactory.CleanRegisteredCompileJob(), Tgstation.Server.Host.Components.Deployment.DmbFactory.CleanUnusedCompileJobs(), Tgstation.Server.Host.Components.Deployment.DreamMaker.CleanupFailedCompile(), Tgstation.Server.Host.Swarm.SwarmService.CommitUpdate(), Tgstation.Server.Host.Components.Deployment.DreamMaker.DeploymentProcess(), Tgstation.Server.Client.ApiClient.DisposeAsync(), Tgstation.Server.Host.Security.IdentityCache.DisposeAsync(), Tgstation.Server.Host.Components.StaticFiles.Configuration.EnsureDirectories(), Tgstation.Server.Host.Components.StaticFiles.Configuration.ExecuteEventScripts(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.GetAllAccessibleTextChannels(), Tgstation.Server.Host.Components.Watchdog.WatchdogBase.HandleEventImpl(), Tgstation.Server.Host.Swarm.SwarmService.HealthCheckNodes(), Tgstation.Server.Host.Components.Engine.OpenDreamInstaller.Install(), Tgstation.Server.Host.Components.Engine.WindowsOpenDreamInstaller.Install(), Tgstation.Server.Host.Components.Engine.PosixByondInstaller.Install(), Tgstation.Server.Host.Components.Engine.WindowsByondInstaller.Install(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), Tgstation.Server.Host.Components.InstanceManager.OfflineInstance(), Tgstation.Server.Host.Components.Deployment.Remote.BaseRemoteDeploymentManager.PostDeploymentComments(), Tgstation.Server.Host.Swarm.SwarmService.RemoteAbortUpdate(), Tgstation.Server.Host.Components.Chat.ChatManager.RemoveProviderChannels(), Tgstation.Server.Host.Server.RestartImpl(), Tgstation.Server.Client.ApiClient.RunRequest< TResult >(), Tgstation.Server.Host.Components.Chat.ChatManager.SendMessage(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendMessage(), Tgstation.Server.Host.Swarm.SwarmService.SendUpdatedServerListToNodes(), Tgstation.Server.Host.Swarm.SwarmService.Shutdown(), Tgstation.Server.Host.Components.Chat.ChatManager.StartAsync(), Tgstation.Server.Host.Components.Engine.EngineManager.StartAsync(), Tgstation.Server.Host.Components.InstanceManager.StopAsync(), Tgstation.Server.Host.Jobs.JobService.StopAsync(), Tgstation.Server.Host.Components.StaticFiles.Configuration.SymlinkStaticFilesTo(), and Tgstation.Server.Host.Controllers.InstanceController.Update().

Here is the caller graph for this function:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.map b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.map index 09dfe136d4..889559894c 100644 --- a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.map +++ b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.map @@ -1,105 +1,106 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.md5 b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.md5 index e994f1c03f..476d830807 100644 --- a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.md5 +++ b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.md5 @@ -1 +1 @@ -dd60652a86251a9c6216f582dd91d2ae \ No newline at end of file +f362d7194955570eef60bb309037ed46 \ No newline at end of file diff --git a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.png b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.png index 9b87ab8b34..5ae9f5af21 100644 Binary files a/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.png and b/class_tgstation_1_1_server_1_1_common_1_1_extensions_1_1_value_task_extensions_a58adbd94d764754bd3e6c566a5fe7fe3_icgraph.png differ diff --git a/class_tgstation_1_1_server_1_1_host_1_1_authority_1_1_user_authority.html b/class_tgstation_1_1_server_1_1_host_1_1_authority_1_1_user_authority.html index f16e948537..3c847596eb 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_authority_1_1_user_authority.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_authority_1_1_user_authority.html @@ -487,7 +487,7 @@ Additional Inherited Members -
Returns
true if checks failed and failResponse was populated, false otherwise.
+
Returns
true if checks failed and failResponse was populated, false otherwise.

Definition at line 190 of file UserAuthority.cs.

194 {
diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_channel_representation.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_channel_representation.html index 59ed2740b5..29232e5723 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_channel_representation.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_channel_representation.html @@ -208,7 +208,7 @@ Properties

Definition at line 38 of file ChannelRepresentation.cs.

38{ get; }
-

Referenced by Tgstation.Server.Host.Components.Chat.ChannelRepresentation.ChannelRepresentation(), and Tgstation.Server.Host.Components.Chat.ChatManager.ProcessMessage().

+

Referenced by Tgstation.Server.Host.Components.Chat.ChannelRepresentation.ChannelRepresentation(), and Tgstation.Server.Host.Components.Chat.ChatManager.ProcessMessage().

@@ -322,7 +322,7 @@ Properties

Definition at line 43 of file ChannelRepresentation.cs.

43{ get; set; }
-

Referenced by Tgstation.Server.Host.Components.Chat.ChatManager.ProcessMessage().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), and Tgstation.Server.Host.Components.Chat.ChatManager.ProcessMessage().

@@ -351,7 +351,7 @@ Properties

Definition at line 48 of file ChannelRepresentation.cs.

48{ get; init; }
-

Referenced by Tgstation.Server.Host.Components.Chat.ChatManager.ProcessMessage().

+

Referenced by Tgstation.Server.Host.Components.Chat.ChatManager.ProcessMessage().

@@ -384,7 +384,7 @@ Properties
27 set => Id = value.ToString(CultureInfo.InvariantCulture);
28 }
-

Referenced by Tgstation.Server.Host.Components.Chat.ChannelRepresentation.ChannelRepresentation(), and Tgstation.Server.Host.Components.Chat.ChatManager.ProcessMessage().

+

Referenced by Tgstation.Server.Host.Components.Chat.ChannelRepresentation.ChannelRepresentation(), and Tgstation.Server.Host.Components.Chat.ChatManager.ProcessMessage().

@@ -413,6 +413,8 @@ Properties

Definition at line 53 of file ChannelRepresentation.cs.

53{ get; set; }
+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl().

+
The documentation for this class was generated from the following file: diff --git a/functions_h.html b/functions_h.html index e275dbb187..1f7de8594a 100644 --- a/functions_h.html +++ b/functions_h.html @@ -98,9 +98,8 @@ $(function() {
  • Head : Tgstation.Server.Host.Components.Repository.IRepository, Tgstation.Server.Host.Components.Repository.Repository
  • Header : Tgstation.Server.Client.GraphQL.AuthorizationMessageHandler
  • headerOverride : Tgstation.Server.Client.GraphQL.AuthorizationMessageHandler
  • -
  • Headers : Tgstation.Server.Client.ApiClient
  • headers : Tgstation.Server.Client.ApiClient
  • -
  • Headers : Tgstation.Server.Client.IApiClient
  • +
  • Headers : Tgstation.Server.Client.ApiClient, Tgstation.Server.Client.IApiClient
  • HeadersException() : Tgstation.Server.Api.HeadersException, Tgstation.Server.Host.Utils.ApiHeadersProvider, Tgstation.Server.Host.Utils.IApiHeadersProvider
  • HeadersIssue() : Tgstation.Server.Host.Controllers.ApiController
  • HeadIncludeLine : Tgstation.Server.Host.Components.StaticFiles.ServerSideModifications
  • diff --git a/functions_j.html b/functions_j.html index 1810d4c680..e5d5e1b095 100644 --- a/functions_j.html +++ b/functions_j.html @@ -87,7 +87,7 @@ $(function() {
  • jobManager : Tgstation.Server.Host.Controllers.DreamDaemonController, Tgstation.Server.Host.Controllers.DreamMakerController, Tgstation.Server.Host.Controllers.EngineController, Tgstation.Server.Host.Controllers.InstanceController, Tgstation.Server.Host.Controllers.JobController, Tgstation.Server.Host.Controllers.RepositoryController
  • JobProgressReporter() : Tgstation.Server.Host.Jobs.JobProgressReporter
  • JobResponse() : Tgstation.Server.Client.Components.EngineClient, Tgstation.Server.Host.Controllers.JobController
  • -
  • Jobs : Tgstation.Server.Api.Routes, Tgstation.Server.Client.Components.IInstanceClient, Tgstation.Server.Client.Components.InstanceClient, Tgstation.Server.Host.Database.DatabaseContext, Tgstation.Server.Host.Database.IDatabaseContext
  • +
  • Jobs : Tgstation.Server.Api.Routes, Tgstation.Server.Client.Components.IInstanceClient, Tgstation.Server.Client.Components.InstanceClient, Tgstation.Server.Host.Database.DatabaseContext, Tgstation.Server.Host.Database.IDatabaseContext
  • jobs : Tgstation.Server.Host.Jobs.JobService
  • Jobs : Tgstation.Server.Host.Models.Instance
  • JobsClient() : Tgstation.Server.Client.Components.JobsClient
  • diff --git a/functions_m.html b/functions_m.html index 5e32e8da21..ddfe2d5d2a 100644 --- a/functions_m.html +++ b/functions_m.html @@ -77,10 +77,10 @@ $(function() {
  • MajorGraphQLApiVersion : Tgstation.Server.Host.GraphQL.Types.GatewayInformation
  • MakeActive() : Tgstation.Server.Host.Components.Deployment.SwappableDmbProvider
  • MapChannels() : Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • -
  • MapChannelsImpl() : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • +
  • MapChannelsImpl() : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • MapConnectionGroups() : Tgstation.Server.Host.Jobs.JobsHubGroupMapper
  • MapMySqlTextField< TEntity >() : Tgstation.Server.Host.Extensions.ModelBuilderExtensions
  • -
  • mappedChannels : Tgstation.Server.Host.Components.Chat.ChatManager
  • +
  • mappedChannels : Tgstation.Server.Host.Components.Chat.ChatManager, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • MapThreads : Tgstation.Server.Api.Models.Internal.DreamDaemonLaunchParameters
  • MapThreadsVersion : Tgstation.Server.Host.Components.Engine.ByondInstallerBase
  • MariaDBDefaultRootPassword : Tgstation.Server.Host.Configuration.InternalConfiguration
  • diff --git a/functions_n.html b/functions_n.html index 3d9eef03ba..5719f7f2fc 100644 --- a/functions_n.html +++ b/functions_n.html @@ -85,8 +85,9 @@ $(function() {
  • NewTestMerges : Tgstation.Server.Api.Models.Request.RepositoryUpdateRequest
  • NewVersion : Tgstation.Server.Api.Models.Request.ServerUpdateRequest, Tgstation.Server.Api.Models.Response.ServerUpdateResponse
  • nextLockManager : Tgstation.Server.Host.Components.Deployment.DmbFactory
  • -
  • NextMessage() : Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • +
  • NextMessage() : Tgstation.Server.Host.Components.Chat.Providers.IProvider
  • nextMessage : Tgstation.Server.Host.Components.Chat.Providers.Provider
  • +
  • NextMessage() : Tgstation.Server.Host.Components.Chat.Providers.Provider
  • NextPayloadId : Tgstation.Server.Host.Components.Interop.Chunker
  • NextRetryDelay() : Tgstation.Server.Client.ApiClientTokenRefreshRetryPolicy, Tgstation.Server.Client.InfiniteThirtySecondMaxRetryPolicy
  • Nickname : Tgstation.Server.Api.Models.IrcConnectionStringBuilder
  • @@ -103,6 +104,7 @@ $(function() {
  • NoReference : Tgstation.Server.Host.Components.Repository.Repository
  • NormalizeAndDelete() : Tgstation.Server.Host.IO.DefaultIOManager
  • NormalizeByondVersion() : Tgstation.Server.Host.Controllers.EngineController
  • +
  • NormalizeMentions() : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • NormalizePath() : Tgstation.Server.Host.Controllers.InstanceController, Tgstation.Server.Host.System.IPlatformIdentifier, Tgstation.Server.Host.System.PlatformIdentifier
  • NotFound() : Tgstation.Server.Host.Controllers.ApiController
  • NotFound< TResult >() : Tgstation.Server.Host.Authority.Core.AuthorityBase
  • diff --git a/functions_o.html b/functions_o.html index 75416f7eb5..da19707eee 100644 --- a/functions_o.html +++ b/functions_o.html @@ -73,17 +73,17 @@ $(function() {
    Here is a list of all class members with links to the classes they belong to:

    - o -

    diff --git a/functions_p.html b/functions_p.html index e3faec1151..9a85b9b593 100644 --- a/functions_p.html +++ b/functions_p.html @@ -109,9 +109,9 @@ $(function() {
  • PayloadId : Tgstation.Server.Host.Components.Interop.ChunkSetInfo, Tgstation.Server.Host.Components.Interop.IChunkPayloadId, Tgstation.Server.Host.Components.Interop.Topic.ChunkedTopicParameters
  • pendingSwappable : Tgstation.Server.Host.Components.Watchdog.AdvancedWatchdog
  • PerformDmbSwap() : Tgstation.Server.Host.Components.Watchdog.AdvancedWatchdog
  • -
  • PermissionSet : Tgstation.Server.Api.Models.Internal.UserApiBase, Tgstation.Server.Api.Models.Internal.UserGroup, Tgstation.Server.Host.GraphQL.Types.InstancePermissionSet, Tgstation.Server.Host.GraphQL.Types.UserGroup, Tgstation.Server.Host.Models.InstancePermissionSet, Tgstation.Server.Host.Models.User, Tgstation.Server.Host.Models.UserGroup, Tgstation.Server.Host.Security.AuthenticationContext
  • +
  • PermissionSet : Tgstation.Server.Api.Models.Internal.UserApiBase, Tgstation.Server.Api.Models.Internal.UserGroup, Tgstation.Server.Host.GraphQL.Types.InstancePermissionSet, Tgstation.Server.Host.GraphQL.Types.UserGroup, Tgstation.Server.Host.Models.InstancePermissionSet, Tgstation.Server.Host.Models.User, Tgstation.Server.Host.Models.UserGroup
  • permissionSet : Tgstation.Server.Host.Security.AuthenticationContext
  • -
  • PermissionSet : Tgstation.Server.Host.Security.IAuthenticationContext
  • +
  • PermissionSet : Tgstation.Server.Host.Security.AuthenticationContext, Tgstation.Server.Host.Security.IAuthenticationContext
  • PermissionSetAuthority() : Tgstation.Server.Host.Authority.PermissionSetAuthority
  • PermissionSetGraphQLTransformer() : Tgstation.Server.Host.Models.Transformers.PermissionSetGraphQLTransformer
  • PermissionSetId : Tgstation.Server.Api.Models.Internal.InstancePermissionSet
  • diff --git a/functions_prop_b.html b/functions_prop_b.html index 00c631c3e3..f41769f653 100644 --- a/functions_prop_b.html +++ b/functions_prop_b.html @@ -77,7 +77,7 @@ $(function() {
  • BaseProvider : Tgstation.Server.Host.Components.Deployment.SwappableDmbProvider
  • Bearer : Tgstation.Server.Api.Models.Response.TokenResponse, Tgstation.Server.Host.GraphQL.Mutations.Payloads.LoginResult
  • BodyAtMerge : Tgstation.Server.Api.Models.Internal.TestMergeModelBase
  • -
  • BotMention : Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • +
  • BotMention : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider, Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • BotToken : Tgstation.Server.Api.Models.DiscordConnectionStringBuilder
  • BroadcastMessage : Tgstation.Server.Api.Models.Request.DreamDaemonRequest, Tgstation.Server.Host.Components.Interop.Topic.TopicParameters
  • ByondRevisionsUrlTemplate : Tgstation.Server.Host.Components.Engine.ByondInstallerBase, Tgstation.Server.Host.Components.Engine.PosixByondInstaller, Tgstation.Server.Host.Components.Engine.WindowsByondInstaller
  • diff --git a/functions_prop_c.html b/functions_prop_c.html index 855b7c0205..8907fdc1a6 100644 --- a/functions_prop_c.html +++ b/functions_prop_c.html @@ -131,7 +131,7 @@ $(function() {
  • ConfigurationType : Tgstation.Server.Api.Models.Instance
  • ConfigVersion : Tgstation.Server.Host.Configuration.GeneralConfiguration
  • ConflictingFiles : Tgstation.Server.Host.Components.Repository.TestMergeResult
  • -
  • Connected : Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • +
  • Connected : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider, Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • ConnectionName : Tgstation.Server.Host.Components.Chat.ChannelRepresentation
  • ConnectionString : Tgstation.Server.Api.Models.Internal.ChatBotSettings, Tgstation.Server.Host.Configuration.DatabaseConfiguration
  • Content : Tgstation.Server.Api.Models.Response.PaginatedResponse< TModel >, Tgstation.Server.Host.Components.Chat.Providers.Message
  • diff --git a/functions_q.html b/functions_q.html index 96c3d2b88e..3e03c1a316 100644 --- a/functions_q.html +++ b/functions_q.html @@ -81,7 +81,7 @@ $(function() {
  • QueryableUsersByGroupId() : Tgstation.Server.Host.GraphQL.Types.UserGroups
  • queryChannelIdMap : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider
  • QueueActiveJobUpdates() : Tgstation.Server.Host.Jobs.IJobsHubUpdater, Tgstation.Server.Host.Jobs.JobService
  • -
  • QueueDeploymentMessage() : Tgstation.Server.Host.Components.Chat.ChatManager, Tgstation.Server.Host.Components.Chat.IChatManager
  • +
  • QueueDeploymentMessage() : Tgstation.Server.Host.Components.Chat.ChatManager, Tgstation.Server.Host.Components.Chat.IChatManager
  • QueueExpiry() : Tgstation.Server.Host.Transfer.FileTransferService
  • QueueMessage() : Tgstation.Server.Host.Components.Chat.ChatManager, Tgstation.Server.Host.Components.Chat.IChatManager
  • QueueMessageInternal() : Tgstation.Server.Host.Components.Chat.ChatManager
  • diff --git a/functions_r.html b/functions_r.html index a0386d4364..edaf65e6d8 100644 --- a/functions_r.html +++ b/functions_r.html @@ -88,7 +88,7 @@ $(function() {
  • RawUserAgent : Tgstation.Server.Api.ApiHeaders
  • RawWebpanelVersion : Tgstation.Server.Host.Properties.MasterVersionsAttribute
  • Read() : Tgstation.Server.Client.AdministrationClient, Tgstation.Server.Client.Components.ConfigurationClient, Tgstation.Server.Client.Components.DreamDaemonClient, Tgstation.Server.Client.Components.DreamMakerClient, Tgstation.Server.Client.Components.IConfigurationClient, Tgstation.Server.Client.Components.IDreamDaemonClient, Tgstation.Server.Client.Components.IDreamMakerClient, Tgstation.Server.Client.Components.IInstancePermissionSetClient, Tgstation.Server.Client.Components.InstancePermissionSetClient, Tgstation.Server.Client.Components.IRepositoryClient, Tgstation.Server.Client.Components.RepositoryClient, Tgstation.Server.Client.IAdministrationClient, Tgstation.Server.Client.IUsersClient, Tgstation.Server.Client.UsersClient, Tgstation.Server.Common.Http.CachedResponseStream, Tgstation.Server.Host.Authority.IUserAuthority, Tgstation.Server.Host.Authority.IUserGroupAuthority, Tgstation.Server.Host.Authority.UserAuthority, Tgstation.Server.Host.Authority.UserGroupAuthority, Tgstation.Server.Host.Components.StaticFiles.Configuration, Tgstation.Server.Host.Components.StaticFiles.IConfiguration, Tgstation.Server.Host.Controllers.AdministrationController, Tgstation.Server.Host.Controllers.DreamDaemonController, Tgstation.Server.Host.Controllers.DreamMakerController, Tgstation.Server.Host.Controllers.EngineController, Tgstation.Server.Host.Controllers.InstancePermissionSetController, Tgstation.Server.Host.Controllers.JobController, Tgstation.Server.Host.Controllers.RepositoryController, Tgstation.Server.Host.Controllers.UserController
  • -
  • Read< TResult >() : Tgstation.Server.Client.ApiClient, Tgstation.Server.Client.IApiClient
  • +
  • Read< TResult >() : Tgstation.Server.Client.ApiClient, Tgstation.Server.Client.IApiClient
  • ReadAllBytes() : Tgstation.Server.Host.IO.DefaultIOManager, Tgstation.Server.Host.IO.IIOManager
  • ReadCacheKey : Tgstation.Server.Host.Authority.AdministrationAuthority
  • ReadFile() : Tgstation.Server.Host.IO.ISynchronousIOManager, Tgstation.Server.Host.IO.SynchronousIOManager
  • @@ -107,13 +107,12 @@ $(function() {
  • ReattachFailure() : Tgstation.Server.Host.Components.Watchdog.WatchdogBase
  • ReattachInformation : Tgstation.Server.Host.Components.Session.ISessionController, Tgstation.Server.Host.Components.Session.ReattachInformation, Tgstation.Server.Host.Components.Session.SessionController, Tgstation.Server.Host.Models.ReattachInformation
  • ReattachInformationBase() : Tgstation.Server.Host.Models.ReattachInformationBase
  • -
  • ReattachInformations : Tgstation.Server.Host.Database.DatabaseContext, Tgstation.Server.Host.Database.IDatabaseContext
  • +
  • ReattachInformations : Tgstation.Server.Host.Database.DatabaseContext, Tgstation.Server.Host.Database.IDatabaseContext
  • reattachInformationsCollection : Tgstation.Server.Host.Database.DatabaseContext
  • Reauthenticate() : Tgstation.Server.Client.GraphQL.GraphQLServerClient
  • rebootBridgeRequestsProcessing : Tgstation.Server.Host.Components.Session.SessionController
  • -
  • RebootGate : Tgstation.Server.Host.Components.Session.ISessionController
  • +
  • RebootGate : Tgstation.Server.Host.Components.Session.ISessionController, Tgstation.Server.Host.Components.Session.SessionController
  • rebootGate : Tgstation.Server.Host.Components.Session.SessionController
  • -
  • RebootGate : Tgstation.Server.Host.Components.Session.SessionController
  • RebootState : Tgstation.Server.Host.Components.Session.ISessionController, Tgstation.Server.Host.Components.Session.SessionController, Tgstation.Server.Host.Components.Watchdog.BasicWatchdog, Tgstation.Server.Host.Components.Watchdog.IWatchdog, Tgstation.Server.Host.Components.Watchdog.WatchdogBase, Tgstation.Server.Host.Models.ReattachInformationBase
  • rebootTcs : Tgstation.Server.Host.Components.Session.SessionController
  • ReceiveJobUpdate() : Tgstation.Server.Api.Hubs.IJobsHub
  • @@ -203,9 +202,9 @@ $(function() {
  • ResetRebootState() : Tgstation.Server.Host.Components.Session.ISessionController, Tgstation.Server.Host.Components.Session.SessionController, Tgstation.Server.Host.Components.Watchdog.BasicWatchdog, Tgstation.Server.Host.Components.Watchdog.IWatchdog, Tgstation.Server.Host.Components.Watchdog.WatchdogBase
  • ResetToOrigin() : Tgstation.Server.Host.Components.Repository.IRepository, Tgstation.Server.Host.Components.Repository.Repository
  • ResetToSha() : Tgstation.Server.Host.Components.Repository.IRepository, Tgstation.Server.Host.Components.Repository.Repository
  • -
  • ResolvePath() : Tgstation.Server.Host.IO.DefaultIOManager, Tgstation.Server.Host.IO.IIOManager, Tgstation.Server.Host.IO.ResolvingIOManager
  • +
  • ResolvePath() : Tgstation.Server.Host.IO.DefaultIOManager, Tgstation.Server.Host.IO.IIOManager, Tgstation.Server.Host.IO.ResolvingIOManager
  • ResolvingIOManager() : Tgstation.Server.Host.IO.ResolvingIOManager
  • -
  • RespondAsync() : Tgstation.Server.Host.Components.Chat.Providers.DiscordForwardingResponder
  • +
  • RespondAsync() : Tgstation.Server.Host.Components.Chat.Providers.DiscordForwardingResponder, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • responseContent : Tgstation.Server.Common.Http.CachedResponseStream
  • ResponseMessage : Tgstation.Server.Client.ClientException
  • responseStream : Tgstation.Server.Common.Http.CachedResponseStream
  • diff --git a/functions_s.html b/functions_s.html index e3b35567e5..ec08d427d7 100644 --- a/functions_s.html +++ b/functions_s.html @@ -114,13 +114,13 @@ $(function() {
  • SendAsync() : Tgstation.Server.Client.GraphQL.AuthorizationMessageHandler, Tgstation.Server.Common.Http.HttpClient, Tgstation.Server.Common.Http.IHttpClient
  • SendCommand() : Tgstation.Server.Host.Components.Session.ISessionController, Tgstation.Server.Host.Components.Session.SessionController
  • SendCommandToHostThroughPipe() : Tgstation.Server.Host.Service.ServiceLifetime
  • -
  • SendMessage() : Tgstation.Server.Host.Components.Chat.ChatManager, Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider, Tgstation.Server.Host.System.NativeMethods
  • +
  • SendMessage() : Tgstation.Server.Host.Components.Chat.ChatManager, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider, Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider, Tgstation.Server.Host.System.NativeMethods
  • SendMessageCount : Tgstation.Server.Host.System.WindowsNetworkPromptReaper
  • SendRawTopic() : Tgstation.Server.Host.Components.Session.SessionController
  • SendSDNotify() : Tgstation.Server.Host.System.SystemDManager
  • SendTopicRequest() : Tgstation.Server.Host.Components.Session.SessionController
  • SendUpdatedServerListToNodes() : Tgstation.Server.Host.Swarm.SwarmService
  • -
  • SendUpdateMessage() : Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • +
  • SendUpdateMessage() : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider, Tgstation.Server.Host.Components.Chat.Providers.IProvider, Tgstation.Server.Host.Components.Chat.Providers.IrcProvider, Tgstation.Server.Host.Components.Chat.Providers.Provider
  • SendUserUpdatedTopics() : Tgstation.Server.Host.Authority.UserAuthority
  • SendWithOptionalPriority() : Tgstation.Server.Host.Extensions.TopicClientExtensions
  • SequenceId : Tgstation.Server.Host.Components.Interop.ChunkData
  • @@ -134,7 +134,7 @@ $(function() {
  • serverControl : Tgstation.Server.Host.Controllers.ApiRootController, Tgstation.Server.Host.Core.CommandPipeManager, Tgstation.Server.Host.Core.ServerUpdater, Tgstation.Server.Host.System.PosixSignalHandler
  • ServerDir : Tgstation.Server.Host.Components.Engine.OpenDreamInstaller
  • serverDllPath : Tgstation.Server.Host.Components.Engine.OpenDreamInstallation
  • -
  • ServerErrorException() : Tgstation.Server.Client.ServerErrorException
  • +
  • ServerErrorException() : Tgstation.Server.Client.ServerErrorException
  • ServerExePath : Tgstation.Server.Host.Components.Engine.ByondInstallation, Tgstation.Server.Host.Components.Engine.EngineExecutableLock, Tgstation.Server.Host.Components.Engine.EngineInstallationBase, Tgstation.Server.Host.Components.Engine.IEngineInstallation, Tgstation.Server.Host.Components.Engine.OpenDreamInstallation
  • ServerFriendlyName : Tgstation.Server.Host.Configuration.TelemetryConfiguration
  • serverHealthCheckCancellationTokenSource : Tgstation.Server.Host.Swarm.SwarmService
  • @@ -162,8 +162,8 @@ $(function() {
  • ServiceCollectionExtensions() : Tgstation.Server.Host.Extensions.ServiceCollectionExtensions
  • serviceLifetime : Tgstation.Server.Host.Service.ServerService
  • ServiceLifetime() : Tgstation.Server.Host.Service.ServiceLifetime
  • -
  • serviceProvider : Tgstation.Server.Client.GraphQL.GraphQLServerClient
  • -
  • ServiceUnavailableException() : Tgstation.Server.Client.ServiceUnavailableException
  • +
  • serviceProvider : Tgstation.Server.Client.GraphQL.GraphQLServerClient, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • +
  • ServiceUnavailableException() : Tgstation.Server.Client.ServiceUnavailableException
  • sessionConfiguration : Tgstation.Server.Host.Components.Deployment.DreamMaker
  • SessionConfiguration : Tgstation.Server.Host.Components.Engine.OpenDreamInstaller
  • sessionConfiguration : Tgstation.Server.Host.Components.Engine.WindowsByondInstaller, Tgstation.Server.Host.Components.InstanceFactory, Tgstation.Server.Host.Components.Session.SessionControllerFactory, Tgstation.Server.Host.Components.StaticFiles.Configuration
  • @@ -257,8 +257,9 @@ $(function() {
  • StartupTimeout : Tgstation.Server.Api.Models.Internal.DreamDaemonLaunchParameters
  • StaticIgnoreFile : Tgstation.Server.Host.Components.StaticFiles.Configuration
  • StaticIgnorePath() : Tgstation.Server.Host.Components.StaticFiles.Configuration
  • -
  • Status : Tgstation.Server.Api.Models.Response.DreamDaemonResponse, Tgstation.Server.Host.Components.Repository.TestMergeResult, Tgstation.Server.Host.Components.Watchdog.IWatchdog, Tgstation.Server.Host.Components.Watchdog.WatchdogBase
  • +
  • Status : Tgstation.Server.Api.Models.Response.DreamDaemonResponse, Tgstation.Server.Host.Components.Repository.TestMergeResult, Tgstation.Server.Host.Components.Watchdog.IWatchdog
  • status : Tgstation.Server.Host.Components.Watchdog.WatchdogBase
  • +
  • Status : Tgstation.Server.Host.Components.Watchdog.WatchdogBase
  • StatusCode() : Tgstation.Server.Host.Controllers.ApiController, Tgstation.Server.Host.Extensions.ControllerBaseExtensions
  • StopAsync() : Tgstation.Server.Host.Components.Chat.ChatManager, Tgstation.Server.Host.Components.Deployment.DmbFactory, Tgstation.Server.Host.Components.Engine.EngineManager, Tgstation.Server.Host.Components.Instance, Tgstation.Server.Host.Components.InstanceFactory, Tgstation.Server.Host.Components.InstanceManager, Tgstation.Server.Host.Components.Repository.RepostoryManagerFactory, Tgstation.Server.Host.Components.StaticFiles.Configuration, Tgstation.Server.Host.Components.Watchdog.WatchdogBase, Tgstation.Server.Host.Core.VersionReportingService, Tgstation.Server.Host.Jobs.JobService, Tgstation.Server.Host.Jobs.JobsHubGroupMapper, Tgstation.Server.Host.System.PosixProcessFeatures, Tgstation.Server.Host.System.PosixSignalHandler
  • StopMonitor() : Tgstation.Server.Host.Components.Watchdog.WatchdogBase
  • @@ -280,6 +281,7 @@ $(function() {
  • Success : Tgstation.Server.Host.Authority.Core.AuthorityResponse< TResult >
  • successfulDeployments : Tgstation.Server.Host.Components.Deployment.DreamMaker
  • SuccessResponse : Tgstation.Server.Host.Authority.Core.AuthorityResponse< TResult >
  • +
  • SupportedGuildChannelTypes : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • supportsMapThreads : Tgstation.Server.Host.Components.Engine.ByondInstallation
  • SuspendProcess() : Tgstation.Server.Host.Components.Session.SessionController, Tgstation.Server.Host.System.IProcessBase, Tgstation.Server.Host.System.IProcessFeatures, Tgstation.Server.Host.System.PosixProcessFeatures, Tgstation.Server.Host.System.Process, Tgstation.Server.Host.System.WindowsProcessFeatures
  • SuspendThread() : Tgstation.Server.Host.System.NativeMethods
  • diff --git a/functions_vars.html b/functions_vars.html index 375678022e..8677e2034e 100644 --- a/functions_vars.html +++ b/functions_vars.html @@ -102,7 +102,7 @@ $(function() {
  • AppTokenExpiryGraceMinutes : Tgstation.Server.Host.Utils.GitHub.GitHubClientFactory
  • arguments : Tgstation.Server.Host.Components.Engine.OpenDreamInstallation
  • assemblyInfo : Tgstation.Server.Host.Components.Chat.Providers.IrcProvider
  • -
  • assemblyInformationProvider : Tgstation.Server.Host.Components.Chat.Commands.CommandFactory, Tgstation.Server.Host.Components.Chat.Commands.VersionCommand, Tgstation.Server.Host.Components.Chat.Providers.ProviderFactory, Tgstation.Server.Host.Components.InstanceFactory, Tgstation.Server.Host.Components.InstanceManager, Tgstation.Server.Host.Components.Session.SessionControllerFactory, Tgstation.Server.Host.Controllers.AdministrationController, Tgstation.Server.Host.Controllers.ApiRootController, Tgstation.Server.Host.Controllers.RootController, Tgstation.Server.Host.Core.VersionReportingService, Tgstation.Server.Host.ServerFactory, Tgstation.Server.Host.Setup.SetupWizard, Tgstation.Server.Host.Swarm.SwarmService, Tgstation.Server.Host.Utils.AbstractHttpClientFactory, Tgstation.Server.Host.Utils.GitHub.GitHubClientFactory
  • +
  • assemblyInformationProvider : Tgstation.Server.Host.Components.Chat.Commands.CommandFactory, Tgstation.Server.Host.Components.Chat.Commands.VersionCommand, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider, Tgstation.Server.Host.Components.Chat.Providers.ProviderFactory, Tgstation.Server.Host.Components.InstanceFactory, Tgstation.Server.Host.Components.InstanceManager, Tgstation.Server.Host.Components.Session.SessionControllerFactory, Tgstation.Server.Host.Controllers.AdministrationController, Tgstation.Server.Host.Controllers.ApiRootController, Tgstation.Server.Host.Controllers.RootController, Tgstation.Server.Host.Core.VersionReportingService, Tgstation.Server.Host.ServerFactory, Tgstation.Server.Host.Setup.SetupWizard, Tgstation.Server.Host.Swarm.SwarmService, Tgstation.Server.Host.Utils.AbstractHttpClientFactory, Tgstation.Server.Host.Utils.GitHub.GitHubClientFactory
  • AssemblyName : Tgstation.Server.Api.ApiHeaders
  • asyncDelayer : Tgstation.Server.Host.Components.Chat.Providers.ProviderFactory, Tgstation.Server.Host.Components.Deployment.DmbFactory, Tgstation.Server.Host.Components.Deployment.DreamMaker, Tgstation.Server.Host.Components.Engine.OpenDreamInstallation, Tgstation.Server.Host.Components.Engine.OpenDreamInstaller, Tgstation.Server.Host.Components.Instance, Tgstation.Server.Host.Components.InstanceFactory, Tgstation.Server.Host.Components.InstanceManager, Tgstation.Server.Host.Components.Session.SessionController, Tgstation.Server.Host.Components.Session.SessionControllerFactory, Tgstation.Server.Host.Core.VersionReportingService, Tgstation.Server.Host.Security.IdentityCache, Tgstation.Server.Host.Security.SessionInvalidationTracker, Tgstation.Server.Host.Setup.SetupWizard, Tgstation.Server.Host.Swarm.SwarmService, Tgstation.Server.Host.System.PosixSignalHandler, Tgstation.Server.Host.System.WindowsNetworkPromptReaper, Tgstation.Server.Host.Transfer.FileTransferService
  • attemptedApiHeadersCreation : Tgstation.Server.Host.Utils.ApiHeadersProvider
  • diff --git a/functions_vars_c.html b/functions_vars_c.html index 72c176c920..9a5aa81094 100644 --- a/functions_vars_c.html +++ b/functions_vars_c.html @@ -132,6 +132,7 @@ $(function() {
  • Configuration : Tgstation.Server.Api.Routes
  • configuration : Tgstation.Server.Host.Components.Deployment.DreamMaker, Tgstation.Server.Host.Components.Events.EventConsumer
  • ConfigurationFile : Tgstation.Server.Api.Routes
  • +
  • connectDisconnectLock : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • connectionMapper : Tgstation.Server.Host.Utils.SignalR.ConnectionMappingHub< TChildHub, THubMethods >
  • connectionsUpdated : Tgstation.Server.Host.Components.Chat.ChatManager
  • console : Tgstation.Server.Host.Components.InstanceManager, Tgstation.Server.Host.Setup.SetupWizard
  • @@ -150,6 +151,7 @@ $(function() {
  • CurrentConfigVersion : Tgstation.Server.Host.Configuration.GeneralConfiguration
  • CurrentDirectory : Tgstation.Server.Host.IO.DefaultIOManager
  • currentDreamMakerOutput : Tgstation.Server.Host.Components.Deployment.DreamMaker
  • +
  • currentUserId : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • customCommandHandler : Tgstation.Server.Host.Components.Chat.ChatManager, Tgstation.Server.Host.Components.Chat.ChatTrackingContext
  • customCommands : Tgstation.Server.Host.Components.Chat.ChatTrackingContext
  • customEventProcessingTask : Tgstation.Server.Host.Components.Session.SessionController
  • diff --git a/functions_vars_d.html b/functions_vars_d.html index a331425bb3..73fd71c793 100644 --- a/functions_vars_d.html +++ b/functions_vars_d.html @@ -114,6 +114,7 @@ $(function() {
  • DefaultVersionReportingRepositoryId : Tgstation.Server.Host.Configuration.TelemetryConfiguration
  • delegatedInstallers : Tgstation.Server.Host.Components.Engine.DelegatingEngineInstaller
  • deploying : Tgstation.Server.Host.Components.Deployment.DreamMaker
  • +
  • deploymentBranding : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • deploymentCleanupGate : Tgstation.Server.Host.Components.Watchdog.AdvancedWatchdog
  • deploymentCleanupTasks : Tgstation.Server.Host.Components.Watchdog.AdvancedWatchdog
  • deploymentLock : Tgstation.Server.Host.Components.Deployment.DreamMaker
  • @@ -128,6 +129,7 @@ $(function() {
  • disposeInvoker : Tgstation.Server.Host.Core.RestartRegistration
  • disposeRan : Tgstation.Server.Host.Utils.DisposeInvoker
  • disposeTasks : Tgstation.Server.Host.GraphQL.Subscriptions.ShutdownAwareTopicEventReceiver
  • +
  • disposing : Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider
  • dmbFactory : Tgstation.Server.Host.Components.Instance, Tgstation.Server.Host.Components.Session.SessionPersistor
  • dmbProvider : Tgstation.Server.Host.Components.Deployment.DeploymentLockManager
  • DmeExtension : Tgstation.Server.Host.Components.Deployment.DreamMaker
  • diff --git a/functions_vars_f.html b/functions_vars_f.html index 3fd8ba3cd4..d3dc896edb 100644 --- a/functions_vars_f.html +++ b/functions_vars_f.html @@ -74,6 +74,7 @@ $(function() {

    - f -