From 0b8607d03be011e7611b636a964969bd2f852360 Mon Sep 17 00:00:00 2001 From: YouHaveTrouble Date: Tue, 6 Aug 2024 00:04:18 +0200 Subject: [PATCH] start storage implementation --- pom.xml | 17 ++++ .../java/me/youhavetrouble/noted/Main.java | 18 ++++- .../noted/SlashCommandListener.java | 52 ++++++------ .../java/me/youhavetrouble/noted/Storage.java | 80 +++++++++++++++++++ .../me/youhavetrouble/noted/note/Note.java | 57 ++++++++++--- 5 files changed, 186 insertions(+), 38 deletions(-) create mode 100644 src/main/java/me/youhavetrouble/noted/Storage.java diff --git a/pom.xml b/pom.xml index 70c62a2..a822342 100644 --- a/pom.xml +++ b/pom.xml @@ -88,6 +88,23 @@ + + org.xerial + sqlite-jdbc + 3.46.0.0 + compile + + + com.zaxxer + HikariCP + 5.1.0 + compile + + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + \ No newline at end of file diff --git a/src/main/java/me/youhavetrouble/noted/Main.java b/src/main/java/me/youhavetrouble/noted/Main.java index d912981..d87c346 100644 --- a/src/main/java/me/youhavetrouble/noted/Main.java +++ b/src/main/java/me/youhavetrouble/noted/Main.java @@ -13,12 +13,15 @@ import java.io.*; import java.util.Collections; import java.util.Properties; import java.util.concurrent.Executors; +import java.util.logging.Logger; public class Main { + private static final Logger logger = Logger.getLogger("Main"); private static final Properties properties = new Properties(); private static String version = "Unknown version"; + private static Storage storage; public static JDA jda; public static void main(String[] args) throws InterruptedException { @@ -28,13 +31,17 @@ public class Main { if (resource != null) { version = new String(resource.readAllBytes()); } else { - System.err.println("Version file missing."); + logger.severe("Version file missing."); } } catch (IOException e) { throw new RuntimeException(e); } - System.out.println("Starting " + version); + storage = new Storage(); + + Runtime.getRuntime().addShutdownHook(new Thread(Main::shutdown)); + + logger.info("Starting " + version); jda = JDABuilder.createLight(properties.getProperty("DISCORD_TOKEN"), Collections.emptyList()) .setCallbackPool(Executors.newVirtualThreadPerTaskExecutor()) @@ -73,7 +80,7 @@ public class Main { try (InputStream resource = Main.class.getClassLoader().getResourceAsStream("noted.properites"); OutputStream outputStream = new FileOutputStream(file)) { if (resource == null) { - System.err.println("Default noted.properties missing."); + logger.severe("Default noted.properties missing."); return; } outputStream.write(resource.readAllBytes()); @@ -86,4 +93,9 @@ public class Main { return version; } + private static void shutdown() { + jda.shutdown(); + storage.shutdown(); + } + } \ No newline at end of file diff --git a/src/main/java/me/youhavetrouble/noted/SlashCommandListener.java b/src/main/java/me/youhavetrouble/noted/SlashCommandListener.java index af330e0..ed60b44 100644 --- a/src/main/java/me/youhavetrouble/noted/SlashCommandListener.java +++ b/src/main/java/me/youhavetrouble/noted/SlashCommandListener.java @@ -4,7 +4,6 @@ import me.youhavetrouble.noted.note.Note; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.interactions.commands.OptionMapping; -import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; public class SlashCommandListener extends ListenerAdapter { @@ -15,35 +14,42 @@ public class SlashCommandListener extends ListenerAdapter { OptionMapping noteIdOption = event.getOption("note-id"); OptionMapping ephemeralOption = event.getOption("ephemeral"); if (noteIdOption == null) { - event.reply("Please provide a note ID.").setEphemeral(true).queue(); + event.reply("Please provide a note ID.") + .setEphemeral(true) + .queue(); return; } - Note note = new Note( - "Sample Note Title", - null, - "Bottom text", - null, - null, - null, - null, - null, - null, - null - ); - ReplyCallbackAction action = event.replyEmbeds(note.toEmbed()); - if (ephemeralOption != null && ephemeralOption.getAsBoolean()) { - action = action.setEphemeral(true); + boolean ephemeral; + try { + ephemeral = ephemeralOption != null && ephemeralOption.getAsBoolean(); + } catch (IllegalArgumentException e) { + event.reply("Invalid value for ephemeral.") + .setEphemeral(true) + .queue(); + return; } - action.queue(); - + String noteId = noteIdOption.getAsString(); + getNote(event, noteId, ephemeral); } - default -> { - event.reply("Unknown command.").setEphemeral(true).queue(); - return; - } + default -> event.reply("Unknown command.") + .setEphemeral(true) + .queue(); } } + private void getNote(SlashCommandInteractionEvent event, String noteId, boolean ephemeral) { + Note note = Note.cache.getIfPresent(noteId); + if (note == null) { + event.reply("Note with ID %s not found.".formatted(noteId)) + .setEphemeral(true) + .queue(); + return; + } + event.replyEmbeds(note.toEmbed()) + .setEphemeral(ephemeral) + .queue(); + } + } diff --git a/src/main/java/me/youhavetrouble/noted/Storage.java b/src/main/java/me/youhavetrouble/noted/Storage.java new file mode 100644 index 0000000..1a65bf8 --- /dev/null +++ b/src/main/java/me/youhavetrouble/noted/Storage.java @@ -0,0 +1,80 @@ +package me.youhavetrouble.noted; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import javax.sql.DataSource; +import java.io.File; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.logging.Logger; + + + +public class Storage { + + private final Logger logger = Logger.getLogger("Storage"); + + private final DataSource dataSource; + + public Storage() { + File dataFolder = new File("data"); + if (!dataFolder.exists()) { + if (!dataFolder.mkdir()) { + throw new RuntimeException("Failed to create data folder"); + } + } + HikariConfig config = new HikariConfig(); + config.setPoolName("DataSQLitePool"); + config.setDriverClassName("org.sqlite.JDBC"); + config.setJdbcUrl("jdbc:sqlite:data/data.db"); + config.setConnectionTestQuery("SELECT 1"); + config.setMaxLifetime(60000); // 60 Sec + config.setMaximumPoolSize(Math.min(1, Runtime.getRuntime().availableProcessors() / 4)); + dataSource = new HikariDataSource(config); + + try (Connection connection = dataSource.getConnection()) { + connection.createStatement().execute("PRAGMA journal_mode=WAL;"); + } catch (SQLException e) { + logger.warning("Failed to set journal mode to WAL"); + } + + createTables(); + + } + + protected void shutdown() { + if (dataSource instanceof HikariDataSource) { + ((HikariDataSource) dataSource).close(); + } + } + + private void createTables() { + try (Connection connection = dataSource.getConnection()) { + connection.createStatement().execute(""" + CREATE TABLE IF NOT EXISTS notes ( + id VARCHAR(36) NOT NULL PRIMARY KEY, + title VARCHAR(256), + title_url VARCHAR(2000), + description VARCHAR(4096), + image_url VARCHAR(2000), + thumbnail_url VARCHAR(2000), + color INTEGER, + author VARCHAR(256), + author_url VARCHAR(2000), + footer VARCHAR(256), + footer_url VARCHAR(2000) + ) + """); + connection.createStatement().execute(""" + CREATE TABLE IF NOT EXISTS aliases ( + alias VARCHAR(256) NOT NULL UNIQUE PRIMARY KEY, + note_id VARCHAR(36) NOT NULL + ) + """); + } catch (SQLException e) { + logger.warning("Failed to create tables"); + } + } + +} diff --git a/src/main/java/me/youhavetrouble/noted/note/Note.java b/src/main/java/me/youhavetrouble/noted/note/Note.java index bd7a874..889beaf 100644 --- a/src/main/java/me/youhavetrouble/noted/note/Note.java +++ b/src/main/java/me/youhavetrouble/noted/note/Note.java @@ -1,23 +1,56 @@ 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; -public record Note( - @NotNull String title, - @Nullable String titleUrl, - @NotNull String content, - @Nullable String imageUrl, - @Nullable String thumbnailUrl, - @Nullable Color color, - @Nullable String author, - @Nullable String authorUrl, - @Nullable String footer, - @Nullable String footerUrl - ) { +public class Note { + + public static final Cache cache = Caffeine.newBuilder() + .maximumSize(100) + .expireAfterWrite(Duration.ofMinutes(1)) + .build(); + + public final String title; + public final String titleUrl; + public final String content; + public final String imageUrl; + public final String thumbnailUrl; + public final Color color; + public final String author; + public final String authorUrl; + public final String footer; + public final String footerUrl; + + private Note( + @NotNull String title, + @Nullable String titleUrl, + @NotNull String content, + @Nullable String imageUrl, + @Nullable String thumbnailUrl, + @Nullable Color color, + @Nullable String author, + @Nullable String authorUrl, + @Nullable String footer, + @Nullable String footerUrl + ) { + this.title = title; + this.titleUrl = titleUrl; + this.content = content; + this.imageUrl = imageUrl; + this.thumbnailUrl = thumbnailUrl; + this.color = color; + this.author = author; + this.authorUrl = authorUrl; + this.footer = footer; + this.footerUrl = footerUrl; + } public MessageEmbed toEmbed() { return new EmbedBuilder()