Added database functions for interviews
This commit is contained in:
parent
c34a396c78
commit
ce836a465f
4 changed files with 125 additions and 14 deletions
|
@ -32,6 +32,8 @@ 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()
|
||||||
|
@ -100,6 +102,6 @@ internal static class Config
|
||||||
password = json.SelectToken("database.password")?.Value<string>() ?? "";
|
password = json.SelectToken("database.password")?.Value<string>() ?? "";
|
||||||
|
|
||||||
// Set up interviewer
|
// Set up interviewer
|
||||||
Interviewer.ParseConfig(json.SelectToken("interviews"));
|
interviews = json.SelectToken("interviews");
|
||||||
}
|
}
|
||||||
}
|
}
|
85
Database.cs
85
Database.cs
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using DSharpPlus;
|
using DSharpPlus;
|
||||||
using MySqlConnector;
|
using MySqlConnector;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace SupportChild;
|
namespace SupportChild;
|
||||||
|
|
||||||
|
@ -107,6 +108,11 @@ public static class Database
|
||||||
"name VARCHAR(256) NOT NULL UNIQUE," +
|
"name VARCHAR(256) NOT NULL UNIQUE," +
|
||||||
"category_id BIGINT UNSIGNED NOT NULL PRIMARY KEY)",
|
"category_id BIGINT UNSIGNED NOT NULL PRIMARY KEY)",
|
||||||
c);
|
c);
|
||||||
|
using MySqlCommand createInterviews = new MySqlCommand(
|
||||||
|
"CREATE TABLE IF NOT EXISTS interviews(" +
|
||||||
|
"channel_id BIGINT UNSIGNED NOT NULL PRIMARY KEY," +
|
||||||
|
"interview JSON NOT NULL)",
|
||||||
|
c);
|
||||||
c.Open();
|
c.Open();
|
||||||
createTickets.ExecuteNonQuery();
|
createTickets.ExecuteNonQuery();
|
||||||
createBlacklisted.ExecuteNonQuery();
|
createBlacklisted.ExecuteNonQuery();
|
||||||
|
@ -114,6 +120,7 @@ public static class Database
|
||||||
createStaffList.ExecuteNonQuery();
|
createStaffList.ExecuteNonQuery();
|
||||||
createMessages.ExecuteNonQuery();
|
createMessages.ExecuteNonQuery();
|
||||||
createCategories.ExecuteNonQuery();
|
createCategories.ExecuteNonQuery();
|
||||||
|
createInterviews.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsOpenTicket(ulong channelID)
|
public static bool IsOpenTicket(ulong channelID)
|
||||||
|
@ -724,6 +731,84 @@ public static class Database
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Dictionary<ulong, Interviewer.InterviewQuestion> GetAllInterviews()
|
||||||
|
{
|
||||||
|
using MySqlConnection c = GetConnection();
|
||||||
|
c.Open();
|
||||||
|
using MySqlCommand selection = new MySqlCommand("SELECT * FROM interviews", c);
|
||||||
|
selection.Prepare();
|
||||||
|
MySqlDataReader results = selection.ExecuteReader();
|
||||||
|
|
||||||
|
// Check if messages exist in the database
|
||||||
|
if (!results.Read())
|
||||||
|
{
|
||||||
|
return new Dictionary<ulong, Interviewer.InterviewQuestion>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<ulong, Interviewer.InterviewQuestion> questions = new Dictionary<ulong, Interviewer.InterviewQuestion>
|
||||||
|
{
|
||||||
|
{ results.GetUInt64("channel_id"), JsonConvert.DeserializeObject<Interviewer.InterviewQuestion>(results.GetString("interview")) }
|
||||||
|
};
|
||||||
|
while (results.Read())
|
||||||
|
{
|
||||||
|
questions.Add(results.GetUInt64("channel_id"), JsonConvert.DeserializeObject<Interviewer.InterviewQuestion>(results.GetString("interview")));
|
||||||
|
}
|
||||||
|
results.Close();
|
||||||
|
|
||||||
|
return questions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SaveInterview(ulong channelID, Interviewer.InterviewQuestion interview)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (TryGetInterview(channelID, out _))
|
||||||
|
{
|
||||||
|
using MySqlConnection c = GetConnection();
|
||||||
|
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
|
||||||
|
{
|
||||||
|
using MySqlConnection c = GetConnection();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (MySqlException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetInterview(ulong channelID, out Interviewer.InterviewQuestion interview)
|
||||||
|
{
|
||||||
|
using MySqlConnection c = GetConnection();
|
||||||
|
c.Open();
|
||||||
|
using MySqlCommand selection = new MySqlCommand(@"SELECT * FROM interviews WHERE channel_id=@channel_id", c);
|
||||||
|
selection.Parameters.AddWithValue("@channel_id", channelID);
|
||||||
|
selection.Prepare();
|
||||||
|
MySqlDataReader results = selection.ExecuteReader();
|
||||||
|
|
||||||
|
// Check if ticket exists in the database
|
||||||
|
if (!results.Read())
|
||||||
|
{
|
||||||
|
interview = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
interview = JsonConvert.DeserializeObject<Interviewer.InterviewQuestion>(results.GetString("interview"));
|
||||||
|
results.Close();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public class Ticket
|
public class Ticket
|
||||||
{
|
{
|
||||||
public uint id;
|
public uint id;
|
||||||
|
|
|
@ -22,7 +22,7 @@ public static class Interviewer
|
||||||
// The tree is generated by the config file when a new ticket is opened or the restart interview command is used.
|
// The tree is generated by the config file when a new ticket is opened or the restart interview command is used.
|
||||||
// Additional components not specified in the config file are populated as the interview progresses.
|
// Additional components not specified in the config file are populated as the interview progresses.
|
||||||
// The entire interview tree is serialized and stored in the database in order to record responses as they are made.
|
// The entire interview tree is serialized and stored in the database in order to record responses as they are made.
|
||||||
public struct InterviewQuestion
|
public class InterviewQuestion
|
||||||
{
|
{
|
||||||
// Message contents sent to the user.
|
// Message contents sent to the user.
|
||||||
public string message;
|
public string message;
|
||||||
|
@ -51,11 +51,18 @@ public static class Interviewer
|
||||||
|
|
||||||
private static Dictionary<ulong, InterviewQuestion> categoryInterviews = [];
|
private static Dictionary<ulong, InterviewQuestion> categoryInterviews = [];
|
||||||
|
|
||||||
|
private static Dictionary<ulong, InterviewQuestion> activeInterviews = [];
|
||||||
|
|
||||||
public static void ParseConfig(JToken interviewConfig)
|
public static void ParseConfig(JToken interviewConfig)
|
||||||
{
|
{
|
||||||
categoryInterviews = JsonConvert.DeserializeObject<Dictionary<ulong, InterviewQuestion>>(interviewConfig.ToString());
|
categoryInterviews = JsonConvert.DeserializeObject<Dictionary<ulong, InterviewQuestion>>(interviewConfig.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void LoadActiveInterviews()
|
||||||
|
{
|
||||||
|
activeInterviews = Database.GetAllInterviews();
|
||||||
|
}
|
||||||
|
|
||||||
public static void StartInterview(DiscordChannel channel)
|
public static void StartInterview(DiscordChannel channel)
|
||||||
{
|
{
|
||||||
if (channel.Parent == null)
|
if (channel.Parent == null)
|
||||||
|
@ -66,23 +73,29 @@ public static class Interviewer
|
||||||
if (categoryInterviews.TryGetValue(channel.Parent.Id, out InterviewQuestion interview))
|
if (categoryInterviews.TryGetValue(channel.Parent.Id, out InterviewQuestion interview))
|
||||||
{
|
{
|
||||||
CreateQuestion(channel, interview);
|
CreateQuestion(channel, interview);
|
||||||
|
Database.SaveInterview(channel.Id, interview);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ProcessResponse(DiscordChannel channel, string response)
|
public static void ProcessResponse(DiscordMessage message)
|
||||||
{
|
{
|
||||||
|
// TODO: Find if channel has open interview.
|
||||||
|
// TODO: Find if message is replying to interview message.
|
||||||
|
// TODO: Handle other interactions like button presses.
|
||||||
|
// TODO: Find out where in the interview tree we are.
|
||||||
|
// TODO: Handle FAIL event, cancelling the interview.
|
||||||
|
// TODO: Handle DONE event, creating a summary.
|
||||||
|
|
||||||
|
//Database.SaveInterview(channel.Id, interview);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async void CreateQuestion(DiscordChannel channel, InterviewQuestion question)
|
private static async void CreateQuestion(DiscordChannel channel, InterviewQuestion question)
|
||||||
{
|
{
|
||||||
DiscordMessageBuilder msgBuilder = new DiscordMessageBuilder
|
DiscordMessageBuilder msgBuilder = new DiscordMessageBuilder();
|
||||||
|
DiscordEmbedBuilder embed = new DiscordEmbedBuilder()
|
||||||
{
|
{
|
||||||
Embed = new DiscordEmbedBuilder
|
Color = Utilities.StringToColor(question.color),
|
||||||
{
|
Description = question.message
|
||||||
Color = Utilities.StringToColor(question.color),
|
|
||||||
Description = question.message
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (question.type)
|
switch (question.type)
|
||||||
|
@ -94,7 +107,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, "supportboi_interviewbutton " + nrOfButtons, question.paths.ToArray()[nrOfButtons].Key));
|
buttonRow.Add(new DiscordButtonComponent(ButtonStyle.Primary, "supportchild_interviewbutton " + nrOfButtons, question.paths.ToArray()[nrOfButtons].Key));
|
||||||
}
|
}
|
||||||
msgBuilder.AddComponents(buttonRow);
|
msgBuilder.AddComponents(buttonRow);
|
||||||
}
|
}
|
||||||
|
@ -110,13 +123,13 @@ public static class Interviewer
|
||||||
{
|
{
|
||||||
categoryOptions.Add(new DiscordSelectComponentOption(question.paths.ToArray()[selectionOptions].Key, selectionOptions.ToString()));
|
categoryOptions.Add(new DiscordSelectComponentOption(question.paths.ToArray()[selectionOptions].Key, selectionOptions.ToString()));
|
||||||
}
|
}
|
||||||
selectionComponents.Add(new DiscordSelectComponent("supportboi_interviewselector " + selectionBoxes, "Select an option...", categoryOptions, false, 0, 1));
|
selectionComponents.Add(new DiscordSelectComponent("supportchild_interviewselector " + selectionBoxes, "Select an option...", categoryOptions, false, 0, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
msgBuilder.AddComponents(selectionComponents);
|
msgBuilder.AddComponents(selectionComponents);
|
||||||
break;
|
break;
|
||||||
case QuestionType.TEXT_INPUT:
|
case QuestionType.TEXT_INPUT:
|
||||||
// TODO: Add embed footer with response info
|
embed.WithFooter("Reply to this message with your answer.");
|
||||||
break;
|
break;
|
||||||
case QuestionType.DONE:
|
case QuestionType.DONE:
|
||||||
case QuestionType.FAIL:
|
case QuestionType.FAIL:
|
||||||
|
@ -124,10 +137,9 @@ public static class Interviewer
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msgBuilder.AddEmbed(embed);
|
||||||
DiscordMessage message = await channel.SendMessageAsync(msgBuilder);
|
DiscordMessage message = await channel.SendMessageAsync(msgBuilder);
|
||||||
question.messageID = message.Id;
|
question.messageID = message.Id;
|
||||||
|
|
||||||
// TODO: Save interview progress
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void CreateSummary()
|
public static void CreateSummary()
|
||||||
|
|
|
@ -152,6 +152,18 @@ internal static class SupportChild
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Log("Connecting to database... (" + Config.hostName + ":" + Config.port + ")");
|
||||||
|
Interviewer.ParseConfig(Config.interviews);
|
||||||
|
Interviewer.LoadActiveInterviews();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.Log("Setting up Discord client...");
|
Logger.Log("Setting up Discord client...");
|
||||||
|
|
||||||
// Setting up client configuration
|
// Setting up client configuration
|
||||||
|
|
Loading…
Reference in a new issue