Add commands to update interview templates
This commit is contained in:
parent
e87208d8bd
commit
d212f13b12
8 changed files with 250 additions and 61 deletions
|
@ -1,7 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using DSharpPlus.Commands;
|
using DSharpPlus.Commands;
|
||||||
using DSharpPlus.Commands.ContextChecks;
|
using DSharpPlus.Commands.ContextChecks;
|
||||||
|
@ -10,6 +14,10 @@ using DSharpPlus.Entities;
|
||||||
using DSharpPlus.Exceptions;
|
using DSharpPlus.Exceptions;
|
||||||
using DSharpPlus.Interactivity;
|
using DSharpPlus.Interactivity;
|
||||||
using DSharpPlus.Interactivity.Extensions;
|
using DSharpPlus.Interactivity.Extensions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Schema;
|
||||||
|
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
|
||||||
|
|
||||||
namespace SupportChild.Commands;
|
namespace SupportChild.Commands;
|
||||||
|
|
||||||
|
@ -215,4 +223,98 @@ public class AdminCommands
|
||||||
Logger.Log("Reloading bot...");
|
Logger.Log("Reloading bot...");
|
||||||
SupportChild.Reload();
|
SupportChild.Reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Command("getinterviewtemplates")]
|
||||||
|
[Description("Provides a copy of the interview templates which you can edit and then reupload.")]
|
||||||
|
public async Task GetInterviewTemplates(SlashCommandContext command)
|
||||||
|
{
|
||||||
|
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(Database.GetInterviewTemplates()));
|
||||||
|
await command.RespondAsync(new DiscordInteractionResponseBuilder().AddFile("interview-templates.json", stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("setinterviewtemplates")]
|
||||||
|
[Description("Uploads an interview template file.")]
|
||||||
|
public async Task SetInterviewTemplates(SlashCommandContext command, [Parameter("file")] DiscordAttachment file)
|
||||||
|
{
|
||||||
|
if (!file.MediaType?.Contains("application/json") ?? false)
|
||||||
|
{
|
||||||
|
await command.RespondAsync(new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
Color = DiscordColor.Red,
|
||||||
|
Description = "The uploaded file is not a JSON file according to Discord."
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream stream = await new HttpClient().GetStreamAsync(file.Url);
|
||||||
|
string json = await new StreamReader(stream).ReadToEndAsync();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<string> errors = [];
|
||||||
|
|
||||||
|
// Convert it to an interview object to validate the template
|
||||||
|
Dictionary<ulong, Interviewer.ValidatedInterviewQuestion> interview = JsonConvert.DeserializeObject<Dictionary<ulong, Interviewer.ValidatedInterviewQuestion>>(json, new JsonSerializerSettings()
|
||||||
|
{
|
||||||
|
//NullValueHandling = NullValueHandling.Include,
|
||||||
|
MissingMemberHandling = MissingMemberHandling.Error,
|
||||||
|
Error = delegate (object sender, ErrorEventArgs args)
|
||||||
|
{
|
||||||
|
// I noticed the main exception mainly has information for developers, not administrators,
|
||||||
|
// so I switched to using the inner message if available.
|
||||||
|
if (string.IsNullOrEmpty(args.ErrorContext.Error.InnerException?.Message))
|
||||||
|
{
|
||||||
|
errors.Add(args.ErrorContext.Error.Message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errors.Add(args.ErrorContext.Error.InnerException.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug("Exception occured when trying to upload interview template:\n" + args.ErrorContext.Error);
|
||||||
|
args.ErrorContext.Handled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (errors.Count != 0)
|
||||||
|
{
|
||||||
|
string errorString = string.Join("\n\n", errors);
|
||||||
|
if (errorString.Length > 1500)
|
||||||
|
{
|
||||||
|
errorString = errorString.Substring(0, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
await command.RespondAsync(new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
Color = DiscordColor.Red,
|
||||||
|
Description = "The uploaded JSON structure could not be converted to an interview template.\n\nErrors:\n```\n" + errorString + "\n```",
|
||||||
|
Footer = new DiscordEmbedBuilder.EmbedFooter()
|
||||||
|
{
|
||||||
|
Text = "More detailed information may be available as debug messages in the bot logs."
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Database.SetInterviewTemplates(JsonConvert.SerializeObject(interview, Formatting.Indented));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await command.RespondAsync(new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
Color = DiscordColor.Red,
|
||||||
|
Description = "The uploaded JSON structure could not be converted to an interview template.\n\nError message:\n```\n" + e.Message + "\n```"
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await command.RespondAsync(new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
Color = DiscordColor.Green,
|
||||||
|
Description = "Uploaded interview template."
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -32,8 +32,6 @@ internal static class Config
|
||||||
internal static string username = "supportchild";
|
internal static string username = "supportchild";
|
||||||
internal static string password = "";
|
internal static string password = "";
|
||||||
|
|
||||||
internal static JToken interviews;
|
|
||||||
|
|
||||||
private static string configPath = "./config.yml";
|
private static string configPath = "./config.yml";
|
||||||
|
|
||||||
public static void LoadConfig()
|
public static void LoadConfig()
|
||||||
|
@ -102,8 +100,5 @@ internal static class Config
|
||||||
database = json.SelectToken("database.name")?.Value<string>() ?? "supportchild";
|
database = json.SelectToken("database.name")?.Value<string>() ?? "supportchild";
|
||||||
username = json.SelectToken("database.user")?.Value<string>() ?? "supportchild";
|
username = json.SelectToken("database.user")?.Value<string>() ?? "supportchild";
|
||||||
password = json.SelectToken("database.password")?.Value<string>() ?? "";
|
password = json.SelectToken("database.password")?.Value<string>() ?? "";
|
||||||
|
|
||||||
// Set up interviewer
|
|
||||||
interviews = json.SelectToken("interviews") ?? new JObject();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
85
Database.cs
85
Database.cs
|
@ -731,6 +731,52 @@ public static class Database
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetInterviewTemplates()
|
||||||
|
{
|
||||||
|
using MySqlConnection c = GetConnection();
|
||||||
|
c.Open();
|
||||||
|
using MySqlCommand selection = new MySqlCommand("SELECT * FROM interviews WHERE channel_id=0", c);
|
||||||
|
selection.Prepare();
|
||||||
|
MySqlDataReader results = selection.ExecuteReader();
|
||||||
|
|
||||||
|
// Check if messages exist in the database
|
||||||
|
if (!results.Read())
|
||||||
|
{
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
|
|
||||||
|
string templates = results.GetString("interview");
|
||||||
|
results.Close();
|
||||||
|
return templates;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SetInterviewTemplates(string templates)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string query;
|
||||||
|
if (TryGetInterview(0, out _))
|
||||||
|
{
|
||||||
|
query = "UPDATE interviews SET interview = @interview WHERE channel_id = 0";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query = "INSERT INTO interviews (channel_id,interview) VALUES (0, @interview)";
|
||||||
|
}
|
||||||
|
|
||||||
|
using MySqlConnection c = GetConnection();
|
||||||
|
c.Open();
|
||||||
|
using MySqlCommand cmd = new MySqlCommand(query, c);
|
||||||
|
cmd.Parameters.AddWithValue("@interview", templates);
|
||||||
|
cmd.Prepare();
|
||||||
|
return cmd.ExecuteNonQuery() > 0;
|
||||||
|
}
|
||||||
|
catch (MySqlException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Dictionary<ulong, Interviewer.InterviewQuestion> GetAllInterviews()
|
public static Dictionary<ulong, Interviewer.InterviewQuestion> GetAllInterviews()
|
||||||
{
|
{
|
||||||
using MySqlConnection c = GetConnection();
|
using MySqlConnection c = GetConnection();
|
||||||
|
@ -745,14 +791,16 @@ public static class Database
|
||||||
return new Dictionary<ulong, Interviewer.InterviewQuestion>();
|
return new Dictionary<ulong, Interviewer.InterviewQuestion>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Dictionary<ulong, Interviewer.InterviewQuestion> questions = new Dictionary<ulong, Interviewer.InterviewQuestion>
|
Dictionary<ulong, Interviewer.InterviewQuestion> questions = new();
|
||||||
|
do
|
||||||
{
|
{
|
||||||
{ results.GetUInt64("channel_id"), JsonConvert.DeserializeObject<Interviewer.InterviewQuestion>(results.GetString("interview")) }
|
// Channel ID 0 is the interview template
|
||||||
};
|
if (results.GetUInt64("channel_id") != 0)
|
||||||
while (results.Read())
|
{
|
||||||
{
|
questions.Add(results.GetUInt64("channel_id"), JsonConvert.DeserializeObject<Interviewer.InterviewQuestion>(results.GetString("interview")));
|
||||||
questions.Add(results.GetUInt64("channel_id"), JsonConvert.DeserializeObject<Interviewer.InterviewQuestion>(results.GetString("interview")));
|
}
|
||||||
}
|
}
|
||||||
|
while (results.Read());
|
||||||
results.Close();
|
results.Close();
|
||||||
|
|
||||||
return questions;
|
return questions;
|
||||||
|
@ -762,26 +810,23 @@ public static class Database
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
string query;
|
||||||
if (TryGetInterview(channelID, out _))
|
if (TryGetInterview(channelID, out _))
|
||||||
{
|
{
|
||||||
using MySqlConnection c = GetConnection();
|
query = "UPDATE interviews SET interview = @interview WHERE channel_id = @channel_id";
|
||||||
c.Open();
|
|
||||||
using MySqlCommand cmd = new MySqlCommand(@"UPDATE interviews SET interview = @interview WHERE channel_id = @channel_id;", c);
|
|
||||||
cmd.Parameters.AddWithValue("@channel_id", channelID);
|
|
||||||
cmd.Parameters.AddWithValue("@interview", JsonConvert.SerializeObject(interview));
|
|
||||||
cmd.Prepare();
|
|
||||||
return cmd.ExecuteNonQuery() > 0;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using MySqlConnection c = GetConnection();
|
query = "INSERT INTO interviews (channel_id,interview) VALUES (@channel_id, @interview)";
|
||||||
c.Open();
|
|
||||||
using MySqlCommand cmd = new MySqlCommand(@"INSERT INTO interviews (channel_id,interview) VALUES (@channel_id, @interview);", c);
|
|
||||||
cmd.Parameters.AddWithValue("@channel_id", channelID);
|
|
||||||
cmd.Parameters.AddWithValue("@interview", JsonConvert.SerializeObject(interview));
|
|
||||||
cmd.Prepare();
|
|
||||||
return cmd.ExecuteNonQuery() > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using MySqlConnection c = GetConnection();
|
||||||
|
c.Open();
|
||||||
|
using MySqlCommand cmd = new MySqlCommand(query, c);
|
||||||
|
cmd.Parameters.AddWithValue("@channel_id", channelID);
|
||||||
|
cmd.Parameters.AddWithValue("@interview", JsonConvert.SerializeObject(interview));
|
||||||
|
cmd.Prepare();
|
||||||
|
return cmd.ExecuteNonQuery() > 0;
|
||||||
}
|
}
|
||||||
catch (MySqlException)
|
catch (MySqlException)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using DSharpPlus;
|
using DSharpPlus;
|
||||||
using DSharpPlus.Entities;
|
using DSharpPlus.Entities;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace SupportChild;
|
namespace SupportChild;
|
||||||
|
|
||||||
|
@ -11,8 +14,8 @@ public static class Interviewer
|
||||||
{
|
{
|
||||||
public enum QuestionType
|
public enum QuestionType
|
||||||
{
|
{
|
||||||
DONE,
|
|
||||||
FAIL,
|
FAIL,
|
||||||
|
DONE,
|
||||||
BUTTONS,
|
BUTTONS,
|
||||||
SELECTOR,
|
SELECTOR,
|
||||||
TEXT_INPUT
|
TEXT_INPUT
|
||||||
|
@ -25,37 +28,95 @@ public static class Interviewer
|
||||||
public class InterviewQuestion
|
public class InterviewQuestion
|
||||||
{
|
{
|
||||||
// Message contents sent to the user.
|
// Message contents sent to the user.
|
||||||
|
[JsonProperty("message")]
|
||||||
public string message;
|
public string message;
|
||||||
|
|
||||||
// The type of question.
|
// The type of question.
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
[JsonProperty("type")]
|
||||||
public QuestionType type;
|
public QuestionType type;
|
||||||
|
|
||||||
// Colour of the message embed.
|
// Colour of the message embed.
|
||||||
|
[JsonProperty("color")]
|
||||||
public string color;
|
public string color;
|
||||||
|
|
||||||
// The ID of this message, populated after it has been sent.
|
// The ID of this message where the bot asked this question,
|
||||||
|
// populated after it has been sent.
|
||||||
|
[JsonProperty("message-id")]
|
||||||
public ulong messageID;
|
public ulong messageID;
|
||||||
|
|
||||||
// Used as label for this question in the post-interview summary.
|
// Used as label for this question in the post-interview summary.
|
||||||
|
[JsonProperty("summary-field")]
|
||||||
public string summaryField;
|
public string summaryField;
|
||||||
|
|
||||||
// The user's response to the question.
|
// The user's response to the question.
|
||||||
|
[JsonProperty("answer")]
|
||||||
public string answer;
|
public string answer;
|
||||||
|
|
||||||
// The ID of the user's answer message, populated after it has been received.
|
// The ID of the user's answer message, populated after it has been received.
|
||||||
|
[JsonProperty("answer-id")]
|
||||||
public ulong answerID;
|
public ulong answerID;
|
||||||
|
|
||||||
// Possible questions to ask next, or DONE/FAIL type in order to finish interview.
|
// Possible questions to ask next, or DONE/FAIL type in order to finish interview.
|
||||||
|
[JsonProperty("paths")]
|
||||||
public Dictionary<string, InterviewQuestion> paths;
|
public Dictionary<string, InterviewQuestion> paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This class is identical to the one above and just exists as a hack to get JSON validation when
|
||||||
|
// new entries are entered but not when read from database in order to be more lenient with old interviews.
|
||||||
|
// I might do this in a more proper way at some point.
|
||||||
|
public class ValidatedInterviewQuestion
|
||||||
|
{
|
||||||
|
// Message contents sent to the user.
|
||||||
|
[JsonProperty("message", Required = Required.Always)]
|
||||||
|
public string message;
|
||||||
|
|
||||||
|
// The type of question.
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
[JsonProperty("type", Required = Required.Always)]
|
||||||
|
public QuestionType type;
|
||||||
|
|
||||||
|
// Colour of the message embed.
|
||||||
|
[JsonProperty("color", Required = Required.Always)]
|
||||||
|
public string color;
|
||||||
|
|
||||||
|
// The ID of this message where the bot asked this question,
|
||||||
|
// populated after it has been sent.
|
||||||
|
[JsonProperty("message-id", Required = Required.Default)]
|
||||||
|
public ulong messageID;
|
||||||
|
|
||||||
|
// Used as label for this question in the post-interview summary.
|
||||||
|
[JsonProperty("summary-field", Required = Required.Default)]
|
||||||
|
public string summaryField;
|
||||||
|
|
||||||
|
// The user's response to the question.
|
||||||
|
[JsonProperty("answer", Required = Required.Default)]
|
||||||
|
public string answer;
|
||||||
|
|
||||||
|
// The ID of the user's answer message, populated after it has been received.
|
||||||
|
[JsonProperty("answer-id", Required = Required.Default)]
|
||||||
|
public ulong answerID;
|
||||||
|
|
||||||
|
// Possible questions to ask next, or DONE/FAIL type in order to finish interview.
|
||||||
|
[JsonProperty("paths", Required = Required.Always)]
|
||||||
|
public Dictionary<string, ValidatedInterviewQuestion> paths;
|
||||||
|
}
|
||||||
|
|
||||||
private static Dictionary<ulong, InterviewQuestion> categoryInterviews = [];
|
private static Dictionary<ulong, InterviewQuestion> categoryInterviews = [];
|
||||||
|
|
||||||
private static Dictionary<ulong, InterviewQuestion> activeInterviews = [];
|
private static Dictionary<ulong, InterviewQuestion> activeInterviews = [];
|
||||||
|
|
||||||
public static void ParseConfig(JToken interviewConfig)
|
public static void ParseTemplates(JToken interviewConfig)
|
||||||
{
|
{
|
||||||
categoryInterviews = JsonConvert.DeserializeObject<Dictionary<ulong, InterviewQuestion>>(interviewConfig.ToString());
|
categoryInterviews = JsonConvert.DeserializeObject<Dictionary<ulong, InterviewQuestion>>(interviewConfig.ToString(), new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
Error = delegate (object sender, ErrorEventArgs args)
|
||||||
|
{
|
||||||
|
Logger.Error("Exception occured when trying to read interview from database:\n" + args.ErrorContext.Error.Message);
|
||||||
|
Logger.Debug("Detailed exception:", args.ErrorContext.Error);
|
||||||
|
args.ErrorContext.Handled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LoadActiveInterviews()
|
public static void LoadActiveInterviews()
|
||||||
|
@ -107,7 +168,7 @@ public static class Interviewer
|
||||||
List<DiscordButtonComponent> buttonRow = [];
|
List<DiscordButtonComponent> buttonRow = [];
|
||||||
for (; nrOfButtons < 5 * (nrOfButtonRows + 1) && nrOfButtons < question.paths.Count; nrOfButtons++)
|
for (; nrOfButtons < 5 * (nrOfButtonRows + 1) && nrOfButtons < question.paths.Count; nrOfButtons++)
|
||||||
{
|
{
|
||||||
buttonRow.Add(new DiscordButtonComponent(ButtonStyle.Primary, "supportchild_interviewbutton " + nrOfButtons, question.paths.ToArray()[nrOfButtons].Key));
|
buttonRow.Add(new DiscordButtonComponent(DiscordButtonStyle.Primary, "supportchild_interviewbutton " + nrOfButtons, question.paths.ToArray()[nrOfButtons].Key));
|
||||||
}
|
}
|
||||||
msgBuilder.AddComponents(buttonRow);
|
msgBuilder.AddComponents(buttonRow);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,11 @@ internal static class SupportChild
|
||||||
Logger.Log("Starting " + Assembly.GetEntryAssembly()?.GetName().Name + " version " + GetVersion() + "...");
|
Logger.Log("Starting " + Assembly.GetEntryAssembly()?.GetName().Name + " version " + GetVersion() + "...");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Reload();
|
if (!await Reload())
|
||||||
|
{
|
||||||
|
Logger.Fatal("Aborting startup due to a fatal error...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Block this task until the program is closed.
|
// Block this task until the program is closed.
|
||||||
await Task.Delay(-1);
|
await Task.Delay(-1);
|
||||||
|
@ -115,7 +119,7 @@ internal static class SupportChild
|
||||||
+ " (" + ThisAssembly.Git.Commit + ")";
|
+ " (" + ThisAssembly.Git.Commit + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async void Reload()
|
public static async Task<bool> Reload()
|
||||||
{
|
{
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
|
@ -141,20 +145,20 @@ internal static class SupportChild
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Fatal("Could not set up database tables, please confirm connection settings, status of the server and permissions of MySQL user. Error: " + e);
|
Logger.Fatal("Could not set up database tables, please confirm connection settings, status of the server and permissions of MySQL user. Error: ", e);
|
||||||
throw;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.Log("Connecting to database... (" + Config.hostName + ":" + Config.port + ")");
|
Logger.Log("Loading interviews from database...");
|
||||||
Interviewer.ParseConfig(Config.interviews);
|
Interviewer.ParseTemplates(Database.GetInterviewTemplates());
|
||||||
Interviewer.LoadActiveInterviews();
|
Interviewer.LoadActiveInterviews();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Logger.Fatal("Could not set up database tables, please confirm connection settings, status of the server and permissions of MySQL user. Error: " + e);
|
Logger.Fatal("Could not load interviews from database. Error: ", e);
|
||||||
throw;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Log("Setting up Discord client...");
|
Logger.Log("Setting up Discord client...");
|
||||||
|
@ -233,6 +237,7 @@ internal static class SupportChild
|
||||||
|
|
||||||
Logger.Log("Connecting to Discord...");
|
Logger.Log("Connecting to Discord...");
|
||||||
await client.ConnectAsync();
|
await client.ConnectAsync();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,11 +42,12 @@
|
||||||
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
<PackageReference Include="JsonExtensions" Version="1.2.0" />
|
||||||
<PackageReference Include="MySqlConnector" Version="2.3.7" />
|
<PackageReference Include="MySqlConnector" Version="2.3.7" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json.Schema" Version="4.0.1" />
|
||||||
<PackageReference Include="Polly" Version="8.4.0" />
|
<PackageReference Include="Polly" Version="8.4.0" />
|
||||||
<PackageReference Include="RazorBlade" Version="0.6.0" />
|
<PackageReference Include="RazorBlade" Version="0.6.0" />
|
||||||
<PackageReference Include="Superpower" Version="3.0.0" />
|
<PackageReference Include="Superpower" Version="3.0.0" />
|
||||||
<PackageReference Include="WebMarkupMin.Core" Version="2.17.0" />
|
<PackageReference Include="WebMarkupMin.Core" Version="2.17.0" />
|
||||||
<PackageReference Include="YamlDotNet" Version="16.1.0" />
|
<PackageReference Include="YamlDotNet" Version="16.1.3" />
|
||||||
<PackageReference Include="YoutubeExplode" Version="6.4.0" />
|
<PackageReference Include="YoutubeExplode" Version="6.4.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,6 @@ public static class Utilities
|
||||||
return verifiedCategories;
|
return verifiedCategories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static string ReadManifestData(string embeddedFileName)
|
public static string ReadManifestData(string embeddedFileName)
|
||||||
{
|
{
|
||||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
# Whether staff members should be randomly assigned tickets when they are made. Individual staff members can opt out using the toggleactive command.
|
# Whether staff members should be randomly assigned tickets when they are made. Individual staff members can opt out using the toggleactive command.
|
||||||
random-assignment: true
|
random-assignment: true
|
||||||
|
|
||||||
# If set to true the rasssign command will include staff members set as inactive if a specific role is specified in the command.
|
# If set to true the rassign command will include staff members set as inactive if a specific role is specified in the command.
|
||||||
# This can be useful if you have admins set as inactive to not automatically receive tickets and then have moderators elevate tickets when needed.
|
# This can be useful if you have admins set as inactive to not automatically receive tickets and then have moderators elevate tickets when needed.
|
||||||
random-assign-role-override: true
|
random-assign-role-override: true
|
||||||
|
|
||||||
|
@ -64,22 +64,3 @@ database:
|
||||||
# Username and password for authentication.
|
# Username and password for authentication.
|
||||||
user: ""
|
user: ""
|
||||||
password: ""
|
password: ""
|
||||||
|
|
||||||
# TODO: May want to move interview entries to subkey to make room for additional interview settings
|
|
||||||
interviews:
|
|
||||||
000000000000000000:
|
|
||||||
message: "Are you appealing your own ban or on behalf of another user?"
|
|
||||||
type: "BUTTONS"
|
|
||||||
color: "CYAN"
|
|
||||||
paths:
|
|
||||||
- "My own ban": # TODO: Can I add button color support somehow?
|
|
||||||
text: "What is your user name?"
|
|
||||||
type: "TEXT_INPUT"
|
|
||||||
summary-field: "Username"
|
|
||||||
paths:
|
|
||||||
- ".*":
|
|
||||||
text: "Please write your appeal below, motivate why you think you should be unbanned."
|
|
||||||
- "Another user's ban":
|
|
||||||
text: "You can only appeal your own ban. Please close this ticket."
|
|
||||||
type: "FAIL"
|
|
||||||
color: "CYAN"
|
|
Loading…
Reference in a new issue