From b3515d685f74bb3fe4b851e166c02bebbe4f5860 Mon Sep 17 00:00:00 2001 From: tgstation-server Date: Sat, 22 Aug 2020 14:40:22 +0000 Subject: [PATCH] Deploy code docs to GitHub Pages for workflow run 208 Commit: 63727e306ce2ec44f899628fdb8d19d3fdb5e14f --- ..._connection_string_builder_8cs_source.html | 54 +- _discord_provider_8cs_source.html | 862 ++++++++--------- class_controller.html | 2 +- class_i_async_disposable.html | 2 +- class_i_design_time_db_context_factory.html | 2 +- class_i_disposable.html | 2 +- class_i_hosted_service.html | 2 +- class_migration.html | 2 +- ...ord_connection_string_builder-members.html | 13 +- ...1_1_discord_connection_string_builder.html | 83 +- ..._server_1_1_api_1_1_models_1_1_limits.html | 2 +- ...api_1_1_models_1_1_limits__coll__graph.map | 2 +- ...api_1_1_models_1_1_limits__coll__graph.md5 | 2 +- ...api_1_1_models_1_1_limits__coll__graph.png | Bin 7807 -> 7288 bytes ...s_1_1_chat_1_1_channel_representation.html | 4 +- ..._components_1_1_chat_1_1_chat_manager.html | 2 +- ...1_1_components_1_1_chat_1_1_chat_user.html | 2 +- ...roviders_1_1_discord_provider-members.html | 71 +- ...at_1_1_providers_1_1_discord_provider.html | 882 +++++++++--------- ...ders_1_1_discord_provider__coll__graph.map | 25 +- ...ders_1_1_discord_provider__coll__graph.md5 | 2 +- ...ders_1_1_discord_provider__coll__graph.png | Bin 84692 -> 93870 bytes ...1_chat_1_1_providers_1_1_irc_provider.html | 41 +- ...roviders_1_1_irc_provider__coll__graph.map | 41 +- ...roviders_1_1_irc_provider__coll__graph.md5 | 2 +- ...roviders_1_1_irc_provider__coll__graph.png | Bin 106782 -> 126065 bytes ...s_1_1_chat_1_1_providers_1_1_provider.html | 15 +- ..._1_providers_1_1_provider__coll__graph.map | 11 +- ..._1_providers_1_1_provider__coll__graph.md5 | 2 +- ..._1_providers_1_1_provider__coll__graph.png | Bin 47601 -> 55022 bytes ..._1_1_host_1_1_components_1_1_instance.html | 4 +- ...1_components_1_1_instance__coll__graph.map | 4 +- ...1_components_1_1_instance__coll__graph.md5 | 2 +- ...1_components_1_1_instance__coll__graph.png | Bin 126920 -> 126812 bytes ...t_1_1_components_1_1_instance_factory.html | 4 +- ...ents_1_1_instance_factory__coll__graph.map | 4 +- ...ents_1_1_instance_factory__coll__graph.md5 | 2 +- ...ents_1_1_instance_factory__coll__graph.png | Bin 245798 -> 246444 bytes ...t_1_1_components_1_1_instance_manager.html | 64 +- ...ents_1_1_instance_manager__coll__graph.map | 64 +- ...ents_1_1_instance_manager__coll__graph.md5 | 2 +- ...ents_1_1_instance_manager__coll__graph.png | Bin 281721 -> 280865 bytes ...ponents_1_1_repository_1_1_repository.html | 25 +- ...repository_1_1_repository__coll__graph.map | 25 +- ...repository_1_1_repository__coll__graph.md5 | 2 +- ...repository_1_1_repository__coll__graph.png | Bin 93026 -> 89497 bytes ...ts_1_1_session_1_1_session_controller.html | 65 +- ...on_1_1_session_controller__coll__graph.map | 65 +- ...on_1_1_session_controller__coll__graph.md5 | 2 +- ...on_1_1_session_controller__coll__graph.png | Bin 239287 -> 207337 bytes ...nfiguration_1_1_general_configuration.html | 6 +- ...1_1_general_configuration__coll__graph.map | 6 +- ...1_1_general_configuration__coll__graph.md5 | 2 +- ...1_1_general_configuration__coll__graph.png | Bin 23344 -> 27767 bytes ...rollers_1_1_administration_controller.html | 4 +- ...administration_controller__coll__graph.map | 4 +- ...administration_controller__coll__graph.md5 | 2 +- ...administration_controller__coll__graph.png | Bin 135370 -> 135644 bytes ...1_1_controllers_1_1_bridge_controller.html | 2 +- ...ers_1_1_bridge_controller__coll__graph.map | 2 +- ...ers_1_1_bridge_controller__coll__graph.md5 | 2 +- ...ers_1_1_bridge_controller__coll__graph.png | Bin 26333 -> 25777 bytes ...t_1_1_controllers_1_1_home_controller.html | 4 +- ...llers_1_1_home_controller__coll__graph.map | 4 +- ...llers_1_1_home_controller__coll__graph.md5 | 2 +- ...llers_1_1_home_controller__coll__graph.png | Bin 106981 -> 106804 bytes ...1_controllers_1_1_instance_controller.html | 4 +- ...s_1_1_instance_controller__coll__graph.map | 4 +- ...s_1_1_instance_controller__coll__graph.md5 | 2 +- ...s_1_1_instance_controller__coll__graph.png | Bin 90464 -> 90350 bytes ...controllers_1_1_repository_controller.html | 4 +- ...1_1_repository_controller__coll__graph.map | 4 +- ...1_1_repository_controller__coll__graph.md5 | 2 +- ...1_1_repository_controller__coll__graph.png | Bin 83897 -> 83645 bytes ...t_1_1_controllers_1_1_user_controller.html | 4 +- ...llers_1_1_user_controller__coll__graph.map | 4 +- ...llers_1_1_user_controller__coll__graph.md5 | 2 +- ...llers_1_1_user_controller__coll__graph.png | Bin 62609 -> 62818 bytes ...1_1_open_api_enum_var_names_extension.html | 4 +- ..._enum_var_names_extension__coll__graph.map | 4 +- ..._enum_var_names_extension__coll__graph.md5 | 2 +- ..._enum_var_names_extension__coll__graph.png | Bin 9906 -> 8637 bytes ...ost_1_1_core_1_1_server_port_proivder.html | 17 +- ..._1_1_server_port_proivder__coll__graph.map | 17 +- ..._1_1_server_port_proivder__coll__graph.md5 | 2 +- ..._1_1_server_port_proivder__coll__graph.png | Bin 39327 -> 43864 bytes ...host_1_1_database_1_1_database_seeder.html | 4 +- ...abase_1_1_database_seeder__coll__graph.map | 4 +- ...abase_1_1_database_seeder__coll__graph.md5 | 2 +- ...abase_1_1_database_seeder__coll__graph.png | Bin 75726 -> 75249 bytes ..._host_1_1_i_o_1_1_default_i_o_manager.html | 2 +- ...o_1_1_default_i_o_manager__coll__graph.map | 2 +- ...o_1_1_default_i_o_manager__coll__graph.md5 | 2 +- ...o_1_1_default_i_o_manager__coll__graph.png | Bin 16499 -> 16515 bytes ...ost_1_1_i_o_1_1_resolving_i_o_manager.html | 2 +- ...1_1_resolving_i_o_manager__coll__graph.map | 2 +- ...1_1_resolving_i_o_manager__coll__graph.md5 | 2 +- ...1_1_resolving_i_o_manager__coll__graph.png | Bin 19961 -> 19972 bytes ...ver_1_1_host_1_1_jobs_1_1_job_manager.html | 8 +- ..._1_1_jobs_1_1_job_manager__coll__graph.map | 8 +- ..._1_1_jobs_1_1_job_manager__coll__graph.md5 | 2 +- ..._1_1_jobs_1_1_job_manager__coll__graph.png | Bin 59096 -> 59533 bytes ...ation_1_1_server_1_1_host_1_1_program.html | 6 +- ...rver_1_1_host_1_1_program__coll__graph.map | 6 +- ...rver_1_1_host_1_1_program__coll__graph.md5 | 2 +- ...rver_1_1_host_1_1_program__coll__graph.png | Bin 9813 -> 11420 bytes ..._1_security_1_1_identity_cache_object.html | 8 +- ...1_1_identity_cache_object__coll__graph.map | 8 +- ...1_1_identity_cache_object__coll__graph.md5 | 2 +- ...1_1_identity_cache_object__coll__graph.png | Bin 12976 -> 11291 bytes ...tation_1_1_server_1_1_host_1_1_server.html | 4 +- ...erver_1_1_host_1_1_server__coll__graph.map | 4 +- ...erver_1_1_host_1_1_server__coll__graph.md5 | 2 +- ...erver_1_1_host_1_1_server__coll__graph.png | Bin 99172 -> 98934 bytes ...ost_1_1_setup_1_1_post_setup_services.html | 4 +- ...p_1_1_post_setup_services__coll__graph.map | 4 +- ...p_1_1_post_setup_services__coll__graph.md5 | 2 +- ...p_1_1_post_setup_services__coll__graph.png | Bin 83905 -> 83607 bytes ...r_1_1_host_1_1_setup_1_1_setup_wizard.html | 4 +- ..._1_setup_1_1_setup_wizard__coll__graph.map | 4 +- ..._1_setup_1_1_setup_wizard__coll__graph.md5 | 2 +- ..._1_setup_1_1_setup_wizard__coll__graph.png | Bin 102934 -> 102957 bytes ...erver_1_1_host_1_1_system_1_1_process.html | 14 +- ...st_1_1_system_1_1_process__coll__graph.map | 14 +- ...st_1_1_system_1_1_process__coll__graph.md5 | 2 +- ...st_1_1_system_1_1_process__coll__graph.png | Bin 40122 -> 40441 bytes classes.html | 18 +- functions.html | 6 +- functions_b.html | 6 + functions_c.html | 12 +- functions_h.html | 4 +- functions_i.html | 4 +- functions_n.html | 4 +- functions_prop_b.html | 3 + functions_s.html | 4 +- functions_vars_b.html | 3 + functions_vars_c.html | 4 +- search/all_1.js | 10 +- search/all_10.js | 198 ++-- search/all_11.js | 2 +- search/all_12.js | 356 +++---- search/all_13.js | 376 ++++---- search/all_14.js | 216 ++--- search/all_15.js | 114 +-- search/all_16.js | 44 +- search/all_17.js | 126 +-- search/all_18.js | 2 +- search/all_19.js | 2 +- search/all_2.js | 135 +-- search/all_3.js | 522 +++++------ search/all_4.js | 338 +++---- search/all_5.js | 68 +- search/all_6.js | 32 +- search/all_7.js | 146 +-- search/all_8.js | 78 +- search/all_9.js | 644 ++++++------- search/all_a.js | 46 +- search/all_b.js | 10 +- search/all_c.js | 100 +- search/all_d.js | 192 ++-- search/all_e.js | 54 +- search/all_f.js | 56 +- search/classes_0.js | 34 +- search/classes_1.js | 26 +- search/classes_10.js | 36 +- search/classes_11.js | 72 +- search/classes_12.js | 24 +- search/classes_13.js | 14 +- search/classes_14.js | 8 +- search/classes_15.js | 26 +- search/classes_2.js | 60 +- search/classes_3.js | 54 +- search/classes_4.js | 12 +- search/classes_5.js | 2 +- search/classes_6.js | 6 +- search/classes_7.js | 8 +- search/classes_8.js | 268 +++--- search/classes_9.js | 14 +- search/classes_a.js | 2 +- search/classes_b.js | 10 +- search/classes_c.js | 96 +- search/classes_d.js | 2 +- search/classes_e.js | 2 +- search/classes_f.js | 52 +- search/enums_0.js | 4 +- search/enums_1.js | 4 +- search/enums_2.js | 10 +- search/enums_3.js | 10 +- search/enums_4.js | 4 +- search/enums_5.js | 2 +- search/enums_6.js | 6 +- search/enums_7.js | 6 +- search/enums_8.js | 6 +- search/enums_9.js | 2 +- search/enums_a.js | 4 +- search/enums_b.js | 2 +- search/enumvalues_0.js | 22 +- search/enumvalues_1.js | 24 +- search/enumvalues_10.js | 90 +- search/enumvalues_11.js | 68 +- search/enumvalues_12.js | 4 +- search/enumvalues_13.js | 24 +- search/enumvalues_14.js | 44 +- search/enumvalues_2.js | 72 +- search/enumvalues_3.js | 44 +- search/enumvalues_4.js | 6 +- search/enumvalues_5.js | 2 +- search/enumvalues_6.js | 8 +- search/enumvalues_7.js | 4 +- search/enumvalues_8.js | 48 +- search/enumvalues_9.js | 2 +- search/enumvalues_a.js | 2 +- search/enumvalues_b.js | 4 +- search/enumvalues_c.js | 12 +- search/enumvalues_d.js | 12 +- search/enumvalues_e.js | 8 +- search/enumvalues_f.js | 8 +- search/files_0.js | 212 ++--- search/files_1.js | 40 +- search/files_10.js | 42 +- search/files_11.js | 44 +- search/files_12.js | 60 +- search/files_13.js | 26 +- search/files_14.js | 14 +- search/files_15.js | 8 +- search/files_16.js | 28 +- search/files_2.js | 30 +- search/files_3.js | 66 +- search/files_4.js | 62 +- search/files_5.js | 16 +- search/files_6.js | 4 +- search/files_7.js | 8 +- search/files_8.js | 10 +- search/files_9.js | 230 ++--- search/files_a.js | 14 +- search/files_b.js | 2 +- search/files_c.js | 10 +- search/files_d.js | 14 +- search/files_e.js | 2 +- search/files_f.js | 2 +- search/functions_0.js | 56 +- search/functions_1.js | 28 +- search/functions_10.js | 120 +-- search/functions_11.js | 134 +-- search/functions_12.js | 32 +- search/functions_13.js | 40 +- search/functions_14.js | 20 +- search/functions_15.js | 40 +- search/functions_16.js | 2 +- search/functions_17.js | 2 +- search/functions_2.js | 216 ++--- search/functions_3.js | 82 +- search/functions_4.js | 20 +- search/functions_5.js | 18 +- search/functions_6.js | 106 +-- search/functions_7.js | 32 +- search/functions_8.js | 68 +- search/functions_9.js | 14 +- search/functions_a.js | 2 +- search/functions_b.js | 42 +- search/functions_c.js | 32 +- search/functions_d.js | 12 +- search/functions_e.js | 20 +- search/functions_f.js | 58 +- search/namespaces_0.js | 74 +- search/pages_0.js | 2 +- search/pages_1.js | 2 +- search/pages_2.js | 4 +- search/pages_3.js | 2 +- search/properties_0.js | 76 +- search/properties_1.js | 23 +- search/properties_10.js | 48 +- search/properties_11.js | 38 +- search/properties_12.js | 24 +- search/properties_13.js | 12 +- search/properties_14.js | 6 +- search/properties_2.js | 112 +-- search/properties_3.js | 68 +- search/properties_4.js | 16 +- search/properties_5.js | 4 +- search/properties_6.js | 20 +- search/properties_7.js | 14 +- search/properties_8.js | 50 +- search/properties_9.js | 8 +- search/properties_a.js | 24 +- search/properties_b.js | 20 +- search/properties_c.js | 18 +- search/properties_d.js | 16 +- search/properties_e.js | 42 +- search/properties_f.js | 50 +- search/variables_0.js | 44 +- search/variables_1.js | 45 +- search/variables_10.js | 2 +- search/variables_11.js | 80 +- search/variables_12.js | 56 +- search/variables_13.js | 38 +- search/variables_14.js | 24 +- search/variables_15.js | 8 +- search/variables_16.js | 8 +- search/variables_2.js | 96 +- search/variables_3.js | 112 +-- search/variables_4.js | 12 +- search/variables_5.js | 6 +- search/variables_6.js | 14 +- search/variables_7.js | 24 +- search/variables_8.js | 48 +- search/variables_9.js | 12 +- search/variables_a.js | 2 +- search/variables_b.js | 24 +- search/variables_c.js | 20 +- search/variables_d.js | 14 +- search/variables_e.js | 18 +- search/variables_f.js | 44 +- 313 files changed, 5915 insertions(+), 5792 deletions(-) diff --git a/_discord_connection_string_builder_8cs_source.html b/_discord_connection_string_builder_8cs_source.html index 37489888ac..c43a478a11 100644 --- a/_discord_connection_string_builder_8cs_source.html +++ b/_discord_connection_string_builder_8cs_source.html @@ -84,34 +84,48 @@ $(function() {
14 
19  public string? BotToken { get; set; }
20 
-
24  public DiscordDMOutputDisplayType DMOutputDisplay { get; set; }
+
24  public bool BasedMeme { get; set; }
25 
-
29  public DiscordConnectionStringBuilder() { }
+
29  public DiscordDMOutputDisplayType DMOutputDisplay { get; set; }
30 
-
35  public DiscordConnectionStringBuilder(string connectionString)
-
36  {
-
37  if(connectionString == null)
-
38  throw new ArgumentNullException(nameof(connectionString));
-
39 
-
40  var splits = connectionString.Split(';');
-
41  BotToken = splits.First();
-
42  if (splits.Length < 2 || !Enum.TryParse<DiscordDMOutputDisplayType>(splits[1], out var dMOutputDisplayType))
-
43  dMOutputDisplayType = DiscordDMOutputDisplayType.Always;
-
44  DMOutputDisplay = dMOutputDisplayType;
-
45  }
-
46 
-
48  public override string ToString() => $"{BotToken};{(int)DMOutputDisplay}";
-
49  }
-
50 }
+
34  public DiscordConnectionStringBuilder()
+
35  {
+
36  BasedMeme = true;
+
37  }
+
38 
+
43  public DiscordConnectionStringBuilder(string connectionString)
+
44  {
+
45  if(connectionString == null)
+
46  throw new ArgumentNullException(nameof(connectionString));
+
47 
+
48  var splits = connectionString.Split(';');
+
49  BotToken = splits.First();
+
50  if (splits.Length < 2 || !Enum.TryParse<DiscordDMOutputDisplayType>(splits[1], out var dMOutputDisplayType))
+
51  dMOutputDisplayType = DiscordDMOutputDisplayType.Always;
+
52  DMOutputDisplay = dMOutputDisplayType;
+
53  if (splits.Length > 1 && Int32.TryParse(splits[2], out Int32 basedMeme))
+
54  {
+
55  BasedMeme = Convert.ToBoolean(basedMeme);
+
56  }
+
57  else
+
58  {
+
59  BasedMeme = true; // Oranges said this needs to be true by default :pensive:
+
60  }
+
61  }
+
62 
+
64  public override string ToString() => $"{BotToken};{(int)DMOutputDisplay};{Convert.ToInt32(BasedMeme)}";
+
65  }
+
66 }
Definition: ChatBot.cs:5
Definition: ApiHeaders.cs:13
-
DiscordDMOutputDisplayType DMOutputDisplay
The DiscordDMOutputDisplayType.
+
DiscordDMOutputDisplayType DMOutputDisplay
The DiscordDMOutputDisplayType.
string? BotToken
The Discord bot token.
DiscordDMOutputDisplayType
When the DM output section of Discord deployment embeds should be shown.
-
DiscordConnectionStringBuilder(string connectionString)
Construct a DiscordConnectionStringBuilder from a connectionString
+
DiscordConnectionStringBuilder(string connectionString)
Construct a DiscordConnectionStringBuilder from a connectionString
override string ToString()
Gets the ChatBot.ConnectionString associated with the ChatConnectionStringBuilder
-
DiscordConnectionStringBuilder()
Construct a DiscordConnectionStringBuilder
+
bool BasedMeme
bool to enable based mode. Will auto reply with a youtube link to a video that says "based on the har...
+
DiscordConnectionStringBuilder()
Construct a DiscordConnectionStringBuilder
ChatConnectionStringBuilder for ChatProvider.Discord
Definition: ApiHeaders.cs:13
Helper for building ChatBot.ConnectionStrings
diff --git a/_discord_provider_8cs_source.html b/_discord_provider_8cs_source.html index 103f2e2fb8..77f3203ceb 100644 --- a/_discord_provider_8cs_source.html +++ b/_discord_provider_8cs_source.html @@ -109,455 +109,458 @@ $(function() {
49 
53  readonly string botToken;
54 
-
58  readonly DiscordDMOutputDisplayType outputDisplayType;
+
58  readonly bool basedMeme;
59 
-
65  static string NormalizeMentions(string fromDiscord) => fromDiscord.Replace("<@!", "<@", StringComparison.Ordinal);
-
66 
-
74  public DiscordProvider(
-
75  IJobManager jobManager,
-
76  IAssemblyInformationProvider assemblyInformationProvider,
-
77  ILogger<DiscordProvider> logger,
-
78  Models.ChatBot chatBot)
-
79  : base(jobManager, logger, chatBot)
-
80  {
-
81  this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
-
82 
-
83  var csb = new DiscordConnectionStringBuilder(chatBot.ConnectionString);
-
84  botToken = csb.BotToken;
-
85  outputDisplayType = csb.DMOutputDisplay;
-
86 
-
87  client = new DiscordSocketClient();
-
88  client.MessageReceived += Client_MessageReceived;
-
89  mappedChannels = new List<ulong>();
-
90  }
-
91 
-
93  public override async ValueTask DisposeAsync()
-
94  {
-
95  await base.DisposeAsync().ConfigureAwait(false);
-
96  client.Dispose();
-
97  }
-
98 
-
104  async Task Client_MessageReceived(SocketMessage e)
-
105  {
-
106  if (e.Author.Id == client.CurrentUser.Id)
-
107  return;
-
108 
-
109  IDisposable typingState = null;
-
110  void StartTyping() => typingState = e.Channel.EnterTypingState();
-
111  try
-
112  {
-
113  if (e.Content.Equals("Based on what?", StringComparison.OrdinalIgnoreCase))
-
114  {
-
115  StartTyping();
-
116 
-
117  // DCT: None available
-
118  await SendMessage(
-
119  e.Channel.Id,
-
120  "https://youtu.be/LrNu-SuFF_o",
-
121  default)
-
122  .ConfigureAwait(false);
-
123  return;
-
124  }
-
125 
-
126  var pm = e.Channel is IPrivateChannel;
-
127  var shouldNotAnswer = !pm;
-
128  if (shouldNotAnswer)
-
129  lock (mappedChannels)
-
130  shouldNotAnswer = !mappedChannels.Contains(e.Channel.Id);
+
63  readonly DiscordDMOutputDisplayType outputDisplayType;
+
64 
+
70  static string NormalizeMentions(string fromDiscord) => fromDiscord.Replace("<@!", "<@", StringComparison.Ordinal);
+
71 
+
79  public DiscordProvider(
+
80  IJobManager jobManager,
+
81  IAssemblyInformationProvider assemblyInformationProvider,
+
82  ILogger<DiscordProvider> logger,
+
83  Models.ChatBot chatBot)
+
84  : base(jobManager, logger, chatBot)
+
85  {
+
86  this.assemblyInformationProvider = assemblyInformationProvider ?? throw new ArgumentNullException(nameof(assemblyInformationProvider));
+
87 
+
88  var csb = new DiscordConnectionStringBuilder(chatBot.ConnectionString);
+
89  botToken = csb.BotToken;
+
90  basedMeme = csb.BasedMeme;
+
91  outputDisplayType = csb.DMOutputDisplay;
+
92 
+
93  client = new DiscordSocketClient();
+
94  client.MessageReceived += Client_MessageReceived;
+
95  mappedChannels = new List<ulong>();
+
96  }
+
97 
+
99  public override async ValueTask DisposeAsync()
+
100  {
+
101  await base.DisposeAsync().ConfigureAwait(false);
+
102  client.Dispose();
+
103  }
+
104 
+
110  async Task Client_MessageReceived(SocketMessage e)
+
111  {
+
112  if (e.Author.Id == client.CurrentUser.Id)
+
113  return;
+
114 
+
115  IDisposable typingState = null;
+
116  void StartTyping() => typingState = e.Channel.EnterTypingState();
+
117  try
+
118  {
+
119  if (basedMeme && e.Content.Equals("Based on what?", StringComparison.OrdinalIgnoreCase))
+
120  {
+
121  StartTyping();
+
122 
+
123  // DCT: None available
+
124  await SendMessage(
+
125  e.Channel.Id,
+
126  "https://youtu.be/LrNu-SuFF_o",
+
127  default)
+
128  .ConfigureAwait(false);
+
129  return;
+
130  }
131 
-
132  var content = NormalizeMentions(e.Content);
-
133  var mentionedUs = e.MentionedUsers.Any(x => x.Id == client.CurrentUser.Id)
-
134  || (!shouldNotAnswer && content.Split(' ').First().Equals(ChatManager.CommonMention, StringComparison.OrdinalIgnoreCase));
-
135  if (mentionedUs)
-
136  StartTyping();
+
132  var pm = e.Channel is IPrivateChannel;
+
133  var shouldNotAnswer = !pm;
+
134  if (shouldNotAnswer)
+
135  lock (mappedChannels)
+
136  shouldNotAnswer = !mappedChannels.Contains(e.Channel.Id);
137 
-
138  if (shouldNotAnswer)
-
139  {
-
140  if (mentionedUs)
-
141  {
-
142  Logger.LogTrace("Ignoring mention from {0} ({1}) by {2} ({3}). Channel not mapped!", e.Channel.Id, e.Channel.Name, e.Author.Id, e.Author.Username);
+
138  var content = NormalizeMentions(e.Content);
+
139  var mentionedUs = e.MentionedUsers.Any(x => x.Id == client.CurrentUser.Id)
+
140  || (!shouldNotAnswer && content.Split(' ').First().Equals(ChatManager.CommonMention, StringComparison.OrdinalIgnoreCase));
+
141  if (mentionedUs)
+
142  StartTyping();
143 
-
144  // DCT: None available
-
145  await SendMessage(e.Channel.Id, "I do not respond to this channel!", default).ConfigureAwait(false);
-
146  }
-
147 
-
148  return;
-
149  }
-
150 
-
151  var result = new Message
-
152  {
-
153  Content = content,
-
154  User = new ChatUser
-
155  {
-
156  RealId = e.Author.Id,
-
157  Channel = new ChannelRepresentation
-
158  {
-
159  RealId = e.Channel.Id,
-
160  IsPrivateChannel = pm,
-
161  ConnectionName = pm ? e.Author.Username : (e.Channel as ITextChannel)?.Guild.Name ?? "UNKNOWN",
-
162  FriendlyName = e.Channel.Name
-
163 
-
164  // isAdmin and Tag populated by manager
-
165  },
-
166  FriendlyName = e.Author.Username,
-
167  Mention = NormalizeMentions(e.Author.Mention)
-
168  },
-
169  Context = typingState
-
170  };
-
171 
-
172  EnqueueMessage(result);
-
173  typingState = null;
-
174  }
-
175  finally
-
176  {
-
177  typingState?.Dispose();
-
178  }
-
179  }
-
180 
-
182  protected override async Task Connect(CancellationToken cancellationToken)
-
183  {
-
184  try
-
185  {
-
186  await client.LoginAsync(TokenType.Bot, botToken, true).ConfigureAwait(false);
-
187 
-
188  Logger.LogTrace("Logged in.");
-
189  cancellationToken.ThrowIfCancellationRequested();
-
190 
-
191  await client.StartAsync().ConfigureAwait(false);
-
192 
-
193  Logger.LogTrace("Started.");
-
194 
-
195  var channelsAvailable = new TaskCompletionSource<object>();
-
196  Task ReadyCallback()
-
197  {
-
198  channelsAvailable.TrySetResult(null);
-
199  return Task.CompletedTask;
-
200  }
-
201 
-
202  client.Ready += ReadyCallback;
-
203  try
-
204  {
-
205  using (cancellationToken.Register(() => channelsAvailable.SetCanceled()))
-
206  await channelsAvailable.Task.ConfigureAwait(false);
-
207  }
-
208  finally
-
209  {
-
210  client.Ready -= ReadyCallback;
-
211  }
-
212  }
-
213  catch (OperationCanceledException)
-
214  {
-
215  throw;
-
216  }
-
217  catch (Exception e)
-
218  {
-
219  throw new JobException(ErrorCode.ChatCannotConnectProvider, e);
-
220  }
-
221  }
-
222 
-
224  protected override async Task DisconnectImpl(CancellationToken cancellationToken)
-
225  {
-
226  try
-
227  {
-
228  cancellationToken.ThrowIfCancellationRequested();
-
229  await client.StopAsync().ConfigureAwait(false);
-
230  Logger.LogTrace("Stopped.");
-
231  await client.LogoutAsync().ConfigureAwait(false);
-
232  Logger.LogDebug("Disconnected!");
-
233  }
-
234  catch (OperationCanceledException)
-
235  {
-
236  throw;
-
237  }
-
238  catch (Exception e)
-
239  {
-
240  Logger.LogWarning(e, "Error disconnecting from discord!");
-
241  }
-
242  }
-
243 
-
245  public override Task<IReadOnlyCollection<ChannelRepresentation>> MapChannels(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken)
-
246  {
-
247  if (channels == null)
-
248  throw new ArgumentNullException(nameof(channels));
+
144  if (shouldNotAnswer)
+
145  {
+
146  if (mentionedUs)
+
147  {
+
148  Logger.LogTrace("Ignoring mention from {0} ({1}) by {2} ({3}). Channel not mapped!", e.Channel.Id, e.Channel.Name, e.Author.Id, e.Author.Username);
+
149 
+
150  // DCT: None available
+
151  await SendMessage(e.Channel.Id, "I do not respond to this channel!", default).ConfigureAwait(false);
+
152  }
+
153 
+
154  return;
+
155  }
+
156 
+
157  var result = new Message
+
158  {
+
159  Content = content,
+
160  User = new ChatUser
+
161  {
+
162  RealId = e.Author.Id,
+
163  Channel = new ChannelRepresentation
+
164  {
+
165  RealId = e.Channel.Id,
+
166  IsPrivateChannel = pm,
+
167  ConnectionName = pm ? e.Author.Username : (e.Channel as ITextChannel)?.Guild.Name ?? "UNKNOWN",
+
168  FriendlyName = e.Channel.Name
+
169 
+
170  // isAdmin and Tag populated by manager
+
171  },
+
172  FriendlyName = e.Author.Username,
+
173  Mention = NormalizeMentions(e.Author.Mention)
+
174  },
+
175  Context = typingState
+
176  };
+
177 
+
178  EnqueueMessage(result);
+
179  typingState = null;
+
180  }
+
181  finally
+
182  {
+
183  typingState?.Dispose();
+
184  }
+
185  }
+
186 
+
188  protected override async Task Connect(CancellationToken cancellationToken)
+
189  {
+
190  try
+
191  {
+
192  await client.LoginAsync(TokenType.Bot, botToken, true).ConfigureAwait(false);
+
193 
+
194  Logger.LogTrace("Logged in.");
+
195  cancellationToken.ThrowIfCancellationRequested();
+
196 
+
197  await client.StartAsync().ConfigureAwait(false);
+
198 
+
199  Logger.LogTrace("Started.");
+
200 
+
201  var channelsAvailable = new TaskCompletionSource<object>();
+
202  Task ReadyCallback()
+
203  {
+
204  channelsAvailable.TrySetResult(null);
+
205  return Task.CompletedTask;
+
206  }
+
207 
+
208  client.Ready += ReadyCallback;
+
209  try
+
210  {
+
211  using (cancellationToken.Register(() => channelsAvailable.SetCanceled()))
+
212  await channelsAvailable.Task.ConfigureAwait(false);
+
213  }
+
214  finally
+
215  {
+
216  client.Ready -= ReadyCallback;
+
217  }
+
218  }
+
219  catch (OperationCanceledException)
+
220  {
+
221  throw;
+
222  }
+
223  catch (Exception e)
+
224  {
+
225  throw new JobException(ErrorCode.ChatCannotConnectProvider, e);
+
226  }
+
227  }
+
228 
+
230  protected override async Task DisconnectImpl(CancellationToken cancellationToken)
+
231  {
+
232  try
+
233  {
+
234  cancellationToken.ThrowIfCancellationRequested();
+
235  await client.StopAsync().ConfigureAwait(false);
+
236  Logger.LogTrace("Stopped.");
+
237  await client.LogoutAsync().ConfigureAwait(false);
+
238  Logger.LogDebug("Disconnected!");
+
239  }
+
240  catch (OperationCanceledException)
+
241  {
+
242  throw;
+
243  }
+
244  catch (Exception e)
+
245  {
+
246  Logger.LogWarning(e, "Error disconnecting from discord!");
+
247  }
+
248  }
249 
-
250  if (!Connected)
-
251  {
-
252  Logger.LogWarning("Cannot map channels, provider disconnected!");
-
253  return Task.FromResult<IReadOnlyCollection<ChannelRepresentation>>(Array.Empty<ChannelRepresentation>());
-
254  }
+
251  public override Task<IReadOnlyCollection<ChannelRepresentation>> MapChannels(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken)
+
252  {
+
253  if (channels == null)
+
254  throw new ArgumentNullException(nameof(channels));
255 
-
256  ChannelRepresentation GetModelChannelFromDBChannel(Api.Models.ChatChannel channelFromDB)
+
256  if (!Connected)
257  {
-
258  if (!channelFromDB.DiscordChannelId.HasValue)
-
259  throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
-
260 
-
261  var channelId = channelFromDB.DiscordChannelId.Value;
-
262  ulong discordChannelId;
-
263  string connectionName;
-
264  string friendlyName;
-
265  if (channelId == 0)
-
266  {
-
267  connectionName = client.CurrentUser.Username;
-
268  friendlyName = "(Unmapped accessible channels)";
-
269  discordChannelId = 0;
-
270  }
-
271  else
+
258  Logger.LogWarning("Cannot map channels, provider disconnected!");
+
259  return Task.FromResult<IReadOnlyCollection<ChannelRepresentation>>(Array.Empty<ChannelRepresentation>());
+
260  }
+
261 
+
262  ChannelRepresentation GetModelChannelFromDBChannel(Api.Models.ChatChannel channelFromDB)
+
263  {
+
264  if (!channelFromDB.DiscordChannelId.HasValue)
+
265  throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
+
266 
+
267  var channelId = channelFromDB.DiscordChannelId.Value;
+
268  ulong discordChannelId;
+
269  string connectionName;
+
270  string friendlyName;
+
271  if (channelId == 0)
272  {
-
273  var discordChannel = client.GetChannel(channelId);
-
274  if (!(discordChannel is ITextChannel textChannel))
-
275  {
-
276  Logger.LogWarning("Cound not map channel {0}! Incorrect type: {1}", channelId, discordChannel?.GetType());
-
277  return null;
-
278  }
-
279 
-
280  discordChannelId = textChannel.Id;
-
281  connectionName = textChannel.Guild.Name;
-
282  friendlyName = textChannel.Name;
-
283  }
-
284 
-
285  var channelModel = new ChannelRepresentation
-
286  {
-
287  RealId = discordChannelId,
-
288  IsAdminChannel = channelFromDB.IsAdminChannel == true,
-
289  ConnectionName = connectionName,
-
290  FriendlyName = friendlyName,
-
291  IsPrivateChannel = false,
-
292  Tag = channelFromDB.Tag
-
293  };
-
294  Logger.LogTrace("Mapped channel {0}: {1}", channelModel.RealId, channelModel.FriendlyName);
-
295  return channelModel;
-
296  }
-
297 
-
298  var enumerator = channels
-
299  .Select(x => GetModelChannelFromDBChannel(x))
-
300  .Where(x => x != null).ToList();
-
301 
-
302  lock (mappedChannels)
-
303  {
-
304  mappedChannels.Clear();
-
305  mappedChannels.AddRange(enumerator.Select(x => x.RealId));
-
306  }
+
273  connectionName = client.CurrentUser.Username;
+
274  friendlyName = "(Unmapped accessible channels)";
+
275  discordChannelId = 0;
+
276  }
+
277  else
+
278  {
+
279  var discordChannel = client.GetChannel(channelId);
+
280  if (!(discordChannel is ITextChannel textChannel))
+
281  {
+
282  Logger.LogWarning("Cound not map channel {0}! Incorrect type: {1}", channelId, discordChannel?.GetType());
+
283  return null;
+
284  }
+
285 
+
286  discordChannelId = textChannel.Id;
+
287  connectionName = textChannel.Guild.Name;
+
288  friendlyName = textChannel.Name;
+
289  }
+
290 
+
291  var channelModel = new ChannelRepresentation
+
292  {
+
293  RealId = discordChannelId,
+
294  IsAdminChannel = channelFromDB.IsAdminChannel == true,
+
295  ConnectionName = connectionName,
+
296  FriendlyName = friendlyName,
+
297  IsPrivateChannel = false,
+
298  Tag = channelFromDB.Tag
+
299  };
+
300  Logger.LogTrace("Mapped channel {0}: {1}", channelModel.RealId, channelModel.FriendlyName);
+
301  return channelModel;
+
302  }
+
303 
+
304  var enumerator = channels
+
305  .Select(x => GetModelChannelFromDBChannel(x))
+
306  .Where(x => x != null).ToList();
307 
-
308  return Task.FromResult<IReadOnlyCollection<ChannelRepresentation>>(enumerator);
-
309  }
-
310 
-
312  public override async Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken)
-
313  {
-
314  var requestOptions = new RequestOptions
-
315  {
-
316  CancelToken = cancellationToken,
-
317  Timeout = 10000 // prevent stupid long hold ups from this
-
318  };
-
319 
-
320  Task SendToChannel(IMessageChannel channel) => channel.SendMessageAsync(
-
321  message,
-
322  false,
-
323  null,
-
324  requestOptions);
+
308  lock (mappedChannels)
+
309  {
+
310  mappedChannels.Clear();
+
311  mappedChannels.AddRange(enumerator.Select(x => x.RealId));
+
312  }
+
313 
+
314  return Task.FromResult<IReadOnlyCollection<ChannelRepresentation>>(enumerator);
+
315  }
+
316 
+
318  public override async Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken)
+
319  {
+
320  var requestOptions = new RequestOptions
+
321  {
+
322  CancelToken = cancellationToken,
+
323  Timeout = 10000 // prevent stupid long hold ups from this
+
324  };
325 
-
326  try
-
327  {
-
328  if (channelId == 0)
-
329  {
-
330  var unmappedTextChannels = client
-
331  .Guilds
-
332  .SelectMany(x => x.TextChannels);
-
333 
-
334  lock (mappedChannels)
-
335  unmappedTextChannels = unmappedTextChannels.Where(x => !mappedChannels.Contains(x.Id));
-
336 
-
337  // discord API confirmed weak boned: https://stackoverflow.com/a/52462336
-
338  var channelCount = 0UL;
-
339  var tasks = unmappedTextChannels
-
340  .Select(x =>
-
341  {
-
342  ++channelCount;
-
343  return SendToChannel(x);
-
344  });
-
345 
-
346  if (channelCount > 0)
-
347  {
-
348  Logger.LogTrace("Dispatched to {0} unmapped channels...", channelCount);
-
349  await Task.WhenAll(tasks).ConfigureAwait(false);
-
350  }
+
326  Task SendToChannel(IMessageChannel channel) => channel.SendMessageAsync(
+
327  message,
+
328  false,
+
329  null,
+
330  requestOptions);
+
331 
+
332  try
+
333  {
+
334  if (channelId == 0)
+
335  {
+
336  var unmappedTextChannels = client
+
337  .Guilds
+
338  .SelectMany(x => x.TextChannels);
+
339 
+
340  lock (mappedChannels)
+
341  unmappedTextChannels = unmappedTextChannels.Where(x => !mappedChannels.Contains(x.Id));
+
342 
+
343  // discord API confirmed weak boned: https://stackoverflow.com/a/52462336
+
344  var channelCount = 0UL;
+
345  var tasks = unmappedTextChannels
+
346  .Select(x =>
+
347  {
+
348  ++channelCount;
+
349  return SendToChannel(x);
+
350  });
351 
-
352  return;
-
353  }
-
354 
-
355  if (!(client.GetChannel(channelId) is IMessageChannel channel))
-
356  return;
+
352  if (channelCount > 0)
+
353  {
+
354  Logger.LogTrace("Dispatched to {0} unmapped channels...", channelCount);
+
355  await Task.WhenAll(tasks).ConfigureAwait(false);
+
356  }
357 
-
358  await SendToChannel(channel).ConfigureAwait(false);
-
359  }
-
360  catch (Exception e)
-
361  {
-
362  if (e is OperationCanceledException)
-
363  cancellationToken.ThrowIfCancellationRequested();
-
364  Logger.LogWarning(e, "Error sending discord message!");
+
358  return;
+
359  }
+
360 
+
361  if (!(client.GetChannel(channelId) is IMessageChannel channel))
+
362  return;
+
363 
+
364  await SendToChannel(channel).ConfigureAwait(false);
365  }
-
366  }
-
367 
-
369  public override async Task<Func<string, string, Task>> SendUpdateMessage(
-
370  Models.RevisionInformation revisionInformation,
-
371  Version byondVersion,
-
372  DateTimeOffset? estimatedCompletionTime,
-
373  string gitHubOwner,
-
374  string gitHubRepo,
-
375  ulong channelId,
-
376  bool localCommitPushed,
-
377  CancellationToken cancellationToken)
-
378  {
-
379  bool gitHub = gitHubOwner != null && gitHubRepo != null;
-
380 
-
381  localCommitPushed |= revisionInformation.CommitSha == revisionInformation.OriginCommitSha;
-
382 
-
383  var fields = new List<EmbedFieldBuilder>
-
384  {
-
385  new EmbedFieldBuilder
-
386  {
-
387  Name = "BYOND Version",
-
388  Value = $"{byondVersion.Major}.{byondVersion.Minor}{(byondVersion.Build > 0 ? $".{byondVersion.Build}" : String.Empty)}",
-
389  IsInline = true
-
390  },
+
366  catch (Exception e)
+
367  {
+
368  if (e is OperationCanceledException)
+
369  cancellationToken.ThrowIfCancellationRequested();
+
370  Logger.LogWarning(e, "Error sending discord message!");
+
371  }
+
372  }
+
373 
+
375  public override async Task<Func<string, string, Task>> SendUpdateMessage(
+
376  Models.RevisionInformation revisionInformation,
+
377  Version byondVersion,
+
378  DateTimeOffset? estimatedCompletionTime,
+
379  string gitHubOwner,
+
380  string gitHubRepo,
+
381  ulong channelId,
+
382  bool localCommitPushed,
+
383  CancellationToken cancellationToken)
+
384  {
+
385  bool gitHub = gitHubOwner != null && gitHubRepo != null;
+
386 
+
387  localCommitPushed |= revisionInformation.CommitSha == revisionInformation.OriginCommitSha;
+
388 
+
389  var fields = new List<EmbedFieldBuilder>
+
390  {
391  new EmbedFieldBuilder
392  {
-
393  Name = "Local Commit",
-
394  Value = localCommitPushed && gitHub
-
395  ? $"[{revisionInformation.CommitSha.Substring(0, 7)}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{revisionInformation.CommitSha})"
-
396  : revisionInformation.CommitSha.Substring(0, 7),
-
397  IsInline = true
-
398  },
-
399  new EmbedFieldBuilder
-
400  {
-
401  Name = "Branch Commit",
-
402  Value = gitHub
-
403  ? $"[{revisionInformation.OriginCommitSha.Substring(0, 7)}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{revisionInformation.OriginCommitSha})"
-
404  : revisionInformation.OriginCommitSha.Substring(0, 7),
-
405  IsInline = true
-
406  }
-
407  };
-
408 
-
409  fields.AddRange((revisionInformation.ActiveTestMerges ?? Enumerable.Empty<RevInfoTestMerge>())
-
410  .Select(x => x.TestMerge)
-
411  .Select(x => new EmbedFieldBuilder
-
412  {
-
413  Name = $"#{x.Number}",
-
414  Value = $"[{x.TitleAtMerge}]({x.Url}) by _[@{x.Author}](https://github.com/{x.Author})_{Environment.NewLine}Commit: [{x.PullRequestRevision.Substring(0, 7)}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{x.PullRequestRevision}){(String.IsNullOrWhiteSpace(x.Comment) ? String.Empty : $"{Environment.NewLine}_**{x.Comment}**_")}"
-
415  }));
-
416 
-
417  var builder = new EmbedBuilder
-
418  {
-
419  Author = new EmbedAuthorBuilder
-
420  {
-
421  Name = assemblyInformationProvider.VersionPrefix,
-
422  Url = "https://github.com/tgstation/tgstation-server",
-
423  IconUrl = "https://avatars0.githubusercontent.com/u/1363778?s=280&v=4"
-
424  },
-
425  Color = Color.Gold,
-
426  Description = "TGS has begun deploying active repository code to production.",
-
427  Fields = fields,
-
428  Title = "Code Deployment",
-
429  Footer = new EmbedFooterBuilder
-
430  {
-
431  Text = $"In progress...{(estimatedCompletionTime.HasValue ? " ETA" : String.Empty)}"
-
432  },
-
433  Timestamp = estimatedCompletionTime
-
434  };
-
435 
-
436  Logger.LogTrace("Attempting to post deploy embed to channel {0}...", channelId);
-
437  if (!(client.GetChannel(channelId) is IMessageChannel channel))
-
438  {
-
439  Logger.LogTrace("Channel ID {0} does not exist or is not an IMessageChannel!", channelId);
-
440  return (errorMessage, dreamMakerOutput) => Task.CompletedTask;
-
441  }
-
442 
-
443  var message = await channel.SendMessageAsync(
-
444  "DM: Deployment in Progress...",
-
445  false,
-
446  builder.Build(),
-
447  new RequestOptions
-
448  {
-
449  CancelToken = cancellationToken
-
450  })
-
451  .ConfigureAwait(false);
-
452 
-
453  return async (errorMessage, dreamMakerOutput) =>
-
454  {
-
455  var completionString = errorMessage == null ? "Succeeded" : "Failed";
-
456  builder.Footer.Text = completionString;
-
457  builder.Color = errorMessage == null ? Color.Green : Color.Red;
-
458  builder.Timestamp = DateTimeOffset.Now;
-
459  builder.Description = errorMessage == null
-
460  ? "The deployment completed successfully and will be available at the next server reboot."
-
461  : "The deployment failed.";
-
462 
-
463  var showDMOutput = outputDisplayType switch
-
464  {
-
465  DiscordDMOutputDisplayType.Always => true,
-
466  DiscordDMOutputDisplayType.Never => false,
-
467  DiscordDMOutputDisplayType.OnError => errorMessage != null,
-
468  _ => throw new InvalidOperationException($"Invalid DiscordDMOutputDisplayType: {outputDisplayType}"),
-
469  };
-
470 
-
471  if (showDMOutput && dreamMakerOutput != null)
-
472  builder.AddField(new EmbedFieldBuilder
-
473  {
-
474  Name = "DreamMaker Output",
-
475  Value = $"```{Environment.NewLine}{dreamMakerOutput}{Environment.NewLine}```"
-
476  });
-
477 
-
478  if (errorMessage != null)
-
479  builder.AddField(new EmbedFieldBuilder
-
480  {
-
481  Name = "Error Message",
-
482  Value = errorMessage
-
483  });
-
484 
-
485  var updatedMessage = $"DM: Deployment {completionString}!";
-
486  try
-
487  {
-
488  await message.ModifyAsync(
-
489  props =>
-
490  {
-
491  props.Content = updatedMessage;
-
492  props.Embed = builder.Build();
-
493  })
-
494  .ConfigureAwait(false);
-
495  }
-
496  catch (Exception ex)
-
497  {
-
498  Logger.LogWarning(ex, "Updating deploy embed {0} failed, attempting new post!", message.Id);
-
499  try
-
500  {
-
501  await channel.SendMessageAsync(
-
502  updatedMessage,
-
503  false,
-
504  builder.Build())
-
505  .ConfigureAwait(false);
-
506  }
-
507  catch (Exception ex2)
-
508  {
-
509  Logger.LogWarning(ex2, "Posting completion deploy embed failed!");
-
510  }
-
511  }
-
512  };
-
513  }
-
514  }
-
515 }
+
393  Name = "BYOND Version",
+
394  Value = $"{byondVersion.Major}.{byondVersion.Minor}{(byondVersion.Build > 0 ? $".{byondVersion.Build}" : String.Empty)}",
+
395  IsInline = true
+
396  },
+
397  new EmbedFieldBuilder
+
398  {
+
399  Name = "Local Commit",
+
400  Value = localCommitPushed && gitHub
+
401  ? $"[{revisionInformation.CommitSha.Substring(0, 7)}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{revisionInformation.CommitSha})"
+
402  : revisionInformation.CommitSha.Substring(0, 7),
+
403  IsInline = true
+
404  },
+
405  new EmbedFieldBuilder
+
406  {
+
407  Name = "Branch Commit",
+
408  Value = gitHub
+
409  ? $"[{revisionInformation.OriginCommitSha.Substring(0, 7)}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{revisionInformation.OriginCommitSha})"
+
410  : revisionInformation.OriginCommitSha.Substring(0, 7),
+
411  IsInline = true
+
412  }
+
413  };
+
414 
+
415  fields.AddRange((revisionInformation.ActiveTestMerges ?? Enumerable.Empty<RevInfoTestMerge>())
+
416  .Select(x => x.TestMerge)
+
417  .Select(x => new EmbedFieldBuilder
+
418  {
+
419  Name = $"#{x.Number}",
+
420  Value = $"[{x.TitleAtMerge}]({x.Url}) by _[@{x.Author}](https://github.com/{x.Author})_{Environment.NewLine}Commit: [{x.PullRequestRevision.Substring(0, 7)}](https://github.com/{gitHubOwner}/{gitHubRepo}/commit/{x.PullRequestRevision}){(String.IsNullOrWhiteSpace(x.Comment) ? String.Empty : $"{Environment.NewLine}_**{x.Comment}**_")}"
+
421  }));
+
422 
+
423  var builder = new EmbedBuilder
+
424  {
+
425  Author = new EmbedAuthorBuilder
+
426  {
+
427  Name = assemblyInformationProvider.VersionPrefix,
+
428  Url = "https://github.com/tgstation/tgstation-server",
+
429  IconUrl = "https://avatars0.githubusercontent.com/u/1363778?s=280&v=4"
+
430  },
+
431  Color = Color.Gold,
+
432  Description = "TGS has begun deploying active repository code to production.",
+
433  Fields = fields,
+
434  Title = "Code Deployment",
+
435  Footer = new EmbedFooterBuilder
+
436  {
+
437  Text = $"In progress...{(estimatedCompletionTime.HasValue ? " ETA" : String.Empty)}"
+
438  },
+
439  Timestamp = estimatedCompletionTime
+
440  };
+
441 
+
442  Logger.LogTrace("Attempting to post deploy embed to channel {0}...", channelId);
+
443  if (!(client.GetChannel(channelId) is IMessageChannel channel))
+
444  {
+
445  Logger.LogTrace("Channel ID {0} does not exist or is not an IMessageChannel!", channelId);
+
446  return (errorMessage, dreamMakerOutput) => Task.CompletedTask;
+
447  }
+
448 
+
449  var message = await channel.SendMessageAsync(
+
450  "DM: Deployment in Progress...",
+
451  false,
+
452  builder.Build(),
+
453  new RequestOptions
+
454  {
+
455  CancelToken = cancellationToken
+
456  })
+
457  .ConfigureAwait(false);
+
458 
+
459  return async (errorMessage, dreamMakerOutput) =>
+
460  {
+
461  var completionString = errorMessage == null ? "Succeeded" : "Failed";
+
462  builder.Footer.Text = completionString;
+
463  builder.Color = errorMessage == null ? Color.Green : Color.Red;
+
464  builder.Timestamp = DateTimeOffset.Now;
+
465  builder.Description = errorMessage == null
+
466  ? "The deployment completed successfully and will be available at the next server reboot."
+
467  : "The deployment failed.";
+
468 
+
469  var showDMOutput = outputDisplayType switch
+
470  {
+
471  DiscordDMOutputDisplayType.Always => true,
+
472  DiscordDMOutputDisplayType.Never => false,
+
473  DiscordDMOutputDisplayType.OnError => errorMessage != null,
+
474  _ => throw new InvalidOperationException($"Invalid DiscordDMOutputDisplayType: {outputDisplayType}"),
+
475  };
+
476 
+
477  if (showDMOutput && dreamMakerOutput != null)
+
478  builder.AddField(new EmbedFieldBuilder
+
479  {
+
480  Name = "DreamMaker Output",
+
481  Value = $"```{Environment.NewLine}{dreamMakerOutput}{Environment.NewLine}```"
+
482  });
+
483 
+
484  if (errorMessage != null)
+
485  builder.AddField(new EmbedFieldBuilder
+
486  {
+
487  Name = "Error Message",
+
488  Value = errorMessage
+
489  });
+
490 
+
491  var updatedMessage = $"DM: Deployment {completionString}!";
+
492  try
+
493  {
+
494  await message.ModifyAsync(
+
495  props =>
+
496  {
+
497  props.Content = updatedMessage;
+
498  props.Embed = builder.Build();
+
499  })
+
500  .ConfigureAwait(false);
+
501  }
+
502  catch (Exception ex)
+
503  {
+
504  Logger.LogWarning(ex, "Updating deploy embed {0} failed, attempting new post!", message.Id);
+
505  try
+
506  {
+
507  await channel.SendMessageAsync(
+
508  updatedMessage,
+
509  false,
+
510  builder.Build())
+
511  .ConfigureAwait(false);
+
512  }
+
513  catch (Exception ex2)
+
514  {
+
515  Logger.LogWarning(ex2, "Posting completion deploy embed failed!");
+
516  }
+
517  }
+
518  };
+
519  }
+
520  }
+
521 }
Definition: IJobManager.cs:7
-
override async Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken)
Send a message to the IProvider
+
override async Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken)
Send a message to the IProvider
readonly IJobManager jobManager
The IJobManager for the Provider.
Definition: Provider.cs:32
Represents a Providers.IProvider channel.
ErrorCode
Types of ErrorMessages that the API may return.
Definition: ErrorCode.cs:11
-
async Task Client_MessageReceived(SocketMessage e)
Handle a message recieved from Discord
+
async Task Client_MessageReceived(SocketMessage e)
Handle a message recieved from Discord
Many to many relationship for Models.RevisionInformation and Models.TestMerge
bool IsAdminChannel
If this is considered a channel for admin commands
-
override Task< IReadOnlyCollection< ChannelRepresentation > > MapChannels(IEnumerable< Api.Models.ChatChannel > channels, CancellationToken cancellationToken)
+
override Task< IReadOnlyCollection< ChannelRepresentation > > MapChannels(IEnumerable< Api.Models.ChatChannel > channels, CancellationToken cancellationToken)
Definition: ApiHeaders.cs:13
For retrieving the Assembly's location.
Operation exceptions thrown from the context of a Models.Job
Definition: JobException.cs:10
-
override async Task DisconnectImpl(CancellationToken cancellationToken)
Gracefully disconnects the provider.
+
override async Task DisconnectImpl(CancellationToken cancellationToken)
Gracefully disconnects the provider.
Definition: User.cs:5
ILogger< Provider > Logger
The ILogger for the Provider.
Definition: Provider.cs:27
@@ -565,28 +568,29 @@ $(function() {
const string CommonMention
The common bot mention.
Definition: ChatManager.cs:27
Definition: ChatBot.cs:6
DiscordDMOutputDisplayType
When the DM output section of Discord deployment embeds should be shown.
-
override async ValueTask DisposeAsync()
+
override async ValueTask DisposeAsync()
IProvider for the Discord app
-
DiscordProvider(IJobManager jobManager, IAssemblyInformationProvider assemblyInformationProvider, ILogger< DiscordProvider > logger, Models.ChatBot chatBot)
Construct a DiscordProvider
+
DiscordProvider(IJobManager jobManager, IAssemblyInformationProvider assemblyInformationProvider, ILogger< DiscordProvider > logger, Models.ChatBot chatBot)
Construct a DiscordProvider
Represents a message recieved by a IProvider
Definition: Message.cs:9
override bool Connected
Definition: ChatManager.cs:23
-
override async Task Connect(CancellationToken cancellationToken)
Attempt to connect the Provider.
+
override async Task Connect(CancellationToken cancellationToken)
Attempt to connect the Provider.
void EnqueueMessage(Message message)
Queues a message for NextMessage(CancellationToken)
Definition: Provider.cs:97
readonly IAssemblyInformationProvider assemblyInformationProvider
The IAssemblyInformationProvider for the DiscordProvider.
Definition: Message.cs:4
string Id
Backing field for RealId. Represented as a string to avoid BYOND percision loss
Definition: ChatUser.cs:15
-
readonly DiscordDMOutputDisplayType outputDisplayType
The DiscordDMOutputDisplayType.
+
readonly DiscordDMOutputDisplayType outputDisplayType
The DiscordDMOutputDisplayType.
readonly string botToken
The Discord bot token.
readonly DiscordSocketClient client
The DiscordSocketClient for the DiscordProvider
ChatConnectionStringBuilder for ChatProvider.Discord
Definition: ApiHeaders.cs:13
Represents a tgs_chat_user datum
Definition: ChatUser.cs:11
+
readonly bool basedMeme
bool to enable based mode. Will auto reply with a youtube link to a video that says "based on the har...
override string BotMention
readonly List< ulong > mappedChannels
List<T> of mapped ITextChannel IEntity<TId>.Ids
-
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)
+
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)
string Id
Backing field for RealId. Represented as a string to avoid BYOND percision loss
Manages the runtime of Jobs
Definition: IJobManager.cs:12
@ Irc
Internet relay chat
diff --git a/class_controller.html b/class_controller.html index 1bbc42984f..8ad2b6f747 100644 --- a/class_controller.html +++ b/class_controller.html @@ -92,7 +92,7 @@ Inheritance diagram for Controller:
[legend]

The documentation for this class was generated from the following file: diff --git a/class_i_async_disposable.html b/class_i_async_disposable.html index 6e70ca1096..659676196f 100644 --- a/class_i_async_disposable.html +++ b/class_i_async_disposable.html @@ -93,7 +93,7 @@ Inheritance diagram for IAsyncDisposable:
[legend]

The documentation for this class was generated from the following file: diff --git a/class_i_design_time_db_context_factory.html b/class_i_design_time_db_context_factory.html index 9c8c1a117c..9a34b5ec20 100644 --- a/class_i_design_time_db_context_factory.html +++ b/class_i_design_time_db_context_factory.html @@ -81,7 +81,7 @@ Inheritance diagram for IDesignTimeDbContextFactory:
[legend]

The documentation for this class was generated from the following file: diff --git a/class_i_disposable.html b/class_i_disposable.html index cfa9c83676..7c7279fdc8 100644 --- a/class_i_disposable.html +++ b/class_i_disposable.html @@ -124,7 +124,7 @@ Inheritance diagram for IDisposable:
[legend]

The documentation for this class was generated from the following file: diff --git a/class_i_hosted_service.html b/class_i_hosted_service.html index 20ecbbbb71..f6a4a1ce4c 100644 --- a/class_i_hosted_service.html +++ b/class_i_hosted_service.html @@ -99,7 +99,7 @@ Inheritance diagram for IHostedService:
[legend]

The documentation for this class was generated from the following file: diff --git a/class_migration.html b/class_migration.html index a43515ba83..75068e2fa4 100644 --- a/class_migration.html +++ b/class_migration.html @@ -71,7 +71,7 @@ $(function() {

Inherited by Tgstation.Server.Host.Database.Migrations.MSAddChatBotReconnectionInterval, Tgstation.Server.Host.Database.Migrations.MSAddCompileJobDMApiVersion, Tgstation.Server.Host.Database.Migrations.MSAddDeploymentColumns, Tgstation.Server.Host.Database.Migrations.MSAddHeartbeat, Tgstation.Server.Host.Database.Migrations.MSAddJobErrorCodes, Tgstation.Server.Host.Database.Migrations.MSAddMinimumSecurity, Tgstation.Server.Host.Database.Migrations.MSAllowNullDMApi, Tgstation.Server.Host.Database.Migrations.MSCheckpointRebuild, Tgstation.Server.Host.Database.Migrations.MSFixCascadingDelete, Tgstation.Server.Host.Database.Migrations.MSFixRevInfoIndex, Tgstation.Server.Host.Database.Migrations.MSInitialCreate, Tgstation.Server.Host.Database.Migrations.MSKillJsonsAddDMApiVersion, Tgstation.Server.Host.Database.Migrations.MSLimitsOnChat, Tgstation.Server.Host.Database.Migrations.MSNullableAndForeignKeyCleanup, Tgstation.Server.Host.Database.Migrations.MSReattachCompileJobRequired, Tgstation.Server.Host.Database.Migrations.MSRemoveExperimentalWatchdog, Tgstation.Server.Host.Database.Migrations.MSRemoveSoftColumns, Tgstation.Server.Host.Database.Migrations.MSToggleTestmergeComments, Tgstation.Server.Host.Database.Migrations.MSTonsOfValidation, Tgstation.Server.Host.Database.Migrations.MSTopicTimeout, Tgstation.Server.Host.Database.Migrations.MYAddChatBotReconnectionInterval, Tgstation.Server.Host.Database.Migrations.MYAddCompileJobDMApiVersion, Tgstation.Server.Host.Database.Migrations.MYAddDeploymentColumns, Tgstation.Server.Host.Database.Migrations.MYAddHeartbeat, Tgstation.Server.Host.Database.Migrations.MYAddJobErrorCodes, Tgstation.Server.Host.Database.Migrations.MYAddMinimumSecurity, Tgstation.Server.Host.Database.Migrations.MYAllowNullDMApi, Tgstation.Server.Host.Database.Migrations.MYCheckpointRebuild, Tgstation.Server.Host.Database.Migrations.MYFixCascadingDelete, Tgstation.Server.Host.Database.Migrations.MYFixForeignKey, Tgstation.Server.Host.Database.Migrations.MYFixRevInfoIndex, Tgstation.Server.Host.Database.Migrations.MYInitialCreate, Tgstation.Server.Host.Database.Migrations.MYKillJsonsAddDMApiVersion, Tgstation.Server.Host.Database.Migrations.MYLimitsOnChat, Tgstation.Server.Host.Database.Migrations.MYNullableAndForeignKeyCleanup, Tgstation.Server.Host.Database.Migrations.MYReattachCompileJobRequired, Tgstation.Server.Host.Database.Migrations.MYRemoveExperimentalWatchdog, Tgstation.Server.Host.Database.Migrations.MYRemoveSoftColumns, Tgstation.Server.Host.Database.Migrations.MYToggleTestmergeComments, Tgstation.Server.Host.Database.Migrations.MYTonsOfValidation, Tgstation.Server.Host.Database.Migrations.MYTopicTimeout, Tgstation.Server.Host.Database.Migrations.PGAddDeploymentColumns, Tgstation.Server.Host.Database.Migrations.PGAllowNullDMApi, Tgstation.Server.Host.Database.Migrations.PGCreate, Tgstation.Server.Host.Database.Migrations.PGRemoveExperimentalWatchdog, Tgstation.Server.Host.Database.Migrations.PGTopicTimeout, Tgstation.Server.Host.Database.Migrations.SLAddDeploymentColumns, Tgstation.Server.Host.Database.Migrations.SLAddHeartbeat, Tgstation.Server.Host.Database.Migrations.SLAllowNullDMApi, Tgstation.Server.Host.Database.Migrations.SLRebuild, Tgstation.Server.Host.Database.Migrations.SLRemoveExperimentalWatchdog, Tgstation.Server.Host.Database.Migrations.SLRemoveSoftColumns, and Tgstation.Server.Host.Database.Migrations.SLTopicTimeout.


The documentation for this class was generated from the following file: diff --git a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_discord_connection_string_builder-members.html b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_discord_connection_string_builder-members.html index b83f929f2e..061fc9c8ca 100644 --- a/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_discord_connection_string_builder-members.html +++ b/class_tgstation_1_1_server_1_1_api_1_1_models_1_1_discord_connection_string_builder-members.html @@ -75,12 +75,13 @@ $(function() {

This is the complete list of members for Tgstation.Server.Api.Models.DiscordConnectionStringBuilder, including all inherited members.

- - - - - - + + + + + + +
BotTokenTgstation.Server.Api.Models.DiscordConnectionStringBuilder
DiscordConnectionStringBuilder()Tgstation.Server.Api.Models.DiscordConnectionStringBuilder
DiscordConnectionStringBuilder(string connectionString)Tgstation.Server.Api.Models.DiscordConnectionStringBuilder
DMOutputDisplayTgstation.Server.Api.Models.DiscordConnectionStringBuilder
ToString()Tgstation.Server.Api.Models.DiscordConnectionStringBuildervirtual
ValidTgstation.Server.Api.Models.DiscordConnectionStringBuilder
BasedMemeTgstation.Server.Api.Models.DiscordConnectionStringBuilder
BotTokenTgstation.Server.Api.Models.DiscordConnectionStringBuilder
DiscordConnectionStringBuilder()Tgstation.Server.Api.Models.DiscordConnectionStringBuilder
DiscordConnectionStringBuilder(string connectionString)Tgstation.Server.Api.Models.DiscordConnectionStringBuilder
DMOutputDisplayTgstation.Server.Api.Models.DiscordConnectionStringBuilder
ToString()Tgstation.Server.Api.Models.DiscordConnectionStringBuildervirtual
ValidTgstation.Server.Api.Models.DiscordConnectionStringBuilder