From 784f11206d7c4460a2cfa33a04ae82740866c44a Mon Sep 17 00:00:00 2001 From: YouHaveTrouble Date: Tue, 6 Aug 2024 23:14:28 +0200 Subject: [PATCH] adding notes --- .../java/me/youhavetrouble/noted/Main.java | 33 +++++- .../noted/SlashCommandListener.java | 53 ++++++++- .../java/me/youhavetrouble/noted/Storage.java | 108 ++++++++++++++++++ .../me/youhavetrouble/noted/note/Note.java | 34 ++++-- src/main/resources/noted.properites | 1 + 5 files changed, 214 insertions(+), 15 deletions(-) diff --git a/src/main/java/me/youhavetrouble/noted/Main.java b/src/main/java/me/youhavetrouble/noted/Main.java index d87c346..8d2cbdf 100644 --- a/src/main/java/me/youhavetrouble/noted/Main.java +++ b/src/main/java/me/youhavetrouble/noted/Main.java @@ -8,6 +8,7 @@ import net.dv8tion.jda.api.interactions.InteractionContextType; 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 org.jetbrains.annotations.Nullable; import java.io.*; import java.util.Collections; @@ -22,6 +23,7 @@ public class Main { private static final Properties properties = new Properties(); private static String version = "Unknown version"; private static Storage storage; + private static Long adminId; public static JDA jda; public static void main(String[] args) throws InterruptedException { @@ -43,6 +45,8 @@ public class Main { logger.info("Starting " + version); + adminId = Long.parseLong(properties.getProperty("ADMIN_USER_ID")); + jda = JDABuilder.createLight(properties.getProperty("DISCORD_TOKEN"), Collections.emptyList()) .setCallbackPool(Executors.newVirtualThreadPerTaskExecutor()) .setActivity(Activity.customStatus("Notekeeping...")) @@ -58,8 +62,24 @@ public class Main { new OptionData(OptionType.BOOLEAN, "ephermeal", "Whether the note should be ephermal").setRequired(false) ) .setContexts(InteractionContextType.BOT_DM, InteractionContextType.GUILD, InteractionContextType.PRIVATE_CHANNEL) - ) - .queue(); + ).queue(); + + jda.upsertCommand(Commands.slash("add-note", "Add a note") + .setIntegrationTypes(IntegrationType.GUILD_INSTALL, IntegrationType.USER_INSTALL) + .addOptions( + new OptionData(OptionType.STRING, "alias", "An alias for the note").setRequired(true), + new OptionData(OptionType.STRING, "title", "The title of the note").setRequired(true), + new OptionData(OptionType.STRING, "content", "The content of the note").setRequired(true), + new OptionData(OptionType.STRING, "image-url", "The image URL of the note").setRequired(false), + new OptionData(OptionType.STRING, "thumbnail-url", "The thumbnail URL of the note").setRequired(false), + new OptionData(OptionType.STRING, "color", "The color of the note").setRequired(false), + new OptionData(OptionType.STRING, "author", "The author of the note").setRequired(false), + new OptionData(OptionType.STRING, "author-url", "The author URL of the note").setRequired(false), + new OptionData(OptionType.STRING, "footer", "The footer of the note").setRequired(false), + new OptionData(OptionType.STRING, "footer-url", "The footer URL of the note").setRequired(false) + ) + .setContexts(InteractionContextType.BOT_DM) + ).queue(); } @@ -89,10 +109,19 @@ public class Main { } } + @Nullable + public static Long getAdminId() { + return adminId; + } + public static String getVersion() { return version; } + public static Storage getStorage() { + return storage; + } + private static void shutdown() { jda.shutdown(); storage.shutdown(); diff --git a/src/main/java/me/youhavetrouble/noted/SlashCommandListener.java b/src/main/java/me/youhavetrouble/noted/SlashCommandListener.java index ed60b44..edc894b 100644 --- a/src/main/java/me/youhavetrouble/noted/SlashCommandListener.java +++ b/src/main/java/me/youhavetrouble/noted/SlashCommandListener.java @@ -19,7 +19,6 @@ public class SlashCommandListener extends ListenerAdapter { .queue(); return; } - boolean ephemeral; try { ephemeral = ephemeralOption != null && ephemeralOption.getAsBoolean(); @@ -32,6 +31,29 @@ public class SlashCommandListener extends ListenerAdapter { String noteId = noteIdOption.getAsString(); getNote(event, noteId, ephemeral); } + case "add-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; + } + + 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; + } + String alias = aliasOption.getAsString(); + String title = titleOption.getAsString(); + String content = contentOption.getAsString(); + addNote(event, alias, title, content); + } default -> event.reply("Unknown command.") .setEphemeral(true) @@ -39,17 +61,40 @@ public class SlashCommandListener extends ListenerAdapter { } } - private void getNote(SlashCommandInteractionEvent event, String noteId, boolean ephemeral) { - Note note = Note.cache.getIfPresent(noteId); + private void getNote(SlashCommandInteractionEvent event, String noteAlias, boolean ephemeral) { + Note note = Main.getStorage().getNote(noteAlias); if (note == null) { - event.reply("Note with ID %s not found.".formatted(noteId)) + event.reply("Note with ID %s not found.".formatted(noteAlias)) .setEphemeral(true) .queue(); return; } + event.replyEmbeds(note.toEmbed()) .setEphemeral(ephemeral) .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); + + if (status == Storage.Status.ALIAS_EXISTS) { + event.reply("Alias already exists.") + .setEphemeral(true) + .queue(); + return; + } + if (status == Storage.Status.ERROR) { + event.reply("An error occurred.") + .setEphemeral(true) + .queue(); + return; + } + + event.reply("Note added.") + .setEphemeral(true) + .queue(); + } + } diff --git a/src/main/java/me/youhavetrouble/noted/Storage.java b/src/main/java/me/youhavetrouble/noted/Storage.java index 1a65bf8..d757dc8 100644 --- a/src/main/java/me/youhavetrouble/noted/Storage.java +++ b/src/main/java/me/youhavetrouble/noted/Storage.java @@ -2,11 +2,18 @@ package me.youhavetrouble.noted; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; +import me.youhavetrouble.noted.note.Note; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.sql.DataSource; +import java.awt.*; import java.io.File; import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; +import java.util.UUID; import java.util.logging.Logger; @@ -77,4 +84,105 @@ public class Storage { } } + public Status addNote(@NotNull Note note, @NotNull String alias) { + try (Connection connection = dataSource.getConnection()) { + connection.setAutoCommit(false); + PreparedStatement statement = connection.prepareStatement(""" + INSERT INTO notes (id, title, title_url, description, image_url, thumbnail_url, color, author, author_url, footer, footer_url) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + """); + + statement.setString(1, note.id.toString()); + statement.setString(2, note.title); + statement.setString(3, note.titleUrl); + statement.setString(4, note.content); + statement.setString(5, note.imageUrl); + statement.setString(6, note.thumbnailUrl); + statement.setInt(7, note.color != null ? note.color.getRGB() : 0); + statement.setString(8, note.author); + statement.setString(9, note.authorUrl); + statement.setString(10, note.footer); + statement.setString(11, note.footerUrl); + statement.executeUpdate(); + + statement = connection.prepareStatement(""" + INSERT INTO aliases (alias, note_id) + VALUES (?, ?) + """); + statement.setString(1, alias); + statement.setString(2, note.id.toString()); + statement.executeUpdate(); + + connection.commit(); + return Status.SUCCESS; + } catch (SQLException e) { + if (e.getErrorCode() == 19) { + return Status.ALIAS_EXISTS; + } + return Status.ERROR; + } + } + + public Status addAlias(@NotNull String alias, @NotNull UUID noteId) throws RuntimeException { + try (Connection connection = dataSource.getConnection()) { + PreparedStatement statement = connection.prepareStatement(""" + INSERT INTO aliases (alias, note_id) + VALUES (?, ?) + """); + statement.setString(1, alias); + statement.setString(2, noteId.toString()); + statement.executeUpdate(); + return Status.SUCCESS; + } catch (SQLException e) { + logger.warning("Failed to add alias"); + return Status.ALIAS_EXISTS; + } + } + + @Nullable + public Note getNote(@NotNull String alias) { + try (Connection connection = dataSource.getConnection()) { + PreparedStatement statement = connection.prepareStatement(""" + SELECT * FROM notes + WHERE id = ( + SELECT note_id FROM aliases + WHERE alias = ? + ) + """); + statement.setString(1, alias); + ResultSet resultSet = statement.executeQuery(); + + Color color = null; + try { + color = new Color(resultSet.getInt("color")); + } catch (SQLException ignored) { + } + + + return new Note( + UUID.fromString(resultSet.getString("id")), + resultSet.getString("title"), + resultSet.getString("title_url"), + resultSet.getString("description"), + resultSet.getString("image_url"), + resultSet.getString("thumbnail_url"), + color, + resultSet.getString("author"), + resultSet.getString("author_url"), + resultSet.getString("footer"), + resultSet.getString("footer_url") + ); + } catch (SQLException e) { + logger.warning("Failed to get note"); + return null; + } + } + + public enum Status { + SUCCESS, + ALIAS_EXISTS, + ALIAS_NOT_FOUND, + ERROR + } + } diff --git a/src/main/java/me/youhavetrouble/noted/note/Note.java b/src/main/java/me/youhavetrouble/noted/note/Note.java index 889beaf..c11ea91 100644 --- a/src/main/java/me/youhavetrouble/noted/note/Note.java +++ b/src/main/java/me/youhavetrouble/noted/note/Note.java @@ -1,22 +1,16 @@ package me.youhavetrouble.noted.note; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.MessageEmbed; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.Color; -import java.time.Duration; +import java.util.UUID; public class Note { - public static final Cache cache = Caffeine.newBuilder() - .maximumSize(100) - .expireAfterWrite(Duration.ofMinutes(1)) - .build(); - + public final UUID id; public final String title; public final String titleUrl; public final String content; @@ -28,7 +22,8 @@ public class Note { public final String footer; public final String footerUrl; - private Note( + public Note( + @NotNull UUID id, @NotNull String title, @Nullable String titleUrl, @NotNull String content, @@ -40,6 +35,7 @@ public class Note { @Nullable String footer, @Nullable String footerUrl ) { + this.id = id; this.title = title; this.titleUrl = titleUrl; this.content = content; @@ -64,4 +60,24 @@ public class Note { .build(); } + public static Note createNew( + @NotNull String title, + @NotNull String content + ) { + UUID id = UUID.randomUUID(); + return new Note( + id, + title, + null, + content, + null, + null, + null, + null, + null, + null, + null + ); + } + } diff --git a/src/main/resources/noted.properites b/src/main/resources/noted.properites index fc6629d..b9cf246 100644 --- a/src/main/resources/noted.properites +++ b/src/main/resources/noted.properites @@ -1 +1,2 @@ DISCORD_TOKEN=your_bot_token_here +ADMIN_USER_ID=your_user_id_here