Added step references, ability to jump to other steps depending on user actions
This commit is contained in:
parent
da5cca1f78
commit
61b57d0afc
6 changed files with 315 additions and 106 deletions
|
@ -62,7 +62,7 @@ public class InterviewCommands
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Database.TryGetInterview(command.Channel.Id, out InterviewStep interviewRoot))
|
if (!Database.TryGetInterview(command.Channel.Id, out Interview _))
|
||||||
{
|
{
|
||||||
await command.RespondAsync(new DiscordEmbedBuilder
|
await command.RespondAsync(new DiscordEmbedBuilder
|
||||||
{
|
{
|
||||||
|
|
|
@ -102,17 +102,34 @@ public class InterviewTemplateCommands
|
||||||
JsonSerializer serializer = new();
|
JsonSerializer serializer = new();
|
||||||
Template template = serializer.Deserialize<Template>(validatingReader);
|
Template template = serializer.Deserialize<Template>(validatingReader);
|
||||||
|
|
||||||
DiscordChannel category = await SupportChild.client.GetChannelAsync(template.categoryID);
|
DiscordChannel category;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
category = await SupportChild.client.GetChannelAsync(template.categoryID);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await command.RespondAsync(new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
Color = DiscordColor.Red,
|
||||||
|
Description = "Could not get the category from the ID in the uploaded JSON structure."
|
||||||
|
}, true);
|
||||||
|
Logger.Warn("Failed to get template category from ID: " + template.categoryID, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!category.IsCategory)
|
if (!category.IsCategory)
|
||||||
{
|
{
|
||||||
await command.RespondAsync(new DiscordEmbedBuilder
|
await command.RespondAsync(new DiscordEmbedBuilder
|
||||||
{
|
{
|
||||||
Color = DiscordColor.Red,
|
Color = DiscordColor.Red,
|
||||||
Description = "The category ID in the uploaded JSON structure is not a valid category."
|
Description = "The channel ID in the uploaded JSON structure is not a category."
|
||||||
}, true);
|
}, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: Validate that any references with reference end steps have an after reference step
|
||||||
|
|
||||||
List<string> errors = [];
|
List<string> errors = [];
|
||||||
List<string> warnings = [];
|
List<string> warnings = [];
|
||||||
template.interview.Validate(ref errors, ref warnings, "interview", 0, 0);
|
template.interview.Validate(ref errors, ref warnings, "interview", 0, 0);
|
||||||
|
@ -208,7 +225,7 @@ public class InterviewTemplateCommands
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Database.TryGetInterviewTemplate(category.Id, out InterviewStep _))
|
if (!Database.TryGetInterviewFromTemplate(category.Id, 0, out Interview _))
|
||||||
{
|
{
|
||||||
await command.RespondAsync(new DiscordEmbedBuilder
|
await command.RespondAsync(new DiscordEmbedBuilder
|
||||||
{
|
{
|
||||||
|
|
39
Database.cs
39
Database.cs
|
@ -113,7 +113,8 @@ public static class Database
|
||||||
using MySqlCommand createInterviews = new MySqlCommand(
|
using MySqlCommand createInterviews = new MySqlCommand(
|
||||||
"CREATE TABLE IF NOT EXISTS interviews(" +
|
"CREATE TABLE IF NOT EXISTS interviews(" +
|
||||||
"channel_id BIGINT UNSIGNED NOT NULL PRIMARY KEY," +
|
"channel_id BIGINT UNSIGNED NOT NULL PRIMARY KEY," +
|
||||||
"interview JSON NOT NULL)",
|
"interview JSON NOT NULL," +
|
||||||
|
"definitions JSON NOT NULL)",
|
||||||
c);
|
c);
|
||||||
using MySqlCommand createInterviewTemplates = new MySqlCommand(
|
using MySqlCommand createInterviewTemplates = new MySqlCommand(
|
||||||
"CREATE TABLE IF NOT EXISTS interview_templates(" +
|
"CREATE TABLE IF NOT EXISTS interview_templates(" +
|
||||||
|
@ -753,7 +754,7 @@ public static class Database
|
||||||
return templates;
|
return templates;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryGetInterviewTemplate(ulong categoryID, out Interviews.InterviewStep template)
|
public static bool TryGetInterviewFromTemplate(ulong categoryID, ulong channelID, out Interviews.Interview interview)
|
||||||
{
|
{
|
||||||
using MySqlConnection c = GetConnection();
|
using MySqlConnection c = GetConnection();
|
||||||
c.Open();
|
c.Open();
|
||||||
|
@ -765,7 +766,7 @@ public static class Database
|
||||||
// Check if messages exist in the database
|
// Check if messages exist in the database
|
||||||
if (!results.Read())
|
if (!results.Read())
|
||||||
{
|
{
|
||||||
template = null;
|
interview = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,7 +775,7 @@ public static class Database
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
template = JsonConvert.DeserializeObject<Interviews.Template>(templateString, new JsonSerializerSettings
|
Template template = JsonConvert.DeserializeObject<Template>(templateString, new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
Error = delegate (object sender, ErrorEventArgs args)
|
Error = delegate (object sender, ErrorEventArgs args)
|
||||||
{
|
{
|
||||||
|
@ -782,21 +783,22 @@ public static class Database
|
||||||
Logger.Debug("Detailed exception:", args.ErrorContext.Error);
|
Logger.Debug("Detailed exception:", args.ErrorContext.Error);
|
||||||
args.ErrorContext.Handled = false;
|
args.ErrorContext.Handled = false;
|
||||||
}
|
}
|
||||||
}).interview;
|
});
|
||||||
|
interview = new Interview(channelID, template.interview, template.definitions);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
template = null;
|
interview = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool SetInterviewTemplate(Interviews.Template template)
|
public static bool SetInterviewTemplate(Template template)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string templateString = JsonConvert.SerializeObject(template, new JsonSerializerSettings()
|
string templateString = JsonConvert.SerializeObject(template, new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
NullValueHandling = NullValueHandling.Ignore,
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
MissingMemberHandling = MissingMemberHandling.Error,
|
MissingMemberHandling = MissingMemberHandling.Error,
|
||||||
|
@ -805,7 +807,7 @@ public static class Database
|
||||||
});
|
});
|
||||||
|
|
||||||
string query;
|
string query;
|
||||||
if (TryGetInterviewTemplate(template.categoryID, out _))
|
if (TryGetInterviewFromTemplate(template.categoryID, 0, out _))
|
||||||
{
|
{
|
||||||
query = "UPDATE interview_templates SET template = @template WHERE category_id=@category_id";
|
query = "UPDATE interview_templates SET template = @template WHERE category_id=@category_id";
|
||||||
}
|
}
|
||||||
|
@ -845,25 +847,26 @@ public static class Database
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool SaveInterview(ulong channelID, Interviews.InterviewStep interview)
|
public static bool SaveInterview(Interview interview)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string query;
|
string query;
|
||||||
if (TryGetInterview(channelID, out _))
|
if (TryGetInterview(interview.channelID, out _))
|
||||||
{
|
{
|
||||||
query = "UPDATE interviews SET interview = @interview WHERE channel_id = @channel_id";
|
query = "UPDATE interviews SET interview = @interview, definitions = @definitions WHERE channel_id = @channel_id";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
query = "INSERT INTO interviews (channel_id,interview) VALUES (@channel_id, @interview)";
|
query = "INSERT INTO interviews (channel_id,interview, definitions) VALUES (@channel_id, @interview, @definitions)";
|
||||||
}
|
}
|
||||||
|
|
||||||
using MySqlConnection c = GetConnection();
|
using MySqlConnection c = GetConnection();
|
||||||
c.Open();
|
c.Open();
|
||||||
using MySqlCommand cmd = new MySqlCommand(query, c);
|
using MySqlCommand cmd = new MySqlCommand(query, c);
|
||||||
cmd.Parameters.AddWithValue("@channel_id", channelID);
|
cmd.Parameters.AddWithValue("@channel_id", interview.channelID);
|
||||||
cmd.Parameters.AddWithValue("@interview", JsonConvert.SerializeObject(interview, Formatting.Indented));
|
cmd.Parameters.AddWithValue("@interview", JsonConvert.SerializeObject(interview.interviewRoot, Formatting.Indented));
|
||||||
|
cmd.Parameters.AddWithValue("@definitions", JsonConvert.SerializeObject(interview.definitions, Formatting.Indented));
|
||||||
cmd.Prepare();
|
cmd.Prepare();
|
||||||
return cmd.ExecuteNonQuery() > 0;
|
return cmd.ExecuteNonQuery() > 0;
|
||||||
}
|
}
|
||||||
|
@ -873,7 +876,7 @@ public static class Database
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryGetInterview(ulong channelID, out Interviews.InterviewStep interview)
|
public static bool TryGetInterview(ulong channelID, out Interview interview)
|
||||||
{
|
{
|
||||||
using MySqlConnection c = GetConnection();
|
using MySqlConnection c = GetConnection();
|
||||||
c.Open();
|
c.Open();
|
||||||
|
@ -888,7 +891,9 @@ public static class Database
|
||||||
interview = null;
|
interview = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
interview = JsonConvert.DeserializeObject<Interviews.InterviewStep>(results.GetString("interview"));
|
interview = new Interview(channelID,
|
||||||
|
JsonConvert.DeserializeObject<InterviewStep>(results.GetString("interview")),
|
||||||
|
JsonConvert.DeserializeObject<Dictionary<string, InterviewStep>>(results.GetString("definitions")));
|
||||||
results.Close();
|
results.Close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using DSharpPlus.Entities;
|
using DSharpPlus.Entities;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
|
@ -22,7 +23,8 @@ public enum MessageType
|
||||||
ROLE_SELECTOR,
|
ROLE_SELECTOR,
|
||||||
MENTIONABLE_SELECTOR, // User or role
|
MENTIONABLE_SELECTOR, // User or role
|
||||||
CHANNEL_SELECTOR,
|
CHANNEL_SELECTOR,
|
||||||
TEXT_INPUT
|
TEXT_INPUT,
|
||||||
|
REFERENCE_END
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ButtonType
|
public enum ButtonType
|
||||||
|
@ -33,6 +35,45 @@ public enum ButtonType
|
||||||
DANGER
|
DANGER
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ReferencedInterviewStep
|
||||||
|
{
|
||||||
|
[JsonProperty("id")]
|
||||||
|
public string id;
|
||||||
|
|
||||||
|
// If this step is on a button, give it this style.
|
||||||
|
[JsonConverter(typeof(StringEnumConverter))]
|
||||||
|
[JsonProperty("button-style")]
|
||||||
|
public ButtonType? buttonStyle;
|
||||||
|
|
||||||
|
// If this step is in a selector, give it this description.
|
||||||
|
[JsonProperty("selector-description")]
|
||||||
|
public string selectorDescription;
|
||||||
|
|
||||||
|
// Runs at the end of the reference
|
||||||
|
[JsonProperty("after-reference-step")]
|
||||||
|
public InterviewStep afterReferenceStep;
|
||||||
|
|
||||||
|
public DiscordButtonStyle GetButtonStyle()
|
||||||
|
{
|
||||||
|
return InterviewStep.GetButtonStyle(buttonStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetReferencedStep(Interview interview, out InterviewStep step)
|
||||||
|
{
|
||||||
|
if (!interview.definitions.TryGetValue(id, out step))
|
||||||
|
{
|
||||||
|
Logger.Error("Could not find referenced step '" + id + "' in interview for channel '" + interview.channelID + "'");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
step.buttonStyle = buttonStyle;
|
||||||
|
step.selectorDescription = selectorDescription;
|
||||||
|
step.afterReferenceStep = afterReferenceStep;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// A tree of steps representing an interview.
|
// A tree of steps representing an interview.
|
||||||
// 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.
|
||||||
|
@ -54,7 +95,7 @@ public class InterviewStep
|
||||||
|
|
||||||
// Colour of the message embed.
|
// Colour of the message embed.
|
||||||
[JsonProperty("color")]
|
[JsonProperty("color")]
|
||||||
public string color;
|
public string color = "CYAN";
|
||||||
|
|
||||||
// Used as label for this answer in the post-interview summary.
|
// Used as label for this answer in the post-interview summary.
|
||||||
[JsonProperty("summary-field")]
|
[JsonProperty("summary-field")]
|
||||||
|
@ -65,11 +106,11 @@ public class InterviewStep
|
||||||
[JsonProperty("button-style")]
|
[JsonProperty("button-style")]
|
||||||
public ButtonType? buttonStyle;
|
public ButtonType? buttonStyle;
|
||||||
|
|
||||||
// If this step is on a selector, give it this placeholder.
|
// If this step is a selector, give it this placeholder.
|
||||||
[JsonProperty("selector-placeholder")]
|
[JsonProperty("selector-placeholder")]
|
||||||
public string selectorPlaceholder;
|
public string selectorPlaceholder;
|
||||||
|
|
||||||
// If this step is on a selector, give it this description.
|
// If this step is in a selector, give it this description.
|
||||||
[JsonProperty("selector-description")]
|
[JsonProperty("selector-description")]
|
||||||
public string selectorDescription;
|
public string selectorDescription;
|
||||||
|
|
||||||
|
@ -81,6 +122,10 @@ public class InterviewStep
|
||||||
[JsonProperty("min-length")]
|
[JsonProperty("min-length")]
|
||||||
public int? minLength;
|
public int? minLength;
|
||||||
|
|
||||||
|
// References to steps defined elsewhere in the template
|
||||||
|
[JsonProperty("step-references")]
|
||||||
|
public Dictionary<string, ReferencedInterviewStep> references = new();
|
||||||
|
|
||||||
// Possible questions to ask next, an error message, or the end of the interview.
|
// Possible questions to ask next, an error message, or the end of the interview.
|
||||||
[JsonProperty("steps")]
|
[JsonProperty("steps")]
|
||||||
public Dictionary<string, InterviewStep> steps = new();
|
public Dictionary<string, InterviewStep> steps = new();
|
||||||
|
@ -105,12 +150,23 @@ public class InterviewStep
|
||||||
[JsonProperty("related-message-ids")]
|
[JsonProperty("related-message-ids")]
|
||||||
public List<ulong> relatedMessageIDs;
|
public List<ulong> relatedMessageIDs;
|
||||||
|
|
||||||
public bool TryGetCurrentStep(out InterviewStep step)
|
// This is only set when the user gets to a referenced step
|
||||||
|
[JsonProperty("after-reference-step")]
|
||||||
|
public InterviewStep afterReferenceStep = null;
|
||||||
|
|
||||||
|
public bool TryGetCurrentStep(out InterviewStep currentStep)
|
||||||
|
{
|
||||||
|
bool result = TryGetTakenSteps(out List<InterviewStep> previousSteps);
|
||||||
|
currentStep = previousSteps.FirstOrDefault();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetTakenSteps(out List<InterviewStep> previousSteps)
|
||||||
{
|
{
|
||||||
// This object has not been initialized, we have checked too deep.
|
// This object has not been initialized, we have checked too deep.
|
||||||
if (messageID == 0)
|
if (messageID == 0)
|
||||||
{
|
{
|
||||||
step = null;
|
previousSteps = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,14 +174,15 @@ public class InterviewStep
|
||||||
foreach (KeyValuePair<string, InterviewStep> childStep in steps)
|
foreach (KeyValuePair<string, InterviewStep> childStep in steps)
|
||||||
{
|
{
|
||||||
// This child either is the one we are looking for or contains the one we are looking for.
|
// This child either is the one we are looking for or contains the one we are looking for.
|
||||||
if (childStep.Value.TryGetCurrentStep(out step))
|
if (childStep.Value.TryGetTakenSteps(out previousSteps))
|
||||||
{
|
{
|
||||||
|
previousSteps.Add(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This object is the deepest object with a message ID set, meaning it is the latest asked question.
|
// This object is the deepest object with a message ID set, meaning it is the latest asked question.
|
||||||
step = this;
|
previousSteps = new List<InterviewStep> { this };
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +195,8 @@ public class InterviewStep
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(summaryField))
|
if (!string.IsNullOrWhiteSpace(summaryField))
|
||||||
{
|
{
|
||||||
summary.Add(summaryField, answer);
|
// TODO: Add option to merge answers
|
||||||
|
summary[summaryField] = answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will always contain exactly one or zero children.
|
// This will always contain exactly one or zero children.
|
||||||
|
@ -184,18 +242,6 @@ public class InterviewStep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DiscordButtonStyle GetButtonStyle()
|
|
||||||
{
|
|
||||||
return buttonStyle switch
|
|
||||||
{
|
|
||||||
ButtonType.PRIMARY => DiscordButtonStyle.Primary,
|
|
||||||
ButtonType.SECONDARY => DiscordButtonStyle.Secondary,
|
|
||||||
ButtonType.SUCCESS => DiscordButtonStyle.Success,
|
|
||||||
ButtonType.DANGER => DiscordButtonStyle.Danger,
|
|
||||||
_ => DiscordButtonStyle.Secondary
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Validate(ref List<string> errors,
|
public void Validate(ref List<string> errors,
|
||||||
ref List<string> warnings,
|
ref List<string> warnings,
|
||||||
string stepID,
|
string stepID,
|
||||||
|
@ -237,7 +283,7 @@ public class InterviewStep
|
||||||
|
|
||||||
if (messageType is MessageType.ERROR or MessageType.END_WITH_SUMMARY or MessageType.END_WITHOUT_SUMMARY)
|
if (messageType is MessageType.ERROR or MessageType.END_WITH_SUMMARY or MessageType.END_WITHOUT_SUMMARY)
|
||||||
{
|
{
|
||||||
if (steps.Count > 0)
|
if (steps.Count > 0 || references.Count > 0)
|
||||||
{
|
{
|
||||||
warnings.Add("Steps of the type '" + messageType + "' cannot have child steps.\n\n" + stepID + ".message-type");
|
warnings.Add("Steps of the type '" + messageType + "' cannot have child steps.\n\n" + stepID + ".message-type");
|
||||||
}
|
}
|
||||||
|
@ -247,7 +293,7 @@ public class InterviewStep
|
||||||
warnings.Add("Steps of the type '" + messageType + "' cannot have summary field names.\n\n" + stepID + ".summary-field");
|
warnings.Add("Steps of the type '" + messageType + "' cannot have summary field names.\n\n" + stepID + ".summary-field");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (steps.Count == 0)
|
else if (steps.Count == 0 && references.Count == 0)
|
||||||
{
|
{
|
||||||
errors.Add("Steps of the type '" + messageType + "' must have at least one child step.\n\n" + stepID + ".message-type");
|
errors.Add("Steps of the type '" + messageType + "' must have at least one child step.\n\n" + stepID + ".message-type");
|
||||||
}
|
}
|
||||||
|
@ -304,6 +350,23 @@ public class InterviewStep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DiscordButtonStyle GetButtonStyle()
|
||||||
|
{
|
||||||
|
return GetButtonStyle(buttonStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DiscordButtonStyle GetButtonStyle(ButtonType? buttonStyle)
|
||||||
|
{
|
||||||
|
return buttonStyle switch
|
||||||
|
{
|
||||||
|
ButtonType.PRIMARY => DiscordButtonStyle.Primary,
|
||||||
|
ButtonType.SECONDARY => DiscordButtonStyle.Secondary,
|
||||||
|
ButtonType.SUCCESS => DiscordButtonStyle.Success,
|
||||||
|
ButtonType.DANGER => DiscordButtonStyle.Danger,
|
||||||
|
_ => DiscordButtonStyle.Secondary
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public class StripInternalPropertiesResolver : DefaultContractResolver
|
public class StripInternalPropertiesResolver : DefaultContractResolver
|
||||||
{
|
{
|
||||||
private static readonly HashSet<string> ignoreProps =
|
private static readonly HashSet<string> ignoreProps =
|
||||||
|
@ -326,11 +389,21 @@ public class InterviewStep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Template(ulong categoryID, InterviewStep interview)
|
public class Interview(ulong channelID, InterviewStep interviewRoot, Dictionary<string, InterviewStep> definitions)
|
||||||
|
{
|
||||||
|
public ulong channelID = channelID;
|
||||||
|
public InterviewStep interviewRoot = interviewRoot;
|
||||||
|
public Dictionary<string, InterviewStep> definitions = definitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Template(ulong categoryID, InterviewStep interview, Dictionary<string, InterviewStep> definitions)
|
||||||
{
|
{
|
||||||
[JsonProperty("category-id", Required = Required.Always)]
|
[JsonProperty("category-id", Required = Required.Always)]
|
||||||
public ulong categoryID = categoryID;
|
public ulong categoryID = categoryID;
|
||||||
|
|
||||||
[JsonProperty("interview", Required = Required.Always)]
|
[JsonProperty("interview", Required = Required.Always)]
|
||||||
public InterviewStep interview = interview;
|
public InterviewStep interview = interview;
|
||||||
|
|
||||||
|
[JsonProperty("definitions", Required = Required.Default)]
|
||||||
|
public Dictionary<string, InterviewStep> definitions = definitions;
|
||||||
}
|
}
|
|
@ -14,22 +14,22 @@ public static class Interviewer
|
||||||
{
|
{
|
||||||
public static async Task<bool> StartInterview(DiscordChannel channel)
|
public static async Task<bool> StartInterview(DiscordChannel channel)
|
||||||
{
|
{
|
||||||
if (!Database.TryGetInterviewTemplate(channel.Parent.Id, out InterviewStep template))
|
if (!Database.TryGetInterviewFromTemplate(channel.Parent.Id, channel.Id, out Interview interview))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SendNextMessage(channel, template);
|
await SendNextMessage(channel, interview.interviewRoot);
|
||||||
return Database.SaveInterview(channel.Id, template);
|
return Database.SaveInterview(interview);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<bool> RestartInterview(DiscordChannel channel)
|
public static async Task<bool> RestartInterview(DiscordChannel channel)
|
||||||
{
|
{
|
||||||
if (Database.TryGetInterview(channel.Id, out InterviewStep interviewRoot))
|
if (Database.TryGetInterview(channel.Id, out Interview interview))
|
||||||
{
|
{
|
||||||
if (Config.deleteMessagesAfterNoSummary)
|
if (Config.deleteMessagesAfterNoSummary)
|
||||||
{
|
{
|
||||||
await DeletePreviousMessages(interviewRoot, channel);
|
await DeletePreviousMessages(interview, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Database.TryDeleteInterview(channel.Id))
|
if (!Database.TryDeleteInterview(channel.Id))
|
||||||
|
@ -43,11 +43,11 @@ public static class Interviewer
|
||||||
|
|
||||||
public static async Task<bool> StopInterview(DiscordChannel channel)
|
public static async Task<bool> StopInterview(DiscordChannel channel)
|
||||||
{
|
{
|
||||||
if (Database.TryGetInterview(channel.Id, out InterviewStep interviewRoot))
|
if (Database.TryGetInterview(channel.Id, out Interview interview))
|
||||||
{
|
{
|
||||||
if (Config.deleteMessagesAfterNoSummary)
|
if (Config.deleteMessagesAfterNoSummary)
|
||||||
{
|
{
|
||||||
await DeletePreviousMessages(interviewRoot, channel);
|
await DeletePreviousMessages(interview, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Database.TryDeleteInterview(channel.Id))
|
if (!Database.TryDeleteInterview(channel.Id))
|
||||||
|
@ -61,7 +61,7 @@ public static class Interviewer
|
||||||
|
|
||||||
public static async Task ProcessButtonOrSelectorResponse(DiscordInteraction interaction)
|
public static async Task ProcessButtonOrSelectorResponse(DiscordInteraction interaction)
|
||||||
{
|
{
|
||||||
if (interaction?.Channel == null || interaction?.Message == null)
|
if (interaction?.Channel == null || interaction.Message == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ public static class Interviewer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if there is no active interview in this channel
|
// Return if there is no active interview in this channel
|
||||||
if (!Database.TryGetInterview(interaction.Channel.Id, out InterviewStep interviewRoot))
|
if (!Database.TryGetInterview(interaction.Channel.Id, out Interview interview))
|
||||||
{
|
{
|
||||||
await interaction.CreateResponseAsync(DiscordInteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder()
|
await interaction.CreateResponseAsync(DiscordInteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder()
|
||||||
.AddEmbed(new DiscordEmbedBuilder()
|
.AddEmbed(new DiscordEmbedBuilder()
|
||||||
|
@ -85,7 +85,7 @@ public static class Interviewer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return if the current question cannot be found in the interview.
|
// Return if the current question cannot be found in the interview.
|
||||||
if (!interviewRoot.TryGetCurrentStep(out InterviewStep currentStep))
|
if (!interview.interviewRoot.TryGetCurrentStep(out InterviewStep currentStep))
|
||||||
{
|
{
|
||||||
await interaction.CreateResponseAsync(DiscordInteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder()
|
await interaction.CreateResponseAsync(DiscordInteractionResponseType.ChannelMessageWithSource, new DiscordInteractionResponseBuilder()
|
||||||
.AddEmbed(new DiscordEmbedBuilder()
|
.AddEmbed(new DiscordEmbedBuilder()
|
||||||
|
@ -126,19 +126,19 @@ public static class Interviewer
|
||||||
case DiscordComponentType.RoleSelect:
|
case DiscordComponentType.RoleSelect:
|
||||||
case DiscordComponentType.ChannelSelect:
|
case DiscordComponentType.ChannelSelect:
|
||||||
case DiscordComponentType.MentionableSelect:
|
case DiscordComponentType.MentionableSelect:
|
||||||
if (interaction.Data.Resolved?.Roles?.Any() ?? false)
|
if (interaction.Data.Resolved.Roles.Any())
|
||||||
{
|
{
|
||||||
answer = interaction.Data.Resolved.Roles.First().Value.Mention;
|
answer = interaction.Data.Resolved.Roles.First().Value.Mention;
|
||||||
}
|
}
|
||||||
else if (interaction.Data.Resolved?.Users?.Any() ?? false)
|
else if (interaction.Data.Resolved.Users.Any())
|
||||||
{
|
{
|
||||||
answer = interaction.Data.Resolved.Users.First().Value.Mention;
|
answer = interaction.Data.Resolved.Users.First().Value.Mention;
|
||||||
}
|
}
|
||||||
else if (interaction.Data.Resolved?.Channels?.Any() ?? false)
|
else if (interaction.Data.Resolved.Channels.Any())
|
||||||
{
|
{
|
||||||
answer = interaction.Data.Resolved.Channels.First().Value.Mention;
|
answer = interaction.Data.Resolved.Channels.First().Value.Mention;
|
||||||
}
|
}
|
||||||
else if (interaction.Data.Resolved?.Messages?.Any() ?? false)
|
else if (interaction.Data.Resolved.Messages.Any())
|
||||||
{
|
{
|
||||||
answer = interaction.Data.Resolved.Messages.First().Value.Id.ToString();
|
answer = interaction.Data.Resolved.Messages.First().Value.Id.ToString();
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ public static class Interviewer
|
||||||
componentID = interaction.Data.Values[0];
|
componentID = interaction.Data.Values[0];
|
||||||
break;
|
break;
|
||||||
case DiscordComponentType.Button:
|
case DiscordComponentType.Button:
|
||||||
componentID = interaction.Data.CustomId.Replace("supportchild_interviewbutton ", "");
|
componentID = interaction.Data.CustomId.Replace("supportboi_interviewbutton ", "");
|
||||||
break;
|
break;
|
||||||
case DiscordComponentType.ActionRow:
|
case DiscordComponentType.ActionRow:
|
||||||
case DiscordComponentType.FormInput:
|
case DiscordComponentType.FormInput:
|
||||||
|
@ -158,12 +158,27 @@ public static class Interviewer
|
||||||
// The different mentionable selectors provide the actual answer, while the others just return the ID.
|
// The different mentionable selectors provide the actual answer, while the others just return the ID.
|
||||||
if (componentID == "")
|
if (componentID == "")
|
||||||
{
|
{
|
||||||
|
foreach (KeyValuePair<string, ReferencedInterviewStep> reference in currentStep.references)
|
||||||
|
{
|
||||||
|
// Skip to the first matching step.
|
||||||
|
if (Regex.IsMatch(answer, reference.Key))
|
||||||
|
{
|
||||||
|
if (TryGetStepFromReference(interview, reference.Value, out InterviewStep referencedStep))
|
||||||
|
{
|
||||||
|
currentStep.steps.Add(reference.Key, referencedStep);
|
||||||
|
await HandleAnswer(answer, referencedStep, interview, currentStep, interaction.Channel);
|
||||||
|
}
|
||||||
|
currentStep.references.Remove(reference.Key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (KeyValuePair<string, InterviewStep> step in currentStep.steps)
|
foreach (KeyValuePair<string, InterviewStep> step in currentStep.steps)
|
||||||
{
|
{
|
||||||
// Skip to the first matching step.
|
// Skip to the first matching step.
|
||||||
if (Regex.IsMatch(answer, step.Key))
|
if (Regex.IsMatch(answer, step.Key))
|
||||||
{
|
{
|
||||||
await HandleAnswer(answer, step.Value, interviewRoot, currentStep, interaction.Channel);
|
await HandleAnswer(answer, step.Value, interview, currentStep, interaction.Channel);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,7 +190,7 @@ public static class Interviewer
|
||||||
Description = "Error: Could not determine the next question based on your answer. Check your response and ask an admin to check the bot logs if this seems incorrect."
|
Description = "Error: Could not determine the next question based on your answer. Check your response and ask an admin to check the bot logs if this seems incorrect."
|
||||||
}).AsEphemeral());
|
}).AsEphemeral());
|
||||||
currentStep.AddRelatedMessageIDs(followupMessage.Id);
|
currentStep.AddRelatedMessageIDs(followupMessage.Id);
|
||||||
Database.SaveInterview(interaction.Channel.Id, interviewRoot);
|
Database.SaveInterview(interview);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -192,10 +207,31 @@ public static class Interviewer
|
||||||
}
|
}
|
||||||
|
|
||||||
(string stepString, InterviewStep nextStep) = currentStep.steps.ElementAt(stepIndex);
|
(string stepString, InterviewStep nextStep) = currentStep.steps.ElementAt(stepIndex);
|
||||||
await HandleAnswer(stepString, nextStep, interviewRoot, currentStep, interaction.Channel);
|
await HandleAnswer(stepString, nextStep, interview, currentStep, interaction.Channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool TryGetStepFromReference(Interview interview, ReferencedInterviewStep reference, out InterviewStep step)
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, InterviewStep> definition in interview.definitions)
|
||||||
|
{
|
||||||
|
if (reference.id == definition.Key)
|
||||||
|
{
|
||||||
|
step = definition.Value;
|
||||||
|
step.buttonStyle = reference.buttonStyle;
|
||||||
|
step.selectorDescription = reference.selectorDescription;
|
||||||
|
if (step.messageType != MessageType.ERROR)
|
||||||
|
{
|
||||||
|
step.afterReferenceStep = reference.afterReferenceStep;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
step = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task ProcessResponseMessage(DiscordMessage answerMessage)
|
public static async Task ProcessResponseMessage(DiscordMessage answerMessage)
|
||||||
{
|
{
|
||||||
// Either the message or the referenced message is null.
|
// Either the message or the referenced message is null.
|
||||||
|
@ -205,12 +241,13 @@ public static class Interviewer
|
||||||
}
|
}
|
||||||
|
|
||||||
// The channel does not have an active interview.
|
// The channel does not have an active interview.
|
||||||
if (!Database.TryGetInterview(answerMessage.ReferencedMessage.Channel.Id, out InterviewStep interviewRoot))
|
if (!Database.TryGetInterview(answerMessage.ReferencedMessage.Channel.Id,
|
||||||
|
out Interview interview))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!interviewRoot.TryGetCurrentStep(out InterviewStep currentStep))
|
if (!interview.interviewRoot.TryGetCurrentStep(out InterviewStep currentStep))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -238,7 +275,7 @@ public static class Interviewer
|
||||||
Color = DiscordColor.Red
|
Color = DiscordColor.Red
|
||||||
});
|
});
|
||||||
currentStep.AddRelatedMessageIDs(answerMessage.Id, lengthMessage.Id);
|
currentStep.AddRelatedMessageIDs(answerMessage.Id, lengthMessage.Id);
|
||||||
Database.SaveInterview(answerMessage.Channel.Id, interviewRoot);
|
Database.SaveInterview(interview);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +287,7 @@ public static class Interviewer
|
||||||
Color = DiscordColor.Red
|
Color = DiscordColor.Red
|
||||||
});
|
});
|
||||||
currentStep.AddRelatedMessageIDs(answerMessage.Id, lengthMessage.Id);
|
currentStep.AddRelatedMessageIDs(answerMessage.Id, lengthMessage.Id);
|
||||||
Database.SaveInterview(answerMessage.Channel.Id, interviewRoot);
|
Database.SaveInterview(interview);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +299,7 @@ public static class Interviewer
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
await HandleAnswer(answerMessage.Content, nextStep, interviewRoot, currentStep, answerMessage.Channel, answerMessage);
|
await HandleAnswer(answerMessage.Content, nextStep, interview, currentStep, answerMessage.Channel, answerMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,12 +310,12 @@ public static class Interviewer
|
||||||
Color = DiscordColor.Red
|
Color = DiscordColor.Red
|
||||||
});
|
});
|
||||||
currentStep.AddRelatedMessageIDs(answerMessage.Id, errorMessage.Id);
|
currentStep.AddRelatedMessageIDs(answerMessage.Id, errorMessage.Id);
|
||||||
Database.SaveInterview(answerMessage.Channel.Id, interviewRoot);
|
Database.SaveInterview(interview);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task HandleAnswer(string answer,
|
private static async Task HandleAnswer(string answer,
|
||||||
InterviewStep nextStep,
|
InterviewStep nextStep,
|
||||||
InterviewStep interviewRoot,
|
Interview interview,
|
||||||
InterviewStep previousStep,
|
InterviewStep previousStep,
|
||||||
DiscordChannel channel,
|
DiscordChannel channel,
|
||||||
DiscordMessage answerMessage = null)
|
DiscordMessage answerMessage = null)
|
||||||
|
@ -302,14 +339,37 @@ public static class Interviewer
|
||||||
case MessageType.USER_SELECTOR:
|
case MessageType.USER_SELECTOR:
|
||||||
case MessageType.CHANNEL_SELECTOR:
|
case MessageType.CHANNEL_SELECTOR:
|
||||||
case MessageType.MENTIONABLE_SELECTOR:
|
case MessageType.MENTIONABLE_SELECTOR:
|
||||||
|
foreach ((string stepPattern, ReferencedInterviewStep reference) in nextStep.references)
|
||||||
|
{
|
||||||
|
if (!reference.TryGetReferencedStep(interview, out InterviewStep step))
|
||||||
|
{
|
||||||
|
if (answerMessage != null)
|
||||||
|
{
|
||||||
|
DiscordMessage lengthMessage = await answerMessage.RespondAsync(new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
Description = "Error: The referenced step id '" + reference.id + "' does not exist in the step definitions.",
|
||||||
|
Color = DiscordColor.Red
|
||||||
|
});
|
||||||
|
nextStep.AddRelatedMessageIDs(answerMessage.Id, lengthMessage.Id);
|
||||||
|
previousStep.answer = null;
|
||||||
|
previousStep.answerID = 0;
|
||||||
|
Database.SaveInterview(interview);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextStep.steps.Add(stepPattern, step);
|
||||||
|
}
|
||||||
|
nextStep.references.Clear();
|
||||||
|
|
||||||
await SendNextMessage(channel, nextStep);
|
await SendNextMessage(channel, nextStep);
|
||||||
Database.SaveInterview(channel.Id, interviewRoot);
|
Database.SaveInterview(interview);
|
||||||
break;
|
break;
|
||||||
case MessageType.END_WITH_SUMMARY:
|
case MessageType.END_WITH_SUMMARY:
|
||||||
OrderedDictionary summaryFields = new OrderedDictionary();
|
OrderedDictionary summaryFields = new OrderedDictionary();
|
||||||
interviewRoot.GetSummary(ref summaryFields);
|
interview.interviewRoot.GetSummary(ref summaryFields);
|
||||||
|
|
||||||
DiscordEmbedBuilder embed = new DiscordEmbedBuilder()
|
DiscordEmbedBuilder embed = new DiscordEmbedBuilder
|
||||||
{
|
{
|
||||||
Color = Utilities.StringToColor(nextStep.color),
|
Color = Utilities.StringToColor(nextStep.color),
|
||||||
Title = nextStep.heading,
|
Title = nextStep.heading,
|
||||||
|
@ -318,14 +378,14 @@ public static class Interviewer
|
||||||
|
|
||||||
foreach (DictionaryEntry entry in summaryFields)
|
foreach (DictionaryEntry entry in summaryFields)
|
||||||
{
|
{
|
||||||
embed.AddField((string)entry.Key, (string)entry.Value);
|
embed.AddField((string)entry.Key, (string)entry.Value ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
await channel.SendMessageAsync(embed);
|
await channel.SendMessageAsync(embed);
|
||||||
|
|
||||||
if (Config.deleteMessagesAfterSummary)
|
if (Config.deleteMessagesAfterSummary)
|
||||||
{
|
{
|
||||||
await DeletePreviousMessages(interviewRoot, channel);
|
await DeletePreviousMessages(interview, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Database.TryDeleteInterview(channel.Id))
|
if (!Database.TryDeleteInterview(channel.Id))
|
||||||
|
@ -334,7 +394,7 @@ public static class Interviewer
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case MessageType.END_WITHOUT_SUMMARY:
|
case MessageType.END_WITHOUT_SUMMARY:
|
||||||
await channel.SendMessageAsync(new DiscordEmbedBuilder()
|
await channel.SendMessageAsync(new DiscordEmbedBuilder
|
||||||
{
|
{
|
||||||
Color = Utilities.StringToColor(nextStep.color),
|
Color = Utilities.StringToColor(nextStep.color),
|
||||||
Title = nextStep.heading,
|
Title = nextStep.heading,
|
||||||
|
@ -343,7 +403,7 @@ public static class Interviewer
|
||||||
|
|
||||||
if (Config.deleteMessagesAfterNoSummary)
|
if (Config.deleteMessagesAfterNoSummary)
|
||||||
{
|
{
|
||||||
await DeletePreviousMessages(interviewRoot, channel);
|
await DeletePreviousMessages(interview, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Database.TryDeleteInterview(channel.Id))
|
if (!Database.TryDeleteInterview(channel.Id))
|
||||||
|
@ -351,40 +411,88 @@ public static class Interviewer
|
||||||
Logger.Error("Could not delete interview from database. Channel ID: " + channel.Id);
|
Logger.Error("Could not delete interview from database. Channel ID: " + channel.Id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MessageType.ERROR:
|
case MessageType.REFERENCE_END:
|
||||||
default:
|
// TODO: What is happening with the summaries?
|
||||||
|
if (interview.interviewRoot.TryGetTakenSteps(out List<InterviewStep> previousSteps))
|
||||||
|
{
|
||||||
|
foreach (InterviewStep step in previousSteps)
|
||||||
|
{
|
||||||
|
if (step.afterReferenceStep != null)
|
||||||
|
{
|
||||||
|
// If the referenced step is also a reference end, skip it and try to find another.
|
||||||
|
if (step.afterReferenceStep.messageType == MessageType.REFERENCE_END)
|
||||||
|
{
|
||||||
|
step.afterReferenceStep = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextStep = step.afterReferenceStep;
|
||||||
|
step.afterReferenceStep = null;
|
||||||
|
|
||||||
|
previousStep.steps.Clear();
|
||||||
|
previousStep.steps.Add(answer, nextStep);
|
||||||
|
await HandleAnswer(answer,
|
||||||
|
nextStep,
|
||||||
|
interview,
|
||||||
|
previousStep,
|
||||||
|
channel,
|
||||||
|
answerMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DiscordEmbedBuilder error = new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
Color = DiscordColor.Red,
|
||||||
|
Description = "An error occured while trying to find the next interview step."
|
||||||
|
};
|
||||||
|
|
||||||
if (answerMessage == null)
|
if (answerMessage == null)
|
||||||
{
|
{
|
||||||
DiscordMessage errorMessage = await channel.SendMessageAsync(new DiscordEmbedBuilder()
|
DiscordMessage errorMessage = await channel.SendMessageAsync(error);
|
||||||
{
|
|
||||||
Color = Utilities.StringToColor(nextStep.color),
|
|
||||||
Title = nextStep.heading,
|
|
||||||
Description = nextStep.message
|
|
||||||
});
|
|
||||||
previousStep.AddRelatedMessageIDs(errorMessage.Id);
|
previousStep.AddRelatedMessageIDs(errorMessage.Id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DiscordMessageBuilder errorMessageBuilder = new DiscordMessageBuilder()
|
DiscordMessage errorMessage = await answerMessage.RespondAsync(error);
|
||||||
.AddEmbed(new DiscordEmbedBuilder()
|
|
||||||
{
|
|
||||||
Color = Utilities.StringToColor(nextStep.color),
|
|
||||||
Title = nextStep.heading,
|
|
||||||
Description = nextStep.message
|
|
||||||
}).WithReply(answerMessage.Id);
|
|
||||||
DiscordMessage errorMessage = await answerMessage.RespondAsync(errorMessageBuilder);
|
|
||||||
previousStep.AddRelatedMessageIDs(errorMessage.Id, answerMessage.Id);
|
previousStep.AddRelatedMessageIDs(errorMessage.Id, answerMessage.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Database.SaveInterview(channel.Id, interviewRoot);
|
Database.SaveInterview(interview);
|
||||||
|
|
||||||
|
Logger.Error("Could not find a step to return to after a reference step in channel " + channel.Id);
|
||||||
|
return;
|
||||||
|
case MessageType.ERROR:
|
||||||
|
default:
|
||||||
|
DiscordEmbedBuilder err = new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
Color = Utilities.StringToColor(nextStep.color),
|
||||||
|
Title = nextStep.heading,
|
||||||
|
Description = nextStep.message
|
||||||
|
};
|
||||||
|
|
||||||
|
if (answerMessage == null)
|
||||||
|
{
|
||||||
|
DiscordMessage errorMessage = await channel.SendMessageAsync(err);
|
||||||
|
previousStep.AddRelatedMessageIDs(errorMessage.Id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DiscordMessage errorMessage = await answerMessage.RespondAsync(err);
|
||||||
|
previousStep.AddRelatedMessageIDs(errorMessage.Id, answerMessage.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Database.SaveInterview(interview);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task DeletePreviousMessages(InterviewStep interviewRoot, DiscordChannel channel)
|
private static async Task DeletePreviousMessages(Interview interview, DiscordChannel channel)
|
||||||
{
|
{
|
||||||
List<ulong> previousMessages = [];
|
List<ulong> previousMessages = [];
|
||||||
interviewRoot.GetMessageIDs(ref previousMessages);
|
interview.interviewRoot.GetMessageIDs(ref previousMessages);
|
||||||
|
|
||||||
foreach (ulong previousMessageID in previousMessages)
|
foreach (ulong previousMessageID in previousMessages)
|
||||||
{
|
{
|
||||||
|
@ -420,7 +528,7 @@ public static class Interviewer
|
||||||
for (; nrOfButtons < 5 * (nrOfButtonRows + 1) && nrOfButtons < step.steps.Count; nrOfButtons++)
|
for (; nrOfButtons < 5 * (nrOfButtonRows + 1) && nrOfButtons < step.steps.Count; nrOfButtons++)
|
||||||
{
|
{
|
||||||
(string stepPattern, InterviewStep nextStep) = step.steps.ToArray()[nrOfButtons];
|
(string stepPattern, InterviewStep nextStep) = step.steps.ToArray()[nrOfButtons];
|
||||||
buttonRow.Add(new DiscordButtonComponent(nextStep.GetButtonStyle(), "supportchild_interviewbutton " + nrOfButtons, stepPattern));
|
buttonRow.Add(new DiscordButtonComponent(nextStep.GetButtonStyle(), "supportboi_interviewbutton " + nrOfButtons, stepPattern));
|
||||||
}
|
}
|
||||||
msgBuilder.AddComponents(buttonRow);
|
msgBuilder.AddComponents(buttonRow);
|
||||||
}
|
}
|
||||||
|
@ -438,26 +546,26 @@ public static class Interviewer
|
||||||
categoryOptions.Add(new DiscordSelectComponentOption(stepPattern, selectionOptions.ToString(), nextStep.selectorDescription));
|
categoryOptions.Add(new DiscordSelectComponentOption(stepPattern, selectionOptions.ToString(), nextStep.selectorDescription));
|
||||||
}
|
}
|
||||||
|
|
||||||
selectionComponents.Add(new DiscordSelectComponent("supportchild_interviewselector " + selectionBoxes, string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
selectionComponents.Add(new DiscordSelectComponent("supportboi_interviewselector " + selectionBoxes, string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
||||||
? "Select an option..." : step.selectorPlaceholder, categoryOptions));
|
? "Select an option..." : step.selectorPlaceholder, categoryOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
msgBuilder.AddComponents(selectionComponents);
|
msgBuilder.AddComponents(selectionComponents);
|
||||||
break;
|
break;
|
||||||
case MessageType.ROLE_SELECTOR:
|
case MessageType.ROLE_SELECTOR:
|
||||||
msgBuilder.AddComponents(new DiscordRoleSelectComponent("supportchild_interviewroleselector", string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
msgBuilder.AddComponents(new DiscordRoleSelectComponent("supportboi_interviewroleselector", string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
||||||
? "Select a role..." : step.selectorPlaceholder));
|
? "Select a role..." : step.selectorPlaceholder));
|
||||||
break;
|
break;
|
||||||
case MessageType.USER_SELECTOR:
|
case MessageType.USER_SELECTOR:
|
||||||
msgBuilder.AddComponents(new DiscordUserSelectComponent("supportchild_interviewuserselector", string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
msgBuilder.AddComponents(new DiscordUserSelectComponent("supportboi_interviewuserselector", string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
||||||
? "Select a user..." : step.selectorPlaceholder));
|
? "Select a user..." : step.selectorPlaceholder));
|
||||||
break;
|
break;
|
||||||
case MessageType.CHANNEL_SELECTOR:
|
case MessageType.CHANNEL_SELECTOR:
|
||||||
msgBuilder.AddComponents(new DiscordChannelSelectComponent("supportchild_interviewchannelselector", string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
msgBuilder.AddComponents(new DiscordChannelSelectComponent("supportboi_interviewchannelselector", string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
||||||
? "Select a channel..." : step.selectorPlaceholder));
|
? "Select a channel..." : step.selectorPlaceholder));
|
||||||
break;
|
break;
|
||||||
case MessageType.MENTIONABLE_SELECTOR:
|
case MessageType.MENTIONABLE_SELECTOR:
|
||||||
msgBuilder.AddComponents(new DiscordMentionableSelectComponent("supportchild_interviewmentionableselector", string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
msgBuilder.AddComponents(new DiscordMentionableSelectComponent("supportboi_interviewmentionableselector", string.IsNullOrWhiteSpace(step.selectorPlaceholder)
|
||||||
? "Select a user or role..." : step.selectorPlaceholder));
|
? "Select a user or role..." : step.selectorPlaceholder));
|
||||||
break;
|
break;
|
||||||
case MessageType.TEXT_INPUT:
|
case MessageType.TEXT_INPUT:
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"reference": {
|
"reference": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "Interview Step",
|
"title": "Interview Step Reference",
|
||||||
"description": "Contains a reference to a step defined in the 'definitions' property. You can also specify a button-style and selector-description here to override the one from the referenced step.",
|
"description": "Contains a reference to a step defined in the 'definitions' property. You can also specify a button-style and selector-description here to override the one from the referenced step.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"id": {
|
"id": {
|
||||||
|
@ -31,6 +31,12 @@
|
||||||
"title": "Selector Description",
|
"title": "Selector Description",
|
||||||
"description": "Description for this option in the parent step's selection box. Requires that the parent step is a 'TEXT_SELECTOR'.",
|
"description": "Description for this option in the parent step's selection box. Requires that the parent step is a 'TEXT_SELECTOR'.",
|
||||||
"minLength": 1
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"after-reference-step": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "Steps",
|
||||||
|
"description": "Any REFERENCE_END steps in the reference will continue to this step.",
|
||||||
|
"$ref": "#/definitions/step"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -69,7 +75,8 @@
|
||||||
"ROLE_SELECTOR",
|
"ROLE_SELECTOR",
|
||||||
"MENTIONABLE_SELECTOR",
|
"MENTIONABLE_SELECTOR",
|
||||||
"CHANNEL_SELECTOR",
|
"CHANNEL_SELECTOR",
|
||||||
"TEXT_INPUT"
|
"TEXT_INPUT",
|
||||||
|
"REFERENCE_END"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"color": {
|
"color": {
|
||||||
|
@ -186,8 +193,7 @@
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"message",
|
"message",
|
||||||
"message-type",
|
"message-type"
|
||||||
"color"
|
|
||||||
],
|
],
|
||||||
"unevaluatedProperties": false
|
"unevaluatedProperties": false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue