From 7f08d72ef8d528d718adcf1789dc809237ef0ef2 Mon Sep 17 00:00:00 2001 From: Jonathan Riedel Date: Fri, 12 Apr 2024 02:25:32 +0200 Subject: [PATCH] Refactored a bunch of things Added Notes Module + Data Structure Added Config Module + Data Structure Added Role Buttons Module + Data Structure --- SMSModLogOutputAnalyzer/Data/Config.cs | 31 +++++ .../Flowchart.FlowchartConnection.cs | 0 .../{ => Data}/Flowchart.FlowchartNode.cs | 0 .../{ => Data}/Flowchart.cs | 0 .../Data/NotesDataObject.Note.cs | 24 ++++ .../Data/NotesDataObject.UserNotes.cs | 14 ++ .../Data/NotesDataObject.cs | 42 ++++++ .../Data/RoleButtonsDataObject.RoleButton.cs | 14 ++ .../Data/RoleButtonsDataObject.cs | 27 ++++ .../AskForLogFile/AskForLogFileModule.cs | 42 ++++++ .../Config/ConfigModule.Commands.Setters.cs | 25 ++++ .../Modules/Config/ConfigModule.Commands.cs | 12 ++ .../Modules/Config/ConfigModule.cs | 14 ++ .../LogAnalyze/LogAnalyzeModule.FoundLine.cs} | 4 +- .../LogAnalyze/LogAnalyzeModule.cs} | 72 +--------- .../NotesModule.Commands.AddNoteModal.cs | 23 +++ .../Modules/Notes/NotesModule.Commands.cs | 108 +++++++++++++++ .../Modules/Notes/NotesModule.cs | 15 ++ .../RoleButtons/RoleButtonModule.Commands.cs | 131 ++++++++++++++++++ .../Modules/RoleButtons/RoleButtonModule.cs | 15 ++ .../Troubleshooting/TroubleshootingModule.cs | 37 +++++ SMSModLogOutputAnalyzer/Program.cs | 59 ++++++-- .../SMSModLogOutputAnalyzer.csproj | 12 ++ 23 files changed, 638 insertions(+), 83 deletions(-) create mode 100644 SMSModLogOutputAnalyzer/Data/Config.cs rename SMSModLogOutputAnalyzer/{ => Data}/Flowchart.FlowchartConnection.cs (100%) rename SMSModLogOutputAnalyzer/{ => Data}/Flowchart.FlowchartNode.cs (100%) rename SMSModLogOutputAnalyzer/{ => Data}/Flowchart.cs (100%) create mode 100644 SMSModLogOutputAnalyzer/Data/NotesDataObject.Note.cs create mode 100644 SMSModLogOutputAnalyzer/Data/NotesDataObject.UserNotes.cs create mode 100644 SMSModLogOutputAnalyzer/Data/NotesDataObject.cs create mode 100644 SMSModLogOutputAnalyzer/Data/RoleButtonsDataObject.RoleButton.cs create mode 100644 SMSModLogOutputAnalyzer/Data/RoleButtonsDataObject.cs create mode 100644 SMSModLogOutputAnalyzer/Modules/AskForLogFile/AskForLogFileModule.cs create mode 100644 SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.Commands.Setters.cs create mode 100644 SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.Commands.cs create mode 100644 SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.cs rename SMSModLogOutputAnalyzer/{CommandModule.FoundLine.cs => Modules/LogAnalyze/LogAnalyzeModule.FoundLine.cs} (76%) rename SMSModLogOutputAnalyzer/{CommandModule.cs => Modules/LogAnalyze/LogAnalyzeModule.cs} (67%) create mode 100644 SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.Commands.AddNoteModal.cs create mode 100644 SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.Commands.cs create mode 100644 SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.cs create mode 100644 SMSModLogOutputAnalyzer/Modules/RoleButtons/RoleButtonModule.Commands.cs create mode 100644 SMSModLogOutputAnalyzer/Modules/RoleButtons/RoleButtonModule.cs create mode 100644 SMSModLogOutputAnalyzer/Modules/Troubleshooting/TroubleshootingModule.cs diff --git a/SMSModLogOutputAnalyzer/Data/Config.cs b/SMSModLogOutputAnalyzer/Data/Config.cs new file mode 100644 index 0000000..6c00363 --- /dev/null +++ b/SMSModLogOutputAnalyzer/Data/Config.cs @@ -0,0 +1,31 @@ +using Discord.Interactions; +using Discord; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; +using Orpticon.Wrappers; +using SMSModLogOutputAnalyzer.Modules; + +namespace SMSModLogOutputAnalyzer.Data +{ + public partial class Config + { + public static ITextChannel NotesLogChannel { get => (ITextChannel)Program.Client.GetChannel(Instance.notesLogChannel); set => Instance.notesLogChannel = value.Id; } + [XmlAttribute] public ulong notesLogChannel { get; set; } + + + + public static void Load() + { + Instance = SerializerXML.DeserializeFromFileOrDefault("Config.xml"); + } + public static void Save() + { + SerializerXML.SerializeToFile(Instance, "Config.xml"); + } + public static Config Instance { get; set; } + } +} diff --git a/SMSModLogOutputAnalyzer/Flowchart.FlowchartConnection.cs b/SMSModLogOutputAnalyzer/Data/Flowchart.FlowchartConnection.cs similarity index 100% rename from SMSModLogOutputAnalyzer/Flowchart.FlowchartConnection.cs rename to SMSModLogOutputAnalyzer/Data/Flowchart.FlowchartConnection.cs diff --git a/SMSModLogOutputAnalyzer/Flowchart.FlowchartNode.cs b/SMSModLogOutputAnalyzer/Data/Flowchart.FlowchartNode.cs similarity index 100% rename from SMSModLogOutputAnalyzer/Flowchart.FlowchartNode.cs rename to SMSModLogOutputAnalyzer/Data/Flowchart.FlowchartNode.cs diff --git a/SMSModLogOutputAnalyzer/Flowchart.cs b/SMSModLogOutputAnalyzer/Data/Flowchart.cs similarity index 100% rename from SMSModLogOutputAnalyzer/Flowchart.cs rename to SMSModLogOutputAnalyzer/Data/Flowchart.cs diff --git a/SMSModLogOutputAnalyzer/Data/NotesDataObject.Note.cs b/SMSModLogOutputAnalyzer/Data/NotesDataObject.Note.cs new file mode 100644 index 0000000..0b70f40 --- /dev/null +++ b/SMSModLogOutputAnalyzer/Data/NotesDataObject.Note.cs @@ -0,0 +1,24 @@ +using Discord; +using System.Reactive; +using System.Xml.Serialization; +using static SMSModLogOutputAnalyzer.Data.NotesDataObject; + +namespace SMSModLogOutputAnalyzer.Data +{ + public partial class NotesDataObject + { + public class Note + { + [XmlAttribute] public string Text { get; set; } + [XmlAttribute] public ulong CreatorID { get; set; } + [XmlAttribute] public DateTime CreationTime { get; set; } + [XmlAttribute] public int Id { get; set; } + public EmbedFieldBuilder ToField() + { + var unix = ((DateTimeOffset)CreationTime).ToUnixTimeSeconds(); + // "Note " + Id, "`" + Text + "`\nCreated by <@" + CreatorID + "> at " + return new EmbedFieldBuilder().WithName(Text).WithValue("ID: `" + Id + "`\nCreated by <@" + CreatorID + "> at "); + } + } + } +} diff --git a/SMSModLogOutputAnalyzer/Data/NotesDataObject.UserNotes.cs b/SMSModLogOutputAnalyzer/Data/NotesDataObject.UserNotes.cs new file mode 100644 index 0000000..83cffa5 --- /dev/null +++ b/SMSModLogOutputAnalyzer/Data/NotesDataObject.UserNotes.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace SMSModLogOutputAnalyzer.Data +{ + public partial class NotesDataObject + { + public class UserNotes + { + [XmlAttribute] public ulong Id { get; set; } + [XmlAttribute] public int Counter { get; set; } = 0; + [XmlElement("Note")] public List Notes { get; set; } = new(); + } + } +} diff --git a/SMSModLogOutputAnalyzer/Data/NotesDataObject.cs b/SMSModLogOutputAnalyzer/Data/NotesDataObject.cs new file mode 100644 index 0000000..d88cb2b --- /dev/null +++ b/SMSModLogOutputAnalyzer/Data/NotesDataObject.cs @@ -0,0 +1,42 @@ +using Discord; +using Orpticon.Custom; +using Orpticon.Wrappers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace SMSModLogOutputAnalyzer.Data +{ + public partial class NotesDataObject + { + [XmlElement("Notes")] public List List { get; set; } = new(); + [XmlIgnore] public UserNotes this[IUser user] => this[user.Id]; + [XmlIgnore] + public UserNotes this[ulong id] + { + get + { + var notes = List.Find(x => x.Id == id); + if(notes == null) + { + notes = new() { Id = id }; + List.Add(notes); + } + return notes; + } + } + + public void Save() + { + SerializerXML.SerializeToFile(this, "NotesData.xml"); + } + public static NotesDataObject Load() + { + var obj = SerializerXML.DeserializeFromFileOrDefault("NotesData.xml"); + return obj; + } + } +} diff --git a/SMSModLogOutputAnalyzer/Data/RoleButtonsDataObject.RoleButton.cs b/SMSModLogOutputAnalyzer/Data/RoleButtonsDataObject.RoleButton.cs new file mode 100644 index 0000000..c986ff7 --- /dev/null +++ b/SMSModLogOutputAnalyzer/Data/RoleButtonsDataObject.RoleButton.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace SMSModLogOutputAnalyzer.Data +{ + public partial class RoleButtonsDataObject + { + public class RoleButton + { + [XmlAttribute] public int Id { get; set; } + [XmlAttribute] public ulong Role { get; set; } + [XmlAttribute] public ulong RequiredRole { get; set; } + } + } +} diff --git a/SMSModLogOutputAnalyzer/Data/RoleButtonsDataObject.cs b/SMSModLogOutputAnalyzer/Data/RoleButtonsDataObject.cs new file mode 100644 index 0000000..72f3fde --- /dev/null +++ b/SMSModLogOutputAnalyzer/Data/RoleButtonsDataObject.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static SMSModLogOutputAnalyzer.Data.NotesDataObject; +using System.Xml.Serialization; +using Orpticon.Wrappers; + +namespace SMSModLogOutputAnalyzer.Data +{ + public partial class RoleButtonsDataObject + { + public void Save() + { + SerializerXML.SerializeToFile(this, "RoleButtons.xml"); + } + public static RoleButtonsDataObject Load() + { + var obj = SerializerXML.DeserializeFromFileOrDefault("RoleButtons.xml"); + return obj; + } + [XmlElement("Button")] public List List { get; set; } = new(); + [XmlAttribute] public int Counter { get; set; } + public RoleButton this[int id] => List.Find(x => x.Id == id); + } +} diff --git a/SMSModLogOutputAnalyzer/Modules/AskForLogFile/AskForLogFileModule.cs b/SMSModLogOutputAnalyzer/Modules/AskForLogFile/AskForLogFileModule.cs new file mode 100644 index 0000000..c206c98 --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/AskForLogFile/AskForLogFileModule.cs @@ -0,0 +1,42 @@ +using Discord; +using Discord.Interactions; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public class AskForLogFileModule : InteractionModuleBase + { + [MessageCommand("Ask for Log file")] + public async Task AskForLog(IMessage message) + { + string title = "Please submit Log File"; + string descr = "In order to assist you further, we are going to need your `BepInEx/LogOutput.log` file."; + if (message.Channel.Id == 1220431757266915400) + { + title = "Merci d'envoyer le fichier journal"; + descr = "Afin de t'aider davantage, nous allons avoir besoin de ton fichier `BepInEx/LogOutput.log`."; + } + if (message.Channel.Id == 1220476339883216977) + { + title = "Bitte schick uns deine Log-Datei."; + descr = "Um dir weiterhelfen zu können, benötigen wir deine `BepInEx/LogOutput.log` Datei."; + } + if (message.Channel.Id == 1220488093124005909) + { + title = "Envía el archivo de registro"; + descr = "Para poder ayudarte mejor, vamos a necesitar tu archivo `BepInEx/LogOutput.log`."; + } + if (message.Channel.Id == 1221180784598188082) + { + title = "Prześlij plik dziennika"; + descr = "Aby pomóc Ci dalej, będziemy potrzebować Twojego pliku `BepInEx/LogOutput.log`."; + } + if (message.Channel.Id == 1225150931306549330) + { + title = "Por favor, envia o ficheiro de registo"; + descr = "Para te podermos ajudar mais, vamos precisar do teu ficheiro `BepInEx/LogOutput.log`."; + } + var stream = new MemoryStream(Properties.Resources.LogOutput); + await RespondWithFileAsync(new FileAttachment(stream, "LogOutput.jpg"), "<@" + message.Author.Id + ">", embed: new EmbedBuilder().WithTitle(title).WithDescription(descr).WithImageUrl("attachment://LogOutput.jpg").WithDefaults().Build()); + } + } +} \ No newline at end of file diff --git a/SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.Commands.Setters.cs b/SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.Commands.Setters.cs new file mode 100644 index 0000000..6e84fda --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.Commands.Setters.cs @@ -0,0 +1,25 @@ +using Discord; +using Discord.Interactions; +using SMSModLogOutputAnalyzer.Data; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public partial class ConfigModule + { + public partial class Commands + { + [Group("set", "Setter Commands")] + public class Setters : InteractionModuleBase + { + [SlashCommand("notes_log_channel", "What channel to log note creations into.")] + [RequireUserPermission(ChannelPermission.ManageChannels)] + public async Task NotesLogChannel(ITextChannel channel) + { + Config.NotesLogChannel = channel; + Config.Save(); + await RespondAsync(embed: new EmbedBuilder().WithDescription("Config value `NotesLogChannel` set to `" + channel.Id + "`.").WithDefaults().Build()); + } + } + } + } +} diff --git a/SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.Commands.cs b/SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.Commands.cs new file mode 100644 index 0000000..d8e8532 --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.Commands.cs @@ -0,0 +1,12 @@ +using Discord.Interactions; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public partial class ConfigModule + { + [Group("config", "Config Commands")] + public partial class Commands : InteractionModuleBase + { + } + } +} diff --git a/SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.cs b/SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.cs new file mode 100644 index 0000000..c5feb41 --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/Config/ConfigModule.cs @@ -0,0 +1,14 @@ +using Discord.Interactions; +using Orpticon.Wrappers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public partial class ConfigModule : InteractionModuleBase + { + } +} diff --git a/SMSModLogOutputAnalyzer/CommandModule.FoundLine.cs b/SMSModLogOutputAnalyzer/Modules/LogAnalyze/LogAnalyzeModule.FoundLine.cs similarity index 76% rename from SMSModLogOutputAnalyzer/CommandModule.FoundLine.cs rename to SMSModLogOutputAnalyzer/Modules/LogAnalyze/LogAnalyzeModule.FoundLine.cs index 31f1b49..c893596 100644 --- a/SMSModLogOutputAnalyzer/CommandModule.FoundLine.cs +++ b/SMSModLogOutputAnalyzer/Modules/LogAnalyze/LogAnalyzeModule.FoundLine.cs @@ -1,6 +1,6 @@ -namespace SMSModLogOutputAnalyzer +namespace SMSModLogOutputAnalyzer.Modules { - public partial class CommandModule + public partial class LogAnalyzeModule { public struct FoundLine { diff --git a/SMSModLogOutputAnalyzer/CommandModule.cs b/SMSModLogOutputAnalyzer/Modules/LogAnalyze/LogAnalyzeModule.cs similarity index 67% rename from SMSModLogOutputAnalyzer/CommandModule.cs rename to SMSModLogOutputAnalyzer/Modules/LogAnalyze/LogAnalyzeModule.cs index a6d798a..df4b4b4 100644 --- a/SMSModLogOutputAnalyzer/CommandModule.cs +++ b/SMSModLogOutputAnalyzer/Modules/LogAnalyze/LogAnalyzeModule.cs @@ -2,9 +2,9 @@ using Discord.Interactions; using System.Text.RegularExpressions; -namespace SMSModLogOutputAnalyzer +namespace SMSModLogOutputAnalyzer.Modules { - public partial class CommandModule : InteractionModuleBase + public partial class LogAnalyzeModule : InteractionModuleBase { public static List KnownMelonLoaderMods = new List { @@ -35,39 +35,6 @@ namespace SMSModLogOutputAnalyzer { Console.WriteLine("Building module"); } - [MessageCommand("Ask for Log file")] - public async Task AskForLog(IMessage message) - { - string title = "Please submit Log File"; - string descr = "In order to assist you further, we are going to need your `BepInEx/LogOutput.log` file."; - if(message.Channel.Id == 1220431757266915400) - { - title = "Merci d'envoyer le fichier journal"; - descr = "Afin de t'aider davantage, nous allons avoir besoin de ton fichier `BepInEx/LogOutput.log`."; - } - if(message.Channel.Id == 1220476339883216977) - { - title = "Bitte schick uns deine Log-Datei."; - descr = "Um dir weiterhelfen zu können, benötigen wir deine `BepInEx/LogOutput.log` Datei."; - } - if (message.Channel.Id == 1220488093124005909) - { - title = "Envía el archivo de registro"; - descr = "Para poder ayudarte mejor, vamos a necesitar tu archivo `BepInEx/LogOutput.log`."; - } - if (message.Channel.Id == 1221180784598188082) - { - title = "Prześlij plik dziennika"; - descr = "Aby pomóc Ci dalej, będziemy potrzebować Twojego pliku `BepInEx/LogOutput.log`."; - } - if (message.Channel.Id == 1225150931306549330) - { - title = "Por favor, envia o ficheiro de registo"; - descr = "Para te podermos ajudar mais, vamos precisar do teu ficheiro `BepInEx/LogOutput.log`."; - } - var stream = new MemoryStream(Properties.Resources.LogOutput); - await RespondWithFileAsync(new FileAttachment(stream, "LogOutput.jpg"), "<@" + message.Author.Id + ">", embed: new EmbedBuilder().WithTitle(title).WithDescription(descr).WithImageUrl("attachment://LogOutput.jpg").Build()); - } [MessageCommand("Analyze Log")] public async Task Analyze(IMessage message) { @@ -76,7 +43,7 @@ namespace SMSModLogOutputAnalyzer await Upload((Attachment)message.Attachments.First()); } else { - await RespondAsync("Message has no attachment.", ephemeral: true); + await RespondAsync(embed: new EmbedBuilder().WithDescription("Message has no attachment.").WithDefaults().Build(), ephemeral: true); } } [SlashCommand("upload", "Upload a LogOutput.log file for analysis.")] @@ -89,7 +56,7 @@ namespace SMSModLogOutputAnalyzer var text = await http.GetStringAsync(file.Url); if(Program.AnalysisCache.ContainsKey(text)) { - await RespondAsync(ephemeral: true, text: "This log file has already been analysed [here](" + Program.AnalysisCache[text].GetJumpUrl() + ")."); + await RespondAsync(ephemeral: true, embed: new EmbedBuilder().WithDescription("This log file has already been analysed [here](" + Program.AnalysisCache[text].GetJumpUrl() + ").").WithDefaults().Build()); return; } await DeferAsync(); @@ -97,7 +64,6 @@ namespace SMSModLogOutputAnalyzer var textLines = text.Split('\n').Select(x => x.Trim()).ToArray(); var embedBuilder = new EmbedBuilder(); embedBuilder.WithAuthor(Context.User); - embedBuilder.WithFooter(Program.Footer); Dictionary> values = new(); var lines = FindLines(textLines, @@ -209,7 +175,7 @@ namespace SMSModLogOutputAnalyzer foreach (var field in fields) Log(field.Name + ", " + field.Value); embedBuilder.WithFields(fields); - embedBuilder.WithColor(Color.DarkBlue); + embedBuilder.WithDefaults(); var embed = embedBuilder.Build(); var message = await ModifyOriginalResponseAsync(func => { func.Embeds = new[] { embed }; func.Components = downloadButton.Build(); }); Program.AnalysisCache.Add(text, message); @@ -236,33 +202,5 @@ namespace SMSModLogOutputAnalyzer } return results; } - [SlashCommand("troubleshoot", "Starts the troubleshooting assistant.")] - [RequireUserPermission(GuildPermission.ManageChannels)] - public async Task Troubleshoot() - { - await RespondAsync("Posting", ephemeral: true); - await Context.Channel.SendMessageAsync(embed: new EmbedBuilder() - .WithTitle("Troubleshooting Assistant") - .WithDescription("Press the button below to be guided through our troubleshooting assistant. Hopefully it can help you with getting mods working.") - .WithFooter(Program.Footer) - .Build(), - components: new ComponentBuilder() - .WithButton("Start", "troubleshoot:root", ButtonStyle.Success) - .Build()); - } - [ComponentInteraction("troubleshoot:*")] - public async Task TroubleshootButton(string node) - { - Flowchart.FlowchartNode fcnode = Program.Troubleshooting.Nodes.Find(x => x.Name == node); - if (fcnode == null) return; - var connections = Program.Troubleshooting.Connections.Where(x => x.FromName == node); - var cmpb = new ComponentBuilder(); - foreach (var conn in connections) cmpb.WithButton(conn.Text, "troubleshoot:" + conn.ToName, conn.Text == "Yes" ? ButtonStyle.Success : conn.Text == "No" ? ButtonStyle.Danger : ButtonStyle.Primary); - await RespondAsync(embed: new EmbedBuilder() - .WithTitle(fcnode.Text) - .WithDescription(fcnode.Description) - .WithFooter(Program.Footer) - .Build(), components: cmpb.Build(), ephemeral: true); - } } } \ No newline at end of file diff --git a/SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.Commands.AddNoteModal.cs b/SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.Commands.AddNoteModal.cs new file mode 100644 index 0000000..1a0cc8d --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.Commands.AddNoteModal.cs @@ -0,0 +1,23 @@ +using Discord; +using Discord.Interactions; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public partial class NotesModule + { + public partial class Commands + { + /// + /// Currently unused because I can't figure out how to get modals to work + /// + public class AddNoteModal : IModal + { + public string Title => "Add Note"; + [RequiredInput(true)] + [InputLabel("Note Text")] + [ModalTextInput("note_text", TextInputStyle.Paragraph, "stinky")] + public string Note { get; set; } + } + } + } +} diff --git a/SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.Commands.cs b/SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.Commands.cs new file mode 100644 index 0000000..409cdaf --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.Commands.cs @@ -0,0 +1,108 @@ +using Discord; +using Discord.Interactions; +using SMSModLogOutputAnalyzer.Data; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public partial class NotesModule + { + [Group("notes", "Various commands related to notes.")] + public partial class Commands : InteractionModuleBase + { + [SlashCommand("view", "View notes that have been added to a user.")] + [RequireUserPermission(GuildPermission.ManageRoles)] + public async Task View(IUser user, [Summary(description: "How many notes to skip (used for pagination)")] int offset = 0) + { + EmbedBuilder embedBuilder = new EmbedBuilder(); + + var notes = Data[user].Notes.Skip(offset).Take(25).ToArray(); + bool addNextPageButton = Data[user].Notes.Count > offset + 25; + if (notes.Length == 0) + { + await RespondAsync(embed: new EmbedBuilder().WithDefaults().WithDescription("No " + (offset > 0 ? "more " : "") + "notes have been added to this user.").Build(), ephemeral: true); + return; + } + + embedBuilder.WithAuthor(user); + embedBuilder.WithDefaults(); + + foreach (var note in notes) + { + embedBuilder.AddField(note.ToField()); + } + + var embed = embedBuilder.Build(); + var button = new ComponentBuilder().WithButton("Next Page", "view_notes:" + user.Id + ":" + (offset + 25)).Build(); + + if (addNextPageButton) + { + await RespondAsync(embed: embed, components: button, ephemeral: true); + } + else + { + await RespondAsync(embed: embed, ephemeral: true); + } + } + [SlashCommand("add", "Add a note to a user.")] + [RequireUserPermission(GuildPermission.ManageRoles)] + public async Task Add(IUser user, [Summary("note")] string text) + { + if (string.IsNullOrEmpty(text)) + { + Console.WriteLine("Sending modal"); + await Context.Interaction.RespondWithModalAsync("add_note:" + user.Id); + return; + } + NotesDataObject.Note note = new(); + + note.Text = text; + note.CreatorID = Context.User.Id; + note.CreationTime = DateTime.Now; + note.Id = ++Data[user].Counter; + + Data[user].Notes.Add(note); + Data.Save(); + + string error = ""; + + var field = note.ToField(); + try + { + await Config.NotesLogChannel.SendMessageAsync(embed: new EmbedBuilder().WithDefaults().WithAuthor(user.Username + " - New Note", user.GetDisplayAvatarUrl()).WithTitle(field.Name).WithDescription((string)field.Value).Build()); + } catch (Exception e) + { + error = "\n\n:x: Logging failed"; + } + + await RespondAsync(embed: new EmbedBuilder().WithDescription("Note **" + note.Id + "** added." + error).WithDefaults().Build(), ephemeral: true); + } + [SlashCommand("remove", "Removes a note from a user.")] + [RequireUserPermission(GuildPermission.ManageRoles)] + public async Task Remove(IUser user, int note_id) + { + int num = Data[user].Notes.RemoveAll(x => x.Id == note_id); + Data.Save(); + if (num > 0) await RespondAsync(embed: new EmbedBuilder().WithDescription("Note **" + note_id + "** removed.").WithDefaults().Build(), ephemeral: true); + else await RespondAsync(embed: new EmbedBuilder().WithDescription("Note **" + note_id + "** was not found.").WithDefaults().Build(), ephemeral: true); + } + [SlashCommand("clear", "Removes all notes from a user.")] + [RequireUserPermission(GuildPermission.ManageRoles)] + public async Task Clear(IUser user) + { + int num = Data[user].Notes.RemoveAll(x => true); + Data.Save(); + await RespondAsync(embed: new EmbedBuilder().WithDescription("**" + num + "** notes were removed.").WithDefaults().Build(), ephemeral: true); + } + + [ModalInteraction("add_note:*", true)] + [RequireUserPermission(ChannelPermission.ManageRoles)] + public async Task AddModal(AddNoteModal modal, string user) => await Add(await Context.Client.GetUserAsync(ulong.Parse(user)), modal.Note); + [ComponentInteraction("view_notes:*:*", true)] + [RequireUserPermission(ChannelPermission.ManageRoles)] + public async Task ViewButton(string user, string offset) => await View(await Context.Client.GetUserAsync(ulong.Parse(user)), int.Parse(offset)); + [MessageCommand("View Notes")] + [RequireUserPermission(ChannelPermission.ManageRoles)] + public async Task UserView(IMessage message) => await View(message.Author); + } + } +} diff --git a/SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.cs b/SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.cs new file mode 100644 index 0000000..b8753ec --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/Notes/NotesModule.cs @@ -0,0 +1,15 @@ +using Discord.Interactions; +using SMSModLogOutputAnalyzer.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public partial class NotesModule : InteractionModuleBase + { + public static NotesDataObject Data { get; set; } + } +} diff --git a/SMSModLogOutputAnalyzer/Modules/RoleButtons/RoleButtonModule.Commands.cs b/SMSModLogOutputAnalyzer/Modules/RoleButtons/RoleButtonModule.Commands.cs new file mode 100644 index 0000000..ac04cc6 --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/RoleButtons/RoleButtonModule.Commands.cs @@ -0,0 +1,131 @@ +using Discord; +using Discord.Interactions; +using Discord.WebSocket; +using System.Drawing; +using System.Globalization; +using static SMSModLogOutputAnalyzer.Data.RoleButtonsDataObject; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public partial class RoleButtonModule + { + [Group("role", "Role Button Commands")] + public class Commands : InteractionModuleBase + { + [Group("button", "Role Button Commands")] + public class Subcommands : InteractionModuleBase + { + [SlashCommand("create", "Creates a new role button.")] + [RequireUserPermission(ChannelPermission.ManageRoles)] + public async Task Create([Summary(description: "What role the button should give")] IRole role, + [Summary(description: "What title the embed should have")] string title, + [Summary(description: "What description the embed should have")] string description, + [Summary(description: "What text the button should have")] string button_text, + [Summary(description: "What emote the button should have")] string button_emote = "", + [Summary(description: "What style the button should have")] ButtonStyle style = ButtonStyle.Primary, + [Summary(description: "What role should the user already need to have")] IRole required_role = null, + [Summary(description: "Embed color (hex code)")] string embed_color = "206694") + { + try + { + RoleButton button = new RoleButton(); + button.Id = Data.Counter++; + button.Role = role.Id; + if (required_role != null) button.RequiredRole = required_role.Id; + Data.List.Add(button); + Data.Save(); + + EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.WithTitle(title); + embedBuilder.WithDescription(description); + embedBuilder.WithColor(Program.ConvertColor(embed_color)); + + ComponentBuilder componentBuilder = new ComponentBuilder(); + var buttonBuilder = new ButtonBuilder().WithLabel(button_text).WithStyle(style).WithCustomId("role_button:" + button.Id); + if (Emote.TryParse(button_emote, out Emote emote)) + { + buttonBuilder.WithEmote(emote); + } + if (Emoji.TryParse(button_emote, out Emoji emote2)) + { + buttonBuilder.WithEmote(emote2); + } + componentBuilder.WithButton(buttonBuilder); + + try + { + await Context.Channel.SendMessageAsync(embed: embedBuilder.Build(), components: componentBuilder.Build()); + } + catch (Exception e) + { + Console.WriteLine(e); + await RespondAsync(embed: new EmbedBuilder().WithDefaults().WithDescription(":x: Message Sending failed. Ensure the bot has sufficient permissions to send in this channel.").Build(), ephemeral: true); + return; + } + await RespondAsync(embed: new EmbedBuilder().WithDefaults().WithDescription(":white_check_mark: Role Button Created").Build(), ephemeral: true); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + + [ComponentInteraction("role_button:*", true)] + public async Task RoleButton(string id) + { + var button = Data[int.Parse(id)]; + + if (Context.User is SocketGuildUser user) + { + if (user.Roles.Any(x => x.Id == button.Role)) + { + await RemoveRole(button.Role); + } + else + { + if (button.RequiredRole == 0) + { + await AddRole(button.Role); + } + else + { + if (user.Roles.Any(x => x.Id == button.RequiredRole)) + { + await AddRole(button.Role); + } + else + { + await RespondAsync(embed: new EmbedBuilder().WithDefaults().WithDescription("You need the role <@&" + button.RequiredRole + "> to be allowed to get this role.").Build(), ephemeral: true); + } + } + } + } + } + + private async Task AddRole(ulong role) + { + try + { + if (Context.User is SocketGuildUser user) + { + await user.AddRoleAsync(role); + await RespondAsync(embed: new EmbedBuilder().WithDefaults().WithDescription(":white_check_mark: Role Added: <@&" + role + ">").Build(), ephemeral: true); + } + } catch (Exception e) + { + Console.WriteLine(e); + } + } + + private async Task RemoveRole(ulong role) + { + if (Context.User is SocketGuildUser user) + { + await user.RemoveRoleAsync(role); + await RespondAsync(embed: new EmbedBuilder().WithDefaults().WithDescription(":x: Role Removed: <@&" + role + ">").Build(), ephemeral: true); + } + } + } + } + } +} diff --git a/SMSModLogOutputAnalyzer/Modules/RoleButtons/RoleButtonModule.cs b/SMSModLogOutputAnalyzer/Modules/RoleButtons/RoleButtonModule.cs new file mode 100644 index 0000000..b70d13b --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/RoleButtons/RoleButtonModule.cs @@ -0,0 +1,15 @@ +using Discord.Interactions; +using SMSModLogOutputAnalyzer.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public partial class RoleButtonModule : InteractionModuleBase + { + public static RoleButtonsDataObject Data { get; set; } + } +} diff --git a/SMSModLogOutputAnalyzer/Modules/Troubleshooting/TroubleshootingModule.cs b/SMSModLogOutputAnalyzer/Modules/Troubleshooting/TroubleshootingModule.cs new file mode 100644 index 0000000..55bd26d --- /dev/null +++ b/SMSModLogOutputAnalyzer/Modules/Troubleshooting/TroubleshootingModule.cs @@ -0,0 +1,37 @@ +using Discord; +using Discord.Interactions; + +namespace SMSModLogOutputAnalyzer.Modules +{ + public class TroubleshootingModule : InteractionModuleBase + { + [SlashCommand("troubleshoot", "Starts the troubleshooting assistant.")] + [RequireUserPermission(GuildPermission.ManageChannels)] + public async Task Troubleshoot() + { + await RespondAsync("Posting", ephemeral: true); + await Context.Channel.SendMessageAsync(embed: new EmbedBuilder() + .WithTitle("Troubleshooting Assistant") + .WithDescription("Press the button below to be guided through our troubleshooting assistant. Hopefully it can help you with getting mods working.") + .WithFooter(Program.Footer) + .Build(), + components: new ComponentBuilder() + .WithButton("Start", "troubleshoot:root", ButtonStyle.Success) + .Build()); + } + [ComponentInteraction("troubleshoot:*")] + public async Task TroubleshootButton(string node) + { + Flowchart.FlowchartNode fcnode = Program.Troubleshooting.Nodes.Find(x => x.Name == node); + if (fcnode == null) return; + var connections = Program.Troubleshooting.Connections.Where(x => x.FromName == node); + var cmpb = new ComponentBuilder(); + foreach (var conn in connections) cmpb.WithButton(conn.Text, "troubleshoot:" + conn.ToName, conn.Text == "Yes" ? ButtonStyle.Success : conn.Text == "No" ? ButtonStyle.Danger : ButtonStyle.Primary); + await RespondAsync(embed: new EmbedBuilder() + .WithTitle(fcnode.Text) + .WithDescription(fcnode.Description) + .WithFooter(Program.Footer) + .Build(), components: cmpb.Build(), ephemeral: true); + } + } +} \ No newline at end of file diff --git a/SMSModLogOutputAnalyzer/Program.cs b/SMSModLogOutputAnalyzer/Program.cs index b41f9da..55d3223 100644 --- a/SMSModLogOutputAnalyzer/Program.cs +++ b/SMSModLogOutputAnalyzer/Program.cs @@ -1,37 +1,60 @@ using Discord.WebSocket; using Discord; using Discord.Interactions; -using System.Reflection; -using System; +using SMSModLogOutputAnalyzer.Modules; +using SMSModLogOutputAnalyzer.Data; +using SMSModLogOutputAnalyzer.Modules; +using System.Globalization; namespace SMSModLogOutputAnalyzer { internal static class Program { - private static DiscordSocketClient _client; + public static DiscordSocketClient Client; public static Flowchart Troubleshooting; public const string Footer = "SMS Mods Bot • Made by BratPfanneTV, for Modded Supermarket Simulator (Unofficial)"; + public static Color Color = Color.DarkBlue; public static Dictionary AnalysisCache = new(); + public static EmbedBuilder WithDefaults(this EmbedBuilder builder) + { + return builder.WithFooter(Footer).WithColor(Color); + } + public static Color ConvertColor(string hex) + { + string colorcode = hex; + colorcode = colorcode.TrimStart('#'); + return new Color( + int.Parse(colorcode.Substring(0, 2), NumberStyles.HexNumber), + int.Parse(colorcode.Substring(2, 2), NumberStyles.HexNumber), + int.Parse(colorcode.Substring(4, 2), NumberStyles.HexNumber)); + } + public static bool TryParseEmote(string str, out IEmote iemote) + { + if (Emote.TryParse(str, out Emote emote)) iemote = emote; + else if (Emoji.TryParse(str, out Emoji emoji)) iemote = emoji; + else iemote = null; + return iemote != null; + } public static async Task Main() { Troubleshooting = Flowchart.Parse(Properties.Resources.Flowchart); - _client = new DiscordSocketClient(new DiscordSocketConfig { LogLevel = LogSeverity.Debug }); + Client = new DiscordSocketClient(new DiscordSocketConfig { LogLevel = LogSeverity.Debug }); - _client.Log += Log; + Client.Log += Log; // You can assign your bot token to a string, and pass that in to connect. // This is, however, insecure, particularly if you plan to have your code hosted in a public repository. var token = File.ReadAllText("token.txt"); - _client.Ready += _client_Ready; + Client.Ready += _client_Ready; // Some alternative options would be to keep your token in an Environment Variable or a standalone file. // var token = Environment.GetEnvironmentVariable("NameOfYourEnvironmentVariable"); // var token = File.ReadAllText("token.txt"); // var token = JsonConvert.DeserializeObject(File.ReadAllText("config.json")).Token; - await _client.LoginAsync(TokenType.Bot, token); - await _client.StartAsync(); + await Client.LoginAsync(TokenType.Bot, token); + await Client.StartAsync(); // Block this task until the program is closed. await Task.Delay(-1); @@ -39,14 +62,22 @@ namespace SMSModLogOutputAnalyzer private static async Task _client_Ready() { - var server = (ulong)652484929950842881; - var _interactionService = new InteractionService(_client.Rest); - var module = await _interactionService.AddModuleAsync(null); - await _interactionService.AddModulesGloballyAsync(true, module); + NotesModule.Data = NotesDataObject.Load(); + RoleButtonModule.Data = RoleButtonsDataObject.Load(); + Config.Load(); - _client.InteractionCreated += async (x) => + var _interactionService = new InteractionService(Client.Rest); + await _interactionService.AddModulesGloballyAsync(true, + await _interactionService.AddModuleAsync(null), + await _interactionService.AddModuleAsync(null), + await _interactionService.AddModuleAsync(null), + await _interactionService.AddModuleAsync(null), + await _interactionService.AddModuleAsync(null), + await _interactionService.AddModuleAsync(null)); + + Client.InteractionCreated += async (x) => { - var ctx = new SocketInteractionContext(_client, x); + var ctx = new SocketInteractionContext(Client, x); await _interactionService.ExecuteCommandAsync(ctx, null); }; } diff --git a/SMSModLogOutputAnalyzer/SMSModLogOutputAnalyzer.csproj b/SMSModLogOutputAnalyzer/SMSModLogOutputAnalyzer.csproj index 53ca377..47a2f0c 100644 --- a/SMSModLogOutputAnalyzer/SMSModLogOutputAnalyzer.csproj +++ b/SMSModLogOutputAnalyzer/SMSModLogOutputAnalyzer.csproj @@ -7,10 +7,22 @@ enable + + + + + + + + + ..\..\OrpticonDLL\bin\Debug\Orpticon.dll + + + True