Compare commits
No commits in common. "98a162a33b6e7ef12cce3906d6b9f4add5fba025" and "3d626bb46f3a17fbf871a90efcbcb3f3e6406d98" have entirely different histories.
98a162a33b
...
3d626bb46f
6
pom.xml
6
pom.xml
@ -30,12 +30,6 @@
|
|||||||
<artifactId>slf4j-simple</artifactId>
|
<artifactId>slf4j-simple</artifactId>
|
||||||
<version>2.0.0</version>
|
<version>2.0.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.xerial</groupId>
|
|
||||||
<artifactId>sqlite-jdbc</artifactId>
|
|
||||||
<version>3.39.4.1</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -1,23 +1,17 @@
|
|||||||
package wtf.beatrice.hidekobot;
|
package wtf.beatrice.hidekobot;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import wtf.beatrice.hidekobot.database.DatabaseManager;
|
|
||||||
import wtf.beatrice.hidekobot.listeners.MessageLogger;
|
import wtf.beatrice.hidekobot.listeners.MessageLogger;
|
||||||
|
|
||||||
public class Configuration
|
public class Configuration
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
private static DatabaseManager dbManager = null;
|
|
||||||
private static boolean verbose = false;
|
private static boolean verbose = false;
|
||||||
private static MessageLogger verbosityLogger;
|
private static MessageLogger verbosityLogger;
|
||||||
|
|
||||||
// todo: allow people to set their own user id
|
// todo: allow people to set their own user id
|
||||||
private static final long botOwnerId = 979809420714332260L;
|
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 =
|
private final static String defaultInviteLink =
|
||||||
"https://discord.com/api/oauth2/authorize?client_id=%userid%&scope=bot+applications.commands&permissions=8";
|
"https://discord.com/api/oauth2/authorize?client_id=%userid%&scope=bot+applications.commands&permissions=8";
|
||||||
|
|
||||||
@ -101,35 +95,4 @@ public class Configuration
|
|||||||
return defaultInviteLink.replace("%userid%", botApplicationId);
|
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; }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,21 +6,17 @@ import net.dv8tion.jda.api.OnlineStatus;
|
|||||||
import net.dv8tion.jda.api.entities.Activity;
|
import net.dv8tion.jda.api.entities.Activity;
|
||||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||||
import sun.misc.Signal;
|
import sun.misc.Signal;
|
||||||
import wtf.beatrice.hidekobot.database.DatabaseManager;
|
|
||||||
import wtf.beatrice.hidekobot.listeners.ButtonInteractionListener;
|
import wtf.beatrice.hidekobot.listeners.ButtonInteractionListener;
|
||||||
import wtf.beatrice.hidekobot.listeners.MessageListener;
|
import wtf.beatrice.hidekobot.listeners.MessageListener;
|
||||||
import wtf.beatrice.hidekobot.listeners.SlashCommandCompleter;
|
import wtf.beatrice.hidekobot.listeners.SlashCommandCompleter;
|
||||||
import wtf.beatrice.hidekobot.listeners.SlashCommandListener;
|
import wtf.beatrice.hidekobot.listeners.SlashCommandListener;
|
||||||
import wtf.beatrice.hidekobot.utils.ExpiredMessageRunner;
|
|
||||||
import wtf.beatrice.hidekobot.utils.Logger;
|
import wtf.beatrice.hidekobot.utils.Logger;
|
||||||
import wtf.beatrice.hidekobot.utils.SlashCommandsUtil;
|
import wtf.beatrice.hidekobot.utils.SlashCommandsUtil;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class HidekoBot
|
public class HidekoBot
|
||||||
@ -97,30 +93,6 @@ public class HidekoBot
|
|||||||
jda.getPresence().setStatus(OnlineStatus.ONLINE);
|
jda.getPresence().setStatus(OnlineStatus.ONLINE);
|
||||||
jda.getPresence().setActivity(Activity.playing("Hatsune Miku: Project DIVA"));
|
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.
|
// print the bot logo.
|
||||||
logger.log("\n\n" + logger.getLogo() + "\nv" + version + " - bot is ready!\n", 2);
|
logger.log("\n\n" + logger.getLogo() + "\nv" + version + " - bot is ready!\n", 2);
|
||||||
|
|
||||||
|
@ -4,15 +4,10 @@ import net.dv8tion.jda.api.entities.Message;
|
|||||||
import net.dv8tion.jda.api.entities.MessageHistory;
|
import net.dv8tion.jda.api.entities.MessageHistory;
|
||||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
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 org.jetbrains.annotations.NotNull;
|
||||||
import wtf.beatrice.hidekobot.Configuration;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -22,161 +17,134 @@ public class ClearChatCommand
|
|||||||
|
|
||||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
{
|
{
|
||||||
|
MessageChannel channel = event.getChannel();
|
||||||
|
|
||||||
// run in a new thread, so we don't block the main one
|
if(!(channel instanceof TextChannel))
|
||||||
new Thread(() ->
|
|
||||||
{
|
{
|
||||||
|
event.reply("Sorry! I can't delete messages here.").queue();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MessageChannel channel = event.getChannel();
|
/* 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(!(channel instanceof TextChannel))
|
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 <toDeleteAmount> messages.
|
||||||
|
int iterations = toDeleteAmount / limit;
|
||||||
|
|
||||||
|
//if there are some messages left, but less than <limit>, 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++)
|
||||||
{
|
{
|
||||||
event.reply("Sorry! I can't delete messages here.").queue();
|
if(outOfBounds) break;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get the amount from the command args.
|
// set how many messages to delete for this iteration (usually <limit> unless there's a remainder)
|
||||||
NULL should not be possible because we specified them as mandatory,
|
int iterationSize = limit;
|
||||||
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(toDeleteAmount <= 0)
|
// if we are at the last iteration...
|
||||||
{
|
if(iteration+1 == iterations)
|
||||||
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 <toDeleteAmount> messages.
|
|
||||||
int iterations = toDeleteAmount / limit;
|
|
||||||
|
|
||||||
//if there are some messages left, but less than <limit>, 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(outOfBounds) break;
|
// check if we have <limit> or fewer messages to delete
|
||||||
|
if(remainder != 0) iterationSize = remainder;
|
||||||
// set how many messages to delete for this iteration (usually <limit> unless there's a remainder)
|
|
||||||
int iterationSize = limit;
|
|
||||||
|
|
||||||
// if we are at the last iteration...
|
|
||||||
if(iteration+1 == iterations)
|
|
||||||
{
|
|
||||||
// check if we have <limit> or fewer messages to delete
|
|
||||||
if(remainder != 0) iterationSize = remainder;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <iterationSize - 1> messages.
|
|
||||||
MessageHistory.MessageRetrieveAction action = channel.getHistoryBefore(messageId, iterationSize - 1);
|
|
||||||
// note: first one is the most recent, last one is the oldest message.
|
|
||||||
List<Message> 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());
|
|
||||||
|
|
||||||
// check if we only have one or zero messages left (trying to delete more than possible)
|
|
||||||
if(messages.size() <= 1)
|
|
||||||
{
|
|
||||||
outOfBounds = true;
|
|
||||||
} else {
|
|
||||||
// before deleting, we need to grab the <previous to the oldest> message's id for next iteration.
|
|
||||||
action = channel.getHistoryBefore(messages.get(messages.size() - 1).getIdLong(), 1);
|
|
||||||
|
|
||||||
List<Message> 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 <list size>
|
|
||||||
deleted += messages.size();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(iterationSize == 1)
|
||||||
Button deleteButton = Button.danger("clear_delete", "Delete").withEmoji(Emoji.fromUnicode("❌"));
|
|
||||||
|
|
||||||
WebhookMessageEditAction<Message> webhookMessageEditAction;
|
|
||||||
|
|
||||||
// log having deleted the messages (or not).
|
|
||||||
if(deleted < 1)
|
|
||||||
{
|
{
|
||||||
webhookMessageEditAction = replyInteraction.editOriginal("\uD83D\uDE22 Couldn't clear any message!");
|
// grab the message
|
||||||
} else if(deleted == 1)
|
Message toDelete = ((TextChannel)channel).retrieveMessageById(messageId).complete();
|
||||||
{
|
//only delete one message
|
||||||
webhookMessageEditAction = replyInteraction.editOriginal("âś‚ Cleared 1 message!");
|
if(toDelete != null) toDelete.delete().queue();
|
||||||
|
else outOfBounds = true;
|
||||||
|
// increase deleted counter by 1
|
||||||
|
deleted++;
|
||||||
} else {
|
} else {
|
||||||
webhookMessageEditAction = replyInteraction.editOriginal("âś‚ Cleared " + deleted + " messages!");
|
// get the last <iterationSize - 1> messages.
|
||||||
|
MessageHistory.MessageRetrieveAction action = channel.getHistoryBefore(messageId, iterationSize - 1);
|
||||||
|
// note: first one is the most recent, last one is the oldest message.
|
||||||
|
List<Message> 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());
|
||||||
|
|
||||||
|
// check if we only have one or zero messages left (trying to delete more than possible)
|
||||||
|
if(messages.size() <= 1)
|
||||||
|
{
|
||||||
|
outOfBounds = true;
|
||||||
|
} else {
|
||||||
|
// before deleting, we need to grab the <previous to the oldest> message's id for next iteration.
|
||||||
|
action = channel.getHistoryBefore(messages.get(messages.size() - 1).getIdLong(), 1);
|
||||||
|
|
||||||
|
List<Message> 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 <list size>
|
||||||
|
deleted += messages.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
}
|
// log having deleted the messages.
|
||||||
|
if(deleted < 1)
|
||||||
|
{
|
||||||
|
replyInteraction.editOriginal("\uD83D\uDE22 Couldn't clear any message!").queue();
|
||||||
|
} else if(deleted == 1)
|
||||||
|
{
|
||||||
|
replyInteraction.editOriginal("âś‚ Cleared 1 message!").queue();
|
||||||
|
} else {
|
||||||
|
replyInteraction.editOriginal("âś‚ Cleared " + deleted + " messages!").queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteButton(ButtonInteractionEvent event)
|
|
||||||
{
|
|
||||||
event.getInteraction().getMessage().delete().queue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package wtf.beatrice.hidekobot.commands.slash;
|
package wtf.beatrice.hidekobot.commands.slash;
|
||||||
|
|
||||||
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.GenericInteractionCreateEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||||
@ -13,31 +14,7 @@ import java.util.List;
|
|||||||
public class CoinFlipCommand
|
public class CoinFlipCommand
|
||||||
{
|
{
|
||||||
|
|
||||||
private final Button reflipButton = Button.primary("coinflip_reflip", "Flip again")
|
|
||||||
.withEmoji(Emoji.fromFormatted("\uD83E\uDE99"));
|
|
||||||
|
|
||||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||||
{
|
|
||||||
// perform coin flip
|
|
||||||
event.reply(genRandom())
|
|
||||||
.addActionRow(reflipButton)
|
|
||||||
.queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void buttonReFlip(ButtonInteractionEvent event)
|
|
||||||
{
|
|
||||||
// set old message's button as disabled
|
|
||||||
List<ActionRow> actionRows = event.getMessage().getActionRows();
|
|
||||||
actionRows.set(0, actionRows.get(0).asDisabled());
|
|
||||||
event.editComponents(actionRows).queue();
|
|
||||||
|
|
||||||
// perform coin flip
|
|
||||||
event.getHook().sendMessage(genRandom())
|
|
||||||
.addActionRow(reflipButton)
|
|
||||||
.queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String genRandom()
|
|
||||||
{
|
{
|
||||||
int rand = RandomUtil.getRandomNumber(0, 1);
|
int rand = RandomUtil.getRandomNumber(0, 1);
|
||||||
String msg;
|
String msg;
|
||||||
@ -49,7 +26,32 @@ public class CoinFlipCommand
|
|||||||
msg = "It's **Tails**! :coin:";
|
msg = "It's **Tails**! :coin:";
|
||||||
}
|
}
|
||||||
|
|
||||||
return msg;
|
event.reply(msg)
|
||||||
|
.addActionRow(Button.primary("reflip", "Flip again")
|
||||||
|
.withEmoji(Emoji.fromFormatted("\uD83E\uDE99")) // coin emoji
|
||||||
|
).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void buttonReflip(ButtonInteractionEvent event)
|
||||||
|
{
|
||||||
|
List<ActionRow> actionRows = event.getMessage().getActionRows();
|
||||||
|
actionRows.set(0, actionRows.get(0).asDisabled());
|
||||||
|
event.editComponents(actionRows).queue();
|
||||||
|
|
||||||
|
int rand = RandomUtil.getRandomNumber(0, 1);
|
||||||
|
String msg;
|
||||||
|
|
||||||
|
if(rand == 1)
|
||||||
|
{
|
||||||
|
msg = ":coin: It's **Heads**!";
|
||||||
|
} else {
|
||||||
|
msg = "It's **Tails**! :coin:";
|
||||||
|
}
|
||||||
|
|
||||||
|
event.getHook().sendMessage(msg)
|
||||||
|
.addActionRow(Button.primary("reflip", "Flip again")
|
||||||
|
.withEmoji(Emoji.fromFormatted("\uD83E\uDE99")) // coin emoji
|
||||||
|
).queue();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,266 +0,0 @@
|
|||||||
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<String> 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<String> getQueuedExpiringMessages()
|
|
||||||
{
|
|
||||||
List<String> 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 |
|
|
||||||
* --------------------------------------------------------------------------
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ package wtf.beatrice.hidekobot.listeners;
|
|||||||
|
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
import wtf.beatrice.hidekobot.commands.slash.ClearChatCommand;
|
|
||||||
import wtf.beatrice.hidekobot.commands.slash.CoinFlipCommand;
|
import wtf.beatrice.hidekobot.commands.slash.CoinFlipCommand;
|
||||||
|
|
||||||
public class ButtonInteractionListener extends ListenerAdapter
|
public class ButtonInteractionListener extends ListenerAdapter
|
||||||
@ -15,10 +14,7 @@ public class ButtonInteractionListener extends ListenerAdapter
|
|||||||
switch (event.getComponentId().toLowerCase()) {
|
switch (event.getComponentId().toLowerCase()) {
|
||||||
|
|
||||||
// coinflip
|
// coinflip
|
||||||
case "coinflip_reflip" -> new CoinFlipCommand().buttonReFlip(event);
|
case "reflip" -> new CoinFlipCommand().buttonReflip(event);
|
||||||
|
|
||||||
// clearchat command
|
|
||||||
case "clear_delete" -> new ClearChatCommand().deleteButton(event);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
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<String> 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<Message> retrieveAction = textChannel.retrieveMessageById(messageId);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
retrieveAction.queue(
|
|
||||||
|
|
||||||
message -> {
|
|
||||||
if(message == null) return; //todo count it as done/solved/removed? message was probably deleted
|
|
||||||
|
|
||||||
List<LayoutComponent> components = message.getComponents();
|
|
||||||
List<LayoutComponent> newComponents = new ArrayList<>();
|
|
||||||
for (LayoutComponent component : components)
|
|
||||||
{
|
|
||||||
component = component.asDisabled();
|
|
||||||
newComponents.add(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
message.editMessageComponents(newComponents).queue();
|
|
||||||
databaseManager.untrackExpiredMessage(messageId);
|
|
||||||
},
|
|
||||||
|
|
||||||
(error) -> {
|
|
||||||
databaseManager.untrackExpiredMessage(messageId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user