diff --git a/_discord_provider_8cs_source.html b/_discord_provider_8cs_source.html index 783066c418..3337ee2a39 100644 --- a/_discord_provider_8cs_source.html +++ b/_discord_provider_8cs_source.html @@ -237,557 +237,557 @@ $(function() {
224 public override async Task SendMessage(Message replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
225 {
226 Optional<IMessageReference> replyToReference = default;
-
227 if (replyTo != null && replyTo is DiscordMessage discordMessage)
-
228 {
-
229 replyToReference = discordMessage.MessageReference;
-
230 }
-
231
-
232 var embeds = ConvertEmbed(message.Embed);
-
233
-
234 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
-
235 async Task SendToChannel(Snowflake channelId)
-
236 {
-
237 var result = await channelsClient.CreateMessageAsync(
-
238 channelId,
-
239 message.Text,
-
240 embeds: embeds,
-
241 messageReference: replyToReference,
-
242 ct: cancellationToken);
-
243
-
244 if (!result.IsSuccess)
-
245 Logger.LogWarning(
-
246 "Failed to send to channel {0}: {1}",
-
247 channelId,
-
248 result.Error);
-
249 }
-
250
-
251 try
-
252 {
-
253 if (channelId == 0)
-
254 {
-
255 var usersClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
-
256 var currentGuildsResponse = await usersClient.GetCurrentUserGuildsAsync(ct: cancellationToken);
-
257 if (!currentGuildsResponse.IsSuccess)
-
258 {
-
259 Logger.LogWarning(
-
260 "Error retrieving current discord guilds: {0}",
-
261 currentGuildsResponse.Error.Message);
-
262 return;
-
263 }
-
264
-
265 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
-
266
-
267 var guildsChannelsTasks = currentGuildsResponse.Entity.Select(
-
268 guild => guildsClient.GetGuildChannelsAsync(guild.ID.Value, cancellationToken));
-
269
-
270 await Task.WhenAll(guildsChannelsTasks);
-
271
-
272 var unmappedTextChannels = guildsChannelsTasks
-
273 .Select(task => task.Result)
-
274 .SelectMany(guildChannels => guildChannels.Entity)
-
275 .Where(guildChannel => guildChannel.Type == ChannelType.GuildText);
+
227 Optional<IAllowedMentions> allowedMentions = default;
+
228 if (replyTo != null && replyTo is DiscordMessage discordMessage)
+
229 {
+
230 replyToReference = discordMessage.MessageReference;
+
231 allowedMentions = new AllowedMentions(
+
232 Parse: new List<MentionType> // reset settings back to how discord acts if this is not passed (which is different than the default if empty)
+
233 {
+
234 MentionType.Everyone,
+
235 MentionType.Roles,
+
236 MentionType.Users,
+
237 },
+
238 MentionRepliedUser: false); // disable reply mentions
+
239 }
+
240
+
241 var embeds = ConvertEmbed(message.Embed);
+
242
+
243 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
+
244 async Task SendToChannel(Snowflake channelId)
+
245 {
+
246 var result = await channelsClient.CreateMessageAsync(
+
247 channelId,
+
248 message.Text,
+
249 embeds: embeds,
+
250 messageReference: replyToReference,
+
251 allowedMentions: allowedMentions,
+
252 ct: cancellationToken);
+
253
+
254 if (!result.IsSuccess)
+
255 Logger.LogWarning(
+
256 "Failed to send to channel {0}: {1}",
+
257 channelId,
+
258 result.Error);
+
259 }
+
260
+
261 try
+
262 {
+
263 if (channelId == 0)
+
264 {
+
265 var usersClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
+
266 var currentGuildsResponse = await usersClient.GetCurrentUserGuildsAsync(ct: cancellationToken);
+
267 if (!currentGuildsResponse.IsSuccess)
+
268 {
+
269 Logger.LogWarning(
+
270 "Error retrieving current discord guilds: {0}",
+
271 currentGuildsResponse.Error.Message);
+
272 return;
+
273 }
+
274
+
275 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
276
-
277 lock (mappedChannels)
-
278 unmappedTextChannels = unmappedTextChannels
-
279 .Where(x => !mappedChannels.Contains(x.ID.Value))
-
280 .ToList();
+
277 var guildsChannelsTasks = currentGuildsResponse.Entity.Select(
+
278 guild => guildsClient.GetGuildChannelsAsync(guild.ID.Value, cancellationToken));
+
279
+
280 await Task.WhenAll(guildsChannelsTasks);
281
-
282 // discord API confirmed weak boned: https://stackoverflow.com/a/52462336
-
283 if (unmappedTextChannels.Any())
-
284 {
-
285 Logger.LogTrace("Dispatching to {0} unmapped channels...", unmappedTextChannels.Count());
-
286 await Task.WhenAll(
-
287 unmappedTextChannels.Select(
-
288 x => SendToChannel(x.ID)));
-
289 }
-
290
-
291 return;
-
292 }
-
293
-
294 await SendToChannel(new Snowflake(channelId));
-
295 }
-
296 catch (Exception e)
-
297 {
-
298 if (e is OperationCanceledException)
-
299 cancellationToken.ThrowIfCancellationRequested();
-
300 Logger.LogWarning(e, "Error sending discord message!");
-
301 }
-
302 }
+
282 var unmappedTextChannels = guildsChannelsTasks
+
283 .Select(task => task.Result)
+
284 .SelectMany(guildChannels => guildChannels.Entity)
+
285 .Where(guildChannel => guildChannel.Type == ChannelType.GuildText);
+
286
+
287 lock (mappedChannels)
+
288 unmappedTextChannels = unmappedTextChannels
+
289 .Where(x => !mappedChannels.Contains(x.ID.Value))
+
290 .ToList();
+
291
+
292 // discord API confirmed weak boned: https://stackoverflow.com/a/52462336
+
293 if (unmappedTextChannels.Any())
+
294 {
+
295 Logger.LogTrace("Dispatching to {0} unmapped channels...", unmappedTextChannels.Count());
+
296 await Task.WhenAll(
+
297 unmappedTextChannels.Select(
+
298 x => SendToChannel(x.ID)));
+
299 }
+
300
+
301 return;
+
302 }
303
-
305 public override async Task<Func<string, string, Task>> SendUpdateMessage(
-
306 Models.RevisionInformation revisionInformation,
-
307 Version byondVersion,
-
308 DateTimeOffset? estimatedCompletionTime,
-
309 string gitHubOwner,
-
310 string gitHubRepo,
-
311 ulong channelId,
-
312 bool localCommitPushed,
-
313 CancellationToken cancellationToken)
-
314 {
-
315 localCommitPushed |= revisionInformation.CommitSha == revisionInformation.OriginCommitSha;
-
316
-
317 var fields = BuildUpdateEmbedFields(revisionInformation, byondVersion, gitHubOwner, gitHubRepo, localCommitPushed);
-
318 var author = new EmbedAuthor(assemblyInformationProvider.VersionPrefix)
-
319 {
-
320 Url = "https://github.com/tgstation/tgstation-server",
-
321 IconUrl = "https://avatars0.githubusercontent.com/u/1363778?s=280&v=4",
-
322 };
-
323 var embed = new Embed
-
324 {
-
325 Author = deploymentBranding ? author : default,
-
326 Colour = Color.FromArgb(0xF1, 0xC4, 0x0F),
-
327 Description = "TGS has begun deploying active repository code to production.",
-
328 Fields = fields,
-
329 Title = "Code Deployment",
-
330 Footer = new EmbedFooter(
-
331 $"In progress...{(estimatedCompletionTime.HasValue ? " ETA" : String.Empty)}"),
-
332 Timestamp = estimatedCompletionTime ?? default,
-
333 };
-
334
-
335 Logger.LogTrace("Attempting to post deploy embed to channel {0}...", channelId);
-
336 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
-
337
-
338 var messageResponse = await channelsClient.CreateMessageAsync(
-
339 new Snowflake(channelId),
-
340 "DM: Deployment in Progress...",
-
341 embeds: new List<IEmbed> { embed },
-
342 ct: cancellationToken)
-
343 ;
+
304 await SendToChannel(new Snowflake(channelId));
+
305 }
+
306 catch (Exception e)
+
307 {
+
308 if (e is OperationCanceledException)
+
309 cancellationToken.ThrowIfCancellationRequested();
+
310 Logger.LogWarning(e, "Error sending discord message!");
+
311 }
+
312 }
+
313
+
315 public override async Task<Func<string, string, Task>> SendUpdateMessage(
+
316 Models.RevisionInformation revisionInformation,
+
317 Version byondVersion,
+
318 DateTimeOffset? estimatedCompletionTime,
+
319 string gitHubOwner,
+
320 string gitHubRepo,
+
321 ulong channelId,
+
322 bool localCommitPushed,
+
323 CancellationToken cancellationToken)
+
324 {
+
325 localCommitPushed |= revisionInformation.CommitSha == revisionInformation.OriginCommitSha;
+
326
+
327 var fields = BuildUpdateEmbedFields(revisionInformation, byondVersion, gitHubOwner, gitHubRepo, localCommitPushed);
+
328 var author = new EmbedAuthor(assemblyInformationProvider.VersionPrefix)
+
329 {
+
330 Url = "https://github.com/tgstation/tgstation-server",
+
331 IconUrl = "https://avatars0.githubusercontent.com/u/1363778?s=280&v=4",
+
332 };
+
333 var embed = new Embed
+
334 {
+
335 Author = deploymentBranding ? author : default,
+
336 Colour = Color.FromArgb(0xF1, 0xC4, 0x0F),
+
337 Description = "TGS has begun deploying active repository code to production.",
+
338 Fields = fields,
+
339 Title = "Code Deployment",
+
340 Footer = new EmbedFooter(
+
341 $"In progress...{(estimatedCompletionTime.HasValue ? " ETA" : String.Empty)}"),
+
342 Timestamp = estimatedCompletionTime ?? default,
+
343 };
344
-
345 if (!messageResponse.IsSuccess)
-
346 Logger.LogWarning("Failed to post deploy embed to channel {0}: {1}", channelId, messageResponse.Error.Message);
+
345 Logger.LogTrace("Attempting to post deploy embed to channel {0}...", channelId);
+
346 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
347
-
348 return async (errorMessage, dreamMakerOutput) =>
-
349 {
-
350 var completionString = errorMessage == null ? "Succeeded" : "Failed";
-
351
-
352 embed = new Embed
-
353 {
-
354 Author = embed.Author,
-
355 Colour = errorMessage == null ? Color.Green : Color.Red,
-
356 Description = errorMessage == null
-
357 ? "The deployment completed successfully and will be available at the next server reboot."
-
358 : "The deployment failed.",
-
359 Fields = fields,
-
360 Title = embed.Title,
-
361 Footer = new EmbedFooter(
-
362 completionString),
-
363 Timestamp = DateTimeOffset.UtcNow,
-
364 };
-
365
-
366 var showDMOutput = outputDisplayType switch
-
367 {
-
368 DiscordDMOutputDisplayType.Always => true,
-
369 DiscordDMOutputDisplayType.Never => false,
-
370 DiscordDMOutputDisplayType.OnError => errorMessage != null,
-
371 _ => throw new InvalidOperationException($"Invalid DiscordDMOutputDisplayType: {outputDisplayType}"),
-
372 };
-
373
-
374 if (dreamMakerOutput != null)
-
375 {
-
376 // https://github.com/discord-net/Discord.Net/blob/8349cd7e1eb92e9a3baff68082c30a7b43e8e9b7/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs#L431
-
377 const int MaxFieldValueLength = 1024;
-
378 showDMOutput = showDMOutput && dreamMakerOutput.Length < MaxFieldValueLength - (6 + Environment.NewLine.Length);
-
379 if (showDMOutput)
-
380 fields.Add(new EmbedField(
-
381 "DreamMaker Output",
-
382 $"```{Environment.NewLine}{dreamMakerOutput}{Environment.NewLine}```",
-
383 false));
-
384 }
-
385
-
386 if (errorMessage != null)
-
387 fields.Add(new EmbedField(
-
388 "Error Message",
-
389 errorMessage,
-
390 false));
-
391
-
392 var updatedMessage = $"DM: Deployment {completionString}!";
-
393
-
394 async Task CreateUpdatedMessage()
-
395 {
-
396 var createUpdatedMessageResponse = await channelsClient.CreateMessageAsync(
-
397 new Snowflake(channelId),
-
398 updatedMessage,
-
399 embeds: new List<IEmbed> { embed },
-
400 ct: cancellationToken)
-
401 ;
-
402
-
403 if (!createUpdatedMessageResponse.IsSuccess)
-
404 Logger.LogWarning(
-
405 "Creating updated deploy embed failed! Error: {0}",
-
406 createUpdatedMessageResponse.Error.Message);
-
407 }
-
408
-
409 if (!messageResponse.IsSuccess)
-
410 await CreateUpdatedMessage();
-
411 else
-
412 {
-
413 var editResponse = await channelsClient.EditMessageAsync(
-
414 new Snowflake(channelId),
-
415 messageResponse.Entity.ID,
-
416 updatedMessage,
-
417 embeds: new List<IEmbed> { embed },
-
418 ct: cancellationToken)
-
419 ;
-
420
-
421 if (!editResponse.IsSuccess)
-
422 {
-
423 Logger.LogWarning(
-
424 "Updating deploy embed {0} failed, attempting new post! Error: {1}",
-
425 messageResponse.Entity.ID,
-
426 editResponse.Error.Message);
-
427 await CreateUpdatedMessage();
-
428 }
-
429 }
-
430 };
-
431 }
-
432
-
434 public async Task<Result> RespondAsync(IMessageCreate messageCreateEvent, CancellationToken cancellationToken)
-
435 {
-
436 if (messageCreateEvent == null)
-
437 throw new ArgumentNullException(nameof(messageCreateEvent));
-
438
-
439 if ((messageCreateEvent.Type != MessageType.Default
-
440 && messageCreateEvent.Type != MessageType.InlineReply)
-
441 || messageCreateEvent.Author.ID == currentUserId)
-
442 return Result.FromSuccess();
-
443
-
444 var messageReference = new MessageReference
-
445 {
-
446 ChannelID = messageCreateEvent.ChannelID,
-
447 GuildID = messageCreateEvent.GuildID,
-
448 MessageID = messageCreateEvent.ID,
-
449 FailIfNotExists = false,
-
450 };
-
451
-
452 if (basedMeme && messageCreateEvent.Content.Equals("Based on what?", StringComparison.OrdinalIgnoreCase))
-
453 {
-
454 // DCT: None available
-
455 await SendMessage(
-
456 new DiscordMessage
-
457 {
-
458 MessageReference = messageReference,
-
459 },
-
460 new MessageContent
-
461 {
-
462 Text = "https://youtu.be/LrNu-SuFF_o",
-
463 },
-
464 messageCreateEvent.ChannelID.Value,
-
465 default);
-
466 return Result.FromSuccess();
-
467 }
-
468
-
469 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
-
470 var channelResponse = await channelsClient.GetChannelAsync(messageCreateEvent.ChannelID, cancellationToken);
-
471 if (!channelResponse.IsSuccess)
-
472 {
-
473 Logger.LogWarning(
-
474 "Failed to get channel {0} in response to message {1}!",
-
475 messageCreateEvent.ChannelID,
-
476 messageCreateEvent.ID);
-
477
-
478 // we'll handle the errors ourselves
-
479 return Result.FromSuccess();
-
480 }
-
481
-
482 var pm = channelResponse.Entity.Type == ChannelType.DM || channelResponse.Entity.Type == ChannelType.GroupDM;
-
483 var shouldNotAnswer = !pm;
-
484 if (shouldNotAnswer)
-
485 lock (mappedChannels)
-
486 shouldNotAnswer = !mappedChannels.Contains(messageCreateEvent.ChannelID.Value) && !mappedChannels.Contains(0);
+
348 var messageResponse = await channelsClient.CreateMessageAsync(
+
349 new Snowflake(channelId),
+
350 "DM: Deployment in Progress...",
+
351 embeds: new List<IEmbed> { embed },
+
352 ct: cancellationToken)
+
353 ;
+
354
+
355 if (!messageResponse.IsSuccess)
+
356 Logger.LogWarning("Failed to post deploy embed to channel {0}: {1}", channelId, messageResponse.Error.Message);
+
357
+
358 return async (errorMessage, dreamMakerOutput) =>
+
359 {
+
360 var completionString = errorMessage == null ? "Succeeded" : "Failed";
+
361
+
362 embed = new Embed
+
363 {
+
364 Author = embed.Author,
+
365 Colour = errorMessage == null ? Color.Green : Color.Red,
+
366 Description = errorMessage == null
+
367 ? "The deployment completed successfully and will be available at the next server reboot."
+
368 : "The deployment failed.",
+
369 Fields = fields,
+
370 Title = embed.Title,
+
371 Footer = new EmbedFooter(
+
372 completionString),
+
373 Timestamp = DateTimeOffset.UtcNow,
+
374 };
+
375
+
376 var showDMOutput = outputDisplayType switch
+
377 {
+
378 DiscordDMOutputDisplayType.Always => true,
+
379 DiscordDMOutputDisplayType.Never => false,
+
380 DiscordDMOutputDisplayType.OnError => errorMessage != null,
+
381 _ => throw new InvalidOperationException($"Invalid DiscordDMOutputDisplayType: {outputDisplayType}"),
+
382 };
+
383
+
384 if (dreamMakerOutput != null)
+
385 {
+
386 // https://github.com/discord-net/Discord.Net/blob/8349cd7e1eb92e9a3baff68082c30a7b43e8e9b7/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs#L431
+
387 const int MaxFieldValueLength = 1024;
+
388 showDMOutput = showDMOutput && dreamMakerOutput.Length < MaxFieldValueLength - (6 + Environment.NewLine.Length);
+
389 if (showDMOutput)
+
390 fields.Add(new EmbedField(
+
391 "DreamMaker Output",
+
392 $"```{Environment.NewLine}{dreamMakerOutput}{Environment.NewLine}```",
+
393 false));
+
394 }
+
395
+
396 if (errorMessage != null)
+
397 fields.Add(new EmbedField(
+
398 "Error Message",
+
399 errorMessage,
+
400 false));
+
401
+
402 var updatedMessage = $"DM: Deployment {completionString}!";
+
403
+
404 async Task CreateUpdatedMessage()
+
405 {
+
406 var createUpdatedMessageResponse = await channelsClient.CreateMessageAsync(
+
407 new Snowflake(channelId),
+
408 updatedMessage,
+
409 embeds: new List<IEmbed> { embed },
+
410 ct: cancellationToken)
+
411 ;
+
412
+
413 if (!createUpdatedMessageResponse.IsSuccess)
+
414 Logger.LogWarning(
+
415 "Creating updated deploy embed failed! Error: {0}",
+
416 createUpdatedMessageResponse.Error.Message);
+
417 }
+
418
+
419 if (!messageResponse.IsSuccess)
+
420 await CreateUpdatedMessage();
+
421 else
+
422 {
+
423 var editResponse = await channelsClient.EditMessageAsync(
+
424 new Snowflake(channelId),
+
425 messageResponse.Entity.ID,
+
426 updatedMessage,
+
427 embeds: new List<IEmbed> { embed },
+
428 ct: cancellationToken)
+
429 ;
+
430
+
431 if (!editResponse.IsSuccess)
+
432 {
+
433 Logger.LogWarning(
+
434 "Updating deploy embed {0} failed, attempting new post! Error: {1}",
+
435 messageResponse.Entity.ID,
+
436 editResponse.Error.Message);
+
437 await CreateUpdatedMessage();
+
438 }
+
439 }
+
440 };
+
441 }
+
442
+
444 public async Task<Result> RespondAsync(IMessageCreate messageCreateEvent, CancellationToken cancellationToken)
+
445 {
+
446 if (messageCreateEvent == null)
+
447 throw new ArgumentNullException(nameof(messageCreateEvent));
+
448
+
449 if ((messageCreateEvent.Type != MessageType.Default
+
450 && messageCreateEvent.Type != MessageType.InlineReply)
+
451 || messageCreateEvent.Author.ID == currentUserId)
+
452 return Result.FromSuccess();
+
453
+
454 var messageReference = new MessageReference
+
455 {
+
456 ChannelID = messageCreateEvent.ChannelID,
+
457 GuildID = messageCreateEvent.GuildID,
+
458 MessageID = messageCreateEvent.ID,
+
459 FailIfNotExists = false,
+
460 };
+
461
+
462 if (basedMeme && messageCreateEvent.Content.Equals("Based on what?", StringComparison.OrdinalIgnoreCase))
+
463 {
+
464 // DCT: None available
+
465 await SendMessage(
+
466 new DiscordMessage
+
467 {
+
468 MessageReference = messageReference,
+
469 },
+
470 new MessageContent
+
471 {
+
472 Text = "https://youtu.be/LrNu-SuFF_o",
+
473 },
+
474 messageCreateEvent.ChannelID.Value,
+
475 default);
+
476 return Result.FromSuccess();
+
477 }
+
478
+
479 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
+
480 var channelResponse = await channelsClient.GetChannelAsync(messageCreateEvent.ChannelID, cancellationToken);
+
481 if (!channelResponse.IsSuccess)
+
482 {
+
483 Logger.LogWarning(
+
484 "Failed to get channel {0} in response to message {1}!",
+
485 messageCreateEvent.ChannelID,
+
486 messageCreateEvent.ID);
487
-
488 var content = NormalizeMentions(messageCreateEvent.Content);
-
489 var mentionedUs = messageCreateEvent.Mentions.Any(x => x.ID == currentUserId)
-
490 || (!shouldNotAnswer && content.Split(' ').First().Equals(ChatManager.CommonMention, StringComparison.OrdinalIgnoreCase));
+
488 // we'll handle the errors ourselves
+
489 return Result.FromSuccess();
+
490 }
491
-
492 if (shouldNotAnswer)
-
493 {
-
494 if (mentionedUs)
-
495 Logger.LogTrace(
-
496 "Ignoring mention from {0} ({1}) by {2} ({3}). Channel not mapped!",
-
497 messageCreateEvent.ChannelID,
-
498 channelResponse.Entity.Name,
-
499 messageCreateEvent.Author.ID,
-
500 messageCreateEvent.Author.Username);
+
492 var pm = channelResponse.Entity.Type == ChannelType.DM || channelResponse.Entity.Type == ChannelType.GroupDM;
+
493 var shouldNotAnswer = !pm;
+
494 if (shouldNotAnswer)
+
495 lock (mappedChannels)
+
496 shouldNotAnswer = !mappedChannels.Contains(messageCreateEvent.ChannelID.Value) && !mappedChannels.Contains(0);
+
497
+
498 var content = NormalizeMentions(messageCreateEvent.Content);
+
499 var mentionedUs = messageCreateEvent.Mentions.Any(x => x.ID == currentUserId)
+
500 || (!shouldNotAnswer && content.Split(' ').First().Equals(ChatManager.CommonMention, StringComparison.OrdinalIgnoreCase));
501
-
502 return Result.FromSuccess();
-
503 }
-
504
-
505 string guildName = "UNKNOWN";
-
506 if (!pm)
-
507 {
-
508 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
-
509 var messageGuildResponse = await guildsClient.GetGuildAsync(messageCreateEvent.GuildID.Value, false, cancellationToken);
-
510 if (messageGuildResponse.IsSuccess)
-
511 guildName = messageGuildResponse.Entity.Name;
-
512 else
-
513 Logger.LogWarning(
-
514 "Failed to get channel {0} in response to message {1}!",
-
515 messageCreateEvent.ChannelID,
-
516 messageCreateEvent.ID);
-
517 }
-
518
-
519 var result = new DiscordMessage
-
520 {
-
521 MessageReference = messageReference,
-
522 Content = content,
-
523 User = new ChatUser
-
524 {
-
525 RealId = messageCreateEvent.Author.ID.Value,
-
526 Channel = new ChannelRepresentation
-
527 {
-
528 RealId = messageCreateEvent.ChannelID.Value,
-
529 IsPrivateChannel = pm,
-
530 ConnectionName = pm ? messageCreateEvent.Author.Username : guildName,
-
531 FriendlyName = channelResponse.Entity.Name.Value,
-
532 EmbedsSupported = true,
-
533
-
534 // isAdmin and Tag populated by manager
-
535 },
-
536 FriendlyName = messageCreateEvent.Author.Username,
-
537 Mention = NormalizeMentions($"<@{messageCreateEvent.Author.ID}>"),
-
538 },
-
539 };
-
540
-
541 EnqueueMessage(result);
-
542 return Result.FromSuccess();
-
543 }
-
544
-
546 public Task<Result> RespondAsync(IReady readyEvent, CancellationToken cancellationToken)
-
547 {
-
548 if (readyEvent == null)
-
549 throw new ArgumentNullException(nameof(readyEvent));
+
502 if (shouldNotAnswer)
+
503 {
+
504 if (mentionedUs)
+
505 Logger.LogTrace(
+
506 "Ignoring mention from {0} ({1}) by {2} ({3}). Channel not mapped!",
+
507 messageCreateEvent.ChannelID,
+
508 channelResponse.Entity.Name,
+
509 messageCreateEvent.Author.ID,
+
510 messageCreateEvent.Author.Username);
+
511
+
512 return Result.FromSuccess();
+
513 }
+
514
+
515 string guildName = "UNKNOWN";
+
516 if (!pm)
+
517 {
+
518 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
+
519 var messageGuildResponse = await guildsClient.GetGuildAsync(messageCreateEvent.GuildID.Value, false, cancellationToken);
+
520 if (messageGuildResponse.IsSuccess)
+
521 guildName = messageGuildResponse.Entity.Name;
+
522 else
+
523 Logger.LogWarning(
+
524 "Failed to get channel {0} in response to message {1}!",
+
525 messageCreateEvent.ChannelID,
+
526 messageCreateEvent.ID);
+
527 }
+
528
+
529 var result = new DiscordMessage
+
530 {
+
531 MessageReference = messageReference,
+
532 Content = content,
+
533 User = new ChatUser
+
534 {
+
535 RealId = messageCreateEvent.Author.ID.Value,
+
536 Channel = new ChannelRepresentation
+
537 {
+
538 RealId = messageCreateEvent.ChannelID.Value,
+
539 IsPrivateChannel = pm,
+
540 ConnectionName = pm ? messageCreateEvent.Author.Username : guildName,
+
541 FriendlyName = channelResponse.Entity.Name.Value,
+
542 EmbedsSupported = true,
+
543
+
544 // isAdmin and Tag populated by manager
+
545 },
+
546 FriendlyName = messageCreateEvent.Author.Username,
+
547 Mention = NormalizeMentions($"<@{messageCreateEvent.Author.ID}>"),
+
548 },
+
549 };
550
-
551 Logger.LogTrace("Gatway ready. Version: {version}", readyEvent.Version);
-
552 gatewayReadyTcs?.TrySetResult(null);
-
553 return Task.FromResult(Result.FromSuccess());
-
554 }
-
555
-
557 protected override async Task Connect(CancellationToken cancellationToken)
-
558 {
-
559 try
-
560 {
-
561 lock (connectDisconnectLock)
-
562 {
-
563 if (gatewayCts != null)
-
564 throw new InvalidOperationException("Discord gateway still active!");
+
551 EnqueueMessage(result);
+
552 return Result.FromSuccess();
+
553 }
+
554
+
556 public Task<Result> RespondAsync(IReady readyEvent, CancellationToken cancellationToken)
+
557 {
+
558 if (readyEvent == null)
+
559 throw new ArgumentNullException(nameof(readyEvent));
+
560
+
561 Logger.LogTrace("Gatway ready. Version: {version}", readyEvent.Version);
+
562 gatewayReadyTcs?.TrySetResult(null);
+
563 return Task.FromResult(Result.FromSuccess());
+
564 }
565
-
566 gatewayCts = new CancellationTokenSource();
-
567 }
-
568
-
569 var gatewayCancellationToken = gatewayCts.Token;
-
570 var gatewayClient = serviceProvider.GetRequiredService<DiscordGatewayClient>();
-
571
-
572 Task<Result> localGatewayTask;
-
573 gatewayReadyTcs = new TaskCompletionSource<object>();
-
574
-
575 using var gatewayConnectionAbortRegistration = cancellationToken.Register(() => gatewayReadyTcs.TrySetCanceled());
-
576 gatewayCancellationToken.Register(() => Logger.LogTrace("Stopping gateway client..."));
-
577
-
578 // reconnects keep happening until we stop or it faults, our auto-reconnector will handle the latter
-
579 localGatewayTask = gatewayClient.RunAsync(gatewayCancellationToken);
-
580 try
-
581 {
-
582 await Task.WhenAny(gatewayReadyTcs.Task, localGatewayTask);
-
583
-
584 if (localGatewayTask.IsCompleted || cancellationToken.IsCancellationRequested)
-
585 throw new JobException(ErrorCode.ChatCannotConnectProvider);
-
586
-
587 var userClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
-
588
-
589 using var localCombinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, gatewayCancellationToken);
-
590 var currentUserResult = await userClient.GetCurrentUserAsync(localCombinedCts.Token);
-
591 if (!currentUserResult.IsSuccess)
-
592 {
-
593 Logger.LogWarning("Unable to retrieve current user: {0}", currentUserResult.Error.Message);
-
594 throw new JobException(ErrorCode.ChatCannotConnectProvider);
-
595 }
+
567 protected override async Task Connect(CancellationToken cancellationToken)
+
568 {
+
569 try
+
570 {
+
571 lock (connectDisconnectLock)
+
572 {
+
573 if (gatewayCts != null)
+
574 throw new InvalidOperationException("Discord gateway still active!");
+
575
+
576 gatewayCts = new CancellationTokenSource();
+
577 }
+
578
+
579 var gatewayCancellationToken = gatewayCts.Token;
+
580 var gatewayClient = serviceProvider.GetRequiredService<DiscordGatewayClient>();
+
581
+
582 Task<Result> localGatewayTask;
+
583 gatewayReadyTcs = new TaskCompletionSource<object>();
+
584
+
585 using var gatewayConnectionAbortRegistration = cancellationToken.Register(() => gatewayReadyTcs.TrySetCanceled());
+
586 gatewayCancellationToken.Register(() => Logger.LogTrace("Stopping gateway client..."));
+
587
+
588 // reconnects keep happening until we stop or it faults, our auto-reconnector will handle the latter
+
589 localGatewayTask = gatewayClient.RunAsync(gatewayCancellationToken);
+
590 try
+
591 {
+
592 await Task.WhenAny(gatewayReadyTcs.Task, localGatewayTask);
+
593
+
594 if (localGatewayTask.IsCompleted || cancellationToken.IsCancellationRequested)
+
595 throw new JobException(ErrorCode.ChatCannotConnectProvider);
596
-
597 currentUserId = currentUserResult.Entity.ID;
-
598 initialUserName = currentUserResult.Entity.Username;
-
599 }
-
600 finally
-
601 {
-
602 gatewayTask = localGatewayTask;
-
603 }
-
604 }
-
605 catch
-
606 {
-
607 // will handle cleanup
-
608 // DCT: Musn't abort
-
609 await DisconnectImpl(default);
-
610 throw;
-
611 }
-
612 }
-
613
-
615 protected override async Task DisconnectImpl(CancellationToken cancellationToken)
-
616 {
-
617 Task<Result> localGatewayTask;
-
618 CancellationTokenSource localGatewayCts;
-
619 lock (connectDisconnectLock)
-
620 {
-
621 localGatewayTask = gatewayTask;
-
622 localGatewayCts = gatewayCts;
-
623 gatewayTask = null;
-
624 gatewayCts = null;
-
625 if (localGatewayTask == null)
-
626 return;
-
627 }
-
628
-
629 localGatewayCts.Cancel();
-
630 var gatewayResult = await localGatewayTask;
-
631 if (!gatewayResult.IsSuccess)
-
632 Logger.LogWarning("Gateway issue: {0}", gatewayResult.Error.Message);
-
633
-
634 localGatewayCts.Dispose();
-
635 }
-
636
-
638 protected override async Task<IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(IEnumerable<Models.ChatChannel> channels, CancellationToken cancellationToken)
-
639 {
-
640 if (channels == null)
-
641 throw new ArgumentNullException(nameof(channels));
-
642
-
643 var remapRequired = false;
-
644
-
645 async Task<Tuple<Models.ChatChannel, ChannelRepresentation>> GetModelChannelFromDBChannel(Models.ChatChannel channelFromDB)
-
646 {
-
647 if (!channelFromDB.DiscordChannelId.HasValue)
-
648 throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
-
649
-
650 var channelId = channelFromDB.DiscordChannelId.Value;
-
651 string connectionName;
-
652 string friendlyName;
-
653 if (channelId == 0)
-
654 {
-
655 connectionName = initialUserName;
-
656 friendlyName = "(Unmapped accessible channels)";
-
657 }
-
658 else
-
659 {
-
660 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
-
661 var discordChannelResponse = await channelsClient.GetChannelAsync(new Snowflake(channelId), cancellationToken);
-
662 if (!discordChannelResponse.IsSuccess)
-
663 {
-
664 Logger.LogWarning(
-
665 "Error retrieving discord channel {channelId}: {error} Inner: {innerError}",
-
666 channelId,
-
667 discordChannelResponse.Error.Message,
-
668 discordChannelResponse.Inner?.Error?.Message);
-
669 remapRequired = true;
-
670 return null;
-
671 }
-
672
-
673 var channelType = discordChannelResponse.Entity.Type;
-
674 if (channelType != ChannelType.GuildText && channelType != ChannelType.GuildAnnouncement)
-
675 {
-
676 Logger.LogWarning("Cound not map channel {channelId}! Incorrect type: {channelType}", channelId, discordChannelResponse.Entity.Type);
-
677 return null;
-
678 }
-
679
-
680 friendlyName = discordChannelResponse.Entity.Name.Value;
-
681 var guildId = discordChannelResponse.Entity.GuildID.Value;
+
597 var userClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
+
598
+
599 using var localCombinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, gatewayCancellationToken);
+
600 var currentUserResult = await userClient.GetCurrentUserAsync(localCombinedCts.Token);
+
601 if (!currentUserResult.IsSuccess)
+
602 {
+
603 Logger.LogWarning("Unable to retrieve current user: {0}", currentUserResult.Error.Message);
+
604 throw new JobException(ErrorCode.ChatCannotConnectProvider);
+
605 }
+
606
+
607 currentUserId = currentUserResult.Entity.ID;
+
608 initialUserName = currentUserResult.Entity.Username;
+
609 }
+
610 finally
+
611 {
+
612 gatewayTask = localGatewayTask;
+
613 }
+
614 }
+
615 catch
+
616 {
+
617 // will handle cleanup
+
618 // DCT: Musn't abort
+
619 await DisconnectImpl(default);
+
620 throw;
+
621 }
+
622 }
+
623
+
625 protected override async Task DisconnectImpl(CancellationToken cancellationToken)
+
626 {
+
627 Task<Result> localGatewayTask;
+
628 CancellationTokenSource localGatewayCts;
+
629 lock (connectDisconnectLock)
+
630 {
+
631 localGatewayTask = gatewayTask;
+
632 localGatewayCts = gatewayCts;
+
633 gatewayTask = null;
+
634 gatewayCts = null;
+
635 if (localGatewayTask == null)
+
636 return;
+
637 }
+
638
+
639 localGatewayCts.Cancel();
+
640 var gatewayResult = await localGatewayTask;
+
641 if (!gatewayResult.IsSuccess)
+
642 Logger.LogWarning("Gateway issue: {0}", gatewayResult.Error.Message);
+
643
+
644 localGatewayCts.Dispose();
+
645 }
+
646
+
648 protected override async Task<IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(IEnumerable<Models.ChatChannel> channels, CancellationToken cancellationToken)
+
649 {
+
650 if (channels == null)
+
651 throw new ArgumentNullException(nameof(channels));
+
652
+
653 var remapRequired = false;
+
654
+
655 async Task<Tuple<Models.ChatChannel, ChannelRepresentation>> GetModelChannelFromDBChannel(Models.ChatChannel channelFromDB)
+
656 {
+
657 if (!channelFromDB.DiscordChannelId.HasValue)
+
658 throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
+
659
+
660 var channelId = channelFromDB.DiscordChannelId.Value;
+
661 string connectionName;
+
662 string friendlyName;
+
663 if (channelId == 0)
+
664 {
+
665 connectionName = initialUserName;
+
666 friendlyName = "(Unmapped accessible channels)";
+
667 }
+
668 else
+
669 {
+
670 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
+
671 var discordChannelResponse = await channelsClient.GetChannelAsync(new Snowflake(channelId), cancellationToken);
+
672 if (!discordChannelResponse.IsSuccess)
+
673 {
+
674 Logger.LogWarning(
+
675 "Error retrieving discord channel {channelId}: {error} Inner: {innerError}",
+
676 channelId,
+
677 discordChannelResponse.Error.Message,
+
678 discordChannelResponse.Inner?.Error?.Message);
+
679 remapRequired = true;
+
680 return null;
+
681 }
682
-
683 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
-
684 var guildsResponse = await guildsClient.GetGuildAsync(
-
685 guildId,
-
686 false,
-
687 cancellationToken);
-
688 if (!guildsResponse.IsSuccess)
-
689 {
-
690 Logger.LogWarning(
-
691 "Error retrieving discord guild {guildID}: {error} Inner: {innerError}",
-
692 guildId,
-
693 guildsResponse.Error.Message,
-
694 guildsResponse.Inner?.Error?.Message);
-
695 remapRequired = true;
-
696 return null;
-
697 }
-
698
-
699 connectionName = guildsResponse.Entity.Name;
-
700 }
-
701
-
702 var channelModel = new ChannelRepresentation
-
703 {
-
704 RealId = channelId,
-
705 IsAdminChannel = channelFromDB.IsAdminChannel == true,
-
706 ConnectionName = connectionName,
-
707 FriendlyName = friendlyName,
-
708 IsPrivateChannel = false,
-
709 Tag = channelFromDB.Tag,
-
710 EmbedsSupported = true,
-
711 };
-
712
-
713 Logger.LogTrace("Mapped channel {0}: {1}", channelModel.RealId, channelModel.FriendlyName);
-
714 return Tuple.Create(channelFromDB, channelModel);
-
715 }
-
716
-
717 var tasks = channels
-
718 .Select(x => GetModelChannelFromDBChannel(x))
-
719 .ToList();
-
720
-
721 await Task.WhenAll(tasks);
+
683 var channelType = discordChannelResponse.Entity.Type;
+
684 if (channelType != ChannelType.GuildText && channelType != ChannelType.GuildAnnouncement)
+
685 {
+
686 Logger.LogWarning("Cound not map channel {channelId}! Incorrect type: {channelType}", channelId, discordChannelResponse.Entity.Type);
+
687 return null;
+
688 }
+
689
+
690 friendlyName = discordChannelResponse.Entity.Name.Value;
+
691 var guildId = discordChannelResponse.Entity.GuildID.Value;
+
692
+
693 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
+
694 var guildsResponse = await guildsClient.GetGuildAsync(
+
695 guildId,
+
696 false,
+
697 cancellationToken);
+
698 if (!guildsResponse.IsSuccess)
+
699 {
+
700 Logger.LogWarning(
+
701 "Error retrieving discord guild {guildID}: {error} Inner: {innerError}",
+
702 guildId,
+
703 guildsResponse.Error.Message,
+
704 guildsResponse.Inner?.Error?.Message);
+
705 remapRequired = true;
+
706 return null;
+
707 }
+
708
+
709 connectionName = guildsResponse.Entity.Name;
+
710 }
+
711
+
712 var channelModel = new ChannelRepresentation
+
713 {
+
714 RealId = channelId,
+
715 IsAdminChannel = channelFromDB.IsAdminChannel == true,
+
716 ConnectionName = connectionName,
+
717 FriendlyName = friendlyName,
+
718 IsPrivateChannel = false,
+
719 Tag = channelFromDB.Tag,
+
720 EmbedsSupported = true,
+
721 };
722
-
723 var enumerator = tasks
-
724 .Select(x => x.Result)
-
725 .Where(x => x != null)
-
726 .ToList();
-
727
-
728 lock (mappedChannels)
-
729 {
-
730 mappedChannels.Clear();
-
731 mappedChannels.AddRange(enumerator.Select(x => x.Item2.RealId));
-
732 }
-
733
-
734 if (remapRequired)
-
735 EnqueueMessage(null);
-
736
-
737 return enumerator;
-
738 }
-
739
-
745 #pragma warning disable CA1502
-
746 private Optional<IReadOnlyList<IEmbed>> ConvertEmbed(ChatEmbed embed)
-
747 {
-
748 if (embed == null)
-
749 return default;
-
750
-
751 List<string> embedErrors = new List<string>();
-
752 Optional<Color> colour = default;
-
753 if (embed.Colour != null)
-
754 if (Int32.TryParse(embed.Colour.Substring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var argb))
-
755 colour = Color.FromArgb(argb);
-
756 else
-
757 embedErrors.Add(
-
758 String.Format(
-
759 CultureInfo.InvariantCulture,
-
760 "Invalid embed colour: {0}",
-
761 embed.Colour));
-
762
-
763 if (embed.Author != null && String.IsNullOrWhiteSpace(embed.Author.Name))
-
764 {
-
765 embedErrors.Add("Null or whitespace embed author name!");
-
766 embed.Author = null;
-
767 }
-
768
-
769 List<IEmbedField> fields = null;
-
770 if (embed.Fields != null)
-
771 {
-
772 fields = new List<IEmbedField>();
-
773 var i = -1;
-
774 foreach (var field in embed.Fields)
-
775 {
-
776 ++i;
-
777 var invalid = false;
-
778 if (String.IsNullOrWhiteSpace(field.Name))
-
779 {
-
780 embedErrors.Add(
-
781 String.Format(
-
782 CultureInfo.InvariantCulture,
-
783 "Null or whitespace field author at index {0}!",
-
784 i));
-
785 invalid = true;
-
786 }
-
787
-
788 if (String.IsNullOrWhiteSpace(field.Value))
+
723 Logger.LogTrace("Mapped channel {0}: {1}", channelModel.RealId, channelModel.FriendlyName);
+
724 return Tuple.Create(channelFromDB, channelModel);
+
725 }
+
726
+
727 var tasks = channels
+
728 .Select(x => GetModelChannelFromDBChannel(x))
+
729 .ToList();
+
730
+
731 await Task.WhenAll(tasks);
+
732
+
733 var enumerator = tasks
+
734 .Select(x => x.Result)
+
735 .Where(x => x != null)
+
736 .ToList();
+
737
+
738 lock (mappedChannels)
+
739 {
+
740 mappedChannels.Clear();
+
741 mappedChannels.AddRange(enumerator.Select(x => x.Item2.RealId));
+
742 }
+
743
+
744 if (remapRequired)
+
745 EnqueueMessage(null);
+
746
+
747 return enumerator;
+
748 }
+
749
+
755 #pragma warning disable CA1502
+
756 private Optional<IReadOnlyList<IEmbed>> ConvertEmbed(ChatEmbed embed)
+
757 {
+
758 if (embed == null)
+
759 return default;
+
760
+
761 List<string> embedErrors = new List<string>();
+
762 Optional<Color> colour = default;
+
763 if (embed.Colour != null)
+
764 if (Int32.TryParse(embed.Colour.Substring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var argb))
+
765 colour = Color.FromArgb(argb);
+
766 else
+
767 embedErrors.Add(
+
768 String.Format(
+
769 CultureInfo.InvariantCulture,
+
770 "Invalid embed colour: {0}",
+
771 embed.Colour));
+
772
+
773 if (embed.Author != null && String.IsNullOrWhiteSpace(embed.Author.Name))
+
774 {
+
775 embedErrors.Add("Null or whitespace embed author name!");
+
776 embed.Author = null;
+
777 }
+
778
+
779 List<IEmbedField> fields = null;
+
780 if (embed.Fields != null)
+
781 {
+
782 fields = new List<IEmbedField>();
+
783 var i = -1;
+
784 foreach (var field in embed.Fields)
+
785 {
+
786 ++i;
+
787 var invalid = false;
+
788 if (String.IsNullOrWhiteSpace(field.Name))
789 {
790 embedErrors.Add(
791 String.Format(
@@ -797,124 +797,134 @@ $(function() {
795 invalid = true;
796 }
797
-
798 if (invalid)
-
799 continue;
-
800
-
801 fields.Add(new EmbedField(field.Name, field.Value)
-
802 {
-
803 IsInline = field.IsInline ?? default(Optional<bool>),
-
804 });
-
805 }
-
806 }
+
798 if (String.IsNullOrWhiteSpace(field.Value))
+
799 {
+
800 embedErrors.Add(
+
801 String.Format(
+
802 CultureInfo.InvariantCulture,
+
803 "Null or whitespace field author at index {0}!",
+
804 i));
+
805 invalid = true;
+
806 }
807
-
808 if (embed.Footer != null && String.IsNullOrWhiteSpace(embed.Footer.Text))
-
809 {
-
810 embedErrors.Add("Null or whitespace embed footer text!");
-
811 embed.Footer = null;
-
812 }
-
813
-
814 if (embed.Image != null && String.IsNullOrWhiteSpace(embed.Image.Url))
-
815 {
-
816 embedErrors.Add("Null or whitespace embed image url!");
-
817 embed.Image = null;
-
818 }
-
819
-
820 if (embed.Thumbnail != null && String.IsNullOrWhiteSpace(embed.Thumbnail.Url))
-
821 {
-
822 embedErrors.Add("Null or whitespace embed thumbnail url!");
-
823 embed.Thumbnail = null;
-
824 }
-
825
-
826 Optional<DateTimeOffset> timestampOptional = default;
-
827 if (embed.Timestamp != null)
-
828 if (DateTimeOffset.TryParse(embed.Timestamp, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var timestamp))
-
829 timestampOptional = timestamp.ToUniversalTime();
-
830 else
-
831 embedErrors.Add(
-
832 String.Format(
-
833 CultureInfo.InvariantCulture,
-
834 "Invalid embed timestamp: {0}",
-
835 embed.Timestamp));
-
836
-
837 var discordEmbed = new Embed
-
838 {
-
839 Author = embed.Author != null
-
840 ? new EmbedAuthor(embed.Author.Name)
-
841 {
-
842 IconUrl = embed.Author.IconUrl ?? default(Optional<string>),
-
843 ProxyIconUrl = embed.Author.ProxyIconUrl ?? default(Optional<string>),
-
844 Url = embed.Author.Url ?? default(Optional<string>),
-
845 }
-
846 : default(Optional<IEmbedAuthor>),
-
847 Colour = colour,
-
848 Description = embed.Description ?? default(Optional<string>),
-
849 Fields = fields ?? default(Optional<IReadOnlyList<IEmbedField>>),
-
850 Footer = embed.Footer != null
-
851 ? new EmbedFooter(embed.Footer.Text)
-
852 {
-
853 IconUrl = embed.Footer.IconUrl ?? default(Optional<string>),
-
854 ProxyIconUrl = embed.Footer.ProxyIconUrl ?? default(Optional<string>),
+
808 if (invalid)
+
809 continue;
+
810
+
811 fields.Add(new EmbedField(field.Name, field.Value)
+
812 {
+
813 IsInline = field.IsInline ?? default(Optional<bool>),
+
814 });
+
815 }
+
816 }
+
817
+
818 if (embed.Footer != null && String.IsNullOrWhiteSpace(embed.Footer.Text))
+
819 {
+
820 embedErrors.Add("Null or whitespace embed footer text!");
+
821 embed.Footer = null;
+
822 }
+
823
+
824 if (embed.Image != null && String.IsNullOrWhiteSpace(embed.Image.Url))
+
825 {
+
826 embedErrors.Add("Null or whitespace embed image url!");
+
827 embed.Image = null;
+
828 }
+
829
+
830 if (embed.Thumbnail != null && String.IsNullOrWhiteSpace(embed.Thumbnail.Url))
+
831 {
+
832 embedErrors.Add("Null or whitespace embed thumbnail url!");
+
833 embed.Thumbnail = null;
+
834 }
+
835
+
836 Optional<DateTimeOffset> timestampOptional = default;
+
837 if (embed.Timestamp != null)
+
838 if (DateTimeOffset.TryParse(embed.Timestamp, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var timestamp))
+
839 timestampOptional = timestamp.ToUniversalTime();
+
840 else
+
841 embedErrors.Add(
+
842 String.Format(
+
843 CultureInfo.InvariantCulture,
+
844 "Invalid embed timestamp: {0}",
+
845 embed.Timestamp));
+
846
+
847 var discordEmbed = new Embed
+
848 {
+
849 Author = embed.Author != null
+
850 ? new EmbedAuthor(embed.Author.Name)
+
851 {
+
852 IconUrl = embed.Author.IconUrl ?? default(Optional<string>),
+
853 ProxyIconUrl = embed.Author.ProxyIconUrl ?? default(Optional<string>),
+
854 Url = embed.Author.Url ?? default(Optional<string>),
855 }
-
856 : default,
-
857 Image = embed.Image != null
-
858 ? new EmbedImage(embed.Image.Url)
-
859 {
-
860 Width = embed.Image.Width ?? default(Optional<int>),
-
861 Height = embed.Image.Height ?? default(Optional<int>),
-
862 ProxyUrl = embed.Image.ProxyUrl ?? default(Optional<string>),
-
863 }
-
864 : default(Optional<IEmbedImage>),
-
865 Provider = embed.Provider != null
-
866 ? new EmbedProvider
-
867 {
-
868 Name = embed.Provider.Name ?? default(Optional<string>),
-
869 Url = embed.Provider.Url ?? default(Optional<string>),
-
870 }
-
871 : default(Optional<IEmbedProvider>),
-
872 Thumbnail = embed.Thumbnail != null
-
873 ? new EmbedThumbnail(embed.Thumbnail.Url)
-
874 {
-
875 Width = embed.Thumbnail.Width ?? default(Optional<int>),
-
876 Height = embed.Thumbnail.Height ?? default(Optional<int>),
-
877 ProxyUrl = embed.Thumbnail.ProxyUrl ?? default(Optional<string>),
-
878 }
-
879 : default(Optional<IEmbedThumbnail>),
-
880 Timestamp = timestampOptional,
-
881 Title = embed.Title ?? default(Optional<string>),
-
882 Url = embed.Url ?? default(Optional<string>),
-
883 Video = embed.Video != null
-
884 ? new EmbedVideo
-
885 {
-
886 Url = embed.Video.Url ?? default(Optional<string>),
-
887 Width = embed.Video.Width ?? default(Optional<int>),
-
888 Height = embed.Video.Height ?? default(Optional<int>),
-
889 ProxyUrl = embed.Video.ProxyUrl ?? default(Optional<string>),
-
890 }
-
891 : default(Optional<IEmbedVideo>),
-
892 };
-
893
-
894 var result = new List<IEmbed> { discordEmbed };
-
895
-
896 if (embedErrors.Count > 0)
-
897 {
-
898 var joinedErrors = String.Join(Environment.NewLine, embedErrors);
-
899 Logger.LogError("Embed description contains errors:{newLine}{issues}", Environment.NewLine, joinedErrors);
-
900 result.Add(new Embed
-
901 {
-
902 Title = "TGS Embed Errors",
-
903 Description = joinedErrors,
-
904 Colour = Color.Red,
-
905 Footer = new EmbedFooter("Please report this to your codebase's maintainers."),
-
906 Timestamp = DateTimeOffset.UtcNow,
-
907 });
-
908 }
-
909
-
910 return result;
-
911 }
-
912 #pragma warning restore CA1502
-
913 }
-
914#pragma warning restore CA1506
-
915}
+
856 : default(Optional<IEmbedAuthor>),
+
857 Colour = colour,
+
858 Description = embed.Description ?? default(Optional<string>),
+
859 Fields = fields ?? default(Optional<IReadOnlyList<IEmbedField>>),
+
860 Footer = embed.Footer != null
+
861 ? new EmbedFooter(embed.Footer.Text)
+
862 {
+
863 IconUrl = embed.Footer.IconUrl ?? default(Optional<string>),
+
864 ProxyIconUrl = embed.Footer.ProxyIconUrl ?? default(Optional<string>),
+
865 }
+
866 : default,
+
867 Image = embed.Image != null
+
868 ? new EmbedImage(embed.Image.Url)
+
869 {
+
870 Width = embed.Image.Width ?? default(Optional<int>),
+
871 Height = embed.Image.Height ?? default(Optional<int>),
+
872 ProxyUrl = embed.Image.ProxyUrl ?? default(Optional<string>),
+
873 }
+
874 : default(Optional<IEmbedImage>),
+
875 Provider = embed.Provider != null
+
876 ? new EmbedProvider
+
877 {
+
878 Name = embed.Provider.Name ?? default(Optional<string>),
+
879 Url = embed.Provider.Url ?? default(Optional<string>),
+
880 }
+
881 : default(Optional<IEmbedProvider>),
+
882 Thumbnail = embed.Thumbnail != null
+
883 ? new EmbedThumbnail(embed.Thumbnail.Url)
+
884 {
+
885 Width = embed.Thumbnail.Width ?? default(Optional<int>),
+
886 Height = embed.Thumbnail.Height ?? default(Optional<int>),
+
887 ProxyUrl = embed.Thumbnail.ProxyUrl ?? default(Optional<string>),
+
888 }
+
889 : default(Optional<IEmbedThumbnail>),
+
890 Timestamp = timestampOptional,
+
891 Title = embed.Title ?? default(Optional<string>),
+
892 Url = embed.Url ?? default(Optional<string>),
+
893 Video = embed.Video != null
+
894 ? new EmbedVideo
+
895 {
+
896 Url = embed.Video.Url ?? default(Optional<string>),
+
897 Width = embed.Video.Width ?? default(Optional<int>),
+
898 Height = embed.Video.Height ?? default(Optional<int>),
+
899 ProxyUrl = embed.Video.ProxyUrl ?? default(Optional<string>),
+
900 }
+
901 : default(Optional<IEmbedVideo>),
+
902 };
+
903
+
904 var result = new List<IEmbed> { discordEmbed };
+
905
+
906 if (embedErrors.Count > 0)
+
907 {
+
908 var joinedErrors = String.Join(Environment.NewLine, embedErrors);
+
909 Logger.LogError("Embed description contains errors:{newLine}{issues}", Environment.NewLine, joinedErrors);
+
910 result.Add(new Embed
+
911 {
+
912 Title = "TGS Embed Errors",
+
913 Description = joinedErrors,
+
914 Colour = Color.Red,
+
915 Footer = new EmbedFooter("Please report this to your codebase's maintainers."),
+
916 Timestamp = DateTimeOffset.UtcNow,
+
917 });
+
918 }
+
919
+
920 return result;
+
921 }
+
922 #pragma warning restore CA1502
+
923 }
+
924#pragma warning restore CA1506
+
925}
Tgstation.Server.Api.Models.DiscordConnectionStringBuilder
ChatConnectionStringBuilder for ChatProvider.Discord.
Definition: DiscordConnectionStringBuilder.cs:12
Tgstation.Server.Api.Models.Internal.ChatBotSettings.ConnectionString
string? ConnectionString
The information used to connect to the Provider.
Definition: ChatBotSettings.cs:44
Tgstation.Server.Host.Components.Chat.ChannelRepresentation
Represents a Providers.IProvider channel.
Definition: ChannelRepresentation.cs:13
@@ -935,20 +945,20 @@ $(function() {
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayTask
Task< Result > gatewayTask
The Task representing the lifetime of the client.
Definition: DiscordProvider.cs:98
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.currentUserId
Snowflake currentUserId
The bot's Snowflake.
Definition: DiscordProvider.cs:103
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.NormalizeMentions
static string NormalizeMentions(string fromDiscord)
Normalize a discord mention string.
-
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl
override async Task< IReadOnlyCollection< Tuple< Models.ChatChannel, ChannelRepresentation > > > MapChannelsImpl(IEnumerable< Models.ChatChannel > channels, CancellationToken cancellationToken)
Definition: DiscordProvider.cs:638
+
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl
override async Task< IReadOnlyCollection< Tuple< Models.ChatChannel, ChannelRepresentation > > > MapChannelsImpl(IEnumerable< Models.ChatChannel > channels, CancellationToken cancellationToken)
Definition: DiscordProvider.cs:648
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayCts
CancellationTokenSource gatewayCts
The CancellationTokenSource for the gatewayTask.
Definition: DiscordProvider.cs:88
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.assemblyInformationProvider
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the DiscordProvider.
Definition: DiscordProvider.cs:53
-
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync
async Task< Result > RespondAsync(IMessageCreate messageCreateEvent, CancellationToken cancellationToken)
Definition: DiscordProvider.cs:434
+
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync
async Task< Result > RespondAsync(IMessageCreate messageCreateEvent, CancellationToken cancellationToken)
Definition: DiscordProvider.cs:444
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.outputDisplayType
readonly DiscordDMOutputDisplayType outputDisplayType
The DiscordDMOutputDisplayType.
Definition: DiscordProvider.cs:83
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.BuildUpdateEmbedFields
static List< IEmbedField > BuildUpdateEmbedFields(Models.RevisionInformation revisionInformation, Version byondVersion, string gitHubOwner, string gitHubRepo, bool localCommitPushed)
Create a List<T> of IEmbedFields for a discord update embed.
Definition: DiscordProvider.cs:131
-
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect
override async Task Connect(CancellationToken cancellationToken)
Attempt to connect the Provider. A Task representing the running operation.
Definition: DiscordProvider.cs:557
-
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage
override async Task< Func< string, string, Task > > SendUpdateMessage(Models.RevisionInformation revisionInformation, Version byondVersion, DateTimeOffset? estimatedCompletionTime, string gitHubOwner, string gitHubRepo, ulong channelId, bool localCommitPushed, CancellationToken cancellationToken)
Definition: DiscordProvider.cs:305
+
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect
override async Task Connect(CancellationToken cancellationToken)
Attempt to connect the Provider. A Task representing the running operation.
Definition: DiscordProvider.cs:567
+
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage
override async Task< Func< string, string, Task > > SendUpdateMessage(Models.RevisionInformation revisionInformation, Version byondVersion, DateTimeOffset? estimatedCompletionTime, string gitHubOwner, string gitHubRepo, ulong channelId, bool localCommitPushed, CancellationToken cancellationToken)
Definition: DiscordProvider.cs:315
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisposeAsync
override async ValueTask DisposeAsync()
Definition: DiscordProvider.cs:203
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connected
override bool Connected
If the IProvider is currently connected.
Definition: DiscordProvider.cs:37
-
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.ConvertEmbed
Optional< IReadOnlyList< IEmbed > > ConvertEmbed(ChatEmbed embed)
Convert a ChatEmbed to an IEmbed parameters.
Definition: DiscordProvider.cs:746
+
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.ConvertEmbed
Optional< IReadOnlyList< IEmbed > > ConvertEmbed(ChatEmbed embed)
Convert a ChatEmbed to an IEmbed parameters.
Definition: DiscordProvider.cs:756
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayReadyTcs
TaskCompletionSource< object > gatewayReadyTcs
The TaskCompletionSource<TResult> for the initial gateway connection event.
Definition: DiscordProvider.cs:93
-
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync
Task< Result > RespondAsync(IReady readyEvent, CancellationToken cancellationToken)
Definition: DiscordProvider.cs:546
-
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl
override async Task DisconnectImpl(CancellationToken cancellationToken)
Gracefully disconnects the provider. A Task representing the running operation.
Definition: DiscordProvider.cs:615
+
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync
Task< Result > RespondAsync(IReady readyEvent, CancellationToken cancellationToken)
Definition: DiscordProvider.cs:556
+
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl
override async Task DisconnectImpl(CancellationToken cancellationToken)
Gracefully disconnects the provider. A Task representing the running operation.
Definition: DiscordProvider.cs:625
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.serviceProvider
readonly ServiceProvider serviceProvider
The ServiceProvider containing Discord services.
Definition: DiscordProvider.cs:58
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.BotMention
override string BotMention
The string that indicates the IProvider was mentioned.
Definition: DiscordProvider.cs:41
Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendMessage
override async Task SendMessage(Message replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
Send a message to the IProvider. A Task representing the running operation.
Definition: DiscordProvider.cs:224
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 f92ad5f586..d9352de6a2 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 @@ -345,7 +345,7 @@ Properties

Definition at line 53 of file ChannelRepresentation.cs.

53{ get; set; }
-

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

+

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

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_chat_manager.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_chat_manager.html index 6d796291d0..1b93058e96 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_chat_manager.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_chat_manager.html @@ -2284,7 +2284,7 @@ Here is the caller graph for this function:

Definition at line 29 of file ChatManager.cs.

-

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

+

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

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_discord_provider.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_discord_provider.html index a109b986b4..c3c89f9514 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_discord_provider.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_discord_provider.html @@ -588,7 +588,7 @@ Private Attributes
167 }
Tgstation.Server.Host.Models.RevInfoTestMerge
Many to many relationship for Models.RevisionInformation and Models.TestMerge.
Definition: RevInfoTestMerge.cs:9
-

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

+

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

Here is the caller graph for this function:
@@ -636,73 +636,73 @@ Here is the caller graph for this function:

Implements Tgstation.Server.Host.Components.Chat.Providers.Provider.

-

Definition at line 557 of file DiscordProvider.cs.

-
558 {
-
559 try
-
560 {
-
561 lock (connectDisconnectLock)
-
562 {
-
563 if (gatewayCts != null)
-
564 throw new InvalidOperationException("Discord gateway still active!");
-
565
-
566 gatewayCts = new CancellationTokenSource();
-
567 }
-
568
-
569 var gatewayCancellationToken = gatewayCts.Token;
-
570 var gatewayClient = serviceProvider.GetRequiredService<DiscordGatewayClient>();
-
571
-
572 Task<Result> localGatewayTask;
-
573 gatewayReadyTcs = new TaskCompletionSource<object>();
-
574
-
575 using var gatewayConnectionAbortRegistration = cancellationToken.Register(() => gatewayReadyTcs.TrySetCanceled());
-
576 gatewayCancellationToken.Register(() => Logger.LogTrace("Stopping gateway client..."));
-
577
-
578 // reconnects keep happening until we stop or it faults, our auto-reconnector will handle the latter
-
579 localGatewayTask = gatewayClient.RunAsync(gatewayCancellationToken);
-
580 try
-
581 {
-
582 await Task.WhenAny(gatewayReadyTcs.Task, localGatewayTask);
-
583
-
584 if (localGatewayTask.IsCompleted || cancellationToken.IsCancellationRequested)
-
585 throw new JobException(ErrorCode.ChatCannotConnectProvider);
-
586
-
587 var userClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
-
588
-
589 using var localCombinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, gatewayCancellationToken);
-
590 var currentUserResult = await userClient.GetCurrentUserAsync(localCombinedCts.Token);
-
591 if (!currentUserResult.IsSuccess)
-
592 {
-
593 Logger.LogWarning("Unable to retrieve current user: {0}", currentUserResult.Error.Message);
-
594 throw new JobException(ErrorCode.ChatCannotConnectProvider);
-
595 }
+

Definition at line 567 of file DiscordProvider.cs.

+
568 {
+
569 try
+
570 {
+ +
572 {
+
573 if (gatewayCts != null)
+
574 throw new InvalidOperationException("Discord gateway still active!");
+
575
+
576 gatewayCts = new CancellationTokenSource();
+
577 }
+
578
+
579 var gatewayCancellationToken = gatewayCts.Token;
+
580 var gatewayClient = serviceProvider.GetRequiredService<DiscordGatewayClient>();
+
581
+
582 Task<Result> localGatewayTask;
+
583 gatewayReadyTcs = new TaskCompletionSource<object>();
+
584
+
585 using var gatewayConnectionAbortRegistration = cancellationToken.Register(() => gatewayReadyTcs.TrySetCanceled());
+
586 gatewayCancellationToken.Register(() => Logger.LogTrace("Stopping gateway client..."));
+
587
+
588 // reconnects keep happening until we stop or it faults, our auto-reconnector will handle the latter
+
589 localGatewayTask = gatewayClient.RunAsync(gatewayCancellationToken);
+
590 try
+
591 {
+
592 await Task.WhenAny(gatewayReadyTcs.Task, localGatewayTask);
+
593
+
594 if (localGatewayTask.IsCompleted || cancellationToken.IsCancellationRequested)
+
595 throw new JobException(ErrorCode.ChatCannotConnectProvider);
596
-
597 currentUserId = currentUserResult.Entity.ID;
-
598 initialUserName = currentUserResult.Entity.Username;
-
599 }
-
600 finally
-
601 {
-
602 gatewayTask = localGatewayTask;
-
603 }
-
604 }
-
605 catch
-
606 {
-
607 // will handle cleanup
-
608 // DCT: Musn't abort
-
609 await DisconnectImpl(default);
-
610 throw;
-
611 }
-
612 }
+
597 var userClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
+
598
+
599 using var localCombinedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, gatewayCancellationToken);
+
600 var currentUserResult = await userClient.GetCurrentUserAsync(localCombinedCts.Token);
+
601 if (!currentUserResult.IsSuccess)
+
602 {
+
603 Logger.LogWarning("Unable to retrieve current user: {0}", currentUserResult.Error.Message);
+
604 throw new JobException(ErrorCode.ChatCannotConnectProvider);
+
605 }
+
606
+
607 currentUserId = currentUserResult.Entity.ID;
+
608 initialUserName = currentUserResult.Entity.Username;
+
609 }
+
610 finally
+
611 {
+
612 gatewayTask = localGatewayTask;
+
613 }
+
614 }
+
615 catch
+
616 {
+
617 // will handle cleanup
+
618 // DCT: Musn't abort
+
619 await DisconnectImpl(default);
+
620 throw;
+
621 }
+
622 }
string initialUserName
The bot's username at the time of connection.
Task< Result > gatewayTask
The Task representing the lifetime of the client.
CancellationTokenSource gatewayCts
The CancellationTokenSource for the gatewayTask.
TaskCompletionSource< object > gatewayReadyTcs
The TaskCompletionSource<TResult> for the initial gateway connection event.
-
override async Task DisconnectImpl(CancellationToken cancellationToken)
Gracefully disconnects the provider. A Task representing the running operation.
+
override async Task DisconnectImpl(CancellationToken cancellationToken)
Gracefully disconnects the provider. A Task representing the running operation.
ILogger< Provider > Logger
The ILogger for the Provider.
Definition: Provider.cs:30
Operation exceptions thrown from the context of a Models.Job.
Definition: JobException.cs:11
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition: ErrorCode.cs:11
-

References Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.connectDisconnectLock, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.currentUserId, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayCts, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayReadyTcs, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayTask, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.initialUserName, Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger, and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.serviceProvider.

+

References Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.connectDisconnectLock, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.currentUserId, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayCts, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayReadyTcs, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayTask, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.initialUserName, Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger, and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.serviceProvider.

Here is the call graph for this function:
@@ -748,49 +748,39 @@ Here is the call graph for this function:
Returns
The parameter for sending a single IEmbed.
-

Definition at line 746 of file DiscordProvider.cs.

-
747 {
-
748 if (embed == null)
-
749 return default;
-
750
-
751 List<string> embedErrors = new List<string>();
-
752 Optional<Color> colour = default;
-
753 if (embed.Colour != null)
-
754 if (Int32.TryParse(embed.Colour.Substring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var argb))
-
755 colour = Color.FromArgb(argb);
-
756 else
-
757 embedErrors.Add(
-
758 String.Format(
-
759 CultureInfo.InvariantCulture,
-
760 "Invalid embed colour: {0}",
-
761 embed.Colour));
-
762
-
763 if (embed.Author != null && String.IsNullOrWhiteSpace(embed.Author.Name))
-
764 {
-
765 embedErrors.Add("Null or whitespace embed author name!");
-
766 embed.Author = null;
-
767 }
-
768
-
769 List<IEmbedField> fields = null;
-
770 if (embed.Fields != null)
-
771 {
-
772 fields = new List<IEmbedField>();
-
773 var i = -1;
-
774 foreach (var field in embed.Fields)
-
775 {
-
776 ++i;
-
777 var invalid = false;
-
778 if (String.IsNullOrWhiteSpace(field.Name))
-
779 {
-
780 embedErrors.Add(
-
781 String.Format(
-
782 CultureInfo.InvariantCulture,
-
783 "Null or whitespace field author at index {0}!",
-
784 i));
-
785 invalid = true;
-
786 }
-
787
-
788 if (String.IsNullOrWhiteSpace(field.Value))
+

Definition at line 756 of file DiscordProvider.cs.

+
757 {
+
758 if (embed == null)
+
759 return default;
+
760
+
761 List<string> embedErrors = new List<string>();
+
762 Optional<Color> colour = default;
+
763 if (embed.Colour != null)
+
764 if (Int32.TryParse(embed.Colour.Substring(1), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var argb))
+
765 colour = Color.FromArgb(argb);
+
766 else
+
767 embedErrors.Add(
+
768 String.Format(
+
769 CultureInfo.InvariantCulture,
+
770 "Invalid embed colour: {0}",
+
771 embed.Colour));
+
772
+
773 if (embed.Author != null && String.IsNullOrWhiteSpace(embed.Author.Name))
+
774 {
+
775 embedErrors.Add("Null or whitespace embed author name!");
+
776 embed.Author = null;
+
777 }
+
778
+
779 List<IEmbedField> fields = null;
+
780 if (embed.Fields != null)
+
781 {
+
782 fields = new List<IEmbedField>();
+
783 var i = -1;
+
784 foreach (var field in embed.Fields)
+
785 {
+
786 ++i;
+
787 var invalid = false;
+
788 if (String.IsNullOrWhiteSpace(field.Name))
789 {
790 embedErrors.Add(
791 String.Format(
@@ -800,120 +790,130 @@ Here is the call graph for this function:
795 invalid = true;
796 }
797
-
798 if (invalid)
-
799 continue;
-
800
-
801 fields.Add(new EmbedField(field.Name, field.Value)
-
802 {
-
803 IsInline = field.IsInline ?? default(Optional<bool>),
-
804 });
-
805 }
-
806 }
+
798 if (String.IsNullOrWhiteSpace(field.Value))
+
799 {
+
800 embedErrors.Add(
+
801 String.Format(
+
802 CultureInfo.InvariantCulture,
+
803 "Null or whitespace field author at index {0}!",
+
804 i));
+
805 invalid = true;
+
806 }
807
-
808 if (embed.Footer != null && String.IsNullOrWhiteSpace(embed.Footer.Text))
-
809 {
-
810 embedErrors.Add("Null or whitespace embed footer text!");
-
811 embed.Footer = null;
-
812 }
-
813
-
814 if (embed.Image != null && String.IsNullOrWhiteSpace(embed.Image.Url))
-
815 {
-
816 embedErrors.Add("Null or whitespace embed image url!");
-
817 embed.Image = null;
-
818 }
-
819
-
820 if (embed.Thumbnail != null && String.IsNullOrWhiteSpace(embed.Thumbnail.Url))
-
821 {
-
822 embedErrors.Add("Null or whitespace embed thumbnail url!");
-
823 embed.Thumbnail = null;
-
824 }
-
825
-
826 Optional<DateTimeOffset> timestampOptional = default;
-
827 if (embed.Timestamp != null)
-
828 if (DateTimeOffset.TryParse(embed.Timestamp, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var timestamp))
-
829 timestampOptional = timestamp.ToUniversalTime();
-
830 else
-
831 embedErrors.Add(
-
832 String.Format(
-
833 CultureInfo.InvariantCulture,
-
834 "Invalid embed timestamp: {0}",
-
835 embed.Timestamp));
-
836
-
837 var discordEmbed = new Embed
-
838 {
-
839 Author = embed.Author != null
-
840 ? new EmbedAuthor(embed.Author.Name)
-
841 {
-
842 IconUrl = embed.Author.IconUrl ?? default(Optional<string>),
-
843 ProxyIconUrl = embed.Author.ProxyIconUrl ?? default(Optional<string>),
-
844 Url = embed.Author.Url ?? default(Optional<string>),
-
845 }
-
846 : default(Optional<IEmbedAuthor>),
-
847 Colour = colour,
-
848 Description = embed.Description ?? default(Optional<string>),
-
849 Fields = fields ?? default(Optional<IReadOnlyList<IEmbedField>>),
-
850 Footer = embed.Footer != null
-
851 ? new EmbedFooter(embed.Footer.Text)
-
852 {
-
853 IconUrl = embed.Footer.IconUrl ?? default(Optional<string>),
-
854 ProxyIconUrl = embed.Footer.ProxyIconUrl ?? default(Optional<string>),
+
808 if (invalid)
+
809 continue;
+
810
+
811 fields.Add(new EmbedField(field.Name, field.Value)
+
812 {
+
813 IsInline = field.IsInline ?? default(Optional<bool>),
+
814 });
+
815 }
+
816 }
+
817
+
818 if (embed.Footer != null && String.IsNullOrWhiteSpace(embed.Footer.Text))
+
819 {
+
820 embedErrors.Add("Null or whitespace embed footer text!");
+
821 embed.Footer = null;
+
822 }
+
823
+
824 if (embed.Image != null && String.IsNullOrWhiteSpace(embed.Image.Url))
+
825 {
+
826 embedErrors.Add("Null or whitespace embed image url!");
+
827 embed.Image = null;
+
828 }
+
829
+
830 if (embed.Thumbnail != null && String.IsNullOrWhiteSpace(embed.Thumbnail.Url))
+
831 {
+
832 embedErrors.Add("Null or whitespace embed thumbnail url!");
+
833 embed.Thumbnail = null;
+
834 }
+
835
+
836 Optional<DateTimeOffset> timestampOptional = default;
+
837 if (embed.Timestamp != null)
+
838 if (DateTimeOffset.TryParse(embed.Timestamp, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var timestamp))
+
839 timestampOptional = timestamp.ToUniversalTime();
+
840 else
+
841 embedErrors.Add(
+
842 String.Format(
+
843 CultureInfo.InvariantCulture,
+
844 "Invalid embed timestamp: {0}",
+
845 embed.Timestamp));
+
846
+
847 var discordEmbed = new Embed
+
848 {
+
849 Author = embed.Author != null
+
850 ? new EmbedAuthor(embed.Author.Name)
+
851 {
+
852 IconUrl = embed.Author.IconUrl ?? default(Optional<string>),
+
853 ProxyIconUrl = embed.Author.ProxyIconUrl ?? default(Optional<string>),
+
854 Url = embed.Author.Url ?? default(Optional<string>),
855 }
-
856 : default,
-
857 Image = embed.Image != null
-
858 ? new EmbedImage(embed.Image.Url)
-
859 {
-
860 Width = embed.Image.Width ?? default(Optional<int>),
-
861 Height = embed.Image.Height ?? default(Optional<int>),
-
862 ProxyUrl = embed.Image.ProxyUrl ?? default(Optional<string>),
-
863 }
-
864 : default(Optional<IEmbedImage>),
-
865 Provider = embed.Provider != null
-
866 ? new EmbedProvider
-
867 {
-
868 Name = embed.Provider.Name ?? default(Optional<string>),
-
869 Url = embed.Provider.Url ?? default(Optional<string>),
-
870 }
-
871 : default(Optional<IEmbedProvider>),
-
872 Thumbnail = embed.Thumbnail != null
-
873 ? new EmbedThumbnail(embed.Thumbnail.Url)
-
874 {
-
875 Width = embed.Thumbnail.Width ?? default(Optional<int>),
-
876 Height = embed.Thumbnail.Height ?? default(Optional<int>),
-
877 ProxyUrl = embed.Thumbnail.ProxyUrl ?? default(Optional<string>),
-
878 }
-
879 : default(Optional<IEmbedThumbnail>),
-
880 Timestamp = timestampOptional,
-
881 Title = embed.Title ?? default(Optional<string>),
-
882 Url = embed.Url ?? default(Optional<string>),
-
883 Video = embed.Video != null
-
884 ? new EmbedVideo
-
885 {
-
886 Url = embed.Video.Url ?? default(Optional<string>),
-
887 Width = embed.Video.Width ?? default(Optional<int>),
-
888 Height = embed.Video.Height ?? default(Optional<int>),
-
889 ProxyUrl = embed.Video.ProxyUrl ?? default(Optional<string>),
-
890 }
-
891 : default(Optional<IEmbedVideo>),
-
892 };
-
893
-
894 var result = new List<IEmbed> { discordEmbed };
-
895
-
896 if (embedErrors.Count > 0)
-
897 {
-
898 var joinedErrors = String.Join(Environment.NewLine, embedErrors);
-
899 Logger.LogError("Embed description contains errors:{newLine}{issues}", Environment.NewLine, joinedErrors);
-
900 result.Add(new Embed
-
901 {
-
902 Title = "TGS Embed Errors",
-
903 Description = joinedErrors,
-
904 Colour = Color.Red,
-
905 Footer = new EmbedFooter("Please report this to your codebase's maintainers."),
-
906 Timestamp = DateTimeOffset.UtcNow,
-
907 });
-
908 }
-
909
-
910 return result;
-
911 }
+
856 : default(Optional<IEmbedAuthor>),
+
857 Colour = colour,
+
858 Description = embed.Description ?? default(Optional<string>),
+
859 Fields = fields ?? default(Optional<IReadOnlyList<IEmbedField>>),
+
860 Footer = embed.Footer != null
+
861 ? new EmbedFooter(embed.Footer.Text)
+
862 {
+
863 IconUrl = embed.Footer.IconUrl ?? default(Optional<string>),
+
864 ProxyIconUrl = embed.Footer.ProxyIconUrl ?? default(Optional<string>),
+
865 }
+
866 : default,
+
867 Image = embed.Image != null
+
868 ? new EmbedImage(embed.Image.Url)
+
869 {
+
870 Width = embed.Image.Width ?? default(Optional<int>),
+
871 Height = embed.Image.Height ?? default(Optional<int>),
+
872 ProxyUrl = embed.Image.ProxyUrl ?? default(Optional<string>),
+
873 }
+
874 : default(Optional<IEmbedImage>),
+
875 Provider = embed.Provider != null
+
876 ? new EmbedProvider
+
877 {
+
878 Name = embed.Provider.Name ?? default(Optional<string>),
+
879 Url = embed.Provider.Url ?? default(Optional<string>),
+
880 }
+
881 : default(Optional<IEmbedProvider>),
+
882 Thumbnail = embed.Thumbnail != null
+
883 ? new EmbedThumbnail(embed.Thumbnail.Url)
+
884 {
+
885 Width = embed.Thumbnail.Width ?? default(Optional<int>),
+
886 Height = embed.Thumbnail.Height ?? default(Optional<int>),
+
887 ProxyUrl = embed.Thumbnail.ProxyUrl ?? default(Optional<string>),
+
888 }
+
889 : default(Optional<IEmbedThumbnail>),
+
890 Timestamp = timestampOptional,
+
891 Title = embed.Title ?? default(Optional<string>),
+
892 Url = embed.Url ?? default(Optional<string>),
+
893 Video = embed.Video != null
+
894 ? new EmbedVideo
+
895 {
+
896 Url = embed.Video.Url ?? default(Optional<string>),
+
897 Width = embed.Video.Width ?? default(Optional<int>),
+
898 Height = embed.Video.Height ?? default(Optional<int>),
+
899 ProxyUrl = embed.Video.ProxyUrl ?? default(Optional<string>),
+
900 }
+
901 : default(Optional<IEmbedVideo>),
+
902 };
+
903
+
904 var result = new List<IEmbed> { discordEmbed };
+
905
+
906 if (embedErrors.Count > 0)
+
907 {
+
908 var joinedErrors = String.Join(Environment.NewLine, embedErrors);
+
909 Logger.LogError("Embed description contains errors:{newLine}{issues}", Environment.NewLine, joinedErrors);
+
910 result.Add(new Embed
+
911 {
+
912 Title = "TGS Embed Errors",
+
913 Description = joinedErrors,
+
914 Colour = Color.Red,
+
915 Footer = new EmbedFooter("Please report this to your codebase's maintainers."),
+
916 Timestamp = DateTimeOffset.UtcNow,
+
917 });
+
918 }
+
919
+
920 return result;
+
921 }
Provider(IJobManager jobManager, ILogger< Provider > logger, ChatBot chatBot)
Initializes a new instance of the Provider class.
Definition: Provider.cs:73
ChatEmbedMedia Image
The ChatEmbedMedia for an image.
Definition: ChatEmbed.cs:45
@@ -977,31 +977,31 @@ Here is the caller graph for this function:

Implements Tgstation.Server.Host.Components.Chat.Providers.Provider.

-

Definition at line 615 of file DiscordProvider.cs.

-
616 {
-
617 Task<Result> localGatewayTask;
-
618 CancellationTokenSource localGatewayCts;
- -
620 {
-
621 localGatewayTask = gatewayTask;
-
622 localGatewayCts = gatewayCts;
-
623 gatewayTask = null;
-
624 gatewayCts = null;
-
625 if (localGatewayTask == null)
-
626 return;
-
627 }
-
628
-
629 localGatewayCts.Cancel();
-
630 var gatewayResult = await localGatewayTask;
-
631 if (!gatewayResult.IsSuccess)
-
632 Logger.LogWarning("Gateway issue: {0}", gatewayResult.Error.Message);
-
633
-
634 localGatewayCts.Dispose();
-
635 }
+

Definition at line 625 of file DiscordProvider.cs.

+
626 {
+
627 Task<Result> localGatewayTask;
+
628 CancellationTokenSource localGatewayCts;
+ +
630 {
+
631 localGatewayTask = gatewayTask;
+
632 localGatewayCts = gatewayCts;
+
633 gatewayTask = null;
+
634 gatewayCts = null;
+
635 if (localGatewayTask == null)
+
636 return;
+
637 }
+
638
+
639 localGatewayCts.Cancel();
+
640 var gatewayResult = await localGatewayTask;
+
641 if (!gatewayResult.IsSuccess)
+
642 Logger.LogWarning("Gateway issue: {0}", gatewayResult.Error.Message);
+
643
+
644 localGatewayCts.Dispose();
+
645 }

References Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.connectDisconnectLock, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayCts, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayTask, and Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger.

-

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

+

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

Here is the caller graph for this function:
@@ -1102,107 +1102,107 @@ Here is the caller graph for this function:

-

Definition at line 638 of file DiscordProvider.cs.

-
639 {
-
640 if (channels == null)
-
641 throw new ArgumentNullException(nameof(channels));
-
642
-
643 var remapRequired = false;
-
644
-
645 async Task<Tuple<Models.ChatChannel, ChannelRepresentation>> GetModelChannelFromDBChannel(Models.ChatChannel channelFromDB)
-
646 {
-
647 if (!channelFromDB.DiscordChannelId.HasValue)
-
648 throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
-
649
-
650 var channelId = channelFromDB.DiscordChannelId.Value;
-
651 string connectionName;
-
652 string friendlyName;
-
653 if (channelId == 0)
-
654 {
-
655 connectionName = initialUserName;
-
656 friendlyName = "(Unmapped accessible channels)";
-
657 }
-
658 else
-
659 {
-
660 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
-
661 var discordChannelResponse = await channelsClient.GetChannelAsync(new Snowflake(channelId), cancellationToken);
-
662 if (!discordChannelResponse.IsSuccess)
-
663 {
-
664 Logger.LogWarning(
-
665 "Error retrieving discord channel {channelId}: {error} Inner: {innerError}",
-
666 channelId,
-
667 discordChannelResponse.Error.Message,
-
668 discordChannelResponse.Inner?.Error?.Message);
-
669 remapRequired = true;
-
670 return null;
-
671 }
-
672
-
673 var channelType = discordChannelResponse.Entity.Type;
-
674 if (channelType != ChannelType.GuildText && channelType != ChannelType.GuildAnnouncement)
-
675 {
-
676 Logger.LogWarning("Cound not map channel {channelId}! Incorrect type: {channelType}", channelId, discordChannelResponse.Entity.Type);
-
677 return null;
-
678 }
-
679
-
680 friendlyName = discordChannelResponse.Entity.Name.Value;
-
681 var guildId = discordChannelResponse.Entity.GuildID.Value;
+

Definition at line 648 of file DiscordProvider.cs.

+
649 {
+
650 if (channels == null)
+
651 throw new ArgumentNullException(nameof(channels));
+
652
+
653 var remapRequired = false;
+
654
+
655 async Task<Tuple<Models.ChatChannel, ChannelRepresentation>> GetModelChannelFromDBChannel(Models.ChatChannel channelFromDB)
+
656 {
+
657 if (!channelFromDB.DiscordChannelId.HasValue)
+
658 throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
+
659
+
660 var channelId = channelFromDB.DiscordChannelId.Value;
+
661 string connectionName;
+
662 string friendlyName;
+
663 if (channelId == 0)
+
664 {
+
665 connectionName = initialUserName;
+
666 friendlyName = "(Unmapped accessible channels)";
+
667 }
+
668 else
+
669 {
+
670 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
+
671 var discordChannelResponse = await channelsClient.GetChannelAsync(new Snowflake(channelId), cancellationToken);
+
672 if (!discordChannelResponse.IsSuccess)
+
673 {
+
674 Logger.LogWarning(
+
675 "Error retrieving discord channel {channelId}: {error} Inner: {innerError}",
+
676 channelId,
+
677 discordChannelResponse.Error.Message,
+
678 discordChannelResponse.Inner?.Error?.Message);
+
679 remapRequired = true;
+
680 return null;
+
681 }
682
-
683 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
-
684 var guildsResponse = await guildsClient.GetGuildAsync(
-
685 guildId,
-
686 false,
-
687 cancellationToken);
-
688 if (!guildsResponse.IsSuccess)
-
689 {
-
690 Logger.LogWarning(
-
691 "Error retrieving discord guild {guildID}: {error} Inner: {innerError}",
-
692 guildId,
-
693 guildsResponse.Error.Message,
-
694 guildsResponse.Inner?.Error?.Message);
-
695 remapRequired = true;
-
696 return null;
-
697 }
-
698
-
699 connectionName = guildsResponse.Entity.Name;
-
700 }
-
701
-
702 var channelModel = new ChannelRepresentation
-
703 {
-
704 RealId = channelId,
-
705 IsAdminChannel = channelFromDB.IsAdminChannel == true,
-
706 ConnectionName = connectionName,
-
707 FriendlyName = friendlyName,
-
708 IsPrivateChannel = false,
-
709 Tag = channelFromDB.Tag,
-
710 EmbedsSupported = true,
-
711 };
-
712
-
713 Logger.LogTrace("Mapped channel {0}: {1}", channelModel.RealId, channelModel.FriendlyName);
-
714 return Tuple.Create(channelFromDB, channelModel);
-
715 }
-
716
-
717 var tasks = channels
-
718 .Select(x => GetModelChannelFromDBChannel(x))
-
719 .ToList();
-
720
-
721 await Task.WhenAll(tasks);
+
683 var channelType = discordChannelResponse.Entity.Type;
+
684 if (channelType != ChannelType.GuildText && channelType != ChannelType.GuildAnnouncement)
+
685 {
+
686 Logger.LogWarning("Cound not map channel {channelId}! Incorrect type: {channelType}", channelId, discordChannelResponse.Entity.Type);
+
687 return null;
+
688 }
+
689
+
690 friendlyName = discordChannelResponse.Entity.Name.Value;
+
691 var guildId = discordChannelResponse.Entity.GuildID.Value;
+
692
+
693 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
+
694 var guildsResponse = await guildsClient.GetGuildAsync(
+
695 guildId,
+
696 false,
+
697 cancellationToken);
+
698 if (!guildsResponse.IsSuccess)
+
699 {
+
700 Logger.LogWarning(
+
701 "Error retrieving discord guild {guildID}: {error} Inner: {innerError}",
+
702 guildId,
+
703 guildsResponse.Error.Message,
+
704 guildsResponse.Inner?.Error?.Message);
+
705 remapRequired = true;
+
706 return null;
+
707 }
+
708
+
709 connectionName = guildsResponse.Entity.Name;
+
710 }
+
711
+
712 var channelModel = new ChannelRepresentation
+
713 {
+
714 RealId = channelId,
+
715 IsAdminChannel = channelFromDB.IsAdminChannel == true,
+
716 ConnectionName = connectionName,
+
717 FriendlyName = friendlyName,
+
718 IsPrivateChannel = false,
+
719 Tag = channelFromDB.Tag,
+
720 EmbedsSupported = true,
+
721 };
722
-
723 var enumerator = tasks
-
724 .Select(x => x.Result)
-
725 .Where(x => x != null)
-
726 .ToList();
-
727
-
728 lock (mappedChannels)
-
729 {
-
730 mappedChannels.Clear();
-
731 mappedChannels.AddRange(enumerator.Select(x => x.Item2.RealId));
-
732 }
-
733
-
734 if (remapRequired)
-
735 EnqueueMessage(null);
-
736
-
737 return enumerator;
-
738 }
+
723 Logger.LogTrace("Mapped channel {0}: {1}", channelModel.RealId, channelModel.FriendlyName);
+
724 return Tuple.Create(channelFromDB, channelModel);
+
725 }
+
726
+
727 var tasks = channels
+
728 .Select(x => GetModelChannelFromDBChannel(x))
+
729 .ToList();
+
730
+
731 await Task.WhenAll(tasks);
+
732
+
733 var enumerator = tasks
+
734 .Select(x => x.Result)
+
735 .Where(x => x != null)
+
736 .ToList();
+
737
+
738 lock (mappedChannels)
+
739 {
+
740 mappedChannels.Clear();
+
741 mappedChannels.AddRange(enumerator.Select(x => x.Item2.RealId));
+
742 }
+
743
+
744 if (remapRequired)
+
745 EnqueueMessage(null);
+
746
+
747 return enumerator;
+
748 }
void EnqueueMessage(Message message)
Queues a message for NextMessage(CancellationToken).
Definition: Provider.cs:210

References Tgstation.Server.Host.Components.Chat.Providers.Provider.EnqueueMessage(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.initialUserName, Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.mappedChannels, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.serviceProvider, and Tgstation.Server.Host.Components.Chat.ChannelRepresentation.Tag.

@@ -1251,7 +1251,7 @@ Here is the call graph for this function:
Returns
The normalized mention string.
-

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

+

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

Here is the caller graph for this function:
@@ -1292,116 +1292,116 @@ Here is the caller graph for this function:

-

Definition at line 434 of file DiscordProvider.cs.

-
435 {
-
436 if (messageCreateEvent == null)
-
437 throw new ArgumentNullException(nameof(messageCreateEvent));
-
438
-
439 if ((messageCreateEvent.Type != MessageType.Default
-
440 && messageCreateEvent.Type != MessageType.InlineReply)
-
441 || messageCreateEvent.Author.ID == currentUserId)
-
442 return Result.FromSuccess();
-
443
-
444 var messageReference = new MessageReference
-
445 {
-
446 ChannelID = messageCreateEvent.ChannelID,
-
447 GuildID = messageCreateEvent.GuildID,
-
448 MessageID = messageCreateEvent.ID,
-
449 FailIfNotExists = false,
-
450 };
-
451
-
452 if (basedMeme && messageCreateEvent.Content.Equals("Based on what?", StringComparison.OrdinalIgnoreCase))
-
453 {
-
454 // DCT: None available
-
455 await SendMessage(
-
456 new DiscordMessage
-
457 {
-
458 MessageReference = messageReference,
-
459 },
- -
461 {
-
462 Text = "https://youtu.be/LrNu-SuFF_o",
-
463 },
-
464 messageCreateEvent.ChannelID.Value,
-
465 default);
-
466 return Result.FromSuccess();
-
467 }
-
468
-
469 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
-
470 var channelResponse = await channelsClient.GetChannelAsync(messageCreateEvent.ChannelID, cancellationToken);
-
471 if (!channelResponse.IsSuccess)
-
472 {
-
473 Logger.LogWarning(
-
474 "Failed to get channel {0} in response to message {1}!",
-
475 messageCreateEvent.ChannelID,
-
476 messageCreateEvent.ID);
-
477
-
478 // we'll handle the errors ourselves
-
479 return Result.FromSuccess();
-
480 }
-
481
-
482 var pm = channelResponse.Entity.Type == ChannelType.DM || channelResponse.Entity.Type == ChannelType.GroupDM;
-
483 var shouldNotAnswer = !pm;
-
484 if (shouldNotAnswer)
-
485 lock (mappedChannels)
-
486 shouldNotAnswer = !mappedChannels.Contains(messageCreateEvent.ChannelID.Value) && !mappedChannels.Contains(0);
+

Definition at line 444 of file DiscordProvider.cs.

+
445 {
+
446 if (messageCreateEvent == null)
+
447 throw new ArgumentNullException(nameof(messageCreateEvent));
+
448
+
449 if ((messageCreateEvent.Type != MessageType.Default
+
450 && messageCreateEvent.Type != MessageType.InlineReply)
+
451 || messageCreateEvent.Author.ID == currentUserId)
+
452 return Result.FromSuccess();
+
453
+
454 var messageReference = new MessageReference
+
455 {
+
456 ChannelID = messageCreateEvent.ChannelID,
+
457 GuildID = messageCreateEvent.GuildID,
+
458 MessageID = messageCreateEvent.ID,
+
459 FailIfNotExists = false,
+
460 };
+
461
+
462 if (basedMeme && messageCreateEvent.Content.Equals("Based on what?", StringComparison.OrdinalIgnoreCase))
+
463 {
+
464 // DCT: None available
+
465 await SendMessage(
+
466 new DiscordMessage
+
467 {
+
468 MessageReference = messageReference,
+
469 },
+ +
471 {
+
472 Text = "https://youtu.be/LrNu-SuFF_o",
+
473 },
+
474 messageCreateEvent.ChannelID.Value,
+
475 default);
+
476 return Result.FromSuccess();
+
477 }
+
478
+
479 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
+
480 var channelResponse = await channelsClient.GetChannelAsync(messageCreateEvent.ChannelID, cancellationToken);
+
481 if (!channelResponse.IsSuccess)
+
482 {
+
483 Logger.LogWarning(
+
484 "Failed to get channel {0} in response to message {1}!",
+
485 messageCreateEvent.ChannelID,
+
486 messageCreateEvent.ID);
487
-
488 var content = NormalizeMentions(messageCreateEvent.Content);
-
489 var mentionedUs = messageCreateEvent.Mentions.Any(x => x.ID == currentUserId)
-
490 || (!shouldNotAnswer && content.Split(' ').First().Equals(ChatManager.CommonMention, StringComparison.OrdinalIgnoreCase));
+
488 // we'll handle the errors ourselves
+
489 return Result.FromSuccess();
+
490 }
491
-
492 if (shouldNotAnswer)
-
493 {
-
494 if (mentionedUs)
-
495 Logger.LogTrace(
-
496 "Ignoring mention from {0} ({1}) by {2} ({3}). Channel not mapped!",
-
497 messageCreateEvent.ChannelID,
-
498 channelResponse.Entity.Name,
-
499 messageCreateEvent.Author.ID,
-
500 messageCreateEvent.Author.Username);
+
492 var pm = channelResponse.Entity.Type == ChannelType.DM || channelResponse.Entity.Type == ChannelType.GroupDM;
+
493 var shouldNotAnswer = !pm;
+
494 if (shouldNotAnswer)
+
495 lock (mappedChannels)
+
496 shouldNotAnswer = !mappedChannels.Contains(messageCreateEvent.ChannelID.Value) && !mappedChannels.Contains(0);
+
497
+
498 var content = NormalizeMentions(messageCreateEvent.Content);
+
499 var mentionedUs = messageCreateEvent.Mentions.Any(x => x.ID == currentUserId)
+
500 || (!shouldNotAnswer && content.Split(' ').First().Equals(ChatManager.CommonMention, StringComparison.OrdinalIgnoreCase));
501
-
502 return Result.FromSuccess();
-
503 }
-
504
-
505 string guildName = "UNKNOWN";
-
506 if (!pm)
-
507 {
-
508 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
-
509 var messageGuildResponse = await guildsClient.GetGuildAsync(messageCreateEvent.GuildID.Value, false, cancellationToken);
-
510 if (messageGuildResponse.IsSuccess)
-
511 guildName = messageGuildResponse.Entity.Name;
-
512 else
-
513 Logger.LogWarning(
-
514 "Failed to get channel {0} in response to message {1}!",
-
515 messageCreateEvent.ChannelID,
-
516 messageCreateEvent.ID);
-
517 }
-
518
-
519 var result = new DiscordMessage
-
520 {
-
521 MessageReference = messageReference,
-
522 Content = content,
-
523 User = new ChatUser
-
524 {
-
525 RealId = messageCreateEvent.Author.ID.Value,
-
526 Channel = new ChannelRepresentation
-
527 {
-
528 RealId = messageCreateEvent.ChannelID.Value,
-
529 IsPrivateChannel = pm,
-
530 ConnectionName = pm ? messageCreateEvent.Author.Username : guildName,
-
531 FriendlyName = channelResponse.Entity.Name.Value,
-
532 EmbedsSupported = true,
-
533
-
534 // isAdmin and Tag populated by manager
-
535 },
-
536 FriendlyName = messageCreateEvent.Author.Username,
-
537 Mention = NormalizeMentions($"<@{messageCreateEvent.Author.ID}>"),
-
538 },
-
539 };
-
540
-
541 EnqueueMessage(result);
-
542 return Result.FromSuccess();
-
543 }
+
502 if (shouldNotAnswer)
+
503 {
+
504 if (mentionedUs)
+
505 Logger.LogTrace(
+
506 "Ignoring mention from {0} ({1}) by {2} ({3}). Channel not mapped!",
+
507 messageCreateEvent.ChannelID,
+
508 channelResponse.Entity.Name,
+
509 messageCreateEvent.Author.ID,
+
510 messageCreateEvent.Author.Username);
+
511
+
512 return Result.FromSuccess();
+
513 }
+
514
+
515 string guildName = "UNKNOWN";
+
516 if (!pm)
+
517 {
+
518 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
+
519 var messageGuildResponse = await guildsClient.GetGuildAsync(messageCreateEvent.GuildID.Value, false, cancellationToken);
+
520 if (messageGuildResponse.IsSuccess)
+
521 guildName = messageGuildResponse.Entity.Name;
+
522 else
+
523 Logger.LogWarning(
+
524 "Failed to get channel {0} in response to message {1}!",
+
525 messageCreateEvent.ChannelID,
+
526 messageCreateEvent.ID);
+
527 }
+
528
+
529 var result = new DiscordMessage
+
530 {
+
531 MessageReference = messageReference,
+
532 Content = content,
+
533 User = new ChatUser
+
534 {
+
535 RealId = messageCreateEvent.Author.ID.Value,
+
536 Channel = new ChannelRepresentation
+
537 {
+
538 RealId = messageCreateEvent.ChannelID.Value,
+
539 IsPrivateChannel = pm,
+
540 ConnectionName = pm ? messageCreateEvent.Author.Username : guildName,
+
541 FriendlyName = channelResponse.Entity.Name.Value,
+
542 EmbedsSupported = true,
+
543
+
544 // isAdmin and Tag populated by manager
+
545 },
+
546 FriendlyName = messageCreateEvent.Author.Username,
+
547 Mention = NormalizeMentions($"<@{messageCreateEvent.Author.ID}>"),
+
548 },
+
549 };
+
550
+
551 EnqueueMessage(result);
+
552 return Result.FromSuccess();
+
553 }
static string NormalizeMentions(string fromDiscord)
Normalize a discord mention string.
override async Task SendMessage(Message replyTo, MessageContent message, ulong channelId, CancellationToken cancellationToken)
Send a message to the IProvider. A Task representing the running operation.
@@ -1452,15 +1452,15 @@ Here is the call graph for this function:

-

Definition at line 546 of file DiscordProvider.cs.

-
547 {
-
548 if (readyEvent == null)
-
549 throw new ArgumentNullException(nameof(readyEvent));
-
550
-
551 Logger.LogTrace("Gatway ready. Version: {version}", readyEvent.Version);
-
552 gatewayReadyTcs?.TrySetResult(null);
-
553 return Task.FromResult(Result.FromSuccess());
-
554 }
+

Definition at line 556 of file DiscordProvider.cs.

+
557 {
+
558 if (readyEvent == null)
+
559 throw new ArgumentNullException(nameof(readyEvent));
+
560
+
561 Logger.LogTrace("Gatway ready. Version: {version}", readyEvent.Version);
+
562 gatewayReadyTcs?.TrySetResult(null);
+
563 return Task.FromResult(Result.FromSuccess());
+
564 }

References Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.gatewayReadyTcs, and Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger.

@@ -1529,89 +1529,99 @@ Here is the call graph for this function:

Definition at line 224 of file DiscordProvider.cs.

225 {
226 Optional<IMessageReference> replyToReference = default;
-
227 if (replyTo != null && replyTo is DiscordMessage discordMessage)
-
228 {
-
229 replyToReference = discordMessage.MessageReference;
-
230 }
-
231
-
232 var embeds = ConvertEmbed(message.Embed);
-
233
-
234 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
-
235 async Task SendToChannel(Snowflake channelId)
-
236 {
-
237 var result = await channelsClient.CreateMessageAsync(
-
238 channelId,
-
239 message.Text,
-
240 embeds: embeds,
-
241 messageReference: replyToReference,
-
242 ct: cancellationToken);
-
243
-
244 if (!result.IsSuccess)
-
245 Logger.LogWarning(
-
246 "Failed to send to channel {0}: {1}",
-
247 channelId,
-
248 result.Error);
-
249 }
-
250
-
251 try
-
252 {
-
253 if (channelId == 0)
-
254 {
-
255 var usersClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
-
256 var currentGuildsResponse = await usersClient.GetCurrentUserGuildsAsync(ct: cancellationToken);
-
257 if (!currentGuildsResponse.IsSuccess)
-
258 {
-
259 Logger.LogWarning(
-
260 "Error retrieving current discord guilds: {0}",
-
261 currentGuildsResponse.Error.Message);
-
262 return;
-
263 }
-
264
-
265 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
-
266
-
267 var guildsChannelsTasks = currentGuildsResponse.Entity.Select(
-
268 guild => guildsClient.GetGuildChannelsAsync(guild.ID.Value, cancellationToken));
-
269
-
270 await Task.WhenAll(guildsChannelsTasks);
-
271
-
272 var unmappedTextChannels = guildsChannelsTasks
-
273 .Select(task => task.Result)
-
274 .SelectMany(guildChannels => guildChannels.Entity)
-
275 .Where(guildChannel => guildChannel.Type == ChannelType.GuildText);
+
227 Optional<IAllowedMentions> allowedMentions = default;
+
228 if (replyTo != null && replyTo is DiscordMessage discordMessage)
+
229 {
+
230 replyToReference = discordMessage.MessageReference;
+
231 allowedMentions = new AllowedMentions(
+
232 Parse: new List<MentionType> // reset settings back to how discord acts if this is not passed (which is different than the default if empty)
+
233 {
+
234 MentionType.Everyone,
+
235 MentionType.Roles,
+
236 MentionType.Users,
+
237 },
+
238 MentionRepliedUser: false); // disable reply mentions
+
239 }
+
240
+
241 var embeds = ConvertEmbed(message.Embed);
+
242
+
243 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
+
244 async Task SendToChannel(Snowflake channelId)
+
245 {
+
246 var result = await channelsClient.CreateMessageAsync(
+
247 channelId,
+
248 message.Text,
+
249 embeds: embeds,
+
250 messageReference: replyToReference,
+
251 allowedMentions: allowedMentions,
+
252 ct: cancellationToken);
+
253
+
254 if (!result.IsSuccess)
+
255 Logger.LogWarning(
+
256 "Failed to send to channel {0}: {1}",
+
257 channelId,
+
258 result.Error);
+
259 }
+
260
+
261 try
+
262 {
+
263 if (channelId == 0)
+
264 {
+
265 var usersClient = serviceProvider.GetRequiredService<IDiscordRestUserAPI>();
+
266 var currentGuildsResponse = await usersClient.GetCurrentUserGuildsAsync(ct: cancellationToken);
+
267 if (!currentGuildsResponse.IsSuccess)
+
268 {
+
269 Logger.LogWarning(
+
270 "Error retrieving current discord guilds: {0}",
+
271 currentGuildsResponse.Error.Message);
+
272 return;
+
273 }
+
274
+
275 var guildsClient = serviceProvider.GetRequiredService<IDiscordRestGuildAPI>();
276
-
277 lock (mappedChannels)
-
278 unmappedTextChannels = unmappedTextChannels
-
279 .Where(x => !mappedChannels.Contains(x.ID.Value))
-
280 .ToList();
+
277 var guildsChannelsTasks = currentGuildsResponse.Entity.Select(
+
278 guild => guildsClient.GetGuildChannelsAsync(guild.ID.Value, cancellationToken));
+
279
+
280 await Task.WhenAll(guildsChannelsTasks);
281
-
282 // discord API confirmed weak boned: https://stackoverflow.com/a/52462336
-
283 if (unmappedTextChannels.Any())
-
284 {
-
285 Logger.LogTrace("Dispatching to {0} unmapped channels...", unmappedTextChannels.Count());
-
286 await Task.WhenAll(
-
287 unmappedTextChannels.Select(
-
288 x => SendToChannel(x.ID)));
-
289 }
-
290
-
291 return;
-
292 }
-
293
-
294 await SendToChannel(new Snowflake(channelId));
-
295 }
-
296 catch (Exception e)
-
297 {
-
298 if (e is OperationCanceledException)
-
299 cancellationToken.ThrowIfCancellationRequested();
-
300 Logger.LogWarning(e, "Error sending discord message!");
-
301 }
-
302 }
-
Optional< IReadOnlyList< IEmbed > > ConvertEmbed(ChatEmbed embed)
Convert a ChatEmbed to an IEmbed parameters.
+
282 var unmappedTextChannels = guildsChannelsTasks
+
283 .Select(task => task.Result)
+
284 .SelectMany(guildChannels => guildChannels.Entity)
+
285 .Where(guildChannel => guildChannel.Type == ChannelType.GuildText);
+
286
+
287 lock (mappedChannels)
+
288 unmappedTextChannels = unmappedTextChannels
+
289 .Where(x => !mappedChannels.Contains(x.ID.Value))
+
290 .ToList();
+
291
+
292 // discord API confirmed weak boned: https://stackoverflow.com/a/52462336
+
293 if (unmappedTextChannels.Any())
+
294 {
+
295 Logger.LogTrace("Dispatching to {0} unmapped channels...", unmappedTextChannels.Count());
+
296 await Task.WhenAll(
+
297 unmappedTextChannels.Select(
+
298 x => SendToChannel(x.ID)));
+
299 }
+
300
+
301 return;
+
302 }
+
303
+
304 await SendToChannel(new Snowflake(channelId));
+
305 }
+
306 catch (Exception e)
+
307 {
+
308 if (e is OperationCanceledException)
+
309 cancellationToken.ThrowIfCancellationRequested();
+
310 Logger.LogWarning(e, "Error sending discord message!");
+
311 }
+
312 }
+
Optional< IReadOnlyList< IEmbed > > ConvertEmbed(ChatEmbed embed)
Convert a ChatEmbed to an IEmbed parameters.
-

References Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.ConvertEmbed(), Tgstation.Server.Host.Components.Interop.MessageContent.Embed, Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.mappedChannels, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.serviceProvider, and Tgstation.Server.Host.Components.Interop.MessageContent.Text.

+

References Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.ConvertEmbed(), Tgstation.Server.Host.Components.Interop.MessageContent.Embed, Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.mappedChannels, Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.serviceProvider, and Tgstation.Server.Host.Components.Interop.MessageContent.Text.

-

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

+

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

Here is the call graph for this function:
@@ -1697,125 +1707,125 @@ Here is the caller graph for this function:

-

Definition at line 305 of file DiscordProvider.cs.

-
314 {
-
315 localCommitPushed |= revisionInformation.CommitSha == revisionInformation.OriginCommitSha;
-
316
-
317 var fields = BuildUpdateEmbedFields(revisionInformation, byondVersion, gitHubOwner, gitHubRepo, localCommitPushed);
-
318 var author = new EmbedAuthor(assemblyInformationProvider.VersionPrefix)
-
319 {
-
320 Url = "https://github.com/tgstation/tgstation-server",
-
321 IconUrl = "https://avatars0.githubusercontent.com/u/1363778?s=280&v=4",
-
322 };
-
323 var embed = new Embed
-
324 {
-
325 Author = deploymentBranding ? author : default,
-
326 Colour = Color.FromArgb(0xF1, 0xC4, 0x0F),
-
327 Description = "TGS has begun deploying active repository code to production.",
-
328 Fields = fields,
-
329 Title = "Code Deployment",
-
330 Footer = new EmbedFooter(
-
331 $"In progress...{(estimatedCompletionTime.HasValue ? " ETA" : String.Empty)}"),
-
332 Timestamp = estimatedCompletionTime ?? default,
-
333 };
-
334
-
335 Logger.LogTrace("Attempting to post deploy embed to channel {0}...", channelId);
-
336 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
-
337
-
338 var messageResponse = await channelsClient.CreateMessageAsync(
-
339 new Snowflake(channelId),
-
340 "DM: Deployment in Progress...",
-
341 embeds: new List<IEmbed> { embed },
-
342 ct: cancellationToken)
-
343 ;
+

Definition at line 315 of file DiscordProvider.cs.

+
324 {
+
325 localCommitPushed |= revisionInformation.CommitSha == revisionInformation.OriginCommitSha;
+
326
+
327 var fields = BuildUpdateEmbedFields(revisionInformation, byondVersion, gitHubOwner, gitHubRepo, localCommitPushed);
+
328 var author = new EmbedAuthor(assemblyInformationProvider.VersionPrefix)
+
329 {
+
330 Url = "https://github.com/tgstation/tgstation-server",
+
331 IconUrl = "https://avatars0.githubusercontent.com/u/1363778?s=280&v=4",
+
332 };
+
333 var embed = new Embed
+
334 {
+
335 Author = deploymentBranding ? author : default,
+
336 Colour = Color.FromArgb(0xF1, 0xC4, 0x0F),
+
337 Description = "TGS has begun deploying active repository code to production.",
+
338 Fields = fields,
+
339 Title = "Code Deployment",
+
340 Footer = new EmbedFooter(
+
341 $"In progress...{(estimatedCompletionTime.HasValue ? " ETA" : String.Empty)}"),
+
342 Timestamp = estimatedCompletionTime ?? default,
+
343 };
344
-
345 if (!messageResponse.IsSuccess)
-
346 Logger.LogWarning("Failed to post deploy embed to channel {0}: {1}", channelId, messageResponse.Error.Message);
+
345 Logger.LogTrace("Attempting to post deploy embed to channel {0}...", channelId);
+
346 var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
347
-
348 return async (errorMessage, dreamMakerOutput) =>
-
349 {
-
350 var completionString = errorMessage == null ? "Succeeded" : "Failed";
-
351
-
352 embed = new Embed
-
353 {
-
354 Author = embed.Author,
-
355 Colour = errorMessage == null ? Color.Green : Color.Red,
-
356 Description = errorMessage == null
-
357 ? "The deployment completed successfully and will be available at the next server reboot."
-
358 : "The deployment failed.",
-
359 Fields = fields,
-
360 Title = embed.Title,
-
361 Footer = new EmbedFooter(
-
362 completionString),
-
363 Timestamp = DateTimeOffset.UtcNow,
-
364 };
-
365
-
366 var showDMOutput = outputDisplayType switch
-
367 {
-
368 DiscordDMOutputDisplayType.Always => true,
-
369 DiscordDMOutputDisplayType.Never => false,
-
370 DiscordDMOutputDisplayType.OnError => errorMessage != null,
-
371 _ => throw new InvalidOperationException($"Invalid DiscordDMOutputDisplayType: {outputDisplayType}"),
-
372 };
-
373
-
374 if (dreamMakerOutput != null)
-
375 {
-
376 // https://github.com/discord-net/Discord.Net/blob/8349cd7e1eb92e9a3baff68082c30a7b43e8e9b7/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs#L431
-
377 const int MaxFieldValueLength = 1024;
-
378 showDMOutput = showDMOutput && dreamMakerOutput.Length < MaxFieldValueLength - (6 + Environment.NewLine.Length);
-
379 if (showDMOutput)
-
380 fields.Add(new EmbedField(
-
381 "DreamMaker Output",
-
382 $"```{Environment.NewLine}{dreamMakerOutput}{Environment.NewLine}```",
-
383 false));
-
384 }
-
385
-
386 if (errorMessage != null)
-
387 fields.Add(new EmbedField(
-
388 "Error Message",
-
389 errorMessage,
-
390 false));
-
391
-
392 var updatedMessage = $"DM: Deployment {completionString}!";
-
393
-
394 async Task CreateUpdatedMessage()
-
395 {
-
396 var createUpdatedMessageResponse = await channelsClient.CreateMessageAsync(
-
397 new Snowflake(channelId),
-
398 updatedMessage,
-
399 embeds: new List<IEmbed> { embed },
-
400 ct: cancellationToken)
-
401 ;
-
402
-
403 if (!createUpdatedMessageResponse.IsSuccess)
-
404 Logger.LogWarning(
-
405 "Creating updated deploy embed failed! Error: {0}",
-
406 createUpdatedMessageResponse.Error.Message);
-
407 }
-
408
-
409 if (!messageResponse.IsSuccess)
-
410 await CreateUpdatedMessage();
-
411 else
-
412 {
-
413 var editResponse = await channelsClient.EditMessageAsync(
-
414 new Snowflake(channelId),
-
415 messageResponse.Entity.ID,
-
416 updatedMessage,
-
417 embeds: new List<IEmbed> { embed },
-
418 ct: cancellationToken)
-
419 ;
-
420
-
421 if (!editResponse.IsSuccess)
-
422 {
-
423 Logger.LogWarning(
-
424 "Updating deploy embed {0} failed, attempting new post! Error: {1}",
-
425 messageResponse.Entity.ID,
-
426 editResponse.Error.Message);
-
427 await CreateUpdatedMessage();
-
428 }
-
429 }
-
430 };
-
431 }
+
348 var messageResponse = await channelsClient.CreateMessageAsync(
+
349 new Snowflake(channelId),
+
350 "DM: Deployment in Progress...",
+
351 embeds: new List<IEmbed> { embed },
+
352 ct: cancellationToken)
+
353 ;
+
354
+
355 if (!messageResponse.IsSuccess)
+
356 Logger.LogWarning("Failed to post deploy embed to channel {0}: {1}", channelId, messageResponse.Error.Message);
+
357
+
358 return async (errorMessage, dreamMakerOutput) =>
+
359 {
+
360 var completionString = errorMessage == null ? "Succeeded" : "Failed";
+
361
+
362 embed = new Embed
+
363 {
+
364 Author = embed.Author,
+
365 Colour = errorMessage == null ? Color.Green : Color.Red,
+
366 Description = errorMessage == null
+
367 ? "The deployment completed successfully and will be available at the next server reboot."
+
368 : "The deployment failed.",
+
369 Fields = fields,
+
370 Title = embed.Title,
+
371 Footer = new EmbedFooter(
+
372 completionString),
+
373 Timestamp = DateTimeOffset.UtcNow,
+
374 };
+
375
+
376 var showDMOutput = outputDisplayType switch
+
377 {
+
378 DiscordDMOutputDisplayType.Always => true,
+
379 DiscordDMOutputDisplayType.Never => false,
+
380 DiscordDMOutputDisplayType.OnError => errorMessage != null,
+
381 _ => throw new InvalidOperationException($"Invalid DiscordDMOutputDisplayType: {outputDisplayType}"),
+
382 };
+
383
+
384 if (dreamMakerOutput != null)
+
385 {
+
386 // https://github.com/discord-net/Discord.Net/blob/8349cd7e1eb92e9a3baff68082c30a7b43e8e9b7/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs#L431
+
387 const int MaxFieldValueLength = 1024;
+
388 showDMOutput = showDMOutput && dreamMakerOutput.Length < MaxFieldValueLength - (6 + Environment.NewLine.Length);
+
389 if (showDMOutput)
+
390 fields.Add(new EmbedField(
+
391 "DreamMaker Output",
+
392 $"```{Environment.NewLine}{dreamMakerOutput}{Environment.NewLine}```",
+
393 false));
+
394 }
+
395
+
396 if (errorMessage != null)
+
397 fields.Add(new EmbedField(
+
398 "Error Message",
+
399 errorMessage,
+
400 false));
+
401
+
402 var updatedMessage = $"DM: Deployment {completionString}!";
+
403
+
404 async Task CreateUpdatedMessage()
+
405 {
+
406 var createUpdatedMessageResponse = await channelsClient.CreateMessageAsync(
+
407 new Snowflake(channelId),
+
408 updatedMessage,
+
409 embeds: new List<IEmbed> { embed },
+
410 ct: cancellationToken)
+
411 ;
+
412
+
413 if (!createUpdatedMessageResponse.IsSuccess)
+
414 Logger.LogWarning(
+
415 "Creating updated deploy embed failed! Error: {0}",
+
416 createUpdatedMessageResponse.Error.Message);
+
417 }
+
418
+
419 if (!messageResponse.IsSuccess)
+
420 await CreateUpdatedMessage();
+
421 else
+
422 {
+
423 var editResponse = await channelsClient.EditMessageAsync(
+
424 new Snowflake(channelId),
+
425 messageResponse.Entity.ID,
+
426 updatedMessage,
+
427 embeds: new List<IEmbed> { embed },
+
428 ct: cancellationToken)
+
429 ;
+
430
+
431 if (!editResponse.IsSuccess)
+
432 {
+
433 Logger.LogWarning(
+
434 "Updating deploy embed {0} failed, attempting new post! Error: {1}",
+
435 messageResponse.Entity.ID,
+
436 editResponse.Error.Message);
+
437 await CreateUpdatedMessage();
+
438 }
+
439 }
+
440 };
+
441 }
static List< IEmbedField > BuildUpdateEmbedFields(Models.RevisionInformation revisionInformation, Version byondVersion, string gitHubOwner, string gitHubRepo, bool localCommitPushed)
Create a List<T> of IEmbedFields for a discord update embed.
@@ -1857,7 +1867,7 @@ Here is the call graph for this function:

Definition at line 53 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage().

@@ -1885,7 +1895,7 @@ Here is the call graph for this function:

Definition at line 73 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync().

@@ -1913,7 +1923,7 @@ Here is the call graph for this function:

Definition at line 68 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider().

@@ -1941,7 +1951,7 @@ Here is the call graph for this function:

Definition at line 103 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync().

@@ -1969,7 +1979,7 @@ Here is the call graph for this function:

Definition at line 78 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage().

@@ -2025,7 +2035,7 @@ Here is the call graph for this function:

Definition at line 88 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisposeAsync().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisposeAsync().

@@ -2053,7 +2063,7 @@ Here is the call graph for this function:

Definition at line 93 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync().

@@ -2081,7 +2091,7 @@ Here is the call graph for this function:

Definition at line 98 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl().

@@ -2109,7 +2119,7 @@ Here is the call graph for this function:

Definition at line 108 of file DiscordProvider.cs.

-

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

+

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

@@ -2137,7 +2147,7 @@ Here is the call graph for this function:

Definition at line 63 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendMessage().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendMessage().

@@ -2165,7 +2175,7 @@ Here is the call graph for this function:

Definition at line 83 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage().

@@ -2193,7 +2203,7 @@ Here is the call graph for this function:

Definition at line 58 of file DiscordProvider.cs.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisposeAsync(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendMessage(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DiscordProvider(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisposeAsync(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendMessage(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_provider.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_provider.html index 659410103e..85738a7e4b 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_provider.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_chat_1_1_providers_1_1_provider.html @@ -633,7 +633,7 @@ Here is the call graph for this function:

References Tgstation.Server.Host.Components.Chat.Providers.Provider.Logger, Tgstation.Server.Host.Components.Chat.Providers.Provider.messageQueue, and Tgstation.Server.Host.Components.Chat.Providers.Provider.nextMessage.

-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.HandleMessage(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), Tgstation.Server.Host.Components.Chat.Providers.Provider.ReconnectionLoop(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.HandleMessage(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), Tgstation.Server.Host.Components.Chat.Providers.Provider.ReconnectionLoop(), and Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync().

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

Definition at line 30 of file Provider.cs.

30{ get; }
-

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.ConvertEmbed(), Tgstation.Server.Host.Components.Chat.Providers.Provider.Disconnect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.DisconnectImpl(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisposeAsync(), Tgstation.Server.Host.Components.Chat.Providers.Provider.DisposeAsync(), Tgstation.Server.Host.Components.Chat.Providers.Provider.EnqueueMessage(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.HardDisconnect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.NonBlockingListen(), Tgstation.Server.Host.Components.Chat.Providers.Provider.Provider(), Tgstation.Server.Host.Components.Chat.Providers.Provider.ReconnectionLoop(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SaslAuthenticate(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendMessage(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage(), and Tgstation.Server.Host.Components.Chat.Providers.Provider.StopReconnectionTimer().

+

Referenced by Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.Connect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.ConvertEmbed(), Tgstation.Server.Host.Components.Chat.Providers.Provider.Disconnect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisconnectImpl(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.DisconnectImpl(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.DisposeAsync(), Tgstation.Server.Host.Components.Chat.Providers.Provider.DisposeAsync(), Tgstation.Server.Host.Components.Chat.Providers.Provider.EnqueueMessage(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.HardDisconnect(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.MapChannelsImpl(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.NonBlockingListen(), Tgstation.Server.Host.Components.Chat.Providers.Provider.Provider(), Tgstation.Server.Host.Components.Chat.Providers.Provider.ReconnectionLoop(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.RespondAsync(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SaslAuthenticate(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendMessage(), Tgstation.Server.Host.Components.Chat.Providers.IrcProvider.SendMessage(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage(), and Tgstation.Server.Host.Components.Chat.Providers.Provider.StopReconnectionTimer().

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed.html index 7e492e6009..05fa29496e 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed.html @@ -154,7 +154,7 @@ Properties

Definition at line 65 of file ChatEmbed.cs.

65{ get; set; }
-

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

+

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

@@ -183,7 +183,7 @@ Properties

Definition at line 35 of file ChatEmbed.cs.

35{ get; set; }
-

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

+

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

@@ -239,7 +239,7 @@ Properties

Definition at line 70 of file ChatEmbed.cs.

70{ get; set; }
-

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

+

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

@@ -268,7 +268,7 @@ Properties

Definition at line 40 of file ChatEmbed.cs.

40{ get; set; }
-

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

+

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

@@ -297,7 +297,7 @@ Properties

Definition at line 45 of file ChatEmbed.cs.

45{ get; set; }
-

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

+

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

@@ -353,7 +353,7 @@ Properties

Definition at line 50 of file ChatEmbed.cs.

50{ get; set; }
-

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

+

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

@@ -382,7 +382,7 @@ Properties

Definition at line 30 of file ChatEmbed.cs.

30{ get; set; }
-

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

+

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

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_footer.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_footer.html index a39b3116ba..8023f11980 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_footer.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_footer.html @@ -181,7 +181,7 @@ Properties

Definition at line 11 of file ChatEmbedFooter.cs.

11{ get; set; }
-

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

+

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

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_media.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_media.html index 9f635e41d6..afaf81899e 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_media.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_media.html @@ -184,7 +184,7 @@ Properties

Definition at line 12 of file ChatEmbedMedia.cs.

12{ get; set; }
-

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

+

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

diff --git a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_provider.html b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_provider.html index 1f38a7db9e..c67f23ee4b 100644 --- a/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_provider.html +++ b/class_tgstation_1_1_server_1_1_host_1_1_components_1_1_interop_1_1_chat_embed_provider.html @@ -133,7 +133,7 @@ Properties

Definition at line 11 of file ChatEmbedProvider.cs.

11{ get; set; }
-

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

+

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

diff --git a/interface_tgstation_1_1_server_1_1_host_1_1_system_1_1_i_assembly_information_provider.html b/interface_tgstation_1_1_server_1_1_host_1_1_system_1_1_i_assembly_information_provider.html index 5273fc34b1..d840f24afc 100644 --- a/interface_tgstation_1_1_server_1_1_host_1_1_system_1_1_i_assembly_information_provider.html +++ b/interface_tgstation_1_1_server_1_1_host_1_1_system_1_1_i_assembly_information_provider.html @@ -271,7 +271,7 @@ Properties

Definition at line 25 of file IAssemblyInformationProvider.cs.

25{ get; }
-

Referenced by Tgstation.Server.Host.Setup.SetupWizard.ConfigureDatabase(), Tgstation.Server.Host.Configuration.FileLoggingConfiguration.GetFullLogDirectory(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage(), and Tgstation.Server.Host.Extensions.ApplicationBuilderExtensions.UseServerBranding().

+

Referenced by Tgstation.Server.Host.Setup.SetupWizard.ConfigureDatabase(), Tgstation.Server.Host.Configuration.FileLoggingConfiguration.GetFullLogDirectory(), Tgstation.Server.Host.Components.Chat.Providers.DiscordProvider.SendUpdateMessage(), and Tgstation.Server.Host.Extensions.ApplicationBuilderExtensions.UseServerBranding().