diff --git a/db.sqlite b/db.sqlite
new file mode 100644
index 0000000..4b93be5
Binary files /dev/null and b/db.sqlite differ
diff --git a/pom.xml b/pom.xml
index d94b454..31684fc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,6 +30,12 @@
slf4j-simple
2.0.0
+
+ org.xerial
+ sqlite-jdbc
+ 3.39.4.1
+
+
diff --git a/src/main/java/wtf/beatrice/hidekobot/Configuration.java b/src/main/java/wtf/beatrice/hidekobot/Configuration.java
index ea43355..db3b5d9 100644
--- a/src/main/java/wtf/beatrice/hidekobot/Configuration.java
+++ b/src/main/java/wtf/beatrice/hidekobot/Configuration.java
@@ -1,17 +1,23 @@
package wtf.beatrice.hidekobot;
+import org.jetbrains.annotations.Nullable;
+import wtf.beatrice.hidekobot.database.DatabaseManager;
import wtf.beatrice.hidekobot.listeners.MessageLogger;
public class Configuration
{
+ private static DatabaseManager dbManager = null;
private static boolean verbose = false;
private static MessageLogger verbosityLogger;
// todo: allow people to set their own user id
private static final long botOwnerId = 979809420714332260L;
+ private final static String expiryTimestampFormat = "yy/MM/dd HH:mm:ss";
+ private final static long expiryTimeSeconds = 60L;
+
private final static String defaultInviteLink =
"https://discord.com/api/oauth2/authorize?client_id=%userid%&scope=bot+applications.commands&permissions=8";
@@ -95,4 +101,35 @@ public class Configuration
return defaultInviteLink.replace("%userid%", botApplicationId);
}
+ /**
+ * Set the already fully-initialized DatabaseManager instance, ready to be accessed and used.
+ *
+ * @param databaseManagerInstance the fully-initialized DatabaseManager instance.
+ */
+ public static void setDatabaseManagerInstance(DatabaseManager databaseManagerInstance)
+ {
+ dbManager = databaseManagerInstance;
+ }
+
+ /**
+ * Get the fully-initialized DatabaseManager instance, ready to be used.
+ *
+ * @return the DatabaseManager instance.
+ */
+ public static @Nullable DatabaseManager getDatabaseManager() { return dbManager; }
+
+ /**
+ * Get the DateTimeFormatter string for parsing the expired messages timestamp.
+ *
+ * @return the String of the DateTimeFormatter format.
+ */
+ public static String getExpiryTimestampFormat(){ return expiryTimestampFormat; }
+
+ /**
+ * Get the amount of seconds after which a message expires.
+ *
+ * @return long value of the expiry seconds.
+ */
+ public static long getExpiryTimeSeconds() { return expiryTimeSeconds; }
+
}
diff --git a/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java b/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java
index d422208..5d32221 100644
--- a/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java
+++ b/src/main/java/wtf/beatrice/hidekobot/HidekoBot.java
@@ -6,17 +6,21 @@ import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.requests.GatewayIntent;
import sun.misc.Signal;
+import wtf.beatrice.hidekobot.database.DatabaseManager;
import wtf.beatrice.hidekobot.listeners.ButtonInteractionListener;
import wtf.beatrice.hidekobot.listeners.MessageListener;
import wtf.beatrice.hidekobot.listeners.SlashCommandCompleter;
import wtf.beatrice.hidekobot.listeners.SlashCommandListener;
+import wtf.beatrice.hidekobot.utils.ExpiredMessageRunner;
import wtf.beatrice.hidekobot.utils.Logger;
import wtf.beatrice.hidekobot.utils.SlashCommandsUtil;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class HidekoBot
@@ -93,6 +97,30 @@ public class HidekoBot
jda.getPresence().setStatus(OnlineStatus.ONLINE);
jda.getPresence().setActivity(Activity.playing("Hatsune Miku: Project DIVA"));
+ // connect to database
+ logger.log("Connecting to database...");
+ String dbFilePath = System.getProperty("user.dir") + File.separator + "db.sqlite"; // in current directory
+ DatabaseManager dbManager = new DatabaseManager(dbFilePath);
+ if(dbManager.connect() && dbManager.initDb())
+ {
+ logger.log("Database connection initialized!");
+ Configuration.setDatabaseManagerInstance(dbManager);
+
+ // load data here...
+
+ logger.log("Database data loaded into memory!");
+ } else {
+ logger.log("Error initializing database connection!");
+ }
+
+ // start scheduled runnables
+ ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
+ ExpiredMessageRunner task = new ExpiredMessageRunner();
+ int initDelay = 5;
+ int periodicDelay = 5;
+ scheduler.scheduleAtFixedRate(task, initDelay, periodicDelay, TimeUnit.SECONDS);
+
+
// print the bot logo.
logger.log("\n\n" + logger.getLogo() + "\nv" + version + " - bot is ready!\n", 2);
diff --git a/src/main/java/wtf/beatrice/hidekobot/commands/slash/ClearChatCommand.java b/src/main/java/wtf/beatrice/hidekobot/commands/slash/ClearChatCommand.java
index 77e36bc..d16f80a 100644
--- a/src/main/java/wtf/beatrice/hidekobot/commands/slash/ClearChatCommand.java
+++ b/src/main/java/wtf/beatrice/hidekobot/commands/slash/ClearChatCommand.java
@@ -10,7 +10,9 @@ import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
+import net.dv8tion.jda.api.requests.restaction.WebhookMessageEditAction;
import org.jetbrains.annotations.NotNull;
+import wtf.beatrice.hidekobot.Configuration;
import java.util.ArrayList;
import java.util.List;
@@ -20,145 +22,161 @@ public class ClearChatCommand
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
{
- MessageChannel channel = event.getChannel();
- if(!(channel instanceof TextChannel))
+ // run in a new thread, so we don't block the main one
+ new Thread(() ->
{
- event.reply("Sorry! I can't delete messages here.").queue();
- return;
- }
- /* get the amount from the command args.
- NULL should not be possible because we specified them as mandatory,
- but apparently the mobile app doesn't care and still sends the command if you omit the args. */
- OptionMapping amountMapping = event.getOption("amount");
- int toDeleteAmount = amountMapping == null ? 1 : amountMapping.getAsInt();
+ MessageChannel channel = event.getChannel();
- if(toDeleteAmount <= 0)
- {
- event.reply("Sorry, I can't delete that amount of messages!").queue();
- }
- else {
- // answer by saying that the operation has begun.
- InteractionHook replyInteraction = event.reply("\uD83D\uDEA7 Clearing...").complete();
-
- // int to keep track of how many messages we actually deleted.
- int deleted = 0;
-
- int limit = 95; //discord limits this method to range 2-100. we set it to 95 to be safe.
-
- // increase the count by 1, because we technically aren't clearing the first ID ever
- // which is actually the slash command's ID and not a message.
- toDeleteAmount++;
-
- // count how many times we have to iterate this to delete the full messages.
- int iterations = toDeleteAmount / limit;
-
- //if there are some messages left, but less than , we need one more iterations.
- int remainder = toDeleteAmount % limit;
- if(remainder != 0) iterations++;
-
- // set the starting point.
- long messageId = event.getInteraction().getIdLong();
-
- // boolean to see if we're trying to delete more messages than possible.
- boolean outOfBounds = false;
-
- // do iterate.
- for(int iteration = 0; iteration < iterations; iteration++)
+ if(!(channel instanceof TextChannel))
{
- if(outOfBounds) break;
+ event.reply("Sorry! I can't delete messages here.").queue();
+ return;
+ }
- // set how many messages to delete for this iteration (usually unless there's a remainder)
- int iterationSize = limit;
+ /* get the amount from the command args.
+ NULL should not be possible because we specified them as mandatory,
+ but apparently the mobile app doesn't care and still sends the command if you omit the args. */
+ OptionMapping amountMapping = event.getOption("amount");
+ int toDeleteAmount = amountMapping == null ? 1 : amountMapping.getAsInt();
- // if we are at the last iteration...
- if(iteration+1 == iterations)
+ if(toDeleteAmount <= 0)
+ {
+ event.reply("Sorry, I can't delete that amount of messages!").queue();
+ }
+ else {
+ // answer by saying that the operation has begun.
+ InteractionHook replyInteraction = event.reply("\uD83D\uDEA7 Clearing...").complete();
+
+ // int to keep track of how many messages we actually deleted.
+ int deleted = 0;
+
+ int limit = 95; //discord limits this method to range 2-100. we set it to 95 to be safe.
+
+ // increase the count by 1, because we technically aren't clearing the first ID ever
+ // which is actually the slash command's ID and not a message.
+ toDeleteAmount++;
+
+ // count how many times we have to iterate this to delete the full messages.
+ int iterations = toDeleteAmount / limit;
+
+ //if there are some messages left, but less than , we need one more iterations.
+ int remainder = toDeleteAmount % limit;
+ if(remainder != 0) iterations++;
+
+ // set the starting point.
+ long messageId = event.getInteraction().getIdLong();
+
+ // boolean to see if we're trying to delete more messages than possible.
+ boolean outOfBounds = false;
+
+ // do iterate.
+ for(int iteration = 0; iteration < iterations; iteration++)
{
- // check if we have or fewer messages to delete
- if(remainder != 0) iterationSize = remainder;
- }
+ if(outOfBounds) break;
- if(iterationSize == 1)
- {
- // grab the message
- Message toDelete = ((TextChannel)channel).retrieveMessageById(messageId).complete();
- //only delete one message
- if(toDelete != null) toDelete.delete().queue();
- else outOfBounds = true;
- // increase deleted counter by 1
- deleted++;
- } else {
- // get the last messages.
- MessageHistory.MessageRetrieveAction action = channel.getHistoryBefore(messageId, iterationSize - 1);
- // note: first one is the most recent, last one is the oldest message.
- List messages = new ArrayList<>();
- // (we are skipping first iteration since it would return an error, given that the id is the slash command and not a message)
- if(iteration!=0) messages.add(((TextChannel)channel).retrieveMessageById(messageId).complete());
- messages.addAll(action.complete().getRetrievedHistory());
+ // set how many messages to delete for this iteration (usually unless there's a remainder)
+ int iterationSize = limit;
- // check if we only have one or zero messages left (trying to delete more than possible)
- if(messages.size() <= 1)
+ // if we are at the last iteration...
+ if(iteration+1 == iterations)
{
- outOfBounds = true;
- } else {
- // before deleting, we need to grab the message's id for next iteration.
- action = channel.getHistoryBefore(messages.get(messages.size() - 1).getIdLong(), 1);
+ // check if we have or fewer messages to delete
+ if(remainder != 0) iterationSize = remainder;
+ }
- List previousMessage = action.complete().getRetrievedHistory();
-
- // if that message exists (we are not out of bounds)... store it
- if(!previousMessage.isEmpty()) messageId = previousMessage.get(0).getIdLong();
+ if(iterationSize == 1)
+ {
+ // grab the message
+ Message toDelete = ((TextChannel)channel).retrieveMessageById(messageId).complete();
+ //only delete one message
+ if(toDelete != null) toDelete.delete().queue();
else outOfBounds = true;
- }
+ // increase deleted counter by 1
+ deleted++;
+ } else {
+ // get the last messages.
+ MessageHistory.MessageRetrieveAction action = channel.getHistoryBefore(messageId, iterationSize - 1);
+ // note: first one is the most recent, last one is the oldest message.
+ List messages = new ArrayList<>();
+ // (we are skipping first iteration since it would return an error, given that the id is the slash command and not a message)
+ if(iteration!=0) messages.add(((TextChannel)channel).retrieveMessageById(messageId).complete());
+ messages.addAll(action.complete().getRetrievedHistory());
- // queue messages for deletion
- if(messages.size() == 1)
- {
- messages.get(0).delete().queue();
- }
- else if(!messages.isEmpty())
- {
- try {
- ((TextChannel) channel).deleteMessages(messages).complete();
- /* alternatively, we could use purgeMessages, which is smarter...
- however, it also tries to delete messages older than 2 weeks
- which are restricted by discord, and thus has to use
- a less efficient way that triggers rate-limiting very quickly. */
- } catch (Exception e)
+ // check if we only have one or zero messages left (trying to delete more than possible)
+ if(messages.size() <= 1)
{
- replyInteraction.editOriginal("\uD83D\uDE22 Sorry, it seems like there was an issue! " + e.getMessage()).queue();
- return; // warning: this quits everything.
+ outOfBounds = true;
+ } else {
+ // before deleting, we need to grab the message's id for next iteration.
+ action = channel.getHistoryBefore(messages.get(messages.size() - 1).getIdLong(), 1);
+
+ List previousMessage = action.complete().getRetrievedHistory();
+
+ // if that message exists (we are not out of bounds)... store it
+ if(!previousMessage.isEmpty()) messageId = previousMessage.get(0).getIdLong();
+ else outOfBounds = true;
}
+
+ // queue messages for deletion
+ if(messages.size() == 1)
+ {
+ messages.get(0).delete().queue();
+ }
+ else if(!messages.isEmpty())
+ {
+ try {
+ ((TextChannel) channel).deleteMessages(messages).complete();
+ /* alternatively, we could use purgeMessages, which is smarter...
+ however, it also tries to delete messages older than 2 weeks
+ which are restricted by discord, and thus has to use
+ a less efficient way that triggers rate-limiting very quickly. */
+ } catch (Exception e)
+ {
+ replyInteraction.editOriginal("\uD83D\uDE22 Sorry, it seems like there was an issue! " + e.getMessage()).queue();
+ return; // warning: this quits everything.
+ }
+ }
+
+ // increase deleted counter by
+ deleted += messages.size();
}
-
- // increase deleted counter by
- deleted += messages.size();
}
+
+
+ Button deleteButton = Button.danger("clear_delete", "Delete").withEmoji(Emoji.fromUnicode("❌"));
+
+ WebhookMessageEditAction webhookMessageEditAction;
+
+ // log having deleted the messages (or not).
+ if(deleted < 1)
+ {
+ webhookMessageEditAction = replyInteraction.editOriginal("\uD83D\uDE22 Couldn't clear any message!");
+ } else if(deleted == 1)
+ {
+ webhookMessageEditAction = replyInteraction.editOriginal("✂ Cleared 1 message!");
+ } else {
+ webhookMessageEditAction = replyInteraction.editOriginal("✂ Cleared " + deleted + " messages!");
+ }
+
+ Message message = webhookMessageEditAction
+ .setActionRow(deleteButton)
+ .complete();
+
+ String replyMessageId = message.getId();
+ String replyChannelId = message.getChannel().getId();
+ String replyGuildId = message.getGuild().getId();
+
+ Configuration.getDatabaseManager().queueDisabling(replyGuildId, replyChannelId, replyMessageId);
+
}
+ }).start();
-
- Button deleteButton = Button.danger("clear_delete", "Delete").withEmoji(Emoji.fromUnicode("❌"));
-
- // log having deleted the messages (or not).
- if(deleted < 1)
- {
- replyInteraction.editOriginal("\uD83D\uDE22 Couldn't clear any message!")
- .setActionRow(deleteButton).queue();
- } else if(deleted == 1)
- {
- replyInteraction.editOriginal("✂ Cleared 1 message!")
- .setActionRow(deleteButton).queue();
- } else {
- replyInteraction.editOriginal("✂ Cleared " + deleted + " messages!")
- .setActionRow(deleteButton).queue();
- }
- }
}
public void deleteButton(ButtonInteractionEvent event)
{
- // todo: permissions check
event.getInteraction().getMessage().delete().queue();
}
}
diff --git a/src/main/java/wtf/beatrice/hidekobot/database/DatabaseManager.java b/src/main/java/wtf/beatrice/hidekobot/database/DatabaseManager.java
new file mode 100644
index 0000000..221104f
--- /dev/null
+++ b/src/main/java/wtf/beatrice/hidekobot/database/DatabaseManager.java
@@ -0,0 +1,266 @@
+package wtf.beatrice.hidekobot.database;
+
+import wtf.beatrice.hidekobot.Configuration;
+import wtf.beatrice.hidekobot.utils.Logger;
+
+import java.sql.*;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DatabaseManager
+{
+
+ private final static String sqliteURL = "jdbc:sqlite:%path%";
+ private Connection dbConnection = null;
+ private final String dbPath;
+ private final Logger logger;
+
+ public DatabaseManager(String dbPath)
+ {
+ this.dbPath = dbPath;
+ this.logger = new Logger(getClass());
+ }
+
+ public boolean connect()
+ {
+ String url = sqliteURL.replace("%path%", dbPath);
+
+ if(!close()) return false;
+
+ try {
+ dbConnection = DriverManager.getConnection(url);
+ logger.log("Database connection established!");
+ return true;
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ }
+
+
+ public boolean close()
+ {
+ if (dbConnection != null)
+ {
+ try {
+ if(!dbConnection.isClosed())
+ {
+ dbConnection.close();
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ dbConnection = null;
+ }
+
+ return true;
+ }
+
+
+
+ public boolean initDb()
+ {
+ List newTables = new ArrayList<>();
+
+ newTables.add("CREATE TABLE IF NOT EXISTS pending_disabled_messages (" +
+ "guild_id TEXT NOT NULL, " +
+ "channel_id TEXT NOT NULL," +
+ "message_id TEXT NOT NULL," +
+ "expiry_timestamp TEXT NOT NULL" +
+ ");");
+
+ newTables.add("CREATE TABLE IF NOT EXISTS command_runners (" +
+ "guild_id TEXT NOT NULL, " +
+ "channel_id TEXT NOT NULL," + // channel the command was run in
+ "message_id TEXT NOT NULL," + // message id of the bot's response
+ "user_id TEXT NOT NULL" + // user who ran the command
+ ");");
+
+ for(String sql : newTables)
+ {
+ try (Statement stmt = dbConnection.createStatement()) {
+ // execute the statement
+ stmt.execute(sql);
+ } catch (SQLException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public boolean queueDisabling(String guildId, String channelId, String messageId)
+ {
+ LocalDateTime expiryTime = LocalDateTime.now().plusSeconds(Configuration.getExpiryTimeSeconds());
+
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(Configuration.getExpiryTimestampFormat());
+ String expiryTimeFormatted = dateTimeFormatter.format(expiryTime);
+
+ String query = "INSERT INTO pending_disabled_messages " +
+ "(guild_id, channel_id, message_id, expiry_timestamp) VALUES " +
+ " (?, ?, ?, ?);";
+
+ try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
+ {
+ preparedStatement.setString(1, guildId);
+ preparedStatement.setString(2, channelId);
+ preparedStatement.setString(3, messageId);
+ preparedStatement.setString(4, expiryTimeFormatted);
+
+ preparedStatement.executeUpdate();
+
+ return true;
+ } catch (SQLException e)
+ {
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ public List getQueuedExpiringMessages()
+ {
+ List messages = new ArrayList<>();
+
+ String query = "SELECT message_id " +
+ "FROM pending_disabled_messages ";
+
+ try (Statement statement = dbConnection.createStatement())
+ {
+ ResultSet resultSet = statement.executeQuery(query);
+ if(resultSet.isClosed()) return messages;
+ while(resultSet.next())
+ {
+ messages.add(resultSet.getString("message_id"));
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+
+ return messages;
+ }
+
+ public boolean untrackExpiredMessage(String messageId)
+ {
+ String query = "DELETE FROM pending_disabled_messages WHERE message_id = ?;";
+
+ try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
+ {
+ preparedStatement.setString(1, messageId);
+ preparedStatement.execute();
+ } catch (SQLException e)
+ {
+ e.printStackTrace();
+ return false;
+ }
+
+ query = "DELETE FROM command_runners WHERE message_id = ?;";
+ try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
+ {
+ preparedStatement.setString(1, messageId);
+ preparedStatement.execute();
+ } catch (SQLException e)
+ {
+ e.printStackTrace();
+ return false;
+ }
+
+ return true;
+ }
+
+ public String getQueuedExpiringMessageExpiryDate(String messageId)
+ {
+ String query = "SELECT expiry_timestamp " +
+ "FROM pending_disabled_messages " +
+ "WHERE message_id = ?;";
+
+ try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
+ {
+ preparedStatement.setString(1, messageId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ if(resultSet.isClosed()) return null;
+ while(resultSet.next())
+ {
+ return resultSet.getString("expiry_timestamp");
+ }
+
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public String getQueuedExpiringMessageChannel(String messageId)
+ {
+ String query = "SELECT channel_id " +
+ "FROM pending_disabled_messages " +
+ "WHERE message_id = ?;";
+
+ try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
+ {
+ preparedStatement.setString(1, messageId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ if(resultSet.isClosed()) return null;
+ while(resultSet.next())
+ {
+ return resultSet.getString("channel_id");
+ }
+
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public String getQueuedExpiringMessageGuild(String messageId)
+ {
+ String query = "SELECT guild_id " +
+ "FROM pending_disabled_messages " +
+ "WHERE message_id = ?;";
+
+ try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
+ {
+ preparedStatement.setString(1, messageId);
+ ResultSet resultSet = preparedStatement.executeQuery();
+ if(resultSet.isClosed()) return null;
+ while(resultSet.next())
+ {
+ return resultSet.getString("guild_id");
+ }
+
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * DB STRUCTURE
+ * TABLE 1: pending_disabled_messages
+ * ----------------------------------------------------------------------------------
+ * | guild_id | channel_id | message_id | expiry_timestamp |
+ * ----------------------------------------------------------------------------------
+ * |39402849302 | 39402849302 | 39402849302 | 2022-11-20 22:45:53:300 |
+ * ----------------------------------------------------------------------------------
+ *
+ *
+ * TABLE 2: command_runners
+ * --------------------------------------------------------------------------
+ * | guild_id | channel_id | message_id | user_id |
+ * --------------------------------------------------------------------------
+ * | 39402849302 | 39402849302 | 39402849302 | 39402849302 |
+ * --------------------------------------------------------------------------
+ *
+ */
+
+
+}
diff --git a/src/main/java/wtf/beatrice/hidekobot/utils/ExpiredMessageRunner.java b/src/main/java/wtf/beatrice/hidekobot/utils/ExpiredMessageRunner.java
new file mode 100644
index 0000000..ff3d151
--- /dev/null
+++ b/src/main/java/wtf/beatrice/hidekobot/utils/ExpiredMessageRunner.java
@@ -0,0 +1,99 @@
+package wtf.beatrice.hidekobot.utils;
+
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
+import net.dv8tion.jda.api.interactions.components.LayoutComponent;
+import net.dv8tion.jda.api.requests.RestAction;
+import wtf.beatrice.hidekobot.Configuration;
+import wtf.beatrice.hidekobot.HidekoBot;
+import wtf.beatrice.hidekobot.database.DatabaseManager;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ExpiredMessageRunner implements Runnable {
+
+ private final DateTimeFormatter formatter;
+ private final Logger logger;
+ private DatabaseManager databaseManager;
+
+
+ public ExpiredMessageRunner()
+ {
+ String format = Configuration.getExpiryTimestampFormat();
+ formatter = DateTimeFormatter.ofPattern(format);
+ databaseManager = Configuration.getDatabaseManager();
+ logger = new Logger(getClass());
+ }
+
+
+ @Override
+ public void run() {
+
+ databaseManager = Configuration.getDatabaseManager();
+ if(databaseManager == null) return;
+
+ List expiringMessages = Configuration.getDatabaseManager().getQueuedExpiringMessages();
+ if(expiringMessages == null || expiringMessages.isEmpty()) return;
+
+ LocalDateTime now = LocalDateTime.now();
+
+ for(String messageId : expiringMessages)
+ {
+ logger.log("ID: " + messageId);
+ String expiryTimestamp = databaseManager.getQueuedExpiringMessageExpiryDate(messageId);
+ if(expiryTimestamp == null || expiryTimestamp.equals("")) continue; //todo: idk count it as expired already?
+
+
+ LocalDateTime expiryDate = LocalDateTime.parse(expiryTimestamp, formatter);
+ if(now.isAfter(expiryDate))
+ {
+ disableExpired(messageId);
+ }
+ }
+
+
+
+ }
+
+ private void disableExpired(String messageId)
+ {
+ String guildId = databaseManager.getQueuedExpiringMessageGuild(messageId);
+ String channelId = databaseManager.getQueuedExpiringMessageChannel(messageId);
+
+
+
+ Guild guild = HidekoBot.getAPI().getGuildById(guildId);
+ if(guild == null) return; //todo count it as done/solved/removed? we probably got kicked
+ TextChannel textChannel = guild.getTextChannelById(channelId);
+ if(textChannel == null) return; //todo count it as done/solved/removed? channel was probably deleted
+
+ RestAction retrieveAction = textChannel.retrieveMessageById(messageId);
+
+
+
+ retrieveAction.queue(
+
+ message -> {
+ if(message == null) return; //todo count it as done/solved/removed? message was probably deleted
+
+ List components = message.getComponents();
+ List newComponents = new ArrayList<>();
+ for (LayoutComponent component : components)
+ {
+ component = component.asDisabled();
+ newComponents.add(component);
+ }
+
+ message.editMessageComponents(newComponents).queue();
+ databaseManager.untrackExpiredMessage(messageId);
+ },
+
+ (error) -> {
+ databaseManager.untrackExpiredMessage(messageId);
+ });
+ }
+}