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();
|
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", channelID);
|
||||||
cmd.Parameters.AddWithValue("@interview", JsonConvert.SerializeObject(interview));
|
cmd.Parameters.AddWithValue("@interview", JsonConvert.SerializeObject(interview, Formatting.Indented));
|
||||||
cmd.Prepare();
|
cmd.Prepare();
|
||||||
return cmd.ExecuteNonQuery() > 0;
|
return cmd.ExecuteNonQuery() > 0;
|
||||||
}
|
}
|
||||||
|
|
168
Interviewer.cs
168
Interviewer.cs
|
@ -2,6 +2,7 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -17,8 +18,8 @@ public static class Interviewer
|
||||||
public enum QuestionType
|
public enum QuestionType
|
||||||
{
|
{
|
||||||
ERROR,
|
ERROR,
|
||||||
CANCEL,
|
END_WITH_SUMMARY,
|
||||||
DONE,
|
END_WITHOUT_SUMMARY,
|
||||||
BUTTONS,
|
BUTTONS,
|
||||||
TEXT_SELECTOR,
|
TEXT_SELECTOR,
|
||||||
TEXT_INPUT
|
TEXT_INPUT
|
||||||
|
@ -53,7 +54,9 @@ public static class Interviewer
|
||||||
[JsonProperty("paths")]
|
[JsonProperty("paths")]
|
||||||
public Dictionary<string, InterviewQuestion> 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.
|
// The ID of this message where the bot asked this question.
|
||||||
[JsonProperty("message-id")]
|
[JsonProperty("message-id")]
|
||||||
|
@ -67,6 +70,10 @@ public static class Interviewer
|
||||||
[JsonProperty("answer-id")]
|
[JsonProperty("answer-id")]
|
||||||
public ulong answerID;
|
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)
|
public bool TryGetCurrentQuestion(out InterviewQuestion question)
|
||||||
{
|
{
|
||||||
// This object has not been initialized, we have checked too deep.
|
// This object has not been initialized, we have checked too deep.
|
||||||
|
@ -117,12 +124,29 @@ public static class Interviewer
|
||||||
messageIDs.Add(answerID);
|
messageIDs.Add(answerID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (relatedMessageIDs != null)
|
||||||
|
{
|
||||||
|
messageIDs.AddRange(relatedMessageIDs);
|
||||||
|
}
|
||||||
|
|
||||||
// This will always contain exactly one or zero children.
|
// This will always contain exactly one or zero children.
|
||||||
foreach (KeyValuePair<string, InterviewQuestion> path in paths)
|
foreach (KeyValuePair<string, InterviewQuestion> path in paths)
|
||||||
{
|
{
|
||||||
path.Value.GetMessageIDs(ref messageIDs);
|
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
|
// 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)
|
public static async Task ProcessButtonOrSelectorResponse(DiscordInteraction interaction)
|
||||||
{
|
{
|
||||||
// TODO: Add error responses.
|
// TODO: Add error responses.
|
||||||
|
@ -192,7 +211,7 @@ public static class Interviewer
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The user selected nothing.
|
// Return if the user didn't select anything
|
||||||
if (interaction.Data.ComponentType == DiscordComponentType.StringSelect && interaction.Data.Values.Length == 0)
|
if (interaction.Data.ComponentType == DiscordComponentType.StringSelect && interaction.Data.Values.Length == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -200,25 +219,25 @@ public static class Interviewer
|
||||||
|
|
||||||
await interaction.CreateResponseAsync(DiscordInteractionResponseType.DeferredMessageUpdate);
|
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))
|
if (!activeInterviews.TryGetValue(interaction.Channel.Id, out InterviewQuestion interviewRoot))
|
||||||
{
|
{
|
||||||
return;
|
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))
|
if (!interviewRoot.TryGetCurrentQuestion(out InterviewQuestion currentQuestion))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This button is for an older question.
|
// Check if this button/selector is for an older question.
|
||||||
if (interaction.Message.Id != currentQuestion.messageID)
|
if (interaction.Message.Id != currentQuestion.messageID)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the response index from the button.
|
// Parse the response index from the button/selector.
|
||||||
string componentID = "";
|
string componentID = "";
|
||||||
|
|
||||||
switch (interaction.Data.ComponentType)
|
switch (interaction.Data.ComponentType)
|
||||||
|
@ -249,10 +268,6 @@ public static class Interviewer
|
||||||
(string questionString, InterviewQuestion nextQuestion) = currentQuestion.paths.ElementAt(pathIndex);
|
(string questionString, InterviewQuestion nextQuestion) = currentQuestion.paths.ElementAt(pathIndex);
|
||||||
|
|
||||||
await HandleAnswer(questionString, nextQuestion, interviewRoot, currentQuestion, interaction.Channel);
|
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)
|
public static async Task ProcessResponseMessage(DiscordMessage message)
|
||||||
|
@ -304,25 +319,28 @@ public static class Interviewer
|
||||||
InterviewQuestion interviewRoot,
|
InterviewQuestion interviewRoot,
|
||||||
InterviewQuestion previousQuestion,
|
InterviewQuestion previousQuestion,
|
||||||
DiscordChannel channel,
|
DiscordChannel channel,
|
||||||
DiscordMessage message = null)
|
DiscordMessage answerMessage = null)
|
||||||
{
|
{
|
||||||
// The answer was provided using a button or selector
|
if (nextQuestion.type != QuestionType.ERROR)
|
||||||
if (message == null)
|
|
||||||
{
|
{
|
||||||
previousQuestion.answer = questionString;
|
// The answer was provided using a button or selector
|
||||||
previousQuestion.answerID = 0;
|
if (answerMessage == null)
|
||||||
}
|
{
|
||||||
else
|
previousQuestion.answer = questionString;
|
||||||
{
|
previousQuestion.answerID = 0;
|
||||||
previousQuestion.answer = message.Content;
|
}
|
||||||
previousQuestion.answerID = message.Id;
|
else
|
||||||
}
|
{
|
||||||
|
previousQuestion.answer = answerMessage.Content;
|
||||||
|
previousQuestion.answerID = answerMessage.Id;
|
||||||
|
}
|
||||||
|
|
||||||
// Remove any other paths from the previous question.
|
// Remove any other paths from the previous question.
|
||||||
previousQuestion.paths = new Dictionary<string, InterviewQuestion>
|
previousQuestion.paths = new Dictionary<string, InterviewQuestion>
|
||||||
{
|
{
|
||||||
{ questionString, nextQuestion }
|
{ questionString, nextQuestion }
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Create next question, or finish the interview.
|
// Create next question, or finish the interview.
|
||||||
switch (nextQuestion.type)
|
switch (nextQuestion.type)
|
||||||
|
@ -333,14 +351,14 @@ public static class Interviewer
|
||||||
await CreateQuestion(channel, nextQuestion);
|
await CreateQuestion(channel, nextQuestion);
|
||||||
Database.SaveInterview(channel.Id, interviewRoot);
|
Database.SaveInterview(channel.Id, interviewRoot);
|
||||||
break;
|
break;
|
||||||
case QuestionType.DONE:
|
case QuestionType.END_WITH_SUMMARY:
|
||||||
OrderedDictionary summaryFields = new OrderedDictionary();
|
OrderedDictionary summaryFields = new OrderedDictionary();
|
||||||
interviewRoot.GetSummary(ref summaryFields);
|
interviewRoot.GetSummary(ref summaryFields);
|
||||||
|
|
||||||
DiscordEmbedBuilder embed = new DiscordEmbedBuilder()
|
DiscordEmbedBuilder embed = new DiscordEmbedBuilder()
|
||||||
{
|
{
|
||||||
Color = Utilities.StringToColor(nextQuestion.color),
|
Color = Utilities.StringToColor(nextQuestion.color),
|
||||||
Title = "Summary:",
|
Title = "Summary:", // TODO: Set title
|
||||||
Description = nextQuestion.message,
|
Description = nextQuestion.message,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -351,41 +369,73 @@ public static class Interviewer
|
||||||
|
|
||||||
await channel.SendMessageAsync(embed);
|
await channel.SendMessageAsync(embed);
|
||||||
|
|
||||||
List<ulong> previousMessages = new List<ulong> { };
|
await DeletePreviousMessages(interviewRoot, channel);
|
||||||
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))
|
if (!Database.TryDeleteInterview(channel.Id))
|
||||||
{
|
{
|
||||||
Logger.Error("Could not delete interview from database. Channel ID: " + channel.Id);
|
Logger.Error("Could not delete interview from database. Channel ID: " + channel.Id);
|
||||||
}
|
}
|
||||||
Reload();
|
Reload();
|
||||||
return;
|
return;
|
||||||
case QuestionType.CANCEL:
|
case QuestionType.END_WITHOUT_SUMMARY:
|
||||||
// TODO: Post fail message.
|
// TODO: Add command to restart interview.
|
||||||
// TODO: Remove active 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;
|
break;
|
||||||
case QuestionType.ERROR:
|
case QuestionType.ERROR:
|
||||||
default:
|
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;
|
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)
|
private static async Task CreateQuestion(DiscordChannel channel, InterviewQuestion question)
|
||||||
{
|
{
|
||||||
DiscordMessageBuilder msgBuilder = new DiscordMessageBuilder();
|
DiscordMessageBuilder msgBuilder = new DiscordMessageBuilder();
|
||||||
|
@ -428,8 +478,8 @@ public static class Interviewer
|
||||||
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;
|
||||||
case QuestionType.DONE:
|
case QuestionType.END_WITH_SUMMARY:
|
||||||
case QuestionType.CANCEL:
|
case QuestionType.END_WITHOUT_SUMMARY:
|
||||||
case QuestionType.ERROR:
|
case QuestionType.ERROR:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in a new issue