Added selection box support
This commit is contained in:
parent
32776826ba
commit
280d18c80b
2 changed files with 123 additions and 123 deletions
|
@ -204,7 +204,7 @@ public static class EventHandler
|
||||||
await OnNewTicketButtonUsed(e.Interaction);
|
await OnNewTicketButtonUsed(e.Interaction);
|
||||||
return;
|
return;
|
||||||
case not null when e.Id.StartsWith("supportchild_interviewbutton"):
|
case not null when e.Id.StartsWith("supportchild_interviewbutton"):
|
||||||
await Interviewer.ProcessButtonResponse(e.Interaction);
|
await Interviewer.ProcessButtonOrSelectorResponse(e.Interaction);
|
||||||
return;
|
return;
|
||||||
case "right":
|
case "right":
|
||||||
case "left":
|
case "left":
|
||||||
|
@ -225,6 +225,9 @@ public static class EventHandler
|
||||||
case not null when e.Id.StartsWith("supportchild_newticketselector"):
|
case not null when e.Id.StartsWith("supportchild_newticketselector"):
|
||||||
await CreateSelectionBoxPanelCommand.OnSelectionMenuUsed(e.Interaction);
|
await CreateSelectionBoxPanelCommand.OnSelectionMenuUsed(e.Interaction);
|
||||||
return;
|
return;
|
||||||
|
case not null when e.Id.StartsWith("supportchild_interviewselector"):
|
||||||
|
await Interviewer.ProcessButtonOrSelectorResponse(e.Interaction);
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
Logger.Warn("Unknown selection box option received! '" + e.Id + "'");
|
Logger.Warn("Unknown selection box option received! '" + e.Id + "'");
|
||||||
return;
|
return;
|
||||||
|
|
241
Interviewer.cs
241
Interviewer.cs
|
@ -13,13 +13,14 @@ namespace SupportChild;
|
||||||
|
|
||||||
public static class Interviewer
|
public static class Interviewer
|
||||||
{
|
{
|
||||||
|
// TODO: Investigate other types of selectors
|
||||||
public enum QuestionType
|
public enum QuestionType
|
||||||
{
|
{
|
||||||
ERROR,
|
ERROR,
|
||||||
CANCEL,
|
CANCEL,
|
||||||
DONE,
|
DONE,
|
||||||
BUTTONS,
|
BUTTONS,
|
||||||
SELECTOR,
|
TEXT_SELECTOR,
|
||||||
TEXT_INPUT
|
TEXT_INPUT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,9 +183,7 @@ public static class Interviewer
|
||||||
return activeInterviews.ContainsKey(channelID);
|
return activeInterviews.ContainsKey(channelID);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add selection box handling.
|
public static async Task ProcessButtonOrSelectorResponse(DiscordInteraction interaction)
|
||||||
|
|
||||||
public static async Task ProcessButtonResponse(DiscordInteraction interaction)
|
|
||||||
{
|
{
|
||||||
// TODO: Add error responses.
|
// TODO: Add error responses.
|
||||||
|
|
||||||
|
@ -193,6 +192,12 @@ public static class Interviewer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The user selected nothing.
|
||||||
|
if (interaction.Data.ComponentType == DiscordComponentType.StringSelect && interaction.Data.Values.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredMessageUpdate);
|
await interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredMessageUpdate);
|
||||||
|
|
||||||
// Could not find active interview.
|
// Could not find active interview.
|
||||||
|
@ -214,69 +219,46 @@ public static class Interviewer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the response index from the button.
|
// Parse the response index from the button.
|
||||||
if (!int.TryParse(interaction.Data.CustomId.Replace("supportchild_interviewbutton ", ""), out int pathIndex))
|
string componentID = "";
|
||||||
|
|
||||||
|
switch (interaction.Data.ComponentType)
|
||||||
{
|
{
|
||||||
Logger.Error("Invalid interview button index: " + interaction.Data.CustomId.Replace("supportchild_interviewbutton ", ""));
|
case DiscordComponentType.StringSelect:
|
||||||
|
componentID = interaction.Data.Values[0];
|
||||||
|
break;
|
||||||
|
case DiscordComponentType.Button:
|
||||||
|
componentID = interaction.Data.CustomId.Replace("supportchild_interviewbutton ", "");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!int.TryParse(componentID, out int pathIndex))
|
||||||
|
{
|
||||||
|
Logger.Error("Invalid interview button/selector index: " + componentID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pathIndex >= currentQuestion.paths.Count || pathIndex < 0)
|
if (pathIndex >= currentQuestion.paths.Count || pathIndex < 0)
|
||||||
{
|
{
|
||||||
Logger.Error("Invalid interview button index: " + pathIndex);
|
Logger.Error("Invalid interview button/selector index: " + pathIndex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyValuePair<string, InterviewQuestion> questionPath = currentQuestion.paths.ElementAt(pathIndex);
|
(string questionString, InterviewQuestion nextQuestion) = currentQuestion.paths.ElementAt(pathIndex);
|
||||||
|
|
||||||
|
await HandleAnswer(questionString, nextQuestion, interviewRoot, currentQuestion, interaction.Channel);
|
||||||
|
|
||||||
currentQuestion.answer = interaction.Data.Name;
|
// Edit message to remove buttons/selectors.
|
||||||
currentQuestion.answerID = 0;
|
// TODO: Add footer with answer.
|
||||||
|
|
||||||
// Create next question, or finish the interview.
|
|
||||||
InterviewQuestion nextQuestion = questionPath.Value;
|
|
||||||
switch (questionPath.Value.type)
|
|
||||||
{
|
|
||||||
case QuestionType.TEXT_INPUT:
|
|
||||||
await CreateQuestion(interaction.Channel, nextQuestion);
|
|
||||||
break;
|
|
||||||
case QuestionType.BUTTONS:
|
|
||||||
await CreateQuestion(interaction.Channel, nextQuestion);
|
|
||||||
// TODO: Remove buttons
|
|
||||||
break;
|
|
||||||
case QuestionType.SELECTOR:
|
|
||||||
await CreateQuestion(interaction.Channel, nextQuestion);
|
|
||||||
// TODO: Remove selector
|
|
||||||
break;
|
|
||||||
case QuestionType.DONE:
|
|
||||||
// TODO: Create summary.
|
|
||||||
// TODO: Remove previous interview messages.
|
|
||||||
// TODO: Remove active interview.
|
|
||||||
Logger.Error("INTERVIEW DONE");
|
|
||||||
break;
|
|
||||||
case QuestionType.CANCEL:
|
|
||||||
default:
|
|
||||||
// TODO: Post fail message.
|
|
||||||
// TODO: Remove active interview.
|
|
||||||
Logger.Error("INTERVIEW FAILED");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove other paths.
|
|
||||||
currentQuestion.paths = new Dictionary<string, InterviewQuestion>
|
|
||||||
{
|
|
||||||
{ questionPath.Key, nextQuestion }
|
|
||||||
};
|
|
||||||
|
|
||||||
await interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().AddEmbed(interaction.Message.Embeds[0]));
|
await interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().AddEmbed(interaction.Message.Embeds[0]));
|
||||||
|
|
||||||
Database.SaveInterview(interaction.Channel.Id, interviewRoot);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ProcessResponseMessage(DiscordMessage message)
|
public static async Task ProcessResponseMessage(DiscordMessage message)
|
||||||
{
|
{
|
||||||
// TODO: Handle other interactions like button presses.
|
// TODO: Handle other interactions like button presses.
|
||||||
// TODO: Handle FAIL event, cancelling the interview.
|
// TODO: Handle FAIL event, cancelling the interview.
|
||||||
// TODO: Handle DONE event, creating a summary.
|
|
||||||
|
|
||||||
// Either the message or the referenced message is null.
|
// Either the message or the referenced message is null.
|
||||||
if (message.Channel == null || message.ReferencedMessage?.Channel == null)
|
if (message.Channel == null || message.ReferencedMessage?.Channel == null)
|
||||||
|
@ -312,78 +294,7 @@ public static class Interviewer
|
||||||
// Skip to the matching path.
|
// Skip to the matching path.
|
||||||
if (!Regex.IsMatch(message.Content, questionString)) continue;
|
if (!Regex.IsMatch(message.Content, questionString)) continue;
|
||||||
|
|
||||||
// TODO: Refactor this into separate function to reduce duplication
|
await HandleAnswer(questionString, nextQuestion, interviewRoot, currentQuestion, message.Channel, message);
|
||||||
|
|
||||||
currentQuestion.answer = message.Content;
|
|
||||||
currentQuestion.answerID = message.Id;
|
|
||||||
|
|
||||||
// Create next question, or finish the interview.
|
|
||||||
switch (nextQuestion.type)
|
|
||||||
{
|
|
||||||
case QuestionType.ERROR:
|
|
||||||
break;
|
|
||||||
case QuestionType.TEXT_INPUT:
|
|
||||||
case QuestionType.BUTTONS:
|
|
||||||
case QuestionType.SELECTOR:
|
|
||||||
await CreateQuestion(message.Channel, nextQuestion);
|
|
||||||
|
|
||||||
// Remove other paths.
|
|
||||||
currentQuestion.paths = new Dictionary<string, InterviewQuestion>
|
|
||||||
{
|
|
||||||
{ questionString, nextQuestion }
|
|
||||||
};
|
|
||||||
|
|
||||||
Database.SaveInterview(message.Channel.Id, interviewRoot);
|
|
||||||
break;
|
|
||||||
case QuestionType.DONE:
|
|
||||||
// TODO: Remove previous interview messages.
|
|
||||||
OrderedDictionary summaryFields = new OrderedDictionary();
|
|
||||||
interviewRoot.GetSummary(ref summaryFields);
|
|
||||||
|
|
||||||
DiscordEmbedBuilder embed = new DiscordEmbedBuilder()
|
|
||||||
{
|
|
||||||
Color = Utilities.StringToColor(nextQuestion.color),
|
|
||||||
Title = "Summary:",
|
|
||||||
Description = nextQuestion.message,
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (DictionaryEntry entry in summaryFields)
|
|
||||||
{
|
|
||||||
embed.AddField((string)entry.Key, (string)entry.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
await message.Channel.SendMessageAsync(embed);
|
|
||||||
|
|
||||||
List<ulong> previousMessages = new List<ulong> { };
|
|
||||||
interviewRoot.GetMessageIDs(ref previousMessages);
|
|
||||||
|
|
||||||
foreach (ulong previousMessageID in previousMessages)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Logger.Debug("Deleting message: " + previousMessageID);
|
|
||||||
DiscordMessage previousMessage = await message.Channel.GetMessageAsync(previousMessageID);
|
|
||||||
await message.Channel.DeleteMessageAsync(previousMessage, "Deleting old interview message.");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.Error("Failed to delete old interview message: " + e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Database.TryDeleteInterview(message.Channel.Id))
|
|
||||||
{
|
|
||||||
Logger.Error("Could not delete interview from database. Channel ID: " + message.Channel.Id);
|
|
||||||
}
|
|
||||||
Reload();
|
|
||||||
return;
|
|
||||||
case QuestionType.CANCEL:
|
|
||||||
default:
|
|
||||||
// TODO: Post fail message.
|
|
||||||
// TODO: Remove active interview.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,6 +302,92 @@ public static class Interviewer
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task HandleAnswer(string questionString,
|
||||||
|
InterviewQuestion nextQuestion,
|
||||||
|
InterviewQuestion interviewRoot,
|
||||||
|
InterviewQuestion previousQuestion,
|
||||||
|
DiscordChannel channel,
|
||||||
|
DiscordMessage message = null)
|
||||||
|
{
|
||||||
|
// The answer was provided using a button or selector
|
||||||
|
if (message == null)
|
||||||
|
{
|
||||||
|
previousQuestion.answer = questionString;
|
||||||
|
previousQuestion.answerID = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
previousQuestion.answer = message.Content;
|
||||||
|
previousQuestion.answerID = message.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any other paths from the previous question.
|
||||||
|
previousQuestion.paths = new Dictionary<string, InterviewQuestion>
|
||||||
|
{
|
||||||
|
{ questionString, nextQuestion }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create next question, or finish the interview.
|
||||||
|
switch (nextQuestion.type)
|
||||||
|
{
|
||||||
|
case QuestionType.TEXT_INPUT:
|
||||||
|
case QuestionType.BUTTONS:
|
||||||
|
case QuestionType.TEXT_SELECTOR:
|
||||||
|
await CreateQuestion(channel, nextQuestion);
|
||||||
|
Database.SaveInterview(channel.Id, interviewRoot);
|
||||||
|
break;
|
||||||
|
case QuestionType.DONE:
|
||||||
|
OrderedDictionary summaryFields = new OrderedDictionary();
|
||||||
|
interviewRoot.GetSummary(ref summaryFields);
|
||||||
|
|
||||||
|
DiscordEmbedBuilder embed = new DiscordEmbedBuilder()
|
||||||
|
{
|
||||||
|
Color = Utilities.StringToColor(nextQuestion.color),
|
||||||
|
Title = "Summary:",
|
||||||
|
Description = nextQuestion.message,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (DictionaryEntry entry in summaryFields)
|
||||||
|
{
|
||||||
|
embed.AddField((string)entry.Key, (string)entry.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
await channel.SendMessageAsync(embed);
|
||||||
|
|
||||||
|
List<ulong> previousMessages = new List<ulong> { };
|
||||||
|
interviewRoot.GetMessageIDs(ref previousMessages);
|
||||||
|
|
||||||
|
foreach (ulong previousMessageID in previousMessages)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Debug("Deleting message: " + previousMessageID);
|
||||||
|
DiscordMessage previousMessage = await channel.GetMessageAsync(previousMessageID);
|
||||||
|
await channel.DeleteMessageAsync(previousMessage, "Deleting old interview message.");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error("Failed to delete old interview message: " + e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Database.TryDeleteInterview(channel.Id))
|
||||||
|
{
|
||||||
|
Logger.Error("Could not delete interview from database. Channel ID: " + channel.Id);
|
||||||
|
}
|
||||||
|
Reload();
|
||||||
|
return;
|
||||||
|
case QuestionType.CANCEL:
|
||||||
|
// TODO: Post fail message.
|
||||||
|
// TODO: Remove active interview.
|
||||||
|
break;
|
||||||
|
case QuestionType.ERROR:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static async Task CreateQuestion(DiscordChannel channel, InterviewQuestion question)
|
private static async Task CreateQuestion(DiscordChannel channel, InterviewQuestion question)
|
||||||
{
|
{
|
||||||
DiscordMessageBuilder msgBuilder = new DiscordMessageBuilder();
|
DiscordMessageBuilder msgBuilder = new DiscordMessageBuilder();
|
||||||
|
@ -414,7 +411,7 @@ public static class Interviewer
|
||||||
msgBuilder.AddComponents(buttonRow);
|
msgBuilder.AddComponents(buttonRow);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QuestionType.SELECTOR:
|
case QuestionType.TEXT_SELECTOR:
|
||||||
List<DiscordSelectComponent> selectionComponents = [];
|
List<DiscordSelectComponent> selectionComponents = [];
|
||||||
|
|
||||||
int selectionOptions = 0;
|
int selectionOptions = 0;
|
||||||
|
|
Loading…
Reference in a new issue