Added support for END_WITHOUT_SUMMARY and ERROR question types
This commit is contained in:
parent
ebd279da45
commit
91991c10f2
2 changed files with 110 additions and 60 deletions
|
@ -862,7 +862,7 @@ public static class Database
|
|||
c.Open();
|
||||
using MySqlCommand cmd = new MySqlCommand(query, c);
|
||||
cmd.Parameters.AddWithValue("@channel_id", channelID);
|
||||
cmd.Parameters.AddWithValue("@interview", JsonConvert.SerializeObject(interview));
|
||||
cmd.Parameters.AddWithValue("@interview", JsonConvert.SerializeObject(interview, Formatting.Indented));
|
||||
cmd.Prepare();
|
||||
return cmd.ExecuteNonQuery() > 0;
|
||||
}
|
||||
|
|
168
Interviewer.cs
168
Interviewer.cs
|
@ -2,6 +2,7 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -17,8 +18,8 @@ public static class Interviewer
|
|||
public enum QuestionType
|
||||
{
|
||||
ERROR,
|
||||
CANCEL,
|
||||
DONE,
|
||||
END_WITH_SUMMARY,
|
||||
END_WITHOUT_SUMMARY,
|
||||
BUTTONS,
|
||||
TEXT_SELECTOR,
|
||||
TEXT_INPUT
|
||||
|
@ -53,7 +54,9 @@ public static class Interviewer
|
|||
[JsonProperty("paths")]
|
||||
public Dictionary<string, InterviewQuestion> paths;
|
||||
|
||||
// The following parameters are populated by the bot, not the json template.
|
||||
// ////////////////////////////////////////////////////////////////////////////
|
||||
// The following parameters are populated by the bot, not the json template. //
|
||||
// ////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The ID of this message where the bot asked this question.
|
||||
[JsonProperty("message-id")]
|
||||
|
@ -67,6 +70,10 @@ public static class Interviewer
|
|||
[JsonProperty("answer-id")]
|
||||
public ulong answerID;
|
||||
|
||||
// Any extra messages generated by the bot that should be removed when the interview ends.
|
||||
[JsonProperty("related-message-ids")]
|
||||
public List<ulong> relatedMessageIDs;
|
||||
|
||||
public bool TryGetCurrentQuestion(out InterviewQuestion question)
|
||||
{
|
||||
// This object has not been initialized, we have checked too deep.
|
||||
|
@ -117,12 +124,29 @@ public static class Interviewer
|
|||
messageIDs.Add(answerID);
|
||||
}
|
||||
|
||||
if (relatedMessageIDs != null)
|
||||
{
|
||||
messageIDs.AddRange(relatedMessageIDs);
|
||||
}
|
||||
|
||||
// This will always contain exactly one or zero children.
|
||||
foreach (KeyValuePair<string, InterviewQuestion> path in paths)
|
||||
{
|
||||
path.Value.GetMessageIDs(ref messageIDs);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddRelatedMessageIDs(params ulong[] messageIDs)
|
||||
{
|
||||
if (relatedMessageIDs == null)
|
||||
{
|
||||
relatedMessageIDs = messageIDs.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
relatedMessageIDs.AddRange(messageIDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This class is identical to the one above and just exists as a hack to get JSON validation when
|
||||
|
@ -178,11 +202,6 @@ public static class Interviewer
|
|||
}
|
||||
}
|
||||
|
||||
public static bool IsInterviewActive(ulong channelID)
|
||||
{
|
||||
return activeInterviews.ContainsKey(channelID);
|
||||
}
|
||||
|
||||
public static async Task ProcessButtonOrSelectorResponse(DiscordInteraction interaction)
|
||||
{
|
||||
// TODO: Add error responses.
|
||||
|
@ -192,7 +211,7 @@ public static class Interviewer
|
|||
return;
|
||||
}
|
||||
|
||||
// The user selected nothing.
|
||||
// Return if the user didn't select anything
|
||||
if (interaction.Data.ComponentType == DiscordComponentType.StringSelect && interaction.Data.Values.Length == 0)
|
||||
{
|
||||
return;
|
||||
|
@ -200,25 +219,25 @@ public static class Interviewer
|
|||
|
||||
await interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredMessageUpdate);
|
||||
|
||||
// Could not find active interview.
|
||||
// Return if there is no active interview in this channel
|
||||
if (!activeInterviews.TryGetValue(interaction.Channel.Id, out InterviewQuestion interviewRoot))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Could not find message id in interview.
|
||||
// Return if the current question cannot be found in the interview.
|
||||
if (!interviewRoot.TryGetCurrentQuestion(out InterviewQuestion currentQuestion))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// This button is for an older question.
|
||||
// Check if this button/selector is for an older question.
|
||||
if (interaction.Message.Id != currentQuestion.messageID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the response index from the button.
|
||||
// Parse the response index from the button/selector.
|
||||
string componentID = "";
|
||||
|
||||
switch (interaction.Data.ComponentType)
|
||||
|
@ -249,10 +268,6 @@ public static class Interviewer
|
|||
(string questionString, InterviewQuestion nextQuestion) = currentQuestion.paths.ElementAt(pathIndex);
|
||||
|
||||
await HandleAnswer(questionString, nextQuestion, interviewRoot, currentQuestion, interaction.Channel);
|
||||
|
||||
// Edit message to remove buttons/selectors.
|
||||
// TODO: Add footer with answer.
|
||||
await interaction.EditOriginalResponseAsync(new DiscordWebhookBuilder().AddEmbed(interaction.Message.Embeds[0]));
|
||||
}
|
||||
|
||||
public static async Task ProcessResponseMessage(DiscordMessage message)
|
||||
|
@ -304,25 +319,28 @@ public static class Interviewer
|
|||
InterviewQuestion interviewRoot,
|
||||
InterviewQuestion previousQuestion,
|
||||
DiscordChannel channel,
|
||||
DiscordMessage message = null)
|
||||
DiscordMessage answerMessage = null)
|
||||
{
|
||||
// The answer was provided using a button or selector
|
||||
if (message == null)
|
||||
if (nextQuestion.type != QuestionType.ERROR)
|
||||
{
|
||||
previousQuestion.answer = questionString;
|
||||
previousQuestion.answerID = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
previousQuestion.answer = message.Content;
|
||||
previousQuestion.answerID = message.Id;
|
||||
}
|
||||
// The answer was provided using a button or selector
|
||||
if (answerMessage == null)
|
||||
{
|
||||
previousQuestion.answer = questionString;
|
||||
previousQuestion.answerID = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
previousQuestion.answer = answerMessage.Content;
|
||||
previousQuestion.answerID = answerMessage.Id;
|
||||
}
|
||||
|
||||
// Remove any other paths from the previous question.
|
||||
previousQuestion.paths = new Dictionary<string, InterviewQuestion>
|
||||
{
|
||||
{ questionString, nextQuestion }
|
||||
};
|
||||
// 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)
|
||||
|
@ -333,14 +351,14 @@ public static class Interviewer
|
|||
await CreateQuestion(channel, nextQuestion);
|
||||
Database.SaveInterview(channel.Id, interviewRoot);
|
||||
break;
|
||||
case QuestionType.DONE:
|
||||
case QuestionType.END_WITH_SUMMARY:
|
||||
OrderedDictionary summaryFields = new OrderedDictionary();
|
||||
interviewRoot.GetSummary(ref summaryFields);
|
||||
|
||||
DiscordEmbedBuilder embed = new DiscordEmbedBuilder()
|
||||
{
|
||||
Color = Utilities.StringToColor(nextQuestion.color),
|
||||
Title = "Summary:",
|
||||
Title = "Summary:", // TODO: Set title
|
||||
Description = nextQuestion.message,
|
||||
};
|
||||
|
||||
|
@ -351,41 +369,73 @@ public static class Interviewer
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await DeletePreviousMessages(interviewRoot, channel);
|
||||
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.
|
||||
case QuestionType.END_WITHOUT_SUMMARY:
|
||||
// TODO: Add command to restart interview.
|
||||
await channel.SendMessageAsync(new DiscordEmbedBuilder()
|
||||
{
|
||||
Color = Utilities.StringToColor(nextQuestion.color),
|
||||
Description = nextQuestion.message
|
||||
});
|
||||
|
||||
await DeletePreviousMessages(interviewRoot, channel);
|
||||
if (!Database.TryDeleteInterview(channel.Id))
|
||||
{
|
||||
Logger.Error("Could not delete interview from database. Channel ID: " + channel.Id);
|
||||
}
|
||||
Reload();
|
||||
break;
|
||||
case QuestionType.ERROR:
|
||||
default:
|
||||
// TODO: Post error message.
|
||||
if (answerMessage == null)
|
||||
{
|
||||
DiscordMessage errorMessage = await channel.SendMessageAsync(new DiscordEmbedBuilder()
|
||||
{
|
||||
Color = Utilities.StringToColor(nextQuestion.color),
|
||||
Description = nextQuestion.message
|
||||
});
|
||||
previousQuestion.AddRelatedMessageIDs(errorMessage.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
DiscordMessageBuilder errorMessageBuilder = new DiscordMessageBuilder()
|
||||
.AddEmbed(new DiscordEmbedBuilder()
|
||||
{
|
||||
Color = Utilities.StringToColor(nextQuestion.color),
|
||||
Description = nextQuestion.message
|
||||
}).WithReply(answerMessage.Id);
|
||||
DiscordMessage errorMessage = await answerMessage.RespondAsync(errorMessageBuilder);
|
||||
previousQuestion.AddRelatedMessageIDs(errorMessage.Id, answerMessage.Id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task DeletePreviousMessages(InterviewQuestion interviewRoot, DiscordChannel channel)
|
||||
{
|
||||
List<ulong> previousMessages = new List<ulong> { };
|
||||
interviewRoot.GetMessageIDs(ref previousMessages);
|
||||
|
||||
foreach (ulong previousMessageID in previousMessages)
|
||||
{
|
||||
try
|
||||
{
|
||||
DiscordMessage previousMessage = await channel.GetMessageAsync(previousMessageID);
|
||||
await channel.DeleteMessageAsync(previousMessage, "Deleting old interview message.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warn("Failed to delete old interview message: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CreateQuestion(DiscordChannel channel, InterviewQuestion question)
|
||||
{
|
||||
DiscordMessageBuilder msgBuilder = new DiscordMessageBuilder();
|
||||
|
@ -428,8 +478,8 @@ public static class Interviewer
|
|||
case QuestionType.TEXT_INPUT:
|
||||
embed.WithFooter("Reply to this message with your answer. You cannot include images or files.");
|
||||
break;
|
||||
case QuestionType.DONE:
|
||||
case QuestionType.CANCEL:
|
||||
case QuestionType.END_WITH_SUMMARY:
|
||||
case QuestionType.END_WITHOUT_SUMMARY:
|
||||
case QuestionType.ERROR:
|
||||
default:
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue