using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Net.Http; using System.Text; using System.Threading.Tasks; using DSharpPlus.Commands; using DSharpPlus.Commands.ContextChecks; using DSharpPlus.Commands.Processors.SlashCommands; using DSharpPlus.Entities; using DSharpPlus.Exceptions; using Newtonsoft.Json; using Newtonsoft.Json.Schema; using SupportChild.Interviews; namespace SupportChild.Commands; [Command("interviewtemplate")] [Description("Interview template management.")] public class InterviewTemplateCommands { private static readonly string jsonSchema = Utilities.ReadManifestData("Interviews.interview_template.schema.json"); [RequireGuild] [Command("get")] [Description("Provides a copy of the interview template for a category which you can edit and then reupload.")] public async Task Get(SlashCommandContext command, [Parameter("category")][Description("The category to get the template for.")] DiscordChannel category) { if (!category?.IsCategory ?? true) { await command.RespondAsync(new DiscordEmbedBuilder { Color = DiscordColor.Red, Description = "That channel is not a category." }, true); return; } string interviewTemplateJSON = Database.GetInterviewTemplateJSON(category.Id); if (interviewTemplateJSON == null) { string defaultTemplate = "{\n" + " \"category-id\": " + category.Id + ",\n" + " \"interview\":\n" + " {\n" + " \"message\": \"\",\n" + " \"message-type\": \"\",\n" + " \"color\": \"\",\n" + " \"steps\":\n" + " {\n" + " \n" + " }\n" + " }\n" + "}"; MemoryStream stream = new(Encoding.UTF8.GetBytes(defaultTemplate)); DiscordInteractionResponseBuilder response = new DiscordInteractionResponseBuilder().AddEmbed(new DiscordEmbedBuilder { Color = DiscordColor.Green, Description = "No interview template found for this category. A default template has been generated." }).AddFile("interview-template-" + category.Id + ".json", stream).AsEphemeral(); await command.RespondAsync(response); } else { MemoryStream stream = new(Encoding.UTF8.GetBytes(interviewTemplateJSON)); await command.RespondAsync(new DiscordInteractionResponseBuilder().AddFile("interview-template-" + category.Id + ".json", stream).AsEphemeral()); } } [RequireGuild] [Command("set")] [Description("Uploads an interview template file.")] public async Task Set(SlashCommandContext command, [Parameter("file")][Description("The file containing the template.")] DiscordAttachment file) { await command.DeferResponseAsync(true); if (!file.MediaType?.Contains("application/json") ?? false) { await command.RespondAsync(new DiscordEmbedBuilder { Color = DiscordColor.Red, Description = "The uploaded file is not a JSON file according to Discord." }, true); return; } Stream stream = await new HttpClient().GetStreamAsync(file.Url); try { JSchemaValidatingReader validatingReader = new(new JsonTextReader(new StreamReader(stream))); validatingReader.Schema = JSchema.Parse(jsonSchema); // The schema seems to throw an additional error with incorrect information if an invalid parameter is included // in the template. Throw here in order to only show the first correct error to the user, also skips unnecessary validation further down. validatingReader.ValidationEventHandler += (_, a) => throw new JsonException(a.Message); JsonSerializer serializer = new(); Template template = serializer.Deserialize