From e1e41b413605728ff5cc618902140cf26432e7b4 Mon Sep 17 00:00:00 2001 From: YouHaveTrouble Date: Sun, 11 Aug 2024 22:27:33 +0200 Subject: [PATCH] refactor command management --- .../java/me/youhavetrouble/noted/Main.java | 145 +----- .../noted/command/AddAliasCommand.java | 78 ++++ .../noted/command/AddNoteCommand.java | 159 +++++++ .../youhavetrouble/noted/command/Command.java | 65 +++ .../noted/command/DeleteAliasCommand.java | 65 +++ .../noted/command/DeleteNoteCommand.java | 68 +++ .../noted/command/EditNoteCommand.java | 198 +++++++++ .../noted/command/NoteCommand.java | 85 ++++ .../noted/listener/SlashCommandListener.java | 415 +----------------- .../me/youhavetrouble/noted/note/Note.java | 4 + 10 files changed, 734 insertions(+), 548 deletions(-) create mode 100644 src/main/java/me/youhavetrouble/noted/command/AddAliasCommand.java create mode 100644 src/main/java/me/youhavetrouble/noted/command/AddNoteCommand.java create mode 100644 src/main/java/me/youhavetrouble/noted/command/Command.java create mode 100644 src/main/java/me/youhavetrouble/noted/command/DeleteAliasCommand.java create mode 100644 src/main/java/me/youhavetrouble/noted/command/DeleteNoteCommand.java create mode 100644 src/main/java/me/youhavetrouble/noted/command/EditNoteCommand.java create mode 100644 src/main/java/me/youhavetrouble/noted/command/NoteCommand.java diff --git a/src/main/java/me/youhavetrouble/noted/Main.java b/src/main/java/me/youhavetrouble/noted/Main.java index 7caa3f6..a5bd2dc 100644 --- a/src/main/java/me/youhavetrouble/noted/Main.java +++ b/src/main/java/me/youhavetrouble/noted/Main.java @@ -1,5 +1,6 @@ package me.youhavetrouble.noted; +import me.youhavetrouble.noted.command.*; import me.youhavetrouble.noted.listener.SlashCommandListener; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; @@ -21,7 +22,7 @@ import java.util.logging.Logger; public class Main { - private static final Logger logger = Logger.getLogger("Main"); + public static final Logger logger = Logger.getLogger("Main"); private static final Properties properties = new Properties(); private static String version = "Unknown version"; private static Storage storage; @@ -66,142 +67,12 @@ public class Main { command = "note"; } - jda.upsertCommand(Commands.slash(command, "Get a note") - .setIntegrationTypes(IntegrationType.GUILD_INSTALL, IntegrationType.USER_INSTALL) - .addOptions( - new OptionData(OptionType.STRING, "alias", "The ID of the note", true, true), - new OptionData(OptionType.BOOLEAN, "ephermeal", "Whether the note should be ephermal", false) - ) - .setContexts(InteractionContextType.BOT_DM, InteractionContextType.GUILD, InteractionContextType.PRIVATE_CHANNEL) - ).queue(); - - jda.upsertCommand(Commands.slash("add-note", "Add a note") - .setIntegrationTypes(IntegrationType.USER_INSTALL) - .addOptions( - new OptionData(OptionType.STRING, "alias", "An alias for the note") - .setMinLength(1) - .setMaxLength(256) - .setRequired(true), - new OptionData(OptionType.STRING, "title", "The title of the note") - .setMinLength(1) - .setMaxLength(256) - .setRequired(true), - new OptionData(OptionType.STRING, "content", "The content of the note") - .setMinLength(1) - .setMaxLength(4096) - .setRequired(true), - new OptionData(OptionType.STRING, "title-url", "The image URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false), - new OptionData(OptionType.STRING, "image-url", "The image URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false), - new OptionData(OptionType.STRING, "thumbnail-url", "The thumbnail URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false), - new OptionData(OptionType.STRING, "color", "The color of the note") - .setMinLength(7) - .setMaxLength(7) - .setRequired(false), - new OptionData(OptionType.STRING, "author", "The author of the note") - .setMinLength(1) - .setMaxLength(256) - .setRequired(false), - new OptionData(OptionType.STRING, "author-url", "The author URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false), - new OptionData(OptionType.STRING, "footer", "The footer of the note") - .setMinLength(1) - .setMaxLength(256) - .setRequired(false), - new OptionData(OptionType.STRING, "footer-url", "The footer URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false) - ) - .setContexts(InteractionContextType.BOT_DM) - ).queue(); - - jda.upsertCommand(Commands.slash("edit-note", "Edit a note") - .setIntegrationTypes(IntegrationType.USER_INSTALL) - .addOptions( - new OptionData(OptionType.STRING, "alias", "An alias for the note") - .setMinLength(1) - .setMaxLength(256) - .setRequired(true), - new OptionData(OptionType.STRING, "title", "The title of the note") - .setMinLength(1) - .setMaxLength(256) - .setRequired(false), - new OptionData(OptionType.STRING, "content", "The content of the note") - .setMinLength(1) - .setMaxLength(4096) - .setRequired(false), - new OptionData(OptionType.STRING, "title-url", "The image URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false), - new OptionData(OptionType.STRING, "image-url", "The image URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false), - new OptionData(OptionType.STRING, "thumbnail-url", "The thumbnail URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false), - new OptionData(OptionType.STRING, "color", "The color of the note") - .setMinLength(7) - .setMaxLength(7) - .setRequired(false), - new OptionData(OptionType.STRING, "author", "The author of the note") - .setMinLength(1) - .setMaxLength(256) - .setRequired(false), - new OptionData(OptionType.STRING, "author-url", "The author URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false), - new OptionData(OptionType.STRING, "footer", "The footer of the note") - .setMinLength(1) - .setMaxLength(256) - .setRequired(false), - new OptionData(OptionType.STRING, "footer-url", "The footer URL of the note") - .setMinLength(1) - .setMaxLength(2000) - .setRequired(false) - ) - .setContexts(InteractionContextType.BOT_DM) - ).queue(); - - jda.upsertCommand(Commands.slash("delete-note", "Delete a note") - .setIntegrationTypes(IntegrationType.USER_INSTALL) - .addOptions( - new OptionData(OptionType.STRING, "alias", "The ID of the note", true, true) - ) - .setContexts(InteractionContextType.BOT_DM) - ).queue(); - - jda.upsertCommand(Commands.slash("add-alias", "Add alias to a note") - .setIntegrationTypes(IntegrationType.USER_INSTALL) - .addOptions( - new OptionData(OptionType.STRING, "alias", "Existing alias of a note", true, true), - new OptionData(OptionType.STRING, "new-alias", "New alias for the note", true) - ) - .setContexts(InteractionContextType.BOT_DM) - ).queue(); - - jda.upsertCommand(Commands.slash("delete-alias", "Remove alias to a note") - .setIntegrationTypes(IntegrationType.USER_INSTALL) - .addOptions( - new OptionData(OptionType.STRING, "alias", "Existing alias of a note", true, true) - ) - .setContexts(InteractionContextType.BOT_DM) - ).queue(); - + Command.registerCommand(new NoteCommand(command)); + Command.registerCommand(new AddNoteCommand()); + Command.registerCommand(new EditNoteCommand()); + Command.registerCommand(new DeleteNoteCommand()); + Command.registerCommand(new AddAliasCommand()); + Command.registerCommand(new DeleteAliasCommand()); } private static void loadProperties() { diff --git a/src/main/java/me/youhavetrouble/noted/command/AddAliasCommand.java b/src/main/java/me/youhavetrouble/noted/command/AddAliasCommand.java new file mode 100644 index 0000000..5c672f1 --- /dev/null +++ b/src/main/java/me/youhavetrouble/noted/command/AddAliasCommand.java @@ -0,0 +1,78 @@ +package me.youhavetrouble.noted.command; + +import me.youhavetrouble.noted.Main; +import me.youhavetrouble.noted.Storage; +import me.youhavetrouble.noted.note.Note; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.IntegrationType; +import net.dv8tion.jda.api.interactions.InteractionContextType; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; + +public class AddAliasCommand extends Command { + + @Override + public void register(JDA jda, String name) { + jda.upsertCommand(Commands.slash("add-alias", "Add alias to a note") + .setIntegrationTypes(IntegrationType.USER_INSTALL) + .addOptions( + new OptionData(OptionType.STRING, "alias", "Existing alias of a note", true, true), + new OptionData(OptionType.STRING, "new-alias", "New alias for the note", true) + ) + .setContexts(InteractionContextType.BOT_DM) + ).queue(); + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + OptionMapping noteAliasMapping = event.getOption("alias"); + if (noteAliasMapping == null) { + event.reply("Please provide a note alias.") + .setEphemeral(true) + .queue(); + return; + } + OptionMapping newAliasMapping = event.getOption("new-alias"); + if (newAliasMapping == null) { + event.reply("Please provide a new alias.") + .setEphemeral(true) + .queue(); + return; + } + + String noteAlias = noteAliasMapping.getAsString(); + + Note note = Main.getStorage().getNote(noteAlias); + if (note == null) { + event.reply("Note with alias %s not found.".formatted(noteAlias)) + .setEphemeral(true) + .queue(); + return; + } + + Storage.Status status = Main.getStorage().addAlias(newAliasMapping.getAsString(), note.id); + if (status == Storage.Status.SUCCESS) { + Note.ALIASES.add(newAliasMapping.getAsString()); + event.reply("Alias added.") + .setEphemeral(true) + .queue(); + } else if (status == Storage.Status.ALIAS_EXISTS) { + event.reply("Alias already exists.") + .setEphemeral(true) + .queue(); + } else { + event.reply("Failed to add alias.") + .setEphemeral(true) + .queue(); + } + } + + @Override + public String getName() { + return "add-alias"; + } + +} diff --git a/src/main/java/me/youhavetrouble/noted/command/AddNoteCommand.java b/src/main/java/me/youhavetrouble/noted/command/AddNoteCommand.java new file mode 100644 index 0000000..ea6b625 --- /dev/null +++ b/src/main/java/me/youhavetrouble/noted/command/AddNoteCommand.java @@ -0,0 +1,159 @@ +package me.youhavetrouble.noted.command; + +import me.youhavetrouble.noted.Main; +import me.youhavetrouble.noted.Storage; +import me.youhavetrouble.noted.note.Note; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.IntegrationType; +import net.dv8tion.jda.api.interactions.InteractionContextType; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; + +import java.awt.*; + +public class AddNoteCommand extends Command { + + @Override + public void register(JDA jda, String name) { + jda.upsertCommand(Commands.slash(getName(), "Add a note") + .setIntegrationTypes(IntegrationType.USER_INSTALL) + .addOptions( + new OptionData(OptionType.STRING, "alias", "An alias for the note") + .setMinLength(1) + .setMaxLength(256) + .setRequired(true), + new OptionData(OptionType.STRING, "title", "The title of the note") + .setMinLength(1) + .setMaxLength(256) + .setRequired(true), + new OptionData(OptionType.STRING, "content", "The content of the note") + .setMinLength(1) + .setMaxLength(4096) + .setRequired(true), + new OptionData(OptionType.STRING, "title-url", "The image URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false), + new OptionData(OptionType.STRING, "image-url", "The image URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false), + new OptionData(OptionType.STRING, "thumbnail-url", "The thumbnail URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false), + new OptionData(OptionType.STRING, "color", "The color of the note") + .setMinLength(7) + .setMaxLength(7) + .setRequired(false), + new OptionData(OptionType.STRING, "author", "The author of the note") + .setMinLength(1) + .setMaxLength(256) + .setRequired(false), + new OptionData(OptionType.STRING, "author-url", "The author URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false), + new OptionData(OptionType.STRING, "footer", "The footer of the note") + .setMinLength(1) + .setMaxLength(256) + .setRequired(false), + new OptionData(OptionType.STRING, "footer-url", "The footer URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false) + ) + .setContexts(InteractionContextType.BOT_DM) + ).queue(); + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + OptionMapping aliasOption = event.getOption("alias"); + OptionMapping titleOption = event.getOption("title"); + OptionMapping contentOption = event.getOption("content"); + + if (titleOption == null || contentOption == null || aliasOption == null) { + event.reply("Please provide a alias, title and content.") + .setEphemeral(true) + .queue(); + return; + } + + Note note = Note.createNew(titleOption.getAsString(), contentOption.getAsString()); + + OptionMapping titleUrlOption = event.getOption("title-url"); + if (titleUrlOption != null) { + note = note.withTitleUrl(titleUrlOption.getAsString()); + } + + OptionMapping imageUrlOption = event.getOption("image-url"); + if (imageUrlOption != null) { + note = note.withImageUrl(imageUrlOption.getAsString()); + } + + OptionMapping thumbnailUrlOption = event.getOption("thumbnail-url"); + if (thumbnailUrlOption != null) { + note = note.withThumbnailUrl(thumbnailUrlOption.getAsString()); + } + + OptionMapping colorOption = event.getOption("color"); + if (colorOption != null) { + try { + note = note.withColor(Color.decode(colorOption.getAsString())); + } catch (NumberFormatException e) { + event.reply("Invalid color.") + .setEphemeral(true) + .queue(); + return; + } + } + + OptionMapping authorOption = event.getOption("author"); + if (authorOption != null) { + note = note.withAuthor(authorOption.getAsString()); + } + + OptionMapping authorUrlOption = event.getOption("author-url"); + if (authorUrlOption != null) { + note = note.withAuthorUrl(authorUrlOption.getAsString()); + } + + OptionMapping footerOption = event.getOption("footer"); + if (footerOption != null) { + note = note.withFooter(footerOption.getAsString()); + } + + OptionMapping footerUrlOption = event.getOption("footer-url"); + if (footerUrlOption != null) { + note = note.withFooterUrl(footerUrlOption.getAsString()); + } + + String alias = aliasOption.getAsString(); + Storage.Status status = Main.getStorage().addNote(note, alias); + + if (status == Storage.Status.SUCCESS) { + Note.ALIASES.add(alias); + event.reply("Note added.") + .setEphemeral(true) + .queue(); + } else if (status == Storage.Status.ALIAS_EXISTS) { + event.reply("Alias already exists.") + .setEphemeral(true) + .queue(); + } else { + event.reply("Failed to add note.") + .setEphemeral(true) + .queue(); + } + } + + @Override + public String getName() { + return "add-note"; + } + +} diff --git a/src/main/java/me/youhavetrouble/noted/command/Command.java b/src/main/java/me/youhavetrouble/noted/command/Command.java new file mode 100644 index 0000000..983ecb3 --- /dev/null +++ b/src/main/java/me/youhavetrouble/noted/command/Command.java @@ -0,0 +1,65 @@ +package me.youhavetrouble.noted.command; + +import me.youhavetrouble.noted.Main; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public abstract class Command extends ListenerAdapter { + + private static final Map commands = new HashMap<>(); + + public void register(JDA jda, String name) { + + } + + public void execute(SlashCommandInteractionEvent event) { + + } + + @Nullable + public String getName() { + return null; + } + + boolean canUse(User user) { + Long adminId = Main.getAdminId(); + return adminId != null && adminId.equals(user.getIdLong()); + } + + public static void registerCommand(Command command) { + String name = command.getName(); + if (commands.containsKey(name)) { + Main.logger.warning("Command " + name + " is already registered."); + return; + } + command.register(Main.jda, name); + commands.put(name, command); + } + + public static void executeCommand(SlashCommandInteractionEvent event, String name) { + Command command = commands.get(name.toLowerCase(Locale.ENGLISH)); + if (command == null) { + event.reply("Unknown command.") + .setEphemeral(true) + .queue(); + return; + } + + if (!command.canUse(event.getUser())) { + event.reply("You do not have permission to use this command.") + .setEphemeral(true) + .queue(); + return; + } + + command.execute(event); + } + +} diff --git a/src/main/java/me/youhavetrouble/noted/command/DeleteAliasCommand.java b/src/main/java/me/youhavetrouble/noted/command/DeleteAliasCommand.java new file mode 100644 index 0000000..03c7a7c --- /dev/null +++ b/src/main/java/me/youhavetrouble/noted/command/DeleteAliasCommand.java @@ -0,0 +1,65 @@ +package me.youhavetrouble.noted.command; + +import me.youhavetrouble.noted.Main; +import me.youhavetrouble.noted.Storage; +import me.youhavetrouble.noted.note.Note; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.IntegrationType; +import net.dv8tion.jda.api.interactions.InteractionContextType; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; + +public class DeleteAliasCommand extends Command { + + @Override + public void register(JDA jda, String name) { + jda.upsertCommand(Commands.slash("delete-alias", "Remove alias to a note") + .setIntegrationTypes(IntegrationType.USER_INSTALL) + .addOptions( + new OptionData(OptionType.STRING, "alias", "Existing alias of a note", true, true) + ) + .setContexts(InteractionContextType.BOT_DM) + ).queue(); + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + OptionMapping aliasMapping = event.getOption("alias"); + if (aliasMapping == null) { + event.reply("Please provide a alias.") + .setEphemeral(true) + .queue(); + return; + } + + String alias = aliasMapping.getAsString(); + + Storage.Status status = Main.getStorage().deleteAlias(alias); + switch (status) { + case SUCCESS -> { + Note.ALIASES.remove(alias); + event.reply("Alias deleted.") + .setEphemeral(true) + .queue(); + } + case ALIAS_NOT_FOUND -> event.reply("Provided alias does not exist.") + .setEphemeral(true) + .queue(); + case ALIAS_IS_REQUIRED -> event.reply("At least one alias per note is required.") + .setEphemeral(true) + .queue(); + case null, default -> event.reply("Failed to delete alias.") + .setEphemeral(true) + .queue(); + } + } + + @Override + public String getName() { + return "delete-alias"; + } + +} diff --git a/src/main/java/me/youhavetrouble/noted/command/DeleteNoteCommand.java b/src/main/java/me/youhavetrouble/noted/command/DeleteNoteCommand.java new file mode 100644 index 0000000..32743db --- /dev/null +++ b/src/main/java/me/youhavetrouble/noted/command/DeleteNoteCommand.java @@ -0,0 +1,68 @@ +package me.youhavetrouble.noted.command; + +import me.youhavetrouble.noted.Main; +import me.youhavetrouble.noted.Storage; +import me.youhavetrouble.noted.note.Note; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.IntegrationType; +import net.dv8tion.jda.api.interactions.InteractionContextType; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; + +import java.util.Set; + +public class DeleteNoteCommand extends Command { + + @Override + public void register(JDA jda, String name) { + jda.upsertCommand(Commands.slash("delete-note", "Delete a note") + .setIntegrationTypes(IntegrationType.USER_INSTALL) + .addOptions( + new OptionData(OptionType.STRING, "alias", "The ID of the note", true, true) + ) + .setContexts(InteractionContextType.BOT_DM) + ).queue(); + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + OptionMapping noteAliasMapping = event.getOption("alias"); + if (noteAliasMapping == null) { + event.reply("Please provide a note alias.") + .setEphemeral(true) + .queue(); + return; + } + String noteAlias = noteAliasMapping.getAsString(); + + Note note = Main.getStorage().getNote(noteAlias); + if (note == null) { + event.reply("Note with alias %s not found.".formatted(noteAlias)) + .setEphemeral(true) + .queue(); + return; + } + + Set noteAliases = Main.getStorage().getAliases(note.id); + Storage.Status status = Main.getStorage().deleteNote(note.id); + if (status == Storage.Status.SUCCESS) { + noteAliases.forEach(Note.ALIASES::remove); + event.reply("Note deleted.") + .setEphemeral(true) + .queue(); + } else { + event.reply("Failed to delete note.") + .setEphemeral(true) + .queue(); + } + } + + @Override + public String getName() { + return "delete-note"; + } + +} diff --git a/src/main/java/me/youhavetrouble/noted/command/EditNoteCommand.java b/src/main/java/me/youhavetrouble/noted/command/EditNoteCommand.java new file mode 100644 index 0000000..f83d186 --- /dev/null +++ b/src/main/java/me/youhavetrouble/noted/command/EditNoteCommand.java @@ -0,0 +1,198 @@ +package me.youhavetrouble.noted.command; + +import me.youhavetrouble.noted.Main; +import me.youhavetrouble.noted.Storage; +import me.youhavetrouble.noted.note.Note; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.IntegrationType; +import net.dv8tion.jda.api.interactions.InteractionContextType; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; + +import java.awt.*; +import java.util.List; + +public class EditNoteCommand extends Command { + + private final java.util.List optionMappingIds = List.of( + "title", + "content", + "title-url", + "image-url", + "thumbnail-url", + "color", + "author", + "author-url", + "footer", + "footer-url" + ); + + @Override + public void register(JDA jda, String name) { + jda.upsertCommand(Commands.slash("edit-note", "Edit a note") + .setIntegrationTypes(IntegrationType.USER_INSTALL) + .addOptions( + new OptionData(OptionType.STRING, "alias", "An alias for the note") + .setMinLength(1) + .setMaxLength(256) + .setRequired(true), + new OptionData(OptionType.STRING, "title", "The title of the note") + .setMinLength(1) + .setMaxLength(256) + .setRequired(false), + new OptionData(OptionType.STRING, "content", "The content of the note") + .setMinLength(1) + .setMaxLength(4096) + .setRequired(false), + new OptionData(OptionType.STRING, "title-url", "The image URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false), + new OptionData(OptionType.STRING, "image-url", "The image URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false), + new OptionData(OptionType.STRING, "thumbnail-url", "The thumbnail URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false), + new OptionData(OptionType.STRING, "color", "The color of the note") + .setMinLength(7) + .setMaxLength(7) + .setRequired(false), + new OptionData(OptionType.STRING, "author", "The author of the note") + .setMinLength(1) + .setMaxLength(256) + .setRequired(false), + new OptionData(OptionType.STRING, "author-url", "The author URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false), + new OptionData(OptionType.STRING, "footer", "The footer of the note") + .setMinLength(1) + .setMaxLength(256) + .setRequired(false), + new OptionData(OptionType.STRING, "footer-url", "The footer URL of the note") + .setMinLength(1) + .setMaxLength(2000) + .setRequired(false) + ) + .setContexts(InteractionContextType.BOT_DM) + ).queue(); + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + OptionMapping noteAliasMapping = event.getOption("alias"); + if (noteAliasMapping == null) { + event.reply("Please provide a note alias.") + .setEphemeral(true) + .queue(); + return; + } + String noteAlias = noteAliasMapping.getAsString(); + + Note note = Main.getStorage().getNote(noteAlias); + if (note == null) { + event.reply("Note with alias %s not found.".formatted(noteAlias)) + .setEphemeral(true) + .queue(); + return; + } + + boolean shouldOpenModal = true; + + for (String optionMappingId : optionMappingIds) { + if (event.getOption(optionMappingId) != null) { + shouldOpenModal = false; + break; + } + } + + if (shouldOpenModal) { + // TODO open modal with few basic fields + event.reply("You need to provide arguments what parts of the note to edit.") + .setEphemeral(true) + .queue(); + return; + } + + OptionMapping titleOption = event.getOption("title"); + if (titleOption != null) { + note = note.withTitle(titleOption.getAsString()); + } + + OptionMapping titleUrlOption = event.getOption("title-url"); + if (titleUrlOption != null) { + note = note.withTitleUrl(titleUrlOption.getAsString()); + } + + OptionMapping contentOption = event.getOption("content"); + if (contentOption != null) { + note = note.withContent(contentOption.getAsString()); + } + + OptionMapping imageUrlOption = event.getOption("image-url"); + if (imageUrlOption != null) { + note = note.withImageUrl(imageUrlOption.getAsString()); + } + + OptionMapping thumbnailUrlOption = event.getOption("thumbnail-url"); + if (thumbnailUrlOption != null) { + note = note.withThumbnailUrl(thumbnailUrlOption.getAsString()); + } + + OptionMapping colorOption = event.getOption("color"); + if (colorOption != null) { + try { + note = note.withColor(Color.decode(colorOption.getAsString())); + } catch (NumberFormatException e) { + event.reply("Invalid color.") + .setEphemeral(true) + .queue(); + return; + } + } + + OptionMapping authorOption = event.getOption("author"); + if (authorOption != null) { + note = note.withAuthor(authorOption.getAsString()); + } + + OptionMapping authorUrlOption = event.getOption("author-url"); + if (authorUrlOption != null) { + note = note.withAuthorUrl(authorUrlOption.getAsString()); + } + + OptionMapping footerOption = event.getOption("footer"); + if (footerOption != null) { + note = note.withFooter(footerOption.getAsString()); + } + + OptionMapping footerUrlOption = event.getOption("footer-url"); + if (footerUrlOption != null) { + note = note.withFooterUrl(footerUrlOption.getAsString()); + } + + Storage.Status status = Main.getStorage().editNote(note.id, note); + + if (status == Storage.Status.SUCCESS) { + event.reply("Note edited.") + .setEphemeral(true) + .queue(); + } else { + event.reply("Failed to edit note.") + .setEphemeral(true) + .queue(); + } + } + + @Override + public String getName() { + return "edit-note"; + } + +} diff --git a/src/main/java/me/youhavetrouble/noted/command/NoteCommand.java b/src/main/java/me/youhavetrouble/noted/command/NoteCommand.java new file mode 100644 index 0000000..67cadc2 --- /dev/null +++ b/src/main/java/me/youhavetrouble/noted/command/NoteCommand.java @@ -0,0 +1,85 @@ +package me.youhavetrouble.noted.command; + +import me.youhavetrouble.noted.Main; +import me.youhavetrouble.noted.note.Note; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.IntegrationType; +import net.dv8tion.jda.api.interactions.InteractionContextType; +import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.commands.build.OptionData; + +import java.util.Locale; + +public class NoteCommand extends Command { + + private final String commandName; + + public NoteCommand(String commandName) { + if (commandName.equals("add-note") || commandName.equals("edit-note")) { + Main.logger.warning("The command name " + commandName + " is reserved. Please change the command name in noted.properties."); + Main.logger.warning("The command name has been changed to 'note'."); + this.commandName = "note"; + return; + } + this.commandName = commandName.toLowerCase(Locale.ENGLISH); + } + + @Override + public void register(JDA jda, String name) { + jda.upsertCommand(Commands.slash(commandName, "Get a note") + .setIntegrationTypes(IntegrationType.GUILD_INSTALL, IntegrationType.USER_INSTALL) + .addOptions( + new OptionData(OptionType.STRING, "alias", "The ID of the note", true, true), + new OptionData(OptionType.BOOLEAN, "ephermeal", "Whether the note should be ephermal", false) + ) + .setContexts(InteractionContextType.BOT_DM, InteractionContextType.GUILD, InteractionContextType.PRIVATE_CHANNEL) + ).queue(); + } + + @Override + public void execute(SlashCommandInteractionEvent event) { + OptionMapping noteIdOption = event.getOption("alias"); + OptionMapping ephemeralOption = event.getOption("ephemeral"); + if (noteIdOption == null) { + event.reply("Please provide a note alias.") + .setEphemeral(true) + .queue(); + return; + } + boolean ephemeral; + try { + ephemeral = ephemeralOption != null && ephemeralOption.getAsBoolean(); + } catch (IllegalArgumentException e) { + event.reply("Invalid value for ephemeral.") + .setEphemeral(true) + .queue(); + return; + } + String noteAlias = noteIdOption.getAsString(); + Note note = Main.getStorage().getNote(noteAlias); + if (note == null) { + event.reply("Note with alias %s not found.".formatted(noteAlias)) + .setEphemeral(true) + .queue(); + return; + } + + event.replyEmbeds(note.toEmbed()) + .setEphemeral(ephemeral) + .queue(); + } + + @Override + public boolean canUse(User user) { + return true; + } + + @Override + public String getName() { + return commandName; + } +} diff --git a/src/main/java/me/youhavetrouble/noted/listener/SlashCommandListener.java b/src/main/java/me/youhavetrouble/noted/listener/SlashCommandListener.java index 9d8577b..29f4ad1 100644 --- a/src/main/java/me/youhavetrouble/noted/listener/SlashCommandListener.java +++ b/src/main/java/me/youhavetrouble/noted/listener/SlashCommandListener.java @@ -1,45 +1,24 @@ package me.youhavetrouble.noted.listener; import me.youhavetrouble.noted.Main; -import me.youhavetrouble.noted.Storage; import me.youhavetrouble.noted.note.Note; -import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.interactions.commands.Command; -import net.dv8tion.jda.api.interactions.commands.OptionMapping; - -import java.awt.Color; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; public class SlashCommandListener extends ListenerAdapter { - private final List optionMappingIds = List.of( - "title", - "content", - "title-url", - "image-url", - "thumbnail-url", - "color", - "author", - "author-url", - "footer", - "footer-url" - ); - - private final Set aliases; - public SlashCommandListener() { - aliases = Main.getStorage().getAliases(); + Note.ALIASES.addAll(Main.getStorage().getAliases()); } @Override public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent event) { if (!event.getFocusedOption().getName().equals("alias")) return; - List options = aliases.stream() + List options = Note.ALIASES.stream() .filter(word -> word.startsWith(event.getFocusedOption().getValue())) .map(word -> new Command.Choice(word, word)) .limit(25) @@ -49,394 +28,8 @@ public class SlashCommandListener extends ListenerAdapter { @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { - if (Main.command.equals(event.getName())) { - OptionMapping noteIdOption = event.getOption("alias"); - OptionMapping ephemeralOption = event.getOption("ephemeral"); - if (noteIdOption == null) { - event.reply("Please provide a note alias.") - .setEphemeral(true) - .queue(); - return; - } - boolean ephemeral = false; - try { - ephemeral = ephemeralOption != null && ephemeralOption.getAsBoolean(); - } catch (IllegalArgumentException e) { - event.reply("Invalid value for ephemeral.") - .setEphemeral(true) - .queue(); - return; - } - String noteId = noteIdOption.getAsString(); - getNote(event, noteId, ephemeral); - return; - } - - switch (event.getName()) { - case "add-note" -> { - if (!isAdmin(event.getUser())) { - event.reply("You do not have permission to use this command.") - .setEphemeral(true) - .queue(); - return; - } - addNote(event); - } - case "edit-note" -> { - if (!isAdmin(event.getUser())) { - event.reply("You do not have permission to use this command.") - .setEphemeral(true) - .queue(); - return; - } - editNote(event); - } - case "delete-note" -> { - if (!isAdmin(event.getUser())) { - event.reply("You do not have permission to use this command.") - .setEphemeral(true) - .queue(); - return; - } - deleteNote(event); - } - case "add-alias" -> { - if (!isAdmin(event.getUser())) { - event.reply("You do not have permission to use this command.") - .setEphemeral(true) - .queue(); - return; - } - addAlias(event); - } - case "delete-alias" -> { - if (!isAdmin(event.getUser())) { - event.reply("You do not have permission to use this command.") - .setEphemeral(true) - .queue(); - return; - } - deleteAlias(event); - } - - default -> event.reply("Unknown command.") - .setEphemeral(true) - .queue(); - } - } - - private boolean isAdmin(User user) { - Long adminId = Main.getAdminId(); - return adminId != null && adminId.equals(user.getIdLong()); - } - - private void getNote(SlashCommandInteractionEvent event, String noteAlias, boolean ephemeral) { - Note note = Main.getStorage().getNote(noteAlias); - if (note == null) { - event.reply("Note with alias %s not found.".formatted(noteAlias)) - .setEphemeral(true) - .queue(); - return; - } - - event.replyEmbeds(note.toEmbed()) - .setEphemeral(ephemeral) - .queue(); - } - - private void addNote(SlashCommandInteractionEvent event) { - OptionMapping aliasOption = event.getOption("alias"); - OptionMapping titleOption = event.getOption("title"); - OptionMapping contentOption = event.getOption("content"); - - if (titleOption == null || contentOption == null || aliasOption == null) { - event.reply("Please provide a alias, title and content.") - .setEphemeral(true) - .queue(); - return; - } - - Note note = Note.createNew(titleOption.getAsString(), contentOption.getAsString()); - - OptionMapping titleUrlOption = event.getOption("title-url"); - if (titleUrlOption != null) { - note = note.withTitleUrl(titleUrlOption.getAsString()); - } - - OptionMapping imageUrlOption = event.getOption("image-url"); - if (imageUrlOption != null) { - note = note.withImageUrl(imageUrlOption.getAsString()); - } - - OptionMapping thumbnailUrlOption = event.getOption("thumbnail-url"); - if (thumbnailUrlOption != null) { - note = note.withThumbnailUrl(thumbnailUrlOption.getAsString()); - } - - OptionMapping colorOption = event.getOption("color"); - if (colorOption != null) { - try { - note = note.withColor(Color.decode(colorOption.getAsString())); - } catch (NumberFormatException e) { - event.reply("Invalid color.") - .setEphemeral(true) - .queue(); - return; - } - } - - OptionMapping authorOption = event.getOption("author"); - if (authorOption != null) { - note = note.withAuthor(authorOption.getAsString()); - } - - OptionMapping authorUrlOption = event.getOption("author-url"); - if (authorUrlOption != null) { - note = note.withAuthorUrl(authorUrlOption.getAsString()); - } - - OptionMapping footerOption = event.getOption("footer"); - if (footerOption != null) { - note = note.withFooter(footerOption.getAsString()); - } - - OptionMapping footerUrlOption = event.getOption("footer-url"); - if (footerUrlOption != null) { - note = note.withFooterUrl(footerUrlOption.getAsString()); - } - - String alias = aliasOption.getAsString(); - Storage.Status status = Main.getStorage().addNote(note, alias); - - if (status == Storage.Status.SUCCESS) { - aliases.add(alias); - event.reply("Note added.") - .setEphemeral(true) - .queue(); - } else if (status == Storage.Status.ALIAS_EXISTS) { - event.reply("Alias already exists.") - .setEphemeral(true) - .queue(); - } else { - event.reply("Failed to add note.") - .setEphemeral(true) - .queue(); - } - - } - - private void editNote(SlashCommandInteractionEvent event) { - OptionMapping noteAliasMapping = event.getOption("alias"); - if (noteAliasMapping == null) { - event.reply("Please provide a note alias.") - .setEphemeral(true) - .queue(); - return; - } - String noteAlias = noteAliasMapping.getAsString(); - - Note note = Main.getStorage().getNote(noteAlias); - if (note == null) { - event.reply("Note with alias %s not found.".formatted(noteAlias)) - .setEphemeral(true) - .queue(); - return; - } - - boolean shouldOpenModal = true; - - for (String optionMappingId : optionMappingIds) { - if (event.getOption(optionMappingId) != null) { - shouldOpenModal = false; - break; - } - } - - if (shouldOpenModal) { - // TODO open modal with few basic fields - event.reply("You need to provide arguments what parts of the note to edit.") - .setEphemeral(true) - .queue(); - return; - } - - OptionMapping titleOption = event.getOption("title"); - if (titleOption != null) { - note = note.withTitle(titleOption.getAsString()); - } - - OptionMapping titleUrlOption = event.getOption("title-url"); - if (titleUrlOption != null) { - note = note.withTitleUrl(titleUrlOption.getAsString()); - } - - OptionMapping contentOption = event.getOption("content"); - if (contentOption != null) { - note = note.withContent(contentOption.getAsString()); - } - - OptionMapping imageUrlOption = event.getOption("image-url"); - if (imageUrlOption != null) { - note = note.withImageUrl(imageUrlOption.getAsString()); - } - - OptionMapping thumbnailUrlOption = event.getOption("thumbnail-url"); - if (thumbnailUrlOption != null) { - note = note.withThumbnailUrl(thumbnailUrlOption.getAsString()); - } - - OptionMapping colorOption = event.getOption("color"); - if (colorOption != null) { - try { - note = note.withColor(Color.decode(colorOption.getAsString())); - } catch (NumberFormatException e) { - event.reply("Invalid color.") - .setEphemeral(true) - .queue(); - return; - } - } - - OptionMapping authorOption = event.getOption("author"); - if (authorOption != null) { - note = note.withAuthor(authorOption.getAsString()); - } - - OptionMapping authorUrlOption = event.getOption("author-url"); - if (authorUrlOption != null) { - note = note.withAuthorUrl(authorUrlOption.getAsString()); - } - - OptionMapping footerOption = event.getOption("footer"); - if (footerOption != null) { - note = note.withFooter(footerOption.getAsString()); - } - - OptionMapping footerUrlOption = event.getOption("footer-url"); - if (footerUrlOption != null) { - note = note.withFooterUrl(footerUrlOption.getAsString()); - } - - Storage.Status status = Main.getStorage().editNote(note.id, note); - - if (status == Storage.Status.SUCCESS) { - event.reply("Note edited.") - .setEphemeral(true) - .queue(); - } else { - event.reply("Failed to edit note.") - .setEphemeral(true) - .queue(); - } - - } - - private void deleteNote(SlashCommandInteractionEvent event) { - OptionMapping noteAliasMapping = event.getOption("alias"); - if (noteAliasMapping == null) { - event.reply("Please provide a note alias.") - .setEphemeral(true) - .queue(); - return; - } - String noteAlias = noteAliasMapping.getAsString(); - - Note note = Main.getStorage().getNote(noteAlias); - if (note == null) { - event.reply("Note with alias %s not found.".formatted(noteAlias)) - .setEphemeral(true) - .queue(); - return; - } - - Set noteAliases = Main.getStorage().getAliases(note.id); - Storage.Status status = Main.getStorage().deleteNote(note.id); - if (status == Storage.Status.SUCCESS) { - noteAliases.forEach(aliases::remove); - event.reply("Note deleted.") - .setEphemeral(true) - .queue(); - } else { - event.reply("Failed to delete note.") - .setEphemeral(true) - .queue(); - } - } - - private void addAlias(SlashCommandInteractionEvent event) { - OptionMapping noteAliasMapping = event.getOption("alias"); - if (noteAliasMapping == null) { - event.reply("Please provide a note alias.") - .setEphemeral(true) - .queue(); - return; - } - OptionMapping newAliasMapping = event.getOption("new-alias"); - if (newAliasMapping == null) { - event.reply("Please provide a new alias.") - .setEphemeral(true) - .queue(); - return; - } - - String noteAlias = noteAliasMapping.getAsString(); - - Note note = Main.getStorage().getNote(noteAlias); - if (note == null) { - event.reply("Note with alias %s not found.".formatted(noteAlias)) - .setEphemeral(true) - .queue(); - return; - } - - Storage.Status status = Main.getStorage().addAlias(newAliasMapping.getAsString(), note.id); - if (status == Storage.Status.SUCCESS) { - aliases.add(newAliasMapping.getAsString()); - event.reply("Alias added.") - .setEphemeral(true) - .queue(); - } else if (status == Storage.Status.ALIAS_EXISTS) { - event.reply("Alias already exists.") - .setEphemeral(true) - .queue(); - } else { - event.reply("Failed to add alias.") - .setEphemeral(true) - .queue(); - } - - } - - private void deleteAlias(SlashCommandInteractionEvent event) { - OptionMapping aliasMapping = event.getOption("alias"); - if (aliasMapping == null) { - event.reply("Please provide a alias.") - .setEphemeral(true) - .queue(); - return; - } - - String alias = aliasMapping.getAsString(); - - Storage.Status status = Main.getStorage().deleteAlias(alias); - switch (status) { - case SUCCESS -> { - aliases.remove(alias); - event.reply("Alias deleted.") - .setEphemeral(true) - .queue(); - } - case ALIAS_NOT_FOUND -> event.reply("Provided alias does not exist.") - .setEphemeral(true) - .queue(); - case ALIAS_IS_REQUIRED -> event.reply("At least one alias per note is required.") - .setEphemeral(true) - .queue(); - case null, default -> event.reply("Failed to delete alias.") - .setEphemeral(true) - .queue(); - } - + String command = event.getName(); + me.youhavetrouble.noted.command.Command.executeCommand(event, command); } } diff --git a/src/main/java/me/youhavetrouble/noted/note/Note.java b/src/main/java/me/youhavetrouble/noted/note/Note.java index ea0760a..cf82696 100644 --- a/src/main/java/me/youhavetrouble/noted/note/Note.java +++ b/src/main/java/me/youhavetrouble/noted/note/Note.java @@ -6,10 +6,14 @@ import net.dv8tion.jda.api.entities.MessageEmbed; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.Color; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; public class Note { + public static final Set ALIASES = new HashSet<>(); + public final UUID id; public final String title; public final String titleUrl;