Add all other discord selector types
This commit is contained in:
parent
f1ce93ae86
commit
f03473ba14
2 changed files with 130 additions and 28 deletions
|
@ -239,9 +239,45 @@ public static class EventHandler
|
||||||
Logger.Warn("Unknown form input received! '" + e.Id + "'");
|
Logger.Warn("Unknown form input received! '" + e.Id + "'");
|
||||||
return;
|
return;
|
||||||
case DiscordComponentType.UserSelect:
|
case DiscordComponentType.UserSelect:
|
||||||
|
switch (e.Id)
|
||||||
|
{
|
||||||
|
case not null when e.Id.StartsWith("supportchild_interviewuserselector"):
|
||||||
|
await Interviewer.ProcessButtonOrSelectorResponse(e.Interaction);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
Logger.Warn("Unknown selection box option received! '" + e.Id + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
case DiscordComponentType.RoleSelect:
|
case DiscordComponentType.RoleSelect:
|
||||||
|
switch (e.Id)
|
||||||
|
{
|
||||||
|
case not null when e.Id.StartsWith("supportchild_interviewroleselector"):
|
||||||
|
await Interviewer.ProcessButtonOrSelectorResponse(e.Interaction);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
Logger.Warn("Unknown selection box option received! '" + e.Id + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
case DiscordComponentType.MentionableSelect:
|
case DiscordComponentType.MentionableSelect:
|
||||||
|
switch (e.Id)
|
||||||
|
{
|
||||||
|
case not null when e.Id.StartsWith("supportchild_interviewmentionableselector"):
|
||||||
|
await Interviewer.ProcessButtonOrSelectorResponse(e.Interaction);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
Logger.Warn("Unknown selection box option received! '" + e.Id + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
case DiscordComponentType.ChannelSelect:
|
case DiscordComponentType.ChannelSelect:
|
||||||
|
switch (e.Id)
|
||||||
|
{
|
||||||
|
case not null when e.Id.StartsWith("supportchild_interviewchannelselector"):
|
||||||
|
await Interviewer.ProcessButtonOrSelectorResponse(e.Interaction);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
Logger.Warn("Unknown selection box option received! '" + e.Id + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
Logger.Warn("Unknown interaction type received! '" + e.Interaction.Data.ComponentType + "'");
|
Logger.Warn("Unknown interaction type received! '" + e.Interaction.Data.ComponentType + "'");
|
||||||
break;
|
break;
|
||||||
|
|
122
Interviewer.cs
122
Interviewer.cs
|
@ -15,14 +15,19 @@ namespace SupportChild;
|
||||||
|
|
||||||
public static class Interviewer
|
public static class Interviewer
|
||||||
{
|
{
|
||||||
// TODO: Investigate other types of selectors
|
// TODO: Validate that the different types have the appropriate amount of subpaths
|
||||||
public enum QuestionType
|
public enum QuestionType
|
||||||
{
|
{
|
||||||
|
// Support multiselector as separate type, with only one subpath supported
|
||||||
ERROR,
|
ERROR,
|
||||||
END_WITH_SUMMARY,
|
END_WITH_SUMMARY,
|
||||||
END_WITHOUT_SUMMARY,
|
END_WITHOUT_SUMMARY,
|
||||||
BUTTONS,
|
BUTTONS,
|
||||||
TEXT_SELECTOR,
|
TEXT_SELECTOR,
|
||||||
|
USER_SELECTOR,
|
||||||
|
ROLE_SELECTOR,
|
||||||
|
MENTIONABLE_SELECTOR, // User or role
|
||||||
|
CHANNEL_SELECTOR,
|
||||||
TEXT_INPUT
|
TEXT_INPUT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +46,7 @@ public static class Interviewer
|
||||||
// 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 class InterviewQuestion
|
public class InterviewQuestion
|
||||||
{
|
{
|
||||||
|
// TODO: Add selector placeholder
|
||||||
// Title of the message embed.
|
// Title of the message embed.
|
||||||
[JsonProperty("title")]
|
[JsonProperty("title")]
|
||||||
public string title;
|
public string title;
|
||||||
|
@ -123,6 +129,11 @@ public static class Interviewer
|
||||||
|
|
||||||
public void GetSummary(ref OrderedDictionary summary)
|
public void GetSummary(ref OrderedDictionary summary)
|
||||||
{
|
{
|
||||||
|
if (messageID == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(summaryField))
|
if (!string.IsNullOrWhiteSpace(summaryField))
|
||||||
{
|
{
|
||||||
summary.Add(summaryField, answer);
|
summary.Add(summaryField, answer);
|
||||||
|
@ -274,7 +285,7 @@ public static class Interviewer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore if option was deselected.
|
// Ignore if option was deselected.
|
||||||
if (interaction.Data.ComponentType == DiscordComponentType.StringSelect && interaction.Data.Values.Length == 0)
|
if (interaction.Data.ComponentType is not DiscordComponentType.Button && interaction.Data.Values.Length == 0)
|
||||||
{
|
{
|
||||||
await interaction.CreateResponseAsync(DiscordInteractionResponseType.UpdateMessage);
|
await interaction.CreateResponseAsync(DiscordInteractionResponseType.UpdateMessage);
|
||||||
return;
|
return;
|
||||||
|
@ -314,13 +325,43 @@ public static class Interviewer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await interaction.CreateResponseAsync(DiscordInteractionResponseType.UpdateMessage);
|
try
|
||||||
|
{
|
||||||
|
// TODO: Debug this with the new selectors
|
||||||
|
await interaction.CreateResponseAsync(DiscordInteractionResponseType.UpdateMessage);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error("Could not update original message:", e);
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the response index from the button/selector.
|
// Parse the response index from the button/selector.
|
||||||
string componentID = "";
|
string componentID = "";
|
||||||
|
string answer = "";
|
||||||
|
|
||||||
switch (interaction.Data.ComponentType)
|
switch (interaction.Data.ComponentType)
|
||||||
{
|
{
|
||||||
|
case DiscordComponentType.UserSelect:
|
||||||
|
case DiscordComponentType.RoleSelect:
|
||||||
|
case DiscordComponentType.ChannelSelect:
|
||||||
|
case DiscordComponentType.MentionableSelect:
|
||||||
|
if (interaction.Data.Resolved?.Roles?.Any() ?? false)
|
||||||
|
{
|
||||||
|
answer = interaction.Data.Resolved.Roles.First().Value.Mention;
|
||||||
|
}
|
||||||
|
else if (interaction.Data.Resolved?.Users?.Any() ?? false)
|
||||||
|
{
|
||||||
|
answer = interaction.Data.Resolved.Users.First().Value.Mention;
|
||||||
|
}
|
||||||
|
else if (interaction.Data.Resolved?.Channels?.Any() ?? false)
|
||||||
|
{
|
||||||
|
answer = interaction.Data.Resolved.Channels.First().Value.Mention;
|
||||||
|
}
|
||||||
|
else if (interaction.Data.Resolved?.Messages?.Any() ?? false)
|
||||||
|
{
|
||||||
|
answer = interaction.Data.Resolved.Messages.First().Value.Id.ToString();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case DiscordComponentType.StringSelect:
|
case DiscordComponentType.StringSelect:
|
||||||
componentID = interaction.Data.Values[0];
|
componentID = interaction.Data.Values[0];
|
||||||
break;
|
break;
|
||||||
|
@ -331,21 +372,37 @@ public static class Interviewer
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!int.TryParse(componentID, out int pathIndex))
|
// The different mentionable selectors provide the actual answer, while the others just return the ID.
|
||||||
|
if (componentID == "")
|
||||||
{
|
{
|
||||||
Logger.Error("Invalid interview button/selector index: " + componentID);
|
// TODO: Handle multipaths
|
||||||
return;
|
if (currentQuestion.paths.Count != 1)
|
||||||
|
{
|
||||||
|
Logger.Error("The interview for channel " + interaction.Channel.Id + " has a question of type " + currentQuestion.type + " and it must have exactly one subpath.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(string _, InterviewQuestion nextQuestion) = currentQuestion.paths.First();
|
||||||
|
await HandleAnswer(answer, nextQuestion, interviewRoot, currentQuestion, interaction.Channel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!int.TryParse(componentID, out int pathIndex))
|
||||||
|
{
|
||||||
|
Logger.Error("Invalid interview button/selector index: " + componentID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pathIndex >= currentQuestion.paths.Count || pathIndex < 0)
|
||||||
|
{
|
||||||
|
Logger.Error("Invalid interview button/selector index: " + pathIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(string questionString, InterviewQuestion nextQuestion) = currentQuestion.paths.ElementAt(pathIndex);
|
||||||
|
await HandleAnswer(questionString, nextQuestion, interviewRoot, currentQuestion, interaction.Channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pathIndex >= currentQuestion.paths.Count || pathIndex < 0)
|
|
||||||
{
|
|
||||||
Logger.Error("Invalid interview button/selector index: " + pathIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(string questionString, InterviewQuestion nextQuestion) = currentQuestion.paths.ElementAt(pathIndex);
|
|
||||||
|
|
||||||
await HandleAnswer(questionString, nextQuestion, interviewRoot, currentQuestion, interaction.Channel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task ProcessResponseMessage(DiscordMessage answerMessage)
|
public static async Task ProcessResponseMessage(DiscordMessage answerMessage)
|
||||||
|
@ -409,7 +466,7 @@ public static class Interviewer
|
||||||
// Skip to the first matching path.
|
// Skip to the first matching path.
|
||||||
if (!Regex.IsMatch(answerMessage.Content, questionString)) continue;
|
if (!Regex.IsMatch(answerMessage.Content, questionString)) continue;
|
||||||
|
|
||||||
await HandleAnswer(questionString, nextQuestion, interviewRoot, currentQuestion, answerMessage.Channel, answerMessage);
|
await HandleAnswer(answerMessage.Content, nextQuestion, interviewRoot, currentQuestion, answerMessage.Channel, answerMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,32 +479,26 @@ public static class Interviewer
|
||||||
currentQuestion.AddRelatedMessageIDs(errorMessage.Id);
|
currentQuestion.AddRelatedMessageIDs(errorMessage.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task HandleAnswer(string questionString,
|
private static async Task HandleAnswer(string answer,
|
||||||
InterviewQuestion nextQuestion,
|
InterviewQuestion nextQuestion,
|
||||||
InterviewQuestion interviewRoot,
|
InterviewQuestion interviewRoot,
|
||||||
InterviewQuestion previousQuestion,
|
InterviewQuestion previousQuestion,
|
||||||
DiscordChannel channel,
|
DiscordChannel channel,
|
||||||
DiscordMessage answerMessage = null)
|
DiscordMessage answerMessage = null)
|
||||||
{
|
{
|
||||||
|
// The error message type should not alter anything about the interview
|
||||||
if (nextQuestion.type != QuestionType.ERROR)
|
if (nextQuestion.type != QuestionType.ERROR)
|
||||||
{
|
{
|
||||||
// The answer was provided using a button or selector
|
previousQuestion.answer = answer;
|
||||||
if (answerMessage == null)
|
if (answerMessage == null)
|
||||||
{
|
{
|
||||||
previousQuestion.answer = questionString;
|
// The answer was provided using a button or selector
|
||||||
previousQuestion.answerID = 0;
|
previousQuestion.answerID = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
previousQuestion.answer = answerMessage.Content;
|
|
||||||
previousQuestion.answerID = answerMessage.Id;
|
previousQuestion.answerID = answerMessage.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any other paths from the previous question.
|
|
||||||
previousQuestion.paths = new Dictionary<string, InterviewQuestion>
|
|
||||||
{
|
|
||||||
{ questionString, nextQuestion }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create next question, or finish the interview.
|
// Create next question, or finish the interview.
|
||||||
|
@ -456,6 +507,10 @@ public static class Interviewer
|
||||||
case QuestionType.TEXT_INPUT:
|
case QuestionType.TEXT_INPUT:
|
||||||
case QuestionType.BUTTONS:
|
case QuestionType.BUTTONS:
|
||||||
case QuestionType.TEXT_SELECTOR:
|
case QuestionType.TEXT_SELECTOR:
|
||||||
|
case QuestionType.ROLE_SELECTOR:
|
||||||
|
case QuestionType.USER_SELECTOR:
|
||||||
|
case QuestionType.CHANNEL_SELECTOR:
|
||||||
|
case QuestionType.MENTIONABLE_SELECTOR:
|
||||||
await CreateQuestion(channel, nextQuestion);
|
await CreateQuestion(channel, nextQuestion);
|
||||||
Database.SaveInterview(channel.Id, interviewRoot);
|
Database.SaveInterview(channel.Id, interviewRoot);
|
||||||
break;
|
break;
|
||||||
|
@ -485,7 +540,6 @@ public static class Interviewer
|
||||||
ReloadInterviews();
|
ReloadInterviews();
|
||||||
return;
|
return;
|
||||||
case QuestionType.END_WITHOUT_SUMMARY:
|
case QuestionType.END_WITHOUT_SUMMARY:
|
||||||
// TODO: Add command to restart interview.
|
|
||||||
await channel.SendMessageAsync(new DiscordEmbedBuilder()
|
await channel.SendMessageAsync(new DiscordEmbedBuilder()
|
||||||
{
|
{
|
||||||
Color = Utilities.StringToColor(nextQuestion.color),
|
Color = Utilities.StringToColor(nextQuestion.color),
|
||||||
|
@ -583,11 +637,23 @@ 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("supportchild_interviewselector " + selectionBoxes, "Select an option...", categoryOptions, false, 0, 1));
|
selectionComponents.Add(new DiscordSelectComponent("supportchild_interviewselector " + selectionBoxes, "Select an option...", categoryOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
msgBuilder.AddComponents(selectionComponents);
|
msgBuilder.AddComponents(selectionComponents);
|
||||||
break;
|
break;
|
||||||
|
case QuestionType.ROLE_SELECTOR:
|
||||||
|
msgBuilder.AddComponents(new DiscordRoleSelectComponent("supportchild_interviewroleselector", "Select a role..."));
|
||||||
|
break;
|
||||||
|
case QuestionType.USER_SELECTOR:
|
||||||
|
msgBuilder.AddComponents(new DiscordUserSelectComponent("supportchild_interviewuserselector", "Select a user..."));
|
||||||
|
break;
|
||||||
|
case QuestionType.CHANNEL_SELECTOR:
|
||||||
|
msgBuilder.AddComponents(new DiscordChannelSelectComponent("supportchild_interviewchannelselector", "Select a channel..."));
|
||||||
|
break;
|
||||||
|
case QuestionType.MENTIONABLE_SELECTOR:
|
||||||
|
msgBuilder.AddComponents(new DiscordMentionableSelectComponent("supportchild_interviewmentionableselector", "Select a mentionable..."));
|
||||||
|
break;
|
||||||
case QuestionType.TEXT_INPUT:
|
case QuestionType.TEXT_INPUT:
|
||||||
embed.WithFooter("Reply to this message with your answer. You cannot include images or files.");
|
embed.WithFooter("Reply to this message with your answer. You cannot include images or files.");
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue