diff --git a/src/main/java/me/youhavetrouble/noted/Main.java b/src/main/java/me/youhavetrouble/noted/Main.java index 92c63e5..dd8a8ba 100644 --- a/src/main/java/me/youhavetrouble/noted/Main.java +++ b/src/main/java/me/youhavetrouble/noted/Main.java @@ -59,14 +59,14 @@ public class Main { jda.upsertCommand(Commands.slash("note", "Get a note") .setIntegrationTypes(IntegrationType.GUILD_INSTALL, IntegrationType.USER_INSTALL) .addOptions( - new OptionData(OptionType.STRING, "note-id", "The ID of the note", true, true), - new OptionData(OptionType.BOOLEAN, "ephermeal", "Whether the note should be ephermal", true) + 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.GUILD_INSTALL, IntegrationType.USER_INSTALL) + .setIntegrationTypes(IntegrationType.USER_INSTALL) .addOptions( new OptionData(OptionType.STRING, "alias", "An alias for the note") .setMinLength(1) @@ -80,6 +80,10 @@ public class Main { .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) @@ -89,7 +93,7 @@ public class Main { .setMaxLength(2000) .setRequired(false), new OptionData(OptionType.STRING, "color", "The color of the note") - .setMinLength(1) + .setMinLength(7) .setMaxLength(7) .setRequired(false), new OptionData(OptionType.STRING, "author", "The author of the note") @@ -112,6 +116,56 @@ public class Main { .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(); } private static void loadProperties() { diff --git a/src/main/java/me/youhavetrouble/noted/Storage.java b/src/main/java/me/youhavetrouble/noted/Storage.java index 11235a1..3adcc2a 100644 --- a/src/main/java/me/youhavetrouble/noted/Storage.java +++ b/src/main/java/me/youhavetrouble/noted/Storage.java @@ -18,8 +18,6 @@ import java.util.List; import java.util.UUID; import java.util.logging.Logger; - - public class Storage { public final List aliases = new ArrayList<>(); @@ -110,8 +108,7 @@ public class Storage { statement.executeUpdate(); statement = connection.prepareStatement(""" - INSERT INTO aliases (alias, note_id) - VALUES (?, ?) + INSERT INTO aliases (alias, note_id) VALUES (?, ?) """); statement.setString(1, alias); statement.setString(2, note.id.toString()); @@ -128,6 +125,31 @@ public class Storage { } } + public Status editNote(UUID noteId, Note note) { + try (Connection connection = dataSource.getConnection()) { + PreparedStatement statement = connection.prepareStatement(""" + UPDATE notes + SET title = ?, title_url = ?, description = ?, image_url = ?, thumbnail_url = ?, color = ?, author = ?, author_url = ?, footer = ?, footer_url = ? + WHERE id = ? + """); + statement.setString(1, note.title); + statement.setString(2, note.titleUrl); + statement.setString(3, note.content); + statement.setString(4, note.imageUrl); + statement.setString(5, note.thumbnailUrl); + statement.setInt(6, note.color != null ? note.color.getRGB() : 0); + statement.setString(7, note.author); + statement.setString(8, note.authorUrl); + statement.setString(9, note.footer); + statement.setString(10, note.footerUrl); + statement.setString(11, noteId.toString()); + statement.executeUpdate(); + return Status.SUCCESS; + } catch (SQLException e) { + return Status.ERROR; + } + } + public Status addAlias(@NotNull String alias, @NotNull UUID noteId) throws RuntimeException { try (Connection connection = dataSource.getConnection()) { PreparedStatement statement = connection.prepareStatement(""" diff --git a/src/main/java/me/youhavetrouble/noted/listener/SlashCommandListener.java b/src/main/java/me/youhavetrouble/noted/listener/SlashCommandListener.java index 63f2951..fa2bd1b 100644 --- a/src/main/java/me/youhavetrouble/noted/listener/SlashCommandListener.java +++ b/src/main/java/me/youhavetrouble/noted/listener/SlashCommandListener.java @@ -6,39 +6,55 @@ import me.youhavetrouble.noted.note.Note; 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.InteractionType; import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.commands.OptionMapping; +import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction; +import java.awt.*; import java.util.List; 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" + ); + @Override public void onCommandAutoCompleteInteraction(CommandAutoCompleteInteractionEvent event) { - if (event.getName().equals("note") && event.getFocusedOption().getName().equals("note-id")) { - List options = Main.getStorage().aliases.stream() - .filter(word -> word.startsWith(event.getFocusedOption().getValue())) - .map(word -> new Command.Choice(word, word)) - .limit(25) - .collect(Collectors.toList()); - event.replyChoices(options).queue(); - } + if (!event.getName().equals("note") || event.getName().equals("edit-note")) return; + if (!event.getFocusedOption().getName().equals("alias")) return; + List options = Main.getStorage().aliases.stream() + .filter(word -> word.startsWith(event.getFocusedOption().getValue())) + .map(word -> new Command.Choice(word, word)) + .limit(25) + .collect(Collectors.toList()); + event.replyChoices(options).queue(); } @Override public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { switch (event.getName()) { case "note" -> { - OptionMapping noteIdOption = event.getOption("note-id"); + OptionMapping noteIdOption = event.getOption("alias"); OptionMapping ephemeralOption = event.getOption("ephemeral"); if (noteIdOption == null) { - event.reply("Please provide a note ID.") + event.reply("Please provide a note alias.") .setEphemeral(true) .queue(); return; } - boolean ephemeral; + boolean ephemeral = false; try { ephemeral = ephemeralOption != null && ephemeralOption.getAsBoolean(); } catch (IllegalArgumentException e) { @@ -58,20 +74,17 @@ public class SlashCommandListener extends ListenerAdapter { .queue(); return; } - - 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.") + addNote(event); + } + case "edit-note" -> { + Long adminId = Main.getAdminId(); + if (adminId == null || !adminId.equals(event.getUser().getIdLong())) { + event.reply("You do not have permission to use this command.") .setEphemeral(true) .queue(); return; } - String alias = aliasOption.getAsString(); - String title = titleOption.getAsString(); - String content = contentOption.getAsString(); - addNote(event, alias, title, content); + editNote(event); } default -> event.reply("Unknown command.") @@ -94,26 +107,189 @@ public class SlashCommandListener extends ListenerAdapter { .queue(); } - private void addNote(SlashCommandInteractionEvent event, String noteAlias, String title, String description) { - Note note = Note.createNew(title, description); - Storage.Status status = Main.getStorage().addNote(note, noteAlias); + private void addNote(SlashCommandInteractionEvent event) { + OptionMapping aliasOption = event.getOption("alias"); + OptionMapping titleOption = event.getOption("title"); + OptionMapping contentOption = event.getOption("content"); - if (status == Storage.Status.ALIAS_EXISTS) { + 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()); + } + + Storage.Status status = Main.getStorage().addNote(note, aliasOption.getAsString()); + + if (status == Storage.Status.SUCCESS) { + 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; } - if (status == Storage.Status.ERROR) { - event.reply("An error occurred.") + 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; } - event.reply("Note added.") - .setEphemeral(true) - .queue(); + 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("Here a modal should open. After I make it.") + .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(); + } + } } diff --git a/src/main/java/me/youhavetrouble/noted/note/Note.java b/src/main/java/me/youhavetrouble/noted/note/Note.java index c11ea91..ea0760a 100644 --- a/src/main/java/me/youhavetrouble/noted/note/Note.java +++ b/src/main/java/me/youhavetrouble/noted/note/Note.java @@ -48,6 +48,165 @@ public class Note { this.footerUrl = footerUrl; } + public Note withTitle(@NotNull String title) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + + public Note withTitleUrl(@Nullable String titleUrl) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + + public Note withContent(@NotNull String content) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + + public Note withImageUrl(@Nullable String imageUrl) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + + public Note withThumbnailUrl(@Nullable String thumbnailUrl) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + + public Note withColor(@Nullable Color color) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + + public Note withAuthor(@Nullable String author) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + public Note withAuthorUrl(@Nullable String authorUrl) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + + public Note withFooter(@Nullable String footer) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + + public Note withFooterUrl(@Nullable String footerUrl) { + return new Note( + id, + title, + titleUrl, + content, + imageUrl, + thumbnailUrl, + color, + author, + authorUrl, + footer, + footerUrl + ); + } + public MessageEmbed toEmbed() { return new EmbedBuilder() .setTitle(title, titleUrl)