Added selection box support

This commit is contained in:
Toastie 2024-12-26 20:16:18 +13:00
parent 32776826ba
commit 280d18c80b
Signed by: toastie_t0ast
GPG key ID: 27F3B6855AFD40A4
2 changed files with 123 additions and 123 deletions

View file

@ -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;

View file

@ -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,31 +294,49 @@ 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);
return;
}
currentQuestion.answer = message.Content; // TODO: No matching path found.
currentQuestion.answerID = message.Id;
// Create next question, or finish the interview. }
switch (nextQuestion.type)
private static async Task HandleAnswer(string questionString,
InterviewQuestion nextQuestion,
InterviewQuestion interviewRoot,
InterviewQuestion previousQuestion,
DiscordChannel channel,
DiscordMessage message = null)
{ {
case QuestionType.ERROR: // The answer was provided using a button or selector
break; if (message == null)
case QuestionType.TEXT_INPUT: {
case QuestionType.BUTTONS: previousQuestion.answer = questionString;
case QuestionType.SELECTOR: previousQuestion.answerID = 0;
await CreateQuestion(message.Channel, nextQuestion); }
else
{
previousQuestion.answer = message.Content;
previousQuestion.answerID = message.Id;
}
// Remove other paths. // Remove any other paths from the previous question.
currentQuestion.paths = new Dictionary<string, InterviewQuestion> previousQuestion.paths = new Dictionary<string, InterviewQuestion>
{ {
{ questionString, nextQuestion } { questionString, nextQuestion }
}; };
Database.SaveInterview(message.Channel.Id, interviewRoot); // 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; break;
case QuestionType.DONE: case QuestionType.DONE:
// TODO: Remove previous interview messages.
OrderedDictionary summaryFields = new OrderedDictionary(); OrderedDictionary summaryFields = new OrderedDictionary();
interviewRoot.GetSummary(ref summaryFields); interviewRoot.GetSummary(ref summaryFields);
@ -352,7 +352,7 @@ public static class Interviewer
embed.AddField((string)entry.Key, (string)entry.Value); embed.AddField((string)entry.Key, (string)entry.Value);
} }
await message.Channel.SendMessageAsync(embed); await channel.SendMessageAsync(embed);
List<ulong> previousMessages = new List<ulong> { }; List<ulong> previousMessages = new List<ulong> { };
interviewRoot.GetMessageIDs(ref previousMessages); interviewRoot.GetMessageIDs(ref previousMessages);
@ -362,8 +362,8 @@ public static class Interviewer
try try
{ {
Logger.Debug("Deleting message: " + previousMessageID); Logger.Debug("Deleting message: " + previousMessageID);
DiscordMessage previousMessage = await message.Channel.GetMessageAsync(previousMessageID); DiscordMessage previousMessage = await channel.GetMessageAsync(previousMessageID);
await message.Channel.DeleteMessageAsync(previousMessage, "Deleting old interview message."); await channel.DeleteMessageAsync(previousMessage, "Deleting old interview message.");
} }
catch (Exception e) catch (Exception e)
{ {
@ -372,23 +372,20 @@ public static class Interviewer
} }
if (!Database.TryDeleteInterview(message.Channel.Id)) if (!Database.TryDeleteInterview(channel.Id))
{ {
Logger.Error("Could not delete interview from database. Channel ID: " + message.Channel.Id); Logger.Error("Could not delete interview from database. Channel ID: " + channel.Id);
} }
Reload(); Reload();
return; return;
case QuestionType.CANCEL: case QuestionType.CANCEL:
default:
// TODO: Post fail message. // TODO: Post fail message.
// TODO: Remove active interview. // TODO: Remove active interview.
break; break;
case QuestionType.ERROR:
default:
break;
} }
return;
}
// TODO: No matching path found.
} }
private static async Task CreateQuestion(DiscordChannel channel, InterviewQuestion question) private static async Task CreateQuestion(DiscordChannel channel, InterviewQuestion question)
@ -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;