Compare commits
107 Commits
f934c7d7f4
...
c843d2cd61
Author | SHA1 | Date | |
---|---|---|---|
c843d2cd61 | |||
3bd09d2867 | |||
c077b92f93 | |||
ecff77867c | |||
5e4e79d505 | |||
e1c22ac059 | |||
3676e9d5ad | |||
5acd900857 | |||
d2f00c781e | |||
35b2c8fb42 | |||
420666eab1 | |||
52fe279f35 | |||
7ac72f9e38 | |||
00c46c1396 | |||
7f7ada9b9e | |||
47bd16fd13 | |||
f5a8c2af48 | |||
50196bb8f9 | |||
26f1c880ea | |||
09c53c07a1 | |||
1c19f3c07f | |||
d4c3afbddd | |||
96f298b654 | |||
48fdb32e15 | |||
eb93362d16 | |||
9f147cee65 | |||
b0d234a454 | |||
c9082e84cc | |||
0be4389448 | |||
c1059bb937 | |||
78f62b5f8d | |||
3a8a44adf0 | |||
cfa7aef333 | |||
0e256e4cb5 | |||
b0622f36aa | |||
e451f59199 | |||
f3cc9a2d75 | |||
8f5c29aa95 | |||
df1e2426e3 | |||
91261f04e5 | |||
bc0463dd38 | |||
161c91b45d | |||
d09c59996b | |||
1c82d2b53b | |||
174b78704f | |||
ba64c02049 | |||
264a94fe0d | |||
9e1888611a | |||
b1b62bab9f | |||
de34caa513 | |||
215e597a4d | |||
217022faca | |||
34c100acde | |||
496304c2c3 | |||
e08fefbda3 | |||
6c4d362ca4 | |||
0e1a7d3459 | |||
e2c84f62c3 | |||
b20fb73371 | |||
61745c36d0 | |||
480b8b5eda | |||
f9fe12a248 | |||
28d7ff18ba | |||
1644a4b07d | |||
68dceaff13 | |||
7dcdf9dbde | |||
c4d81fb0e4 | |||
24a55e14fd | |||
cce57b8108 | |||
fc846fa901 | |||
4476dd2f7b | |||
2d7cadea02 | |||
b4c80fe56a | |||
60ee5f2ae2 | |||
8ca70dac78 | |||
d412801758 | |||
4ef42ffa9e | |||
0f54211ecd | |||
d5664eb646 | |||
1421d52598 | |||
639c54bc52 | |||
28c0f1d750 | |||
3259a49ace | |||
4d888d68b9 | |||
7959044335 | |||
fb3c08fc41 | |||
09ec600234 | |||
c9ff329cbb | |||
8f4f341aab | |||
a030821197 | |||
e531eef1d6 | |||
b033763704 | |||
982902fc6d | |||
00441f089f | |||
1f6f23e917 | |||
1e07ede83e | |||
1a8409994c | |||
b0a1381589 | |||
9504921f27 | |||
c9528848bc | |||
2d1f6699ba | |||
595e81e02c | |||
764ff23010 | |||
32ea099690 | |||
1410e4e8af | |||
81e621ec1a | |||
c486630adb |
@ -12,7 +12,7 @@ make the bot change its behavior.
|
||||
|
||||
Additionally available parameters are:
|
||||
- **verbose**: log every message that the bot receives, plus additional debugging messages. Very spammy and performance heavy.
|
||||
- **refresh**: force refresh the bot's commands.
|
||||
- **refresh**: force refresh the slash commands. This is useful in case there was a simple update to a command that did not drastically change it, so no changes are found at bootup (eg: fixing a typo in the command description).
|
||||
|
||||
*Note: Java 16 or later is required.*
|
||||
|
||||
@ -24,4 +24,9 @@ Edit the configuration file and set all values according to your needs.
|
||||
|
||||
Save the file and start the bot again. If there are no issues, everything will load and it will print an
|
||||
invite-link in your console. Click on the link to add your bot to any server with the correct permissions
|
||||
already set-up.
|
||||
already set-up. The bot supports both slash commands and message commands, with prefix `hideko`. Most
|
||||
commands support both systems, but some of them are limited in one way or another.
|
||||
|
||||
The bot currently supports SQLite as a database backend. A database file will be created after the first boot
|
||||
in the same directory that you ran it. Do not delete the database file to avoid corruption and unpredictable
|
||||
behavior.
|
20
pom.xml
20
pom.xml
@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>wtf.beatrice.hidekobot</groupId>
|
||||
<artifactId>HidekoBot</artifactId>
|
||||
<version>0.5.0</version>
|
||||
<version>0.5.16</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>16</maven.compiler.source>
|
||||
@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>net.dv8tion</groupId>
|
||||
<artifactId>JDA</artifactId>
|
||||
<version>5.0.0-alpha.22</version>
|
||||
<version>5.0.0-beta.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
@ -40,6 +40,22 @@
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.33</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.15.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.10.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20220924</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
@ -14,6 +14,10 @@ import wtf.beatrice.hidekobot.util.Logger;
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
public class Cache
|
||||
{
|
||||
@ -23,6 +27,16 @@ public class Cache
|
||||
private static final String botPrefix = "hideko";
|
||||
private static final Logger logger = new Logger(Cache.class);
|
||||
|
||||
// the Random instance that we should always use when looking for an RNG based thing.
|
||||
// the seed is updated periodically.
|
||||
private static final Random randomInstance = new Random();
|
||||
|
||||
// map to store results of "love calculator", to avoid people re-running the same command until
|
||||
// they get what they wanted.
|
||||
// i didn't think this was worthy of a whole database table with a runnable checking for expiration,
|
||||
// and it will get cleared after a few minutes anyway, so RAM caching is more than good enough.
|
||||
private static final HashMap<String, Integer> loveCalculatorValues = new HashMap<>();
|
||||
|
||||
private static PropertiesSource propertiesSource = null;
|
||||
private static ConfigurationSource configurationSource = null;
|
||||
private static DatabaseSource databaseSource = null;
|
||||
@ -32,11 +46,17 @@ public class Cache
|
||||
private final static String expiryTimestampFormat = "yy/MM/dd HH:mm:ss";
|
||||
|
||||
// note: discord sets interactions' expiry time to 15 minutes by default, so we can't go higher than that.
|
||||
private final static long expiryTimeSeconds = 15L;
|
||||
private final static long expiryTimeSeconds = 30L;
|
||||
|
||||
// used to count e.g. uptime
|
||||
private static LocalDateTime startupTime = null;
|
||||
|
||||
// date of when the first bot commit was made (CEST time)
|
||||
private static final LocalDateTime botBirthDate = LocalDateTime.of(2022, 8, 25, 21, 50);
|
||||
|
||||
// the scheduler that should always be used when running a scheduled task.
|
||||
private final static ScheduledExecutorService taskScheduler = Executors.newSingleThreadScheduledExecutor(); // todo: try-with-resources
|
||||
|
||||
private final static String execPath = System.getProperty("user.dir");
|
||||
private static final String botName = "Hideko";
|
||||
|
||||
@ -60,6 +80,14 @@ public class Cache
|
||||
*/
|
||||
public static int[] getSupportedAvatarResolutions() { return supportedAvatarResolutions; }
|
||||
|
||||
public static Random getRandom() {
|
||||
return randomInstance;
|
||||
}
|
||||
|
||||
public static void setRandomSeed(long seed) {
|
||||
randomInstance.setSeed(seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the bot has been started with the verbose argument.
|
||||
*
|
||||
@ -214,7 +242,7 @@ public class Cache
|
||||
try {
|
||||
Field field = Color.class.getField(colorName);
|
||||
color = (Color)field.get(null);
|
||||
} catch (Exception e) {
|
||||
} catch (RuntimeException | NoSuchFieldException | IllegalAccessException e) {
|
||||
logger.log("Unknown color: " + colorName);
|
||||
}
|
||||
return color == null ? defaultColor : color;
|
||||
@ -255,6 +283,13 @@ public class Cache
|
||||
*/
|
||||
public static LocalDateTime getStartupTime() { return startupTime; }
|
||||
|
||||
/**
|
||||
* Get the time of when the bot was created.
|
||||
*
|
||||
* @return a LocalDateTime object of the first commit's instant.
|
||||
*/
|
||||
public static LocalDateTime getBotBirthDate() { return botBirthDate; }
|
||||
|
||||
public static String getFullHeartBeatLink() {
|
||||
return configurationSource == null ? null : (String) configurationSource.getConfigValue(ConfigurationEntry.HEARTBEAT_LINK);
|
||||
}
|
||||
@ -274,4 +309,31 @@ public class Cache
|
||||
*/
|
||||
public static String getBotPrefix() { return botPrefix; }
|
||||
|
||||
public static void cacheLoveCalculatorValue(String userId1, String userId2, int value)
|
||||
{
|
||||
String merged = userId1 + "|" + userId2;
|
||||
loveCalculatorValues.put(merged, value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Integer getLoveCalculatorValue(String userId1, String userId2)
|
||||
{
|
||||
String merged1 = userId1 + "|" + userId2;
|
||||
String merged2 = userId2 + "|" + userId1;
|
||||
Integer value = null;
|
||||
value = loveCalculatorValues.get(merged1);
|
||||
if(value == null) value = loveCalculatorValues.get(merged2);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void removeLoveCalculatorValue(String userId1, String userId2)
|
||||
{
|
||||
loveCalculatorValues.remove(userId1 + "|" + userId2);
|
||||
loveCalculatorValues.remove(userId2 + "|" + userId1);
|
||||
}
|
||||
|
||||
public static ScheduledExecutorService getTaskScheduler() {
|
||||
return taskScheduler;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,23 +3,21 @@ package wtf.beatrice.hidekobot;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.JDABuilder;
|
||||
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.commands.completer.AvatarCommandCompleter;
|
||||
import wtf.beatrice.hidekobot.commands.completer.ProfileImageCommandCompleter;
|
||||
import wtf.beatrice.hidekobot.commands.message.HelloCommand;
|
||||
import wtf.beatrice.hidekobot.commands.slash.*;
|
||||
import wtf.beatrice.hidekobot.datasources.ConfigurationSource;
|
||||
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
|
||||
import wtf.beatrice.hidekobot.datasources.PropertiesSource;
|
||||
import wtf.beatrice.hidekobot.listeners.ButtonInteractionListener;
|
||||
import wtf.beatrice.hidekobot.listeners.MessageCommandListener;
|
||||
import wtf.beatrice.hidekobot.listeners.SlashCommandCompletionListener;
|
||||
import wtf.beatrice.hidekobot.listeners.SlashCommandListener;
|
||||
import wtf.beatrice.hidekobot.listeners.*;
|
||||
import wtf.beatrice.hidekobot.runnables.ExpiredMessageTask;
|
||||
import wtf.beatrice.hidekobot.runnables.HeartBeatTask;
|
||||
import wtf.beatrice.hidekobot.runnables.RandomSeedTask;
|
||||
import wtf.beatrice.hidekobot.runnables.StatusUpdateTask;
|
||||
import wtf.beatrice.hidekobot.util.CommandUtil;
|
||||
import wtf.beatrice.hidekobot.util.Logger;
|
||||
import wtf.beatrice.hidekobot.util.SlashCommandUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
@ -112,43 +110,68 @@ public class HidekoBot
|
||||
SlashCommandListener slashCommandListener = new SlashCommandListener();
|
||||
SlashCommandCompletionListener slashCommandCompletionListener = new SlashCommandCompletionListener();
|
||||
AvatarCommand avatarCommand = new AvatarCommand();
|
||||
AvatarCommandCompleter avatarCommandCompleter = new AvatarCommandCompleter(avatarCommand);
|
||||
ProfileImageCommandCompleter avatarCommandCompleter = new ProfileImageCommandCompleter(avatarCommand);
|
||||
slashCommandListener.registerCommand(avatarCommand);
|
||||
slashCommandCompletionListener.registerCommandCompleter(avatarCommandCompleter);
|
||||
slashCommandListener.registerCommand(new BanCommand());
|
||||
BannerCommand bannerCommand = new BannerCommand();
|
||||
ProfileImageCommandCompleter bannerCommandCompleter = new ProfileImageCommandCompleter(bannerCommand);
|
||||
slashCommandListener.registerCommand(bannerCommand);
|
||||
slashCommandCompletionListener.registerCommandCompleter(bannerCommandCompleter);
|
||||
slashCommandListener.registerCommand(new BotInfoCommand());
|
||||
slashCommandListener.registerCommand(new ClearCommand());
|
||||
slashCommandListener.registerCommand(new CoinFlipCommand());
|
||||
slashCommandListener.registerCommand(new DiceRollCommand());
|
||||
slashCommandListener.registerCommand(new DieCommand());
|
||||
slashCommandListener.registerCommand(new HelpCommand());
|
||||
slashCommandListener.registerCommand(new InviteCommand());
|
||||
slashCommandListener.registerCommand(new KickCommand());
|
||||
slashCommandListener.registerCommand(new LoveCalculatorCommand());
|
||||
slashCommandListener.registerCommand(new MagicBallCommand());
|
||||
slashCommandListener.registerCommand(new PingCommand());
|
||||
slashCommandListener.registerCommand(new SayCommand());
|
||||
Cache.setSlashCommandListener(slashCommandListener);
|
||||
Cache.setSlashCommandCompletionListener(slashCommandCompletionListener);
|
||||
slashCommandListener.registerCommand(new TimeoutCommand());
|
||||
slashCommandListener.registerCommand(new TriviaCommand());
|
||||
slashCommandListener.registerCommand(new UrbanDictionaryCommand());
|
||||
|
||||
// register message commands
|
||||
MessageCommandListener messageCommandListener = new MessageCommandListener();
|
||||
messageCommandListener.registerCommand(new HelloCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.InviteCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.AliasCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.AvatarCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BanCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BannerCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BotInfoCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.CoinFlipCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.ClearCommand());
|
||||
Cache.setMessageCommandListener(messageCommandListener);
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.DiceRollCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.HelpCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.InviteCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.KickCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.LoveCalculatorCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.MagicBallCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.SayCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.TimeoutCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.TriviaCommand());
|
||||
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.UrbanDictionaryCommand());
|
||||
|
||||
// register listeners
|
||||
Cache.setSlashCommandListener(slashCommandListener);
|
||||
Cache.setSlashCommandCompletionListener(slashCommandCompletionListener);
|
||||
Cache.setMessageCommandListener(messageCommandListener);
|
||||
jda.addEventListener(messageCommandListener);
|
||||
jda.addEventListener(slashCommandListener);
|
||||
jda.addEventListener(slashCommandCompletionListener);
|
||||
jda.addEventListener(new ButtonInteractionListener());
|
||||
jda.addEventListener(new SelectMenuInteractionListener());
|
||||
|
||||
// update slash commands (delayed)
|
||||
final boolean finalForceUpdateCommands = forceUpdateCommands;
|
||||
Executors.newSingleThreadScheduledExecutor().schedule(() -> // todo: try-with-resources
|
||||
SlashCommandUtil.updateSlashCommands(finalForceUpdateCommands), 1, TimeUnit.SECONDS);
|
||||
CommandUtil.updateSlashCommands(finalForceUpdateCommands), 1, TimeUnit.SECONDS);
|
||||
|
||||
// set the bot's status
|
||||
jda.getPresence().setStatus(OnlineStatus.ONLINE);
|
||||
jda.getPresence().setActivity(Activity.playing("Hatsune Miku: Project DIVA"));
|
||||
|
||||
// connect to database
|
||||
logger.log("Connecting to database...");
|
||||
@ -167,11 +190,15 @@ public class HidekoBot
|
||||
}
|
||||
|
||||
// start scheduled runnables
|
||||
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // todo: try-with-resources
|
||||
ScheduledExecutorService scheduler = Cache.getTaskScheduler();
|
||||
ExpiredMessageTask expiredMessageTask = new ExpiredMessageTask();
|
||||
scheduler.scheduleAtFixedRate(expiredMessageTask, 5, 5, TimeUnit.SECONDS); //every 5 seconds
|
||||
HeartBeatTask heartBeatTask = new HeartBeatTask();
|
||||
scheduler.scheduleAtFixedRate(heartBeatTask, 10, 30, TimeUnit.SECONDS); //every 30 seconds
|
||||
StatusUpdateTask statusUpdateTask = new StatusUpdateTask();
|
||||
scheduler.scheduleAtFixedRate(statusUpdateTask, 0, 60 * 5, TimeUnit.SECONDS); // every 5 minutes
|
||||
RandomSeedTask randomSeedTask = new RandomSeedTask();
|
||||
scheduler.scheduleAtFixedRate(randomSeedTask, 0, 60, TimeUnit.SECONDS); // every minute
|
||||
|
||||
// register shutdown interrupt signal listener for proper shutdown.
|
||||
Signal.handle(new Signal("INT"), signal -> shutdown());
|
||||
|
@ -0,0 +1,23 @@
|
||||
package wtf.beatrice.hidekobot.commands.base;
|
||||
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class Alias
|
||||
{
|
||||
public static String generateNiceAliases(MessageCommand command)
|
||||
{
|
||||
LinkedList<String> aliases = command.getCommandLabels();
|
||||
StringBuilder aliasesStringBuilder = new StringBuilder();
|
||||
for(int i = 0; i < aliases.size(); i++)
|
||||
{
|
||||
aliasesStringBuilder.append("`").append(aliases.get(i)).append("`");
|
||||
|
||||
if(i + 1 != aliases.size())
|
||||
aliasesStringBuilder.append(", "); // separate with comma except on last iteration
|
||||
}
|
||||
|
||||
return aliasesStringBuilder.toString();
|
||||
}
|
||||
}
|
@ -91,13 +91,18 @@ public class BotInfo
|
||||
embedBuilder.addField("Maintainer", developerMention, true);
|
||||
|
||||
// uptime field
|
||||
embedBuilder.addField("Uptime", FormatUtil.getNiceUptime(), true);
|
||||
embedBuilder.addField("Uptime", FormatUtil.getNiceTimeDiff(Cache.getStartupTime()), true);
|
||||
|
||||
// issue tracker field
|
||||
embedBuilder.addField("Support",
|
||||
"[Issue tracker](https://git.beatrice.wtf/mind-overflow/HidekoBot/issues)",
|
||||
true); //todo: we should probably make this a final field in the config class
|
||||
|
||||
// bot birthday field
|
||||
embedBuilder.addField("Bot age",
|
||||
Cache.getBotName() + " was created " + FormatUtil.getNiceTimeDiff(Cache.getBotBirthDate()) + "ago!",
|
||||
false);
|
||||
|
||||
return embedBuilder.build();
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,7 @@ import net.dv8tion.jda.api.entities.channel.Channel;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -46,12 +43,15 @@ public class ClearChat
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int delete(int toDeleteAmount, long startingMessageId, MessageChannel channel)
|
||||
public static int delete(int toDeleteAmount,
|
||||
long startingMessageId,
|
||||
MessageChannel channel)
|
||||
{
|
||||
// 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.
|
||||
int limit = 95; //discord limits this method to only 2<x<100 deletions per run.
|
||||
// we set this slightly lower to be safe, and iterate as needed.
|
||||
|
||||
// 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.
|
||||
@ -132,9 +132,8 @@ public class ClearChat
|
||||
which are restricted by discord, and thus has to use
|
||||
a less efficient way that triggers rate-limiting very quickly. */
|
||||
|
||||
} catch (Exception e)
|
||||
} catch (RuntimeException ignored)
|
||||
{
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -149,12 +148,13 @@ public class ClearChat
|
||||
|
||||
public static Button getDismissButton()
|
||||
{
|
||||
return Button.primary("clear_dismiss", "Dismiss")
|
||||
return Button.primary("generic_dismiss", "Dismiss")
|
||||
.withEmoji(Emoji.fromUnicode("❌"));
|
||||
}
|
||||
|
||||
public static String parseAmount(int deleted)
|
||||
{
|
||||
|
||||
if(deleted < 1)
|
||||
{
|
||||
return "\uD83D\uDE22 Couldn't clear any message!";
|
||||
@ -166,27 +166,7 @@ public class ClearChat
|
||||
}
|
||||
}
|
||||
|
||||
private void respond(Object responseFlowObj, String content)
|
||||
{
|
||||
if(responseFlowObj instanceof InteractionHook) {
|
||||
((InteractionHook) responseFlowObj).editOriginal(content).queue();
|
||||
} else if (responseFlowObj instanceof Message) {
|
||||
((Message) responseFlowObj).reply(content).queue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void dismissMessage(ButtonInteractionEvent event)
|
||||
{
|
||||
|
||||
if(!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId())))
|
||||
{
|
||||
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
|
||||
} else
|
||||
{
|
||||
event.getInteraction().getMessage().delete().queue();
|
||||
}
|
||||
}
|
||||
// cap the amount to avoid abuse.
|
||||
public static int getMaxAmount() { return 1000; }
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ public class CoinFlip
|
||||
|
||||
public static Button getReflipButton() {
|
||||
return Button.primary("coinflip_reflip", "Flip again")
|
||||
.withEmoji(Emoji.fromFormatted("\uD83E\uDE99"));
|
||||
.withEmoji(Emoji.fromUnicode("\uD83E\uDE99"));
|
||||
}
|
||||
|
||||
public static String genRandom()
|
||||
@ -61,8 +61,6 @@ public class CoinFlip
|
||||
|
||||
public static void trackAndRestrict(Message replyMessage, User user)
|
||||
{
|
||||
String replyMessageId = replyMessage.getId();
|
||||
|
||||
Cache.getDatabaseSource().queueDisabling(replyMessage);
|
||||
Cache.getDatabaseSource().trackRanCommandReply(replyMessage, user);
|
||||
}
|
||||
|
157
src/main/java/wtf/beatrice/hidekobot/commands/base/DiceRoll.java
Normal file
157
src/main/java/wtf/beatrice/hidekobot/commands/base/DiceRoll.java
Normal file
@ -0,0 +1,157 @@
|
||||
package wtf.beatrice.hidekobot.commands.base;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.fun.Dice;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DiceRoll
|
||||
{
|
||||
public static MessageResponse buildResponse(User author, String[] args)
|
||||
{
|
||||
LinkedHashMap<Dice, Integer> dicesToRoll = new LinkedHashMap<>();
|
||||
String diceRegex = "d[0-9]+";
|
||||
String amountRegex = "[0-9]+";
|
||||
|
||||
Dice currentDice = null;
|
||||
int currentAmount;
|
||||
UUID lastPushedDice = null;
|
||||
int totalRolls = 0;
|
||||
|
||||
for(String arg : args)
|
||||
{
|
||||
if(totalRolls > 200)
|
||||
{
|
||||
return new MessageResponse("Too many total rolls!", null);
|
||||
}
|
||||
|
||||
if(arg.matches(amountRegex))
|
||||
{
|
||||
currentAmount = Integer.parseInt(arg);
|
||||
|
||||
if(currentDice == null)
|
||||
{
|
||||
currentDice = new Dice(6);
|
||||
} else {
|
||||
currentDice = new Dice(currentDice);
|
||||
}
|
||||
|
||||
if(currentAmount > 100)
|
||||
{
|
||||
return new MessageResponse("Too many rolls (`" + currentAmount + "`)!", null);
|
||||
}
|
||||
|
||||
lastPushedDice = currentDice.getUUID();
|
||||
dicesToRoll.put(currentDice, currentAmount);
|
||||
totalRolls += currentAmount;
|
||||
}
|
||||
else if(arg.matches(diceRegex))
|
||||
{
|
||||
int sides = Integer.parseInt(arg.substring(1));
|
||||
|
||||
if(sides > 10000)
|
||||
{
|
||||
return new MessageResponse("Too many sides (`" + sides + "`)!", null);
|
||||
}
|
||||
|
||||
if(args.length == 1)
|
||||
{
|
||||
dicesToRoll.put(new Dice(sides), 1);
|
||||
totalRolls++;
|
||||
} else
|
||||
{
|
||||
if(currentDice != null)
|
||||
{
|
||||
if(lastPushedDice == null || !lastPushedDice.equals(currentDice.getUUID()))
|
||||
{
|
||||
dicesToRoll.put(currentDice, 1);
|
||||
lastPushedDice = currentDice.getUUID();
|
||||
totalRolls++;
|
||||
}
|
||||
}
|
||||
|
||||
currentDice = new Dice(sides);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(lastPushedDice == null)
|
||||
{
|
||||
if(currentDice != null)
|
||||
{
|
||||
dicesToRoll.put(currentDice, 1);
|
||||
totalRolls++;
|
||||
}
|
||||
} else
|
||||
{
|
||||
if(!lastPushedDice.equals(currentDice.getUUID()))
|
||||
{
|
||||
dicesToRoll.put(new Dice(currentDice), 1);
|
||||
totalRolls++;
|
||||
}
|
||||
}
|
||||
|
||||
LinkedList<Dice> rolledDices = new LinkedList<>();
|
||||
|
||||
// in case no dice was specified (or invalid), roll a standard 6-sided dice.
|
||||
if(dicesToRoll.isEmpty())
|
||||
{
|
||||
Dice standardDice = new Dice(6);
|
||||
dicesToRoll.put(standardDice, 1);
|
||||
totalRolls = 1;
|
||||
}
|
||||
|
||||
for(Dice dice : dicesToRoll.keySet())
|
||||
{
|
||||
for(int roll = 0; roll < dicesToRoll.get(dice); roll++)
|
||||
{
|
||||
dice.roll();
|
||||
rolledDices.add(new Dice(dice));
|
||||
}
|
||||
}
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
|
||||
embedBuilder.setTitle("Dice Roll");
|
||||
|
||||
StringBuilder message = new StringBuilder();
|
||||
int total = 0;
|
||||
|
||||
int previousDiceSides = 0;
|
||||
for (Dice dice : rolledDices) {
|
||||
int diceSize = dice.getSides();
|
||||
|
||||
if (previousDiceSides != diceSize) {
|
||||
message.append("\nd").append(diceSize).append(": ");
|
||||
previousDiceSides = diceSize;
|
||||
} else if (previousDiceSides != 0) {
|
||||
message.append(", ");
|
||||
}
|
||||
|
||||
message.append("`").append(dice.getValue()).append("`");
|
||||
|
||||
total += dice.getValue();
|
||||
}
|
||||
|
||||
// discord doesn't allow embed fields to be longer than 1024 and errors out
|
||||
if(message.length() > 1024)
|
||||
{
|
||||
return new MessageResponse("Too many rolls!", null);
|
||||
}
|
||||
|
||||
embedBuilder.addField("\uD83C\uDFB2 Rolls", message.toString(), false);
|
||||
|
||||
String rolls = totalRolls == 1 ? "roll" : "rolls";
|
||||
|
||||
embedBuilder.addField("✨ Total", totalRolls + " " + rolls + ": " + total, false);
|
||||
|
||||
return new MessageResponse(null, embedBuilder.build());
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package wtf.beatrice.hidekobot.commands.base;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class LoveCalculator
|
||||
{
|
||||
public static MessageEmbed buildEmbedAndCacheResult(User author, User user1, User user2)
|
||||
{
|
||||
String userId1 = user1.getId();
|
||||
String userId2 = user2.getId();
|
||||
|
||||
Integer loveAmount = Cache.getLoveCalculatorValue(userId1, userId2);
|
||||
if(loveAmount == null)
|
||||
{
|
||||
loveAmount = RandomUtil.getRandomNumber(0, 100);
|
||||
Cache.cacheLoveCalculatorValue(userId1, userId2, loveAmount);
|
||||
Cache.getTaskScheduler().schedule(() ->
|
||||
Cache.removeLoveCalculatorValue(userId1, userId2), 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
String formattedAmount = loveAmount + "%";
|
||||
if(loveAmount <= 30) formattedAmount += "... \uD83D\uDE22";
|
||||
else if(loveAmount < 60) formattedAmount += "! \uD83E\uDDD0";
|
||||
else if(loveAmount < 75) formattedAmount += "!!! \uD83E\uDD73";
|
||||
else formattedAmount = "✨ " + formattedAmount + "!!! \uD83D\uDE0D\uD83D\uDCA5";
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
|
||||
embedBuilder.setTitle("Love Calculator");
|
||||
|
||||
embedBuilder.addField("\uD83D\uDC65 People",
|
||||
user1.getAsMention() + " & " + user2.getAsMention(),
|
||||
false);
|
||||
|
||||
embedBuilder.addField("❤️\u200D\uD83D\uDD25 Match",
|
||||
formattedAmount,
|
||||
false);
|
||||
|
||||
return embedBuilder.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package wtf.beatrice.hidekobot.commands.base;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class MagicBall
|
||||
{
|
||||
|
||||
public static LinkedList<String> getLabels()
|
||||
{
|
||||
return new LinkedList<>(Arrays.asList("8ball", "8b", "eightball", "magicball"));
|
||||
}
|
||||
|
||||
private final static List<String> answers = new ArrayList<>(
|
||||
Arrays.asList("It is certain.",
|
||||
"It is decidedly so.",
|
||||
"Without a doubt.",
|
||||
"Yes, definitely.",
|
||||
"That would be a yes.",
|
||||
"As I see it, yes.",
|
||||
"Most likely.",
|
||||
"Looks like it.",
|
||||
"Yes.",
|
||||
"Signs point to yes.",
|
||||
"Reply hazy, try again.",
|
||||
"Ask again later.",
|
||||
"Better not tell you now.",
|
||||
"Seems uncertain.",
|
||||
"Concentrate and ask again.",
|
||||
"Don't count on it.",
|
||||
"My answer is no.",
|
||||
"My sources say no.",
|
||||
"Outlook not so good.",
|
||||
"Very doubtful."));
|
||||
|
||||
public static String getRandomAnswer()
|
||||
{
|
||||
int answerPos = RandomUtil.getRandomNumber(0, answers.size() - 1);
|
||||
return answers.get(answerPos);
|
||||
}
|
||||
|
||||
public static MessageEmbed generateEmbed(String question, User author)
|
||||
{
|
||||
// add a question mark at the end, if missing.
|
||||
// this might not always apply but it's fun
|
||||
if(!question.endsWith("?")) question += "?";
|
||||
|
||||
String answer = getRandomAnswer();
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
|
||||
embedBuilder.setTitle("Magic Ball");
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.addField("❓ Question", question, false);
|
||||
embedBuilder.addField("\uD83C\uDFB1 Answer", answer, false);
|
||||
|
||||
return embedBuilder.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package wtf.beatrice.hidekobot.commands.base;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.utils.ImageProxy;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
|
||||
public class ProfileImage
|
||||
{
|
||||
public static int parseResolution(int resolution)
|
||||
{
|
||||
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
|
||||
|
||||
// method to find closest value to accepted values
|
||||
int distance = Math.abs(acceptedSizes[0] - resolution);
|
||||
int idx = 0;
|
||||
for(int c = 1; c < acceptedSizes.length; c++){
|
||||
int cdistance = Math.abs(acceptedSizes[c] - resolution);
|
||||
if(cdistance < distance){
|
||||
idx = c;
|
||||
distance = cdistance;
|
||||
}
|
||||
}
|
||||
|
||||
return acceptedSizes[idx];
|
||||
}
|
||||
|
||||
public static MessageResponse buildResponse(int resolution, User user, ImageType imageType)
|
||||
{
|
||||
String imageTypeName = imageType.name().toLowerCase();
|
||||
String resolutionString;
|
||||
String imageLink = null;
|
||||
|
||||
User.Profile userProfile = user.retrieveProfile().complete();
|
||||
ImageProxy bannerProxy = userProfile.getBanner();
|
||||
|
||||
if(imageType == ImageType.AVATAR)
|
||||
{
|
||||
resolutionString = resolution + " × " + resolution;
|
||||
imageLink = user.getEffectiveAvatar().getUrl(resolution);
|
||||
} else {
|
||||
int verticalRes = 361 * resolution / 1024;
|
||||
resolutionString = resolution + " × " + verticalRes;
|
||||
if(bannerProxy != null)
|
||||
imageLink = bannerProxy.getUrl(resolution);
|
||||
}
|
||||
|
||||
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
|
||||
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setTitle("Profile " + imageTypeName);
|
||||
|
||||
embedBuilder.addField("User", user.getAsMention(), false);
|
||||
|
||||
embedBuilder.addField("Current resolution", resolutionString, false);
|
||||
|
||||
// string builder to create a string that links to all available resolutions
|
||||
StringBuilder links = new StringBuilder();
|
||||
for(int pos = 0; pos < acceptedSizes.length; pos++)
|
||||
{
|
||||
int currSize = acceptedSizes[pos];
|
||||
|
||||
String currLink;
|
||||
if(imageType == ImageType.AVATAR)
|
||||
{
|
||||
currLink = user.getEffectiveAvatar().getUrl(currSize);
|
||||
} else {
|
||||
if(bannerProxy == null) break;
|
||||
currLink = bannerProxy.getUrl(currSize);
|
||||
}
|
||||
|
||||
links.append("**[").append(currSize).append("px](").append(currLink).append(")**");
|
||||
if(pos + 1 != acceptedSizes.length) // don't add a separator on the last iteration
|
||||
{
|
||||
links.append(" | ");
|
||||
}
|
||||
}
|
||||
|
||||
embedBuilder.addField("Available resolutions", links.toString(), false);
|
||||
|
||||
|
||||
|
||||
if(imageLink != null)
|
||||
embedBuilder.setImage(imageLink);
|
||||
|
||||
|
||||
if(imageLink == null) {
|
||||
String error = "I couldn't find " + user.getAsMention() + "'s " + imageTypeName + "!";
|
||||
return new MessageResponse(error, null);
|
||||
} else {
|
||||
return new MessageResponse(null, embedBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
public enum ImageType
|
||||
{
|
||||
AVATAR, BANNER;
|
||||
}
|
||||
}
|
11
src/main/java/wtf/beatrice/hidekobot/commands/base/Say.java
Normal file
11
src/main/java/wtf/beatrice/hidekobot/commands/base/Say.java
Normal file
@ -0,0 +1,11 @@
|
||||
package wtf.beatrice.hidekobot.commands.base;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
|
||||
public class Say
|
||||
{
|
||||
|
||||
public static Permission getPermission() {
|
||||
return Permission.MESSAGE_MANAGE;
|
||||
}
|
||||
}
|
284
src/main/java/wtf/beatrice/hidekobot/commands/base/Trivia.java
Normal file
284
src/main/java/wtf/beatrice/hidekobot/commands/base/Trivia.java
Normal file
@ -0,0 +1,284 @@
|
||||
package wtf.beatrice.hidekobot.commands.base;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
|
||||
import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.comparators.TriviaCategoryComparator;
|
||||
import wtf.beatrice.hidekobot.objects.fun.TriviaCategory;
|
||||
import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion;
|
||||
import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
|
||||
import wtf.beatrice.hidekobot.runnables.TriviaTask;
|
||||
import wtf.beatrice.hidekobot.util.CommandUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Trivia
|
||||
{
|
||||
private final static String triviaLink = "https://opentdb.com/api.php?amount=10&type=multiple&category=";
|
||||
private final static String categoriesLink = "https://opentdb.com/api_category.php";
|
||||
|
||||
public static List<String> channelsRunningTrivia = new ArrayList<>();
|
||||
|
||||
// first string is the channelId, the list contain all users who responded there
|
||||
public static HashMap<String, List<String>> channelAndWhoResponded = new HashMap<>();
|
||||
|
||||
// first string is the channelId, the list contain all score records for that channel
|
||||
public static HashMap<String, LinkedList<TriviaScore>> channelAndScores = new HashMap<>();
|
||||
|
||||
public static String getTriviaLink(int categoryId) {return triviaLink + categoryId; }
|
||||
public static String getCategoriesLink() {return categoriesLink; }
|
||||
|
||||
public static String getNoDMsError() {
|
||||
return "\uD83D\uDE22 Sorry! Trivia doesn't work in DMs.";
|
||||
}
|
||||
|
||||
public static String getTriviaAlreadyRunningError() {
|
||||
// todo nicer looking
|
||||
return "Trivia is already running here!";
|
||||
}
|
||||
|
||||
public static MessageResponse generateMainScreen()
|
||||
{
|
||||
// todo null checks
|
||||
JSONObject categoriesJson = Trivia.fetchJson(Trivia.getCategoriesLink());
|
||||
List<TriviaCategory> categories = Trivia.parseCategories(categoriesJson);
|
||||
categories.sort(new TriviaCategoryComparator());
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setTitle("\uD83C\uDFB2 Trivia");
|
||||
embedBuilder.addField("\uD83D\uDCD6 Begin here",
|
||||
"Select a category from the dropdown menu to start a match!",
|
||||
false);
|
||||
embedBuilder.addField("❓ How to play",
|
||||
"A new question gets posted every few seconds." +
|
||||
"\nIf you get it right, you earn points!" +
|
||||
"\nIf you choose a wrong answer, you lose points." +
|
||||
"\nIf you are unsure, you can wait without answering and your score won't change!",
|
||||
false);
|
||||
|
||||
StringSelectMenu.Builder menuBuilder = StringSelectMenu.create("trivia_categories");
|
||||
|
||||
for(TriviaCategory category : categories)
|
||||
{
|
||||
String name = category.categoryName();
|
||||
int id = category.categoryId();
|
||||
menuBuilder.addOption(name, String.valueOf(id));
|
||||
}
|
||||
|
||||
return new MessageResponse(null, embedBuilder.build(), menuBuilder.build());
|
||||
}
|
||||
|
||||
public static JSONObject fetchJson(String link)
|
||||
{
|
||||
try {
|
||||
URL url = new URL(link);
|
||||
URLConnection connection = url.openConnection();
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
|
||||
String currentChar;
|
||||
StringBuilder jsonStrBuilder = new StringBuilder();
|
||||
while((currentChar = bufferedReader.readLine()) != null)
|
||||
{
|
||||
jsonStrBuilder.append(currentChar);
|
||||
}
|
||||
bufferedReader.close();
|
||||
return new JSONObject(jsonStrBuilder.toString());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<TriviaQuestion> parseQuestions(JSONObject jsonObject)
|
||||
{
|
||||
List<TriviaQuestion> questions = new ArrayList<>();
|
||||
|
||||
JSONArray results = jsonObject.getJSONArray("results");
|
||||
|
||||
for(Object currentQuestionGeneric : results)
|
||||
{
|
||||
JSONObject questionJson = (JSONObject) currentQuestionGeneric;
|
||||
String question = StringEscapeUtils.unescapeHtml4(questionJson.getString("question"));
|
||||
String correctAnswer = StringEscapeUtils.unescapeHtml4(questionJson.getString("correct_answer"));
|
||||
|
||||
List<String> incorrectAnswersList = new ArrayList<>();
|
||||
|
||||
JSONArray incorrectAnswers = questionJson.getJSONArray("incorrect_answers");
|
||||
for(Object incorrectAnswerGeneric : incorrectAnswers)
|
||||
{
|
||||
String incorrectAnswer = (String) incorrectAnswerGeneric;
|
||||
incorrectAnswersList.add(StringEscapeUtils.unescapeHtml4(incorrectAnswer));
|
||||
}
|
||||
|
||||
TriviaQuestion triviaQuestion = new TriviaQuestion(question, correctAnswer, incorrectAnswersList);
|
||||
questions.add(triviaQuestion);
|
||||
}
|
||||
|
||||
return questions;
|
||||
}
|
||||
|
||||
public static List<TriviaCategory> parseCategories(JSONObject jsonObject)
|
||||
{
|
||||
List<TriviaCategory> categories = new ArrayList<>();
|
||||
JSONArray categoriesArray = jsonObject.getJSONArray("trivia_categories");
|
||||
for(Object categoryObject : categoriesArray)
|
||||
{
|
||||
JSONObject categoryJson = (JSONObject) categoryObject;
|
||||
|
||||
String name = categoryJson.getString("name");
|
||||
int id = categoryJson.getInt("id");
|
||||
|
||||
categories.add(new TriviaCategory(name, id));
|
||||
}
|
||||
|
||||
return categories;
|
||||
}
|
||||
|
||||
public static void handleAnswer(ButtonInteractionEvent event, AnswerType answerType)
|
||||
{
|
||||
User user = event.getUser();
|
||||
String channelId = event.getChannel().getId();
|
||||
|
||||
if(trackResponse(user, event.getChannel()))
|
||||
{
|
||||
LinkedList<TriviaScore> scores = channelAndScores.get(channelId);
|
||||
if(scores == null) scores = new LinkedList<>();
|
||||
TriviaScore currentUserScore = null;
|
||||
for(TriviaScore score : scores)
|
||||
{
|
||||
if(score.getUser().equals(user))
|
||||
{
|
||||
currentUserScore = score;
|
||||
scores.remove(score);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(currentUserScore == null)
|
||||
{
|
||||
currentUserScore = new TriviaScore(user);
|
||||
}
|
||||
|
||||
if(answerType.equals(AnswerType.CORRECT))
|
||||
{
|
||||
|
||||
event.reply(user.getAsMention() + " got it right! \uD83E\uDD73 (**+3**)").queue();
|
||||
currentUserScore.changeScore(3);
|
||||
|
||||
} else {
|
||||
event.reply("❌ " + user.getAsMention() + ", that's not the right answer! (**-1**)").queue();
|
||||
currentUserScore.changeScore(-1);
|
||||
}
|
||||
|
||||
scores.add(currentUserScore);
|
||||
channelAndScores.put(channelId, scores);
|
||||
} else {
|
||||
event.reply("☹️ " + user.getAsMention() + ", you can't answer twice!")
|
||||
.queue(interaction ->
|
||||
Cache.getTaskScheduler().schedule(() ->
|
||||
interaction.deleteOriginal().queue(), 3, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean trackResponse(User user, MessageChannel channel)
|
||||
{
|
||||
String userId = user.getId();
|
||||
String channelId = channel.getId();
|
||||
|
||||
List<String> responders = channelAndWhoResponded.get(channelId);
|
||||
|
||||
if(responders == null)
|
||||
{
|
||||
responders = new ArrayList<>();
|
||||
}
|
||||
|
||||
if(responders.isEmpty() || !responders.contains(userId))
|
||||
{
|
||||
responders.add(userId);
|
||||
channelAndWhoResponded.put(channelId, responders);
|
||||
return true; // response was successfully tracked
|
||||
} else {
|
||||
return false; // response wasn't tracked because there already was an entry
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleMenuSelection(StringSelectInteractionEvent event)
|
||||
{
|
||||
// check if the user interacting is the same one who ran the command
|
||||
if(!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId())))
|
||||
{
|
||||
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
// todo: we shouldn't use this method, since it messes with the database... look at coin reflip
|
||||
CommandUtil.disableExpired(event.getMessageId());
|
||||
|
||||
SelectOption pickedOption = event.getInteraction().getSelectedOptions().get(0);
|
||||
String categoryName = pickedOption.getLabel();
|
||||
String categoryIdString = pickedOption.getValue();
|
||||
Integer categoryId = Integer.parseInt(categoryIdString);
|
||||
|
||||
TriviaCategory category = new TriviaCategory(categoryName, categoryId);
|
||||
|
||||
startTrivia(event, category);
|
||||
}
|
||||
|
||||
public static void startTrivia(StringSelectInteractionEvent event, TriviaCategory category)
|
||||
{
|
||||
User author = event.getUser();
|
||||
Message message = event.getMessage();
|
||||
MessageChannel channel = message.getChannel();
|
||||
|
||||
if(Trivia.channelsRunningTrivia.contains(channel.getId()))
|
||||
{
|
||||
// todo nicer looking
|
||||
// todo: also what if the bot stops (database...?)
|
||||
// todo: also what if the message is already deleted
|
||||
Message err = event.reply("Trivia is already running here!").complete().retrieveOriginal().complete();
|
||||
Cache.getTaskScheduler().schedule(() -> err.delete().queue(), 10, TimeUnit.SECONDS);
|
||||
return;
|
||||
} else {
|
||||
// todo nicer looking
|
||||
event.reply("Starting new Trivia session!").queue();
|
||||
}
|
||||
|
||||
|
||||
TriviaTask triviaTask = new TriviaTask(author, channel, category);
|
||||
ScheduledFuture<?> future =
|
||||
Cache.getTaskScheduler().scheduleAtFixedRate(triviaTask,
|
||||
0,
|
||||
15,
|
||||
TimeUnit.SECONDS);
|
||||
triviaTask.setScheduledFuture(future);
|
||||
|
||||
Trivia.channelsRunningTrivia.add(channel.getId());
|
||||
}
|
||||
|
||||
public enum AnswerType {
|
||||
CORRECT, WRONG
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,323 @@
|
||||
package wtf.beatrice.hidekobot.commands.base;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||
import net.dv8tion.jda.api.interactions.components.ItemComponent;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.apache.commons.text.WordUtils;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
|
||||
import wtf.beatrice.hidekobot.util.SerializationUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class UrbanDictionary
|
||||
{
|
||||
|
||||
public static LinkedList<String> getCommandLabels()
|
||||
{ return new LinkedList<>(Arrays.asList("urban", "urbandictionary", "ud")); }
|
||||
|
||||
|
||||
public static String getBaseUrl() {
|
||||
return "https://www.urbandictionary.com/define.php?term=";
|
||||
}
|
||||
|
||||
public static Button getPreviousPageButton()
|
||||
{
|
||||
return Button.primary("urban_previouspage", "Back")
|
||||
.withEmoji(Emoji.fromFormatted("⬅️"));
|
||||
}
|
||||
|
||||
public static Button getNextPageButton()
|
||||
{
|
||||
return Button.primary("urban_nextpage", "Next")
|
||||
.withEmoji(Emoji.fromFormatted("➡️"));
|
||||
}
|
||||
|
||||
public static Button getDeleteButton()
|
||||
{
|
||||
return Button.danger("generic_dismiss", "Delete")
|
||||
.withEmoji(Emoji.fromFormatted("\uD83D\uDDD1️"));
|
||||
}
|
||||
|
||||
public static String getNoArgsError() {
|
||||
return "\uD83D\uDE22 I need to know what to search for!";
|
||||
}
|
||||
|
||||
public static String sanitizeArgs(String term, boolean forUrl)
|
||||
{
|
||||
term = term.replaceAll("[^\\w\\s]", ""); // only keep letters, numbers and spaces
|
||||
term = WordUtils.capitalizeFully(term); // Make Every Word Start With A Capital Letter
|
||||
if(forUrl) term = term.replaceAll("\\s+", "+"); // replace all whitespaces with + for the url
|
||||
if (term.length() > 64) term = term.substring(0, 64); // cut it to length to avoid abuse
|
||||
return term;
|
||||
}
|
||||
|
||||
public static String generateUrl(String term)
|
||||
{
|
||||
return getBaseUrl() + sanitizeArgs(term, true);
|
||||
}
|
||||
|
||||
public static MessageEmbed buildEmbed(String term,
|
||||
String url,
|
||||
User author,
|
||||
UrbanSearch search,
|
||||
int page)
|
||||
{
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setTitle(term + ", on Urban Dictionary", url);
|
||||
embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
|
||||
embedBuilder.addField("\uD83D\uDCD6 Definition", search.getPlaintextMeanings().get(page), false);
|
||||
embedBuilder.addField("\uD83D\uDCAD Example", search.getPlaintextExamples().get(page), false);
|
||||
embedBuilder.addField("\uD83D\uDCCC Submission",
|
||||
"*Entry " + (page+1) + " | Sent by " + search.getContributorsNames().get(page) +
|
||||
" on" + search.getSubmissionDates().get(page) + "*",
|
||||
false);
|
||||
|
||||
return embedBuilder.build();
|
||||
}
|
||||
|
||||
|
||||
public static String getTermNotFoundError()
|
||||
{
|
||||
return "\uD83D\uDE22 I couldn't find that term!";
|
||||
}
|
||||
|
||||
public static void track(Message message, User user, UrbanSearch search, String sanitizedTerm)
|
||||
{
|
||||
Cache.getDatabaseSource().queueDisabling(message);
|
||||
Cache.getDatabaseSource().trackRanCommandReply(message, user);
|
||||
Cache.getDatabaseSource().trackUrban(search.getSerializedMeanings(),
|
||||
search.getSerializedExamples(),
|
||||
search.getSerializedContributors(),
|
||||
search.getSerializedDates(),
|
||||
message,
|
||||
sanitizedTerm);
|
||||
}
|
||||
|
||||
public static void changePage(ButtonInteractionEvent event, ChangeType changeType)
|
||||
{
|
||||
String messageId = event.getMessageId();
|
||||
DatabaseSource database = Cache.getDatabaseSource();
|
||||
|
||||
// check if the user interacting is the same one who ran the command
|
||||
if (!(database.isUserTrackedFor(event.getUser().getId(), messageId))) {
|
||||
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
// get current page and calculate how many pages there are
|
||||
int page = Cache.getDatabaseSource().getUrbanPage(messageId);
|
||||
|
||||
String term = database.getUrbanTerm(messageId);
|
||||
String url = generateUrl(term);
|
||||
|
||||
// get serialized parameters
|
||||
String serializedMeanings = database.getUrbanMeanings(messageId);
|
||||
String serializedExamples = database.getUrbanExamples(messageId);
|
||||
String serializedContributors = database.getUrbanContributors(messageId);
|
||||
String serializedDates = database.getUrbanDates(messageId);
|
||||
|
||||
// construct object by passing serialized parameters
|
||||
UrbanSearch search = new UrbanSearch(serializedMeanings,
|
||||
serializedExamples, serializedContributors, serializedDates);
|
||||
|
||||
// move to new page
|
||||
if(changeType == ChangeType.NEXT)
|
||||
page++;
|
||||
else if(changeType == ChangeType.PREVIOUS)
|
||||
page--;
|
||||
|
||||
term = UrbanDictionary.sanitizeArgs(term, false);
|
||||
|
||||
// generate embed with new results
|
||||
MessageEmbed updatedEmbed = UrbanDictionary.buildEmbed(term, url, event.getUser(), search, page);
|
||||
|
||||
// get all attached components and check which ones need to be enabled or disabled
|
||||
List<ItemComponent> components = new ArrayList<>();
|
||||
|
||||
if(page > 0)
|
||||
{
|
||||
components.add(UrbanDictionary.getPreviousPageButton().asEnabled());
|
||||
} else {
|
||||
components.add(UrbanDictionary.getPreviousPageButton().asDisabled());
|
||||
}
|
||||
|
||||
if(page + 1 == search.getPages())
|
||||
{
|
||||
components.add(UrbanDictionary.getNextPageButton().asDisabled());
|
||||
} else {
|
||||
components.add(UrbanDictionary.getNextPageButton().asEnabled());
|
||||
}
|
||||
|
||||
// update the components on the object
|
||||
components.add(UrbanDictionary.getDeleteButton());
|
||||
ActionRow currentRow = ActionRow.of(components);
|
||||
|
||||
// update the message
|
||||
event.editComponents(currentRow).setEmbeds(updatedEmbed).queue();
|
||||
database.setUrbanPage(messageId, page);
|
||||
database.resetExpiryTimestamp(messageId);
|
||||
}
|
||||
|
||||
public static class UrbanSearch
|
||||
{
|
||||
final LinkedList<String> plaintextMeanings;
|
||||
final LinkedList<String> plaintextExamples;
|
||||
final LinkedList<String> contributorsNames;
|
||||
final LinkedList<String> submissionDates;
|
||||
|
||||
final String serializedMeanings;
|
||||
final String serializedExamples;
|
||||
final String serializedContributors;
|
||||
final String serializedDates;
|
||||
|
||||
final int pages;
|
||||
|
||||
public UrbanSearch(String serializedMeanings,
|
||||
String serializedExamples,
|
||||
String serializedContributors,
|
||||
String serializedDates)
|
||||
{
|
||||
this.serializedMeanings = serializedMeanings;
|
||||
this.serializedExamples = serializedExamples;
|
||||
this.serializedContributors = serializedContributors;
|
||||
this.serializedDates = serializedDates;
|
||||
|
||||
this.plaintextMeanings = SerializationUtil.deserializeBase64(serializedMeanings);
|
||||
this.plaintextExamples = SerializationUtil.deserializeBase64(serializedExamples);
|
||||
this.contributorsNames = SerializationUtil.deserializeBase64(serializedContributors);
|
||||
this.submissionDates = SerializationUtil.deserializeBase64(serializedDates);
|
||||
|
||||
this.pages = submissionDates.size();
|
||||
}
|
||||
|
||||
public UrbanSearch(Elements definitions)
|
||||
{
|
||||
plaintextMeanings = new LinkedList<>();
|
||||
plaintextExamples = new LinkedList<>();
|
||||
contributorsNames = new LinkedList<>();
|
||||
submissionDates = new LinkedList<>();
|
||||
|
||||
for(Element definition : definitions)
|
||||
{
|
||||
Elements meaningSingleton = definition.getElementsByClass("meaning");
|
||||
if(meaningSingleton.isEmpty())
|
||||
{
|
||||
plaintextMeanings.add(" ");
|
||||
} else
|
||||
{
|
||||
Element meaning = meaningSingleton.get(0);
|
||||
String text = meaning.html()
|
||||
.replaceAll("<br\\s*?>", "\n") // keep newlines
|
||||
.replaceAll("<.*?>", ""); // remove all other html tags
|
||||
// this is used to fix eg. & being shown literally instead of being parsed
|
||||
text = StringEscapeUtils.unescapeHtml4(text);
|
||||
// discord only allows 1024 characters for embed fields
|
||||
if(text.length() > 1024) text = text.substring(0, 1020) + "...";
|
||||
plaintextMeanings.add(text);
|
||||
}
|
||||
|
||||
Elements exampleSingleton = definition.getElementsByClass("example");
|
||||
|
||||
if(exampleSingleton.isEmpty())
|
||||
{
|
||||
plaintextExamples.add(" ");
|
||||
} else
|
||||
{
|
||||
Element example = exampleSingleton.get(0);
|
||||
String text = example.html()
|
||||
.replaceAll("<br\\s*?>", "\n") // keep newlines
|
||||
.replaceAll("<.*?>", ""); // remove all other html tags
|
||||
// this is used to fix eg. & being shown literally instead of being parsed
|
||||
text = StringEscapeUtils.unescapeHtml4(text);
|
||||
// discord only allows 1024 characters for embed fields
|
||||
if(text.length() > 1024) text = text.substring(0, 1020) + "...";
|
||||
plaintextExamples.add(text);
|
||||
}
|
||||
|
||||
Elements contributorSingleton = definition.getElementsByClass("contributor");
|
||||
if(contributorSingleton.isEmpty())
|
||||
{
|
||||
contributorsNames.add("Unknown");
|
||||
} else
|
||||
{
|
||||
Element contributor = contributorSingleton.get(0);
|
||||
|
||||
String htmlContributor = contributor.html();
|
||||
String htmlContributorName = contributor.select("a").html();
|
||||
String htmlSubmitDate = htmlContributor.substring(
|
||||
htmlContributor.indexOf("</a>") + 4);
|
||||
|
||||
contributorsNames.add(htmlContributorName
|
||||
.replaceAll("<.*?>", "")); // remove all html tags;
|
||||
|
||||
submissionDates.add(htmlSubmitDate
|
||||
.replaceAll("<.*?>", "")); // remove all html tags;
|
||||
}
|
||||
}
|
||||
|
||||
serializedMeanings = SerializationUtil.serializeBase64(plaintextMeanings);
|
||||
serializedExamples = SerializationUtil.serializeBase64(plaintextExamples);
|
||||
serializedContributors = SerializationUtil.serializeBase64(contributorsNames);
|
||||
serializedDates = SerializationUtil.serializeBase64(submissionDates);
|
||||
|
||||
pages = submissionDates.size();
|
||||
}
|
||||
|
||||
public List<String> getPlaintextMeanings() {
|
||||
return this.plaintextMeanings;
|
||||
}
|
||||
|
||||
public List<String> getPlaintextExamples() {
|
||||
return this.plaintextExamples;
|
||||
}
|
||||
|
||||
public List<String> getContributorsNames() {
|
||||
return this.contributorsNames;
|
||||
}
|
||||
|
||||
public List<String> getSubmissionDates() {
|
||||
return this.submissionDates;
|
||||
}
|
||||
|
||||
public String getSerializedMeanings() {
|
||||
return serializedMeanings;
|
||||
}
|
||||
|
||||
public String getSerializedExamples() {
|
||||
return serializedExamples;
|
||||
}
|
||||
|
||||
public String getSerializedContributors() {
|
||||
return serializedContributors;
|
||||
}
|
||||
|
||||
public String getSerializedDates() {
|
||||
return serializedDates;
|
||||
}
|
||||
|
||||
public int getPages() {
|
||||
return pages;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ChangeType
|
||||
{
|
||||
NEXT,
|
||||
PREVIOUS;
|
||||
}
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
package wtf.beatrice.hidekobot.commands.base;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.IMentionable;
|
||||
import net.dv8tion.jda.api.entities.Mentions;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.util.FormatUtil;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class UserPunishment
|
||||
{
|
||||
|
||||
private final static Duration maxTimeoutDuration = Duration.of(28, ChronoUnit.DAYS);
|
||||
private final static Duration minTimeoutDuration = Duration.of(30, ChronoUnit.SECONDS);
|
||||
|
||||
public static void handle(SlashCommandInteractionEvent event, PunishmentType punishmentType)
|
||||
{
|
||||
// this might take a sec
|
||||
event.deferReply().queue();
|
||||
|
||||
User targetUser = null;
|
||||
|
||||
OptionMapping targetUserArg = event.getOption("target");
|
||||
if(targetUserArg != null)
|
||||
{
|
||||
targetUser = targetUserArg.getAsUser();
|
||||
}
|
||||
|
||||
List<IMentionable> mentions = null;
|
||||
if(targetUser != null) mentions = new ArrayList<>(Collections.singletonList(targetUser));
|
||||
|
||||
String reason = null;
|
||||
OptionMapping reasonArg = event.getOption("reason");
|
||||
if(reasonArg != null)
|
||||
{
|
||||
reason = reasonArg.getAsString();
|
||||
}
|
||||
|
||||
String timeDiff = null;
|
||||
OptionMapping timeDiffArg = event.getOption("duration");
|
||||
if(timeDiffArg != null)
|
||||
{
|
||||
timeDiff = timeDiffArg.getAsString();
|
||||
}
|
||||
|
||||
// todo: the following code is not great, because we are making an array and then
|
||||
// we are also recreating the string later in code. this is useless and a bit hacked on,
|
||||
// but works for now. this happened because the function was NOT written with slash commands
|
||||
// in mind, but with message commands, that send every word as a separate argument.
|
||||
// we should probably rework the it so that it works better in both scenarios.
|
||||
String[] reasonSplit = null;
|
||||
// generate the arguments array by splitting the string
|
||||
if(reason != null) reasonSplit = reason.split("\\s+");
|
||||
//prepend timediff at index 0
|
||||
if(timeDiff != null) reasonSplit = ArrayUtils.insert(0, reasonSplit, timeDiff);
|
||||
// in message-commands, the first arg would contain the user mention. since we have no one mentioned here,
|
||||
// because it's in its own argument, we just prepend an empty string. note that this makes relying on the
|
||||
// first argument BAD, because it is no longer ensured that it contains the user mention.
|
||||
if(timeDiff != null) reasonSplit = ArrayUtils.insert(0, reasonSplit, "");
|
||||
|
||||
MessageResponse response = getResponse(event.getUser(),
|
||||
punishmentType,
|
||||
event.getChannel(),
|
||||
mentions,
|
||||
reasonSplit);
|
||||
|
||||
if(response.embed() != null)
|
||||
event.getHook().editOriginalEmbeds(response.embed()).queue();
|
||||
else if(response.content() != null)
|
||||
event.getHook().editOriginal(response.content()).queue();
|
||||
}
|
||||
|
||||
public static void handle(MessageReceivedEvent event, String[] args, PunishmentType punishmentType)
|
||||
{
|
||||
Mentions msgMentions = event.getMessage().getMentions();
|
||||
List<IMentionable> mentions = msgMentions.getMentions();
|
||||
|
||||
MessageResponse response = getResponse(event.getAuthor(),
|
||||
punishmentType,
|
||||
event.getChannel(),
|
||||
mentions,
|
||||
args);
|
||||
|
||||
if(response.embed() != null)
|
||||
event.getMessage().replyEmbeds(response.embed()).queue();
|
||||
else if(response.content() != null)
|
||||
event.getMessage().reply(response.content()).queue();
|
||||
}
|
||||
|
||||
public static MessageResponse getResponse(User author,
|
||||
PunishmentType punishmentType,
|
||||
MessageChannelUnion channel,
|
||||
List<IMentionable> mentions,
|
||||
String[] args)
|
||||
{
|
||||
String punishmentTypeName = punishmentType.name().toLowerCase();
|
||||
|
||||
|
||||
if(!(channel instanceof TextChannel))
|
||||
{
|
||||
// todo nicer looking with emojis
|
||||
return new MessageResponse("Sorry! I can't " + punishmentTypeName + " people in DMs.", null);
|
||||
}
|
||||
|
||||
if(mentions == null || mentions.isEmpty())
|
||||
{
|
||||
// todo nicer looking with emojis
|
||||
return new MessageResponse("You have to tell me who to " + punishmentTypeName + "!", null);
|
||||
}
|
||||
|
||||
String mentionedId = mentions.get(0).getId();
|
||||
User mentioned = null;
|
||||
|
||||
try {
|
||||
mentioned = HidekoBot.getAPI().retrieveUserById(mentionedId).complete();
|
||||
} catch (RuntimeException ignored)
|
||||
{
|
||||
// todo nicer looking with emojis
|
||||
return new MessageResponse("I can't " + punishmentTypeName + " that user!", null);
|
||||
}
|
||||
|
||||
StringBuilder reasonBuilder = new StringBuilder();
|
||||
String reason = "";
|
||||
|
||||
// some commands require an additional parameter before the reason, so in that case, we should start at 2.
|
||||
int startingPoint = punishmentType == PunishmentType.TIMEOUT ? 2 : 1;
|
||||
|
||||
if(args != null && args.length > startingPoint)
|
||||
{
|
||||
for(int i = startingPoint; i < args.length; i++)
|
||||
{
|
||||
String arg = args[i];
|
||||
reasonBuilder.append(arg);
|
||||
|
||||
if(i + 1 != arg.length())
|
||||
reasonBuilder.append(" "); // separate args with a space except on last iteration.
|
||||
}
|
||||
|
||||
reason = reasonBuilder.toString();
|
||||
}
|
||||
|
||||
if(mentioned == null)
|
||||
{
|
||||
// todo nicer looking with emojis
|
||||
return new MessageResponse("I can't " + punishmentTypeName + " that user!", null);
|
||||
}
|
||||
|
||||
Guild guild = ((TextChannel) channel).getGuild();
|
||||
Duration duration = null;
|
||||
|
||||
AuditableRestAction<Void> punishmentAction = null;
|
||||
|
||||
try {
|
||||
switch (punishmentType) {
|
||||
case BAN -> punishmentAction = guild.ban(mentioned, 0, TimeUnit.SECONDS);
|
||||
case KICK -> punishmentAction = guild.kick(mentioned);
|
||||
case TIMEOUT -> {
|
||||
if(args != null)
|
||||
{
|
||||
String durationStr = args[1];
|
||||
duration = FormatUtil.parseDuration(durationStr);
|
||||
}
|
||||
|
||||
boolean isDurationValid = true;
|
||||
|
||||
if(duration == null) isDurationValid = false;
|
||||
else
|
||||
{
|
||||
if(duration.compareTo(maxTimeoutDuration) > 0) isDurationValid = false;
|
||||
if(minTimeoutDuration.compareTo(duration) > 0) isDurationValid = false;
|
||||
}
|
||||
|
||||
if(duration == null || !isDurationValid)
|
||||
{
|
||||
// todo nicer looking with emojis
|
||||
return new MessageResponse("Sorry, but the specified duration is invalid!", null);
|
||||
}
|
||||
|
||||
punishmentAction = guild.timeoutFor(mentioned, duration);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException ignored) {
|
||||
// todo nicer looking with emojis
|
||||
return new MessageResponse("Sorry, I couldn't " + punishmentTypeName + " " + mentioned.getAsMention() + "!",
|
||||
null);
|
||||
}
|
||||
|
||||
if(!reason.isEmpty() && !reasonBuilder.isEmpty())
|
||||
punishmentAction.reason("[" + author.getAsTag() + "] " + reason);
|
||||
|
||||
try {
|
||||
punishmentAction.complete();
|
||||
} catch (RuntimeException ignored)
|
||||
{
|
||||
// todo nicer looking with emojis
|
||||
return new MessageResponse("Sorry, I couldn't " + punishmentTypeName + " " + mentioned.getAsMention() + "!",
|
||||
null);
|
||||
}
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
|
||||
embedBuilder.setAuthor(author.getAsTag(), null, author.getAvatarUrl());
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setTitle("User " + punishmentType.getPastTense());
|
||||
|
||||
embedBuilder.addField("\uD83D\uDC64 User", mentioned.getAsMention(), false);
|
||||
embedBuilder.addField("✂️ By", author.getAsMention(), false);
|
||||
if(duration != null)
|
||||
embedBuilder.addField("⏱️ Duration", FormatUtil.getNiceDuration(duration), false);
|
||||
|
||||
if(reason.isEmpty())
|
||||
reason = "*No reason specified*";
|
||||
|
||||
embedBuilder.addField("\uD83D\uDCD6 Reason", reason, false);
|
||||
|
||||
|
||||
return new MessageResponse(null, embedBuilder.build());
|
||||
|
||||
}
|
||||
|
||||
public enum PunishmentType {
|
||||
KICK("kicked"),
|
||||
BAN("banned"),
|
||||
TIMEOUT("timed out"),
|
||||
|
||||
;
|
||||
|
||||
private final String pastTense;
|
||||
|
||||
PunishmentType(String pastTense)
|
||||
{
|
||||
this.pastTense = pastTense;
|
||||
}
|
||||
|
||||
public String getPastTense()
|
||||
{
|
||||
return pastTense;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -10,10 +10,10 @@ import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AvatarCommandCompleter extends SlashArgumentsCompleterImpl
|
||||
public class ProfileImageCommandCompleter extends SlashArgumentsCompleterImpl
|
||||
{
|
||||
|
||||
public AvatarCommandCompleter(SlashCommand parentCommand) {
|
||||
public ProfileImageCommandCompleter(SlashCommand parentCommand) {
|
||||
super(parentCommand);
|
||||
}
|
||||
|
@ -0,0 +1,78 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.commands.base.Alias;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class AliasCommand implements MessageCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Arrays.asList("alias", "aliases"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null; // anyone can use it
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.TOOLS;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "See other command aliases.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "<command>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
if(args.length == 0)
|
||||
{
|
||||
event.getMessage().reply("\uD83D\uDE20 Hey, you have to specify a command!").queue();
|
||||
return;
|
||||
}
|
||||
|
||||
String commandLabel = args[0].toLowerCase();
|
||||
MessageCommand command = Cache.getMessageCommandListener().getRegisteredCommand(commandLabel);
|
||||
if(command == null)
|
||||
{
|
||||
event.getMessage().reply("Unrecognized command: `" + commandLabel + "`!").queue(); // todo prettier
|
||||
return;
|
||||
}
|
||||
|
||||
String aliases = Alias.generateNiceAliases(command);
|
||||
aliases = "Aliases for **" + command.getCommandLabels().get(0) + "**: " + aliases;
|
||||
|
||||
event.getMessage()
|
||||
.reply(aliases)
|
||||
.queue();
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Mentions;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.commands.base.ProfileImage;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class AvatarCommand implements MessageCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Collections.singletonList("avatar"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null; // anyone can use it
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get someone's avatar, or your own. You can additionally specify a resolution.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "[mentioned user] [resolution]";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.TOOLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
|
||||
|
||||
User user;
|
||||
int resolution = -1;
|
||||
|
||||
// we have no specific order for user and resolution, so let's try parsing any arg as resolution
|
||||
// (mentions are handled differently by a specific method)
|
||||
boolean resFound = false;
|
||||
|
||||
for (String arg : args) {
|
||||
try {
|
||||
int givenRes = Integer.parseInt(arg);
|
||||
resolution = ProfileImage.parseResolution(givenRes);
|
||||
resFound = true;
|
||||
break;
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// fallback in case we didn't find any specified resolution
|
||||
if(!resFound) resolution = ProfileImage.parseResolution(512);
|
||||
|
||||
// check if someone is mentioned
|
||||
Mentions mentions = event.getMessage().getMentions();
|
||||
if(mentions.getMentions().isEmpty())
|
||||
{
|
||||
user = event.getAuthor();
|
||||
} else
|
||||
{
|
||||
String mentionedId = mentions.getMentions().get(0).getId();
|
||||
user = HidekoBot.getAPI().retrieveUserById(mentionedId).complete();
|
||||
}
|
||||
|
||||
// in case of issues, fallback to the sender
|
||||
if(user == null) user = event.getAuthor();
|
||||
|
||||
// send a response
|
||||
MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR);
|
||||
if(response.content() != null)
|
||||
{
|
||||
event.getMessage().reply(response.content()).queue();
|
||||
} else if(response.embed() != null)
|
||||
{
|
||||
event.getMessage().replyEmbeds(response.embed()).queue();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.IMentionable;
|
||||
import net.dv8tion.jda.api.entities.Mentions;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.commands.base.UserPunishment;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BanCommand implements MessageCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Collections.singletonList("ban"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return new ArrayList<Permission>(Collections.singletonList(Permission.BAN_MEMBERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.MODERATION;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Ban the mentioned user.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "<mentioned user> [reason]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
UserPunishment.handle(event, args, UserPunishment.PunishmentType.BAN);
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Mentions;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.commands.base.ProfileImage;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class BannerCommand implements MessageCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Collections.singletonList("banner"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null; // anyone can use it
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get someone's profile banner, or your own.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "[mentioned user] [resolution]";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.TOOLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
User user;
|
||||
int resolution = -1;
|
||||
|
||||
// we have no specific order for user and resolution, so let's try parsing any arg as resolution
|
||||
// (mentions are handled differently by a specific method)
|
||||
boolean resFound = false;
|
||||
|
||||
for (String arg : args) {
|
||||
try {
|
||||
int givenRes = Integer.parseInt(arg);
|
||||
resolution = ProfileImage.parseResolution(givenRes);
|
||||
resFound = true;
|
||||
break;
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
// fallback in case we didn't find any specified resolution
|
||||
if(!resFound) resolution = ProfileImage.parseResolution(512);
|
||||
|
||||
// check if someone is mentioned
|
||||
Mentions mentions = event.getMessage().getMentions();
|
||||
if(mentions.getMentions().isEmpty())
|
||||
{
|
||||
user = event.getAuthor();
|
||||
} else
|
||||
{
|
||||
String mentionedId = mentions.getMentions().get(0).getId();
|
||||
user = HidekoBot.getAPI().retrieveUserById(mentionedId).complete();
|
||||
}
|
||||
|
||||
// in case of issues, fallback to the sender
|
||||
if(user == null) user = event.getAuthor();
|
||||
|
||||
// send a response
|
||||
MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER);
|
||||
if(response.content() != null)
|
||||
{
|
||||
event.getMessage().reply(response.content()).queue();
|
||||
} else if(response.embed() != null)
|
||||
{
|
||||
event.getMessage().replyEmbeds(response.embed()).queue();
|
||||
}
|
||||
}
|
||||
}
|
@ -3,11 +3,14 @@ package wtf.beatrice.hidekobot.commands.message;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.commands.base.BotInfo;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -17,7 +20,7 @@ public class BotInfoCommand implements MessageCommand
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Collections.singletonList("botinfo"));
|
||||
return new LinkedList<>(Arrays.asList("botinfo", "info"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -31,6 +34,24 @@ public class BotInfoCommand implements MessageCommand
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get general info about the bot.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.TOOLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args) {
|
||||
|
||||
|
@ -4,8 +4,11 @@ import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.commands.base.ClearChat;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
@ -28,6 +31,24 @@ public class ClearCommand implements MessageCommand
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.MODERATION;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Clear the current channel's chat history.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "[amount]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
@ -43,7 +64,18 @@ public class ClearCommand implements MessageCommand
|
||||
// get the amount from the command args.
|
||||
Integer toDeleteAmount;
|
||||
if (args.length == 0) toDeleteAmount = 1;
|
||||
else toDeleteAmount = Integer.parseInt(args[0]);
|
||||
else
|
||||
{
|
||||
try {
|
||||
toDeleteAmount = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException e)
|
||||
{
|
||||
toDeleteAmount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// cap the amount to avoid abuse.
|
||||
if(toDeleteAmount > ClearChat.getMaxAmount()) toDeleteAmount = 0;
|
||||
|
||||
error = ClearChat.checkDeleteAmount(toDeleteAmount);
|
||||
if (error != null) {
|
||||
@ -64,14 +96,16 @@ public class ClearCommand implements MessageCommand
|
||||
|
||||
// edit the message text and attach a button.
|
||||
Button dismiss = ClearChat.getDismissButton();
|
||||
// ^ todo: maybe the dismiss button should also delete the original message sent by the user?
|
||||
// todo: but then, we need to differentiate between command type in the database, and store
|
||||
// todo: that message's id too.
|
||||
botMessage = botMessage.editMessage(content).setActionRow(dismiss).complete();
|
||||
Message finalMessage = event.getChannel().sendMessage(content).setActionRow(dismiss).complete();
|
||||
|
||||
// add the message to database.
|
||||
Cache.getDatabaseSource().queueDisabling(botMessage);
|
||||
Cache.getDatabaseSource().trackRanCommandReply(botMessage, event.getAuthor());
|
||||
Cache.getDatabaseSource().queueDisabling(finalMessage);
|
||||
Cache.getDatabaseSource().trackRanCommandReply(finalMessage, event.getAuthor());
|
||||
|
||||
// delete the sender's message.
|
||||
event.getMessage().delete().queue();
|
||||
// delete the "clearing" info message.
|
||||
botMessage.delete().queue();
|
||||
|
||||
}
|
||||
|
||||
|
@ -2,8 +2,10 @@ package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.commands.base.CoinFlip;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -29,6 +31,24 @@ public class CoinFlipCommand implements MessageCommand
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Flip a coin.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.FUN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args) {
|
||||
|
||||
|
@ -0,0 +1,73 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.commands.base.DiceRoll;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class DiceRollCommand implements MessageCommand
|
||||
{
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Arrays.asList("diceroll", "droll", "roll"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null; // anyone can use it
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Roll dice. You can roll multiple dice at the same time." +
|
||||
"\nExamples:" +
|
||||
"\n - `d8 10` to roll an 8-sided die 10 times." +
|
||||
"\n - `d12 3 d5 10` to roll a 12-sided die 3 times, and then a 5-sided die 10 times." +
|
||||
"\n - `30` to roll a standard 6-sided die 30 times." +
|
||||
"\n - `d10` to roll a 10-sided die once.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "[dice size] [rolls]";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.FUN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
|
||||
MessageResponse response = DiceRoll.buildResponse(event.getAuthor(), args);
|
||||
|
||||
if(response.content() != null)
|
||||
{
|
||||
event.getMessage().reply(response.content()).queue();
|
||||
} else if(response.embed() != null)
|
||||
{
|
||||
event.getMessage().replyEmbeds(response.embed()).queue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,9 @@ package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -24,11 +27,29 @@ public class HelloCommand implements MessageCommand
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get pinged by the bot.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.FUN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
String senderId = event.getMessage().getAuthor().getId();
|
||||
event.getMessage().reply("Hi, <@" + senderId + ">! :sparkles:").queue();
|
||||
String sender = event.getMessage().getAuthor().getAsMention();
|
||||
event.getMessage().reply("Hi, " + sender + "! :sparkles:").queue();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,157 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.apache.commons.text.WordUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.commands.base.Alias;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class HelpCommand implements MessageCommand
|
||||
{
|
||||
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Collections.singletonList("help"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() { return null; }
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get general help on the bot. Specify a command if you want specific help about that command.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "[command]";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.TOOLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
LinkedHashMap<CommandCategory, LinkedList<MessageCommand>> commandCategories = new LinkedHashMap<>();
|
||||
|
||||
if(args.length == 0)
|
||||
{
|
||||
for(CommandCategory category : CommandCategory.values())
|
||||
{
|
||||
LinkedList<MessageCommand> commandsOfThisCategory = new LinkedList<>();
|
||||
for (MessageCommand command : Cache.getMessageCommandListener().getRegisteredCommands())
|
||||
{
|
||||
if(command.getCategory().equals(category))
|
||||
{
|
||||
commandsOfThisCategory.add(command);
|
||||
}
|
||||
}
|
||||
|
||||
commandCategories.put(category, commandsOfThisCategory);
|
||||
}
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setTitle("Bot Help");
|
||||
|
||||
embedBuilder.addField("General Help",
|
||||
"Type `" + Cache.getBotPrefix() + " help [command]` to get help on a specific command." +
|
||||
"\nYou will find a list of commands organized in categories below.",
|
||||
false);
|
||||
|
||||
for(CommandCategory category : commandCategories.keySet())
|
||||
{
|
||||
StringBuilder commandsList = new StringBuilder();
|
||||
LinkedList<MessageCommand> commandsOfThisCategory = commandCategories.get(category);
|
||||
|
||||
for(int pos = 0; pos < commandsOfThisCategory.size(); pos++)
|
||||
{
|
||||
MessageCommand command = commandsOfThisCategory.get(pos);
|
||||
commandsList.append("`").append(command.getCommandLabels().get(0)).append("`");
|
||||
|
||||
if(pos + 1 != commandsOfThisCategory.size())
|
||||
commandsList.append(", "); // separate with comma except on last run
|
||||
}
|
||||
|
||||
String niceCategoryName = category.name().replace("_", " ");
|
||||
niceCategoryName = WordUtils.capitalizeFully(niceCategoryName);
|
||||
niceCategoryName = category.getEmoji() + " " + niceCategoryName;
|
||||
|
||||
embedBuilder.addField(niceCategoryName, commandsList.toString(), false);
|
||||
}
|
||||
|
||||
event.getMessage().replyEmbeds(embedBuilder.build()).queue();
|
||||
} else {
|
||||
|
||||
String commandLabel = args[0].toLowerCase();
|
||||
MessageCommand command = Cache.getMessageCommandListener().getRegisteredCommand(commandLabel);
|
||||
if(command == null)
|
||||
{
|
||||
event.getMessage().reply("Unrecognized command: `" + commandLabel + "`!").queue(); // todo prettier
|
||||
return;
|
||||
}
|
||||
|
||||
commandLabel = command.getCommandLabels().get(0);
|
||||
String usage = "`" + Cache.getBotPrefix() + " " + commandLabel;
|
||||
String internalUsage = command.getUsage();
|
||||
if(internalUsage != null) usage += " " + internalUsage;
|
||||
usage += "`";
|
||||
|
||||
String aliases = Alias.generateNiceAliases(command);
|
||||
|
||||
List<Permission> permissions = command.getPermissions();
|
||||
StringBuilder permissionsStringBuilder = new StringBuilder();
|
||||
if(permissions == null)
|
||||
{
|
||||
permissionsStringBuilder = new StringBuilder("Available to everyone");
|
||||
} else {
|
||||
for(int i = 0; i < permissions.size(); i++)
|
||||
{
|
||||
Permission permission = permissions.get(i);
|
||||
permissionsStringBuilder.append("**").append(permission.getName()).append("**");
|
||||
|
||||
if(i + 1 != permissions.size())
|
||||
permissionsStringBuilder.append(", "); // separate with comma expect on last iteration
|
||||
}
|
||||
}
|
||||
|
||||
String title = command.getCategory().getEmoji() +
|
||||
" \"" + WordUtils.capitalizeFully(commandLabel + "\" help");
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setTitle(title);
|
||||
|
||||
embedBuilder.addField("Description", command.getDescription(), false);
|
||||
embedBuilder.addField("Usage", usage, false);
|
||||
embedBuilder.addField("Aliases", aliases, false);
|
||||
embedBuilder.addField("Permissions", permissionsStringBuilder.toString(), false);
|
||||
|
||||
event.getMessage().replyEmbeds(embedBuilder.build()).queue();
|
||||
}
|
||||
}
|
||||
}
|
@ -5,8 +5,10 @@ import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.commands.base.Invite;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
@ -32,6 +34,24 @@ public class InviteCommand implements MessageCommand
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get the bot's invite link.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.MODERATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
@ -49,9 +69,7 @@ public class InviteCommand implements MessageCommand
|
||||
.addActionRow(inviteButton)
|
||||
.queue();
|
||||
event.getMessage().addReaction(Emoji.fromUnicode("✅")).queue();
|
||||
}, (error) -> {
|
||||
event.getMessage().addReaction(Emoji.fromUnicode("❌")).queue();
|
||||
});
|
||||
}, error -> event.getMessage().addReaction(Emoji.fromUnicode("❌")).queue());
|
||||
} else {
|
||||
event.getMessage()
|
||||
.replyEmbeds(inviteEmbed)
|
||||
|
@ -0,0 +1,67 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.IMentionable;
|
||||
import net.dv8tion.jda.api.entities.Mentions;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.requests.restaction.AuditableRestAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.commands.base.UserPunishment;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class KickCommand implements MessageCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Collections.singletonList("kick"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return new ArrayList<Permission>(Collections.singletonList(Permission.KICK_MEMBERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.MODERATION;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Kick the mentioned user from the guild.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "<mentioned user> [reason]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
UserPunishment.handle(event, args, UserPunishment.PunishmentType.KICK);
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.IMentionable;
|
||||
import net.dv8tion.jda.api.entities.Mentions;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.commands.base.LoveCalculator;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class LoveCalculatorCommand implements MessageCommand
|
||||
{
|
||||
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels()
|
||||
{
|
||||
return new LinkedList<>(Arrays.asList("lovecalc", "lovecalculator", "lc"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null; //anyone can use it
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Calculate how much two people love each other. You can mention two people or just one.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "<person 1> [person 2]";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.FUN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
|
||||
Mentions mentionsObj = event.getMessage().getMentions();
|
||||
List<IMentionable> mentions = mentionsObj.getMentions();
|
||||
|
||||
|
||||
if(args.length == 0 || mentions.isEmpty())
|
||||
{
|
||||
event.getMessage()
|
||||
.reply("\uD83D\uDE22 I need to know who to check! Please mention them.")
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
User user1, user2;
|
||||
|
||||
String mentionedUserId = mentions.get(0).getId();
|
||||
user1 = HidekoBot.getAPI().retrieveUserById(mentionedUserId).complete();
|
||||
|
||||
if(mentions.size() == 1)
|
||||
{
|
||||
user2 = event.getAuthor();
|
||||
} else {
|
||||
mentionedUserId = mentions.get(1).getId();
|
||||
user2 = HidekoBot.getAPI().retrieveUserById(mentionedUserId).complete();
|
||||
}
|
||||
|
||||
MessageEmbed embed = LoveCalculator.buildEmbedAndCacheResult(event.getAuthor(), user1, user2);
|
||||
event.getChannel().sendMessageEmbeds(embed).queue();
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.commands.base.MagicBall;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class MagicBallCommand implements MessageCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return MagicBall.getLabels();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null; // anyone can use it
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Ask a question to the Magic Ball.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "<question>";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.FUN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
if(args.length == 0)
|
||||
{
|
||||
event.getMessage().reply("You need to specify a question!").queue();
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder questionBuilder = new StringBuilder();
|
||||
for(int i = 0; i < args.length; i++)
|
||||
{
|
||||
String arg = args[i];
|
||||
questionBuilder.append(arg);
|
||||
if(i + 1 != args.length) // don't add a separator on the last iteration
|
||||
questionBuilder.append(" ");
|
||||
}
|
||||
|
||||
String question = questionBuilder.toString();
|
||||
|
||||
|
||||
event.getChannel().sendMessageEmbeds(MagicBall.generateEmbed(question, event.getAuthor())).queue();
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.commands.base.Say;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class SayCommand implements MessageCommand
|
||||
{
|
||||
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Collections.singletonList("say"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() { return Collections.singletonList(Say.getPermission()); }
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Make the bot say something for you.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "<text>";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.TOOLS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
|
||||
String messageContent;
|
||||
if(args.length != 0 && !args[0].isEmpty())
|
||||
{
|
||||
messageContent = args[0];
|
||||
} else {
|
||||
event.getMessage().reply("\uD83D\uDE20 Hey, you have to tell me what to say!")
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
event.getChannel().sendMessage(messageContent).queue();
|
||||
event.getMessage().delete().queue();
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.commands.base.UserPunishment;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class TimeoutCommand implements MessageCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Collections.singletonList("timeout"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return new ArrayList<Permission>(Collections.singletonList(Permission.MODERATE_MEMBERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.MODERATION;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Timeout the mentioned user.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "<mentioned user> <duration> [reason]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
UserPunishment.handle(event, args, UserPunishment.PunishmentType.TIMEOUT);
|
||||
}
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.commands.base.Trivia;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class TriviaCommand implements MessageCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return new LinkedList<>(Collections.singletonList("trivia"));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.FUN;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Start a Trivia session and play with others!";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
MessageChannel channel = event.getChannel();
|
||||
|
||||
if(!(channel instanceof TextChannel))
|
||||
{
|
||||
channel.sendMessage(Trivia.getNoDMsError()).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Trivia.channelsRunningTrivia.contains(channel.getId()))
|
||||
{
|
||||
// todo: also what if the bot stops (database...?)
|
||||
// todo: also what if the message is already deleted
|
||||
Message err = event.getMessage().reply(Trivia.getTriviaAlreadyRunningError()).complete();
|
||||
Cache.getTaskScheduler().schedule(() -> err.delete().queue(), 10, TimeUnit.SECONDS);
|
||||
return;
|
||||
}
|
||||
|
||||
MessageResponse response = Trivia.generateMainScreen();
|
||||
|
||||
event.getMessage().replyEmbeds(response.embed()).addActionRow(response.components()).queue(message ->
|
||||
{
|
||||
Cache.getDatabaseSource().trackRanCommandReply(message, event.getAuthor());
|
||||
Cache.getDatabaseSource().queueDisabling(message);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package wtf.beatrice.hidekobot.commands.message;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
import wtf.beatrice.hidekobot.commands.base.UrbanDictionary;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class UrbanDictionaryCommand implements MessageCommand
|
||||
{
|
||||
|
||||
@Override
|
||||
public LinkedList<String> getCommandLabels() {
|
||||
return UrbanDictionary.getCommandLabels();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<Permission> getPermissions() {
|
||||
return null; //anyone can use it
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean passRawArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Look something up in the Urban Dictionary.";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getUsage() {
|
||||
return "<query>";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.FUN;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void runCommand(MessageReceivedEvent event, String label, String[] args)
|
||||
{
|
||||
if(args.length == 0)
|
||||
{
|
||||
event.getMessage().reply(UrbanDictionary.getNoArgsError()).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
// sanitize args by only keeping letters and numbers, and adding "+" instead of spaces for HTML parsing
|
||||
StringBuilder termBuilder = new StringBuilder();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String arg = args[i];
|
||||
termBuilder.append(arg);
|
||||
|
||||
if(i + 1 != args.length) // add spaces between args, but not on the last run
|
||||
termBuilder.append(" ");
|
||||
}
|
||||
|
||||
String term = UrbanDictionary.sanitizeArgs(termBuilder.toString(), false);
|
||||
String url = UrbanDictionary.generateUrl(term);
|
||||
|
||||
Document doc;
|
||||
|
||||
try {
|
||||
doc = Jsoup.connect(url).get();
|
||||
} catch (IOException e) {
|
||||
event.getMessage().reply(UrbanDictionary.getTermNotFoundError()).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
Elements definitions = doc.getElementsByClass("definition");
|
||||
UrbanDictionary.UrbanSearch search = new UrbanDictionary.UrbanSearch(definitions);
|
||||
MessageEmbed embed = UrbanDictionary.buildEmbed(term, url, event.getAuthor(), search, 0);
|
||||
|
||||
// disable next page if we only have one result
|
||||
Button nextPageBtnLocal = UrbanDictionary.getNextPageButton();
|
||||
if(search.getPages() == 1) nextPageBtnLocal = nextPageBtnLocal.asDisabled();
|
||||
|
||||
event.getChannel()
|
||||
.sendMessageEmbeds(embed)
|
||||
.addActionRow(UrbanDictionary.getPreviousPageButton().asDisabled(),
|
||||
//disabled by default because we're on page 0
|
||||
nextPageBtnLocal,
|
||||
UrbanDictionary.getDeleteButton())
|
||||
.queue(message -> UrbanDictionary.track(message, event.getAuthor(), search, term));
|
||||
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
@ -8,7 +7,8 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.commands.base.ProfileImage;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
public class AvatarCommand extends SlashCommandImpl
|
||||
@ -31,9 +31,6 @@ public class AvatarCommand extends SlashCommandImpl
|
||||
User user;
|
||||
int resolution;
|
||||
|
||||
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
|
||||
|
||||
|
||||
OptionMapping userArg = event.getOption("user");
|
||||
if(userArg != null)
|
||||
{
|
||||
@ -45,57 +42,18 @@ public class AvatarCommand extends SlashCommandImpl
|
||||
OptionMapping sizeArg = event.getOption("size");
|
||||
if(sizeArg != null)
|
||||
{
|
||||
resolution = sizeArg.getAsInt();
|
||||
|
||||
// method to find closest value to accepted values
|
||||
int distance = Math.abs(acceptedSizes[0] - resolution);
|
||||
int idx = 0;
|
||||
for(int c = 1; c < acceptedSizes.length; c++){
|
||||
int cdistance = Math.abs(acceptedSizes[c] - resolution);
|
||||
if(cdistance < distance){
|
||||
idx = c;
|
||||
distance = cdistance;
|
||||
}
|
||||
}
|
||||
resolution = acceptedSizes[idx];
|
||||
|
||||
resolution = ProfileImage.parseResolution(sizeArg.getAsInt());
|
||||
} else {
|
||||
resolution = 512;
|
||||
resolution = ProfileImage.parseResolution(512);
|
||||
}
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
|
||||
// embed processing
|
||||
MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.AVATAR);
|
||||
if(response.content() != null)
|
||||
{
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setTitle("Profile picture");
|
||||
|
||||
embedBuilder.addField("User", "<@" + user.getId() + ">", false);
|
||||
|
||||
embedBuilder.addField("Current resolution", resolution + " × " + resolution, false);
|
||||
|
||||
// string builder to create a string that links to all available resolutions
|
||||
StringBuilder links = new StringBuilder();
|
||||
for(int pos = 0; pos < acceptedSizes.length; pos++)
|
||||
event.getHook().editOriginal(response.content()).queue();
|
||||
} else if(response.embed() != null)
|
||||
{
|
||||
int currSize = acceptedSizes[pos];
|
||||
|
||||
String currLink = user.getEffectiveAvatar().getUrl(currSize);
|
||||
|
||||
links.append("[").append(currSize).append("px](").append(currLink).append(")");
|
||||
if(pos + 1 != acceptedSizes.length) // don't add a separator on the last iteration
|
||||
{
|
||||
links.append(" | ");
|
||||
event.getHook().editOriginalEmbeds(response.embed()).queue();
|
||||
}
|
||||
}
|
||||
|
||||
embedBuilder.addField("Available resolutions", links.toString(), false);
|
||||
|
||||
|
||||
|
||||
embedBuilder.setImage(user.getEffectiveAvatar().getUrl(resolution));
|
||||
}
|
||||
|
||||
event.getHook().editOriginalEmbeds(embedBuilder.build()).queue();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.commands.base.UserPunishment;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
public class BanCommand extends SlashCommandImpl
|
||||
{
|
||||
@Override
|
||||
public CommandData getSlashCommandData()
|
||||
{
|
||||
|
||||
return Commands.slash("ban", "Ban someone from the guild.")
|
||||
.addOption(OptionType.MENTIONABLE, "target",
|
||||
"The member user to ban.",
|
||||
true,
|
||||
false)
|
||||
.addOption(OptionType.STRING, "reason",
|
||||
"The reason for the punishment.",
|
||||
false,
|
||||
false)
|
||||
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.BAN_MEMBERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||
{
|
||||
UserPunishment.handle(event, UserPunishment.PunishmentType.BAN);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.commands.base.ProfileImage;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
public class BannerCommand extends SlashCommandImpl
|
||||
{
|
||||
@Override
|
||||
public CommandData getSlashCommandData() {
|
||||
return Commands.slash("banner", "Get someone's profile banner.")
|
||||
.addOption(OptionType.USER, "user", "User you want to grab the banner of.")
|
||||
.addOption(OptionType.INTEGER, "size", "The size of the returned image.",
|
||||
false,
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||
{
|
||||
// defer reply because this might take a moment
|
||||
event.deferReply().queue();
|
||||
|
||||
User user;
|
||||
int resolution;
|
||||
|
||||
OptionMapping userArg = event.getOption("user");
|
||||
if(userArg != null)
|
||||
{
|
||||
user = userArg.getAsUser();
|
||||
} else {
|
||||
user = event.getUser();
|
||||
}
|
||||
|
||||
OptionMapping sizeArg = event.getOption("size");
|
||||
if(sizeArg != null)
|
||||
{
|
||||
resolution = ProfileImage.parseResolution(sizeArg.getAsInt());
|
||||
} else {
|
||||
resolution = ProfileImage.parseResolution(512);
|
||||
}
|
||||
|
||||
MessageResponse response = ProfileImage.buildResponse(resolution, user, ProfileImage.ImageType.BANNER);
|
||||
if(response.content() != null)
|
||||
{
|
||||
event.getHook().editOriginal(response.content()).queue();
|
||||
} else if(response.embed() != null)
|
||||
{
|
||||
event.getHook().editOriginalEmbeds(response.embed()).queue();
|
||||
}
|
||||
}
|
||||
}
|
@ -44,6 +44,9 @@ public class ClearCommand extends SlashCommandImpl
|
||||
OptionMapping amountOption = event.getOption("amount");
|
||||
int toDeleteAmount = amountOption == null ? 1 : amountOption.getAsInt();
|
||||
|
||||
// cap the amount to avoid abuse.
|
||||
if(toDeleteAmount > ClearChat.getMaxAmount()) toDeleteAmount = 0;
|
||||
|
||||
error = ClearChat.checkDeleteAmount(toDeleteAmount);
|
||||
if(error != null)
|
||||
{
|
||||
|
@ -0,0 +1,50 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.commands.base.DiceRoll;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
public class DiceRollCommand extends SlashCommandImpl
|
||||
{
|
||||
@Override
|
||||
public CommandData getSlashCommandData()
|
||||
{
|
||||
|
||||
return Commands.slash("diceroll", "Roll dice. You can roll multiple dice at the same time.")
|
||||
.addOption(OptionType.STRING, "query",
|
||||
"The dice to roll.",
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||
{
|
||||
event.deferReply().queue();
|
||||
|
||||
OptionMapping textOption = event.getOption("query");
|
||||
String messageContent = "";
|
||||
if(textOption != null)
|
||||
{
|
||||
messageContent = textOption.getAsString();
|
||||
}
|
||||
|
||||
String[] args = messageContent.split("\\s");
|
||||
|
||||
MessageResponse response = DiceRoll.buildResponse(event.getUser(), args);
|
||||
|
||||
if(response.content() != null)
|
||||
{
|
||||
event.getHook().editOriginal(response.content()).queue();
|
||||
} else if(response.embed() != null)
|
||||
{
|
||||
event.getHook().editOriginalEmbeds(response.embed()).queue();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.IMentionable;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.commands.base.Say;
|
||||
import wtf.beatrice.hidekobot.commands.base.UserPunishment;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class KickCommand extends SlashCommandImpl
|
||||
{
|
||||
@Override
|
||||
public CommandData getSlashCommandData()
|
||||
{
|
||||
|
||||
return Commands.slash("kick", "Kick someone from the guild.")
|
||||
.addOption(OptionType.MENTIONABLE, "target",
|
||||
"The member user to kick.",
|
||||
true,
|
||||
false)
|
||||
.addOption(OptionType.STRING, "reason",
|
||||
"The reason for the punishment.",
|
||||
false,
|
||||
false)
|
||||
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.KICK_MEMBERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||
{
|
||||
UserPunishment.handle(event, UserPunishment.PunishmentType.KICK);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.commands.base.LoveCalculator;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
public class LoveCalculatorCommand extends SlashCommandImpl
|
||||
{
|
||||
@Override
|
||||
public CommandData getSlashCommandData()
|
||||
{
|
||||
|
||||
return Commands.slash("lovecalc",
|
||||
"Calculate how much two people love each other.")
|
||||
|
||||
.addOption(OptionType.MENTIONABLE,
|
||||
"first",
|
||||
"The first person to account for",
|
||||
true)
|
||||
|
||||
.addOption(OptionType.MENTIONABLE,
|
||||
"second",
|
||||
"The second person to account for",
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||
{
|
||||
User firstUser, secondUser;
|
||||
|
||||
OptionMapping firsUserArg = event.getOption("first");
|
||||
if(firsUserArg != null)
|
||||
{
|
||||
firstUser = firsUserArg.getAsUser(); //todo null check?
|
||||
} else {
|
||||
event.reply("\uD83D\uDE22 I need to know who to check! Please mention them.")
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
OptionMapping secondUserArg = event.getOption("second");
|
||||
if(secondUserArg != null)
|
||||
{
|
||||
secondUser = secondUserArg.getAsUser(); //todo null check?
|
||||
} else {
|
||||
secondUser = event.getUser();
|
||||
}
|
||||
|
||||
MessageEmbed embed = LoveCalculator.buildEmbedAndCacheResult(event.getUser(), firstUser, secondUser);
|
||||
event.replyEmbeds(embed).queue();
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.commands.base.MagicBall;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
public class MagicBallCommand extends SlashCommandImpl
|
||||
{
|
||||
@Override
|
||||
public CommandData getSlashCommandData()
|
||||
{
|
||||
|
||||
return Commands.slash(MagicBall.getLabels().get(0),
|
||||
"Ask a question to the magic ball.")
|
||||
.addOption(OptionType.STRING, "question",
|
||||
"The question to ask.",
|
||||
true,
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||
{
|
||||
// get the asked question
|
||||
OptionMapping textOption = event.getOption("question");
|
||||
String question = "";
|
||||
if(textOption != null)
|
||||
{
|
||||
question = textOption.getAsString();
|
||||
}
|
||||
|
||||
if(textOption == null || question.isEmpty())
|
||||
{
|
||||
event.reply("\uD83D\uDE20 Hey, you have to ask me a question!")
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
MessageEmbed response = MagicBall.generateEmbed(question, event.getUser());
|
||||
event.replyEmbeds(response).queue();
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
@ -9,6 +8,7 @@ import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.commands.base.Say;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
public class SayCommand extends SlashCommandImpl
|
||||
@ -22,7 +22,7 @@ public class SayCommand extends SlashCommandImpl
|
||||
"The message to send.",
|
||||
true,
|
||||
false)
|
||||
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MESSAGE_MANAGE));
|
||||
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Say.getPermission()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,40 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.commands.base.UserPunishment;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
public class TimeoutCommand extends SlashCommandImpl
|
||||
{
|
||||
@Override
|
||||
public CommandData getSlashCommandData()
|
||||
{
|
||||
|
||||
return Commands.slash("timeout", "Timeout someone in the guild.")
|
||||
.addOption(OptionType.MENTIONABLE, "target",
|
||||
"The member user to time out.",
|
||||
true,
|
||||
false)
|
||||
.addOption(OptionType.STRING, "duration",
|
||||
"The duration of the timeout.",
|
||||
true,
|
||||
false)
|
||||
.addOption(OptionType.STRING, "reason",
|
||||
"The reason for the punishment.",
|
||||
false,
|
||||
false)
|
||||
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MODERATE_MEMBERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||
{
|
||||
UserPunishment.handle(event, UserPunishment.PunishmentType.TIMEOUT);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.commands.base.Trivia;
|
||||
import wtf.beatrice.hidekobot.objects.MessageResponse;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
public class TriviaCommand extends SlashCommandImpl
|
||||
{
|
||||
@Override
|
||||
public CommandData getSlashCommandData()
|
||||
{
|
||||
|
||||
return Commands.slash("trivia",
|
||||
"Start a Trivia session and play with others!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||
{
|
||||
MessageChannel channel = event.getChannel();
|
||||
|
||||
if(!(channel instanceof TextChannel))
|
||||
{
|
||||
event.reply(Trivia.getNoDMsError()).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Trivia.channelsRunningTrivia.contains(channel.getId()))
|
||||
{
|
||||
event.reply(Trivia.getTriviaAlreadyRunningError()).setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
// if we got here, this might take a bit
|
||||
event.deferReply().queue();
|
||||
MessageResponse response = Trivia.generateMainScreen();
|
||||
|
||||
event.getHook().editOriginalEmbeds(response.embed()).setActionRow(response.components()).queue(message ->
|
||||
{
|
||||
Cache.getDatabaseSource().trackRanCommandReply(message, event.getUser());
|
||||
Cache.getDatabaseSource().queueDisabling(message);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package wtf.beatrice.hidekobot.commands.slash;
|
||||
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
import wtf.beatrice.hidekobot.commands.base.UrbanDictionary;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class UrbanDictionaryCommand extends SlashCommandImpl
|
||||
{
|
||||
@Override
|
||||
public CommandData getSlashCommandData()
|
||||
{
|
||||
|
||||
return Commands.slash(UrbanDictionary.getCommandLabels().get(0),
|
||||
"Look up a term on Urban Dictionary.")
|
||||
.addOption(OptionType.STRING, "term", "The term to look up", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSlashCommand(@NotNull SlashCommandInteractionEvent event)
|
||||
{
|
||||
event.deferReply().queue();
|
||||
|
||||
// get the term to look up
|
||||
OptionMapping textOption = event.getOption("term");
|
||||
String term = "";
|
||||
if(textOption != null)
|
||||
{
|
||||
term = textOption.getAsString();
|
||||
}
|
||||
|
||||
if(textOption == null || term.isEmpty())
|
||||
{
|
||||
event.reply(UrbanDictionary.getNoArgsError())
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
final String sanitizedTerm = UrbanDictionary.sanitizeArgs(term, false);
|
||||
String url = UrbanDictionary.generateUrl(sanitizedTerm);
|
||||
|
||||
Document doc;
|
||||
|
||||
try {
|
||||
doc = Jsoup.connect(url).get();
|
||||
} catch (IOException e) {
|
||||
event.reply(UrbanDictionary.getTermNotFoundError())
|
||||
.setEphemeral(true)
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
Elements definitions = doc.getElementsByClass("definition");
|
||||
UrbanDictionary.UrbanSearch search = new UrbanDictionary.UrbanSearch(definitions);
|
||||
MessageEmbed embed = UrbanDictionary.buildEmbed(sanitizedTerm, url, event.getUser(), search, 0);
|
||||
|
||||
// disable next page if we only have one result
|
||||
Button nextPageBtnLocal = UrbanDictionary.getNextPageButton();
|
||||
if(search.getPages() == 1) nextPageBtnLocal = nextPageBtnLocal.asDisabled();
|
||||
|
||||
ActionRow actionRow = ActionRow.of(UrbanDictionary.getPreviousPageButton().asDisabled(),
|
||||
//disabled by default because we're on page 0
|
||||
nextPageBtnLocal,
|
||||
UrbanDictionary.getDeleteButton());
|
||||
event.getHook().editOriginalEmbeds(embed).setComponents(actionRow).queue(message ->
|
||||
UrbanDictionary.track(message, event.getUser(), search, sanitizedTerm));
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package wtf.beatrice.hidekobot.datasources;
|
||||
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.util.Logger;
|
||||
|
||||
@ -57,7 +58,7 @@ public class ConfigurationSource
|
||||
}
|
||||
}
|
||||
// load the YAML file from the filesystem
|
||||
Yaml fsConfigYaml = new Yaml();
|
||||
Yaml fsConfigYaml = new Yaml(new SafeConstructor());
|
||||
LinkedHashMap<String, Object> fsConfigContents = null; // map holding all file entries
|
||||
try (InputStream fsConfigStream = new FileInputStream(fsConfigFile))
|
||||
{ fsConfigContents = fsConfigYaml.load(fsConfigStream); }
|
||||
|
@ -81,6 +81,12 @@ public class DatabaseSource
|
||||
* | 39402849302 | 39402849302 | 39402849302 | 39402849302 | PRIVATE |
|
||||
* --------------------------------------------------------------------------------------------
|
||||
*
|
||||
* TABLE 3: urban_dictionary
|
||||
* -----------------------------------------------------------------------------------------------------
|
||||
* | message_id | page | meanings | examples | contributors | dates | term |
|
||||
* -----------------------------------------------------------------------------------------------------
|
||||
* | 39402849302 | 0 | base64 | base64 | base64 | base64 | miku |
|
||||
* -----------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
//todo: javadocs
|
||||
@ -104,6 +110,16 @@ public class DatabaseSource
|
||||
"channel_type TEXT NOT NULL" + // channel type (PRIVATE, FORUM, ...)
|
||||
");");
|
||||
|
||||
newTables.add("CREATE TABLE IF NOT EXISTS urban_dictionary (" +
|
||||
"message_id TEXT NOT NULL, " + // message id of the bot's response
|
||||
"page INTEGER NOT NULL," + // page that we are currently on
|
||||
"meanings TEXT NOT NULL," + // list of all meanings, serialized and encoded
|
||||
"examples TEXT NOT NULL, " + // list of all examples, serialized and encoded
|
||||
"contributors TEXT NOT NULL, " + // list of all contributors, serialized and encoded
|
||||
"dates TEXT NOT NULL, " + // list of all submission dates, serialized and encoded
|
||||
"term TEXT NOT NULL" + // the term that was searched
|
||||
");");
|
||||
|
||||
for(String sql : newTables)
|
||||
{
|
||||
try (Statement stmt = dbConnection.createStatement()) {
|
||||
@ -301,6 +317,17 @@ public class DatabaseSource
|
||||
return false;
|
||||
}
|
||||
|
||||
query = "DELETE FROM urban_dictionary WHERE message_id = ?;";
|
||||
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
|
||||
{
|
||||
preparedStatement.setString(1, messageId);
|
||||
preparedStatement.execute();
|
||||
} catch (SQLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -373,5 +400,220 @@ public class DatabaseSource
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean trackUrban(String meanings, String examples,
|
||||
String contributors, String dates,
|
||||
Message message, String term)
|
||||
{
|
||||
|
||||
String query = "INSERT INTO urban_dictionary " +
|
||||
"(message_id, page, meanings, examples, contributors, dates, term) VALUES " +
|
||||
" (?, ?, ?, ?, ?, ?, ?);";
|
||||
|
||||
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
|
||||
{
|
||||
preparedStatement.setString(1, message.getId());
|
||||
preparedStatement.setInt(2, 0);
|
||||
preparedStatement.setString(3, meanings);
|
||||
preparedStatement.setString(4, examples);
|
||||
preparedStatement.setString(5, contributors);
|
||||
preparedStatement.setString(6, dates);
|
||||
preparedStatement.setString(7, term);
|
||||
|
||||
preparedStatement.executeUpdate();
|
||||
|
||||
return true;
|
||||
} catch (SQLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getUrbanPage(String messageId)
|
||||
{
|
||||
String query = "SELECT page " +
|
||||
"FROM urban_dictionary " +
|
||||
"WHERE message_id = ?;";
|
||||
|
||||
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
|
||||
{
|
||||
preparedStatement.setString(1, messageId);
|
||||
ResultSet resultSet = preparedStatement.executeQuery();
|
||||
if(resultSet.isClosed()) return 0;
|
||||
while(resultSet.next())
|
||||
{
|
||||
return resultSet.getInt("page");
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String getUrbanMeanings(String messageId)
|
||||
{
|
||||
String query = "SELECT meanings " +
|
||||
"FROM urban_dictionary " +
|
||||
"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("meanings");
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getUrbanExamples(String messageId)
|
||||
{
|
||||
String query = "SELECT examples " +
|
||||
"FROM urban_dictionary " +
|
||||
"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("examples");
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getUrbanContributors(String messageId)
|
||||
{
|
||||
String query = "SELECT contributors " +
|
||||
"FROM urban_dictionary " +
|
||||
"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("contributors");
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getUrbanDates(String messageId)
|
||||
{
|
||||
String query = "SELECT dates " +
|
||||
"FROM urban_dictionary " +
|
||||
"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("dates");
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getUrbanTerm(String messageId)
|
||||
{
|
||||
String query = "SELECT term " +
|
||||
"FROM urban_dictionary " +
|
||||
"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("term");
|
||||
}
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean setUrbanPage(String messageId, int page)
|
||||
{
|
||||
String query = "UPDATE urban_dictionary " +
|
||||
"SET page = ? " +
|
||||
"WHERE message_id = ?;";
|
||||
|
||||
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
|
||||
{
|
||||
preparedStatement.setInt(1, page);
|
||||
preparedStatement.setString(2, messageId);
|
||||
preparedStatement.executeUpdate();
|
||||
|
||||
return true;
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean resetExpiryTimestamp(String messageId)
|
||||
{
|
||||
LocalDateTime expiryTime = LocalDateTime.now().plusSeconds(Cache.getExpiryTimeSeconds());
|
||||
|
||||
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(Cache.getExpiryTimestampFormat());
|
||||
String expiryTimeFormatted = dateTimeFormatter.format(expiryTime);
|
||||
|
||||
String query = "UPDATE pending_disabled_messages " +
|
||||
"SET expiry_timestamp = ? " +
|
||||
"WHERE message_id = ?;";
|
||||
|
||||
try(PreparedStatement preparedStatement = dbConnection.prepareStatement(query))
|
||||
{
|
||||
preparedStatement.setString(1, expiryTimeFormatted);
|
||||
preparedStatement.setString(2, messageId);
|
||||
preparedStatement.executeUpdate();
|
||||
|
||||
return true;
|
||||
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package wtf.beatrice.hidekobot.listeners;
|
||||
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import wtf.beatrice.hidekobot.commands.base.ClearChat;
|
||||
import wtf.beatrice.hidekobot.commands.base.CoinFlip;
|
||||
import wtf.beatrice.hidekobot.commands.base.Trivia;
|
||||
import wtf.beatrice.hidekobot.commands.base.UrbanDictionary;
|
||||
import wtf.beatrice.hidekobot.util.CommandUtil;
|
||||
|
||||
public class ButtonInteractionListener extends ListenerAdapter
|
||||
{
|
||||
@ -17,8 +19,17 @@ public class ButtonInteractionListener extends ListenerAdapter
|
||||
// coinflip
|
||||
case "coinflip_reflip" -> CoinFlip.buttonReFlip(event);
|
||||
|
||||
// clearchat command
|
||||
case "clear_dismiss" -> ClearChat.dismissMessage(event);
|
||||
// generic dismiss button
|
||||
case "generic_dismiss" -> CommandUtil.delete(event);
|
||||
|
||||
// urban dictionary navigation
|
||||
case "urban_nextpage" -> UrbanDictionary.changePage(event, UrbanDictionary.ChangeType.NEXT);
|
||||
case "urban_previouspage" -> UrbanDictionary.changePage(event, UrbanDictionary.ChangeType.PREVIOUS);
|
||||
|
||||
// trivia
|
||||
case "trivia_correct" -> Trivia.handleAnswer(event, Trivia.AnswerType.CORRECT);
|
||||
case "trivia_wrong_1", "trivia_wrong_2", "trivia_wrong_3" ->
|
||||
Trivia.handleAnswer(event, Trivia.AnswerType.WRONG);
|
||||
|
||||
}
|
||||
|
||||
|
@ -7,14 +7,13 @@ import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.objects.commands.CommandCategory;
|
||||
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
|
||||
import wtf.beatrice.hidekobot.objects.comparators.MessageCommandAliasesComparator;
|
||||
import wtf.beatrice.hidekobot.util.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
import java.util.*;
|
||||
|
||||
public class MessageCommandListener extends ListenerAdapter
|
||||
{
|
||||
@ -23,6 +22,10 @@ public class MessageCommandListener extends ListenerAdapter
|
||||
private final TreeMap<LinkedList<String>, MessageCommand> registeredCommands =
|
||||
new TreeMap<LinkedList<String>, MessageCommand>(new MessageCommandAliasesComparator());
|
||||
|
||||
// map commands and their categories.
|
||||
// this is not strictly needed but it's better to have it so we avoid looping every time we need to check the cat.
|
||||
LinkedHashMap<CommandCategory, LinkedList<MessageCommand>> commandCategories = new LinkedHashMap<>();
|
||||
|
||||
private final String commandRegex = "(?i)^(hideko|hde)\\b";
|
||||
// (?i) -> case insensitive flag
|
||||
// ^ -> start of string (not in middle of a sentence)
|
||||
@ -57,10 +60,14 @@ public class MessageCommandListener extends ListenerAdapter
|
||||
@Override
|
||||
public void onMessageReceived(@NotNull MessageReceivedEvent event)
|
||||
{
|
||||
String eventMessage = event.getMessage().getContentDisplay();
|
||||
// check if a bot is sending this message, and ignore it
|
||||
if(event.getAuthor().isBot()) return;
|
||||
|
||||
// warning: we are getting the RAW value of the message content, not the DISPLAY value!
|
||||
String eventMessage = event.getMessage().getContentRaw();
|
||||
|
||||
// check if the sent message matches the bot activation regex (prefix, name, ...)
|
||||
if(!eventMessage.toLowerCase().matches(commandRegex + ".*"))
|
||||
if(!eventMessage.toLowerCase().matches(commandRegex + "((.|\\n)*)"))
|
||||
return;
|
||||
|
||||
// generate args from the string
|
||||
@ -74,7 +81,9 @@ public class MessageCommandListener extends ListenerAdapter
|
||||
// it will be the whole text as a single element.
|
||||
if(argsString.isEmpty())
|
||||
{
|
||||
event.getMessage().reply("Hello there! ✨").queue();
|
||||
event.getMessage()
|
||||
.reply("Hello there! ✨ Type `" + Cache.getBotPrefix() + " help` to get started!")
|
||||
.queue();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -87,7 +96,11 @@ public class MessageCommandListener extends ListenerAdapter
|
||||
|
||||
if(commandObject == null)
|
||||
{
|
||||
/* temporarily disabled because when people talk about the bot, it replies with this spammy message.
|
||||
|
||||
event.getMessage().reply("Unrecognized command: `" + commandLabel + "`!").queue(); // todo prettier
|
||||
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
@ -120,8 +133,9 @@ public class MessageCommandListener extends ListenerAdapter
|
||||
String[] commandArgs;
|
||||
if(commandObject.passRawArgs())
|
||||
{
|
||||
|
||||
// remove first argument, which is the command label
|
||||
argsString = argsString.replaceAll("^[\\S]+\\s+", "");
|
||||
argsString = argsString.replaceAll("^[\\S]+\\s*", "");
|
||||
// pass all other arguments as a single argument as the first array element
|
||||
commandArgs = new String[]{argsString};
|
||||
}
|
||||
|
@ -0,0 +1,19 @@
|
||||
package wtf.beatrice.hidekobot.listeners;
|
||||
|
||||
import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import wtf.beatrice.hidekobot.commands.base.Trivia;
|
||||
|
||||
public class SelectMenuInteractionListener extends ListenerAdapter
|
||||
{
|
||||
|
||||
@Override
|
||||
public void onStringSelectInteraction(StringSelectInteractionEvent event)
|
||||
{
|
||||
switch (event.getComponentId().toLowerCase()) {
|
||||
|
||||
// trivia
|
||||
case "trivia_categories" -> Trivia.handleMenuSelection(event);
|
||||
}
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ public class SlashCommandListener extends ListenerAdapter
|
||||
if(command == null) return;
|
||||
|
||||
|
||||
// finally run the command, in a new thread to avoid locking.
|
||||
// finally run the command, in a new thread to avoid locking the main one.
|
||||
new Thread(() -> command.runSlashCommand(event)).start();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package wtf.beatrice.hidekobot.objects;
|
||||
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.interactions.components.ItemComponent;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record MessageResponse(@Nullable String content,
|
||||
@Nullable MessageEmbed embed,
|
||||
@Nullable ItemComponent... components) {
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package wtf.beatrice.hidekobot.objects.commands;
|
||||
|
||||
public enum CommandCategory
|
||||
{
|
||||
MODERATION("️\uD83D\uDC40"),
|
||||
FUN("\uD83C\uDFB2"),
|
||||
TOOLS("\uD83D\uDEE0"),
|
||||
|
||||
;
|
||||
|
||||
private String emoji;
|
||||
CommandCategory(String emoji)
|
||||
{
|
||||
this.emoji = emoji;
|
||||
}
|
||||
|
||||
public String getEmoji() { return emoji; }
|
||||
}
|
@ -2,6 +2,7 @@ package wtf.beatrice.hidekobot.objects.commands;
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedList;
|
||||
@ -39,6 +40,31 @@ public interface MessageCommand
|
||||
*/
|
||||
boolean passRawArgs();
|
||||
|
||||
/**
|
||||
* Say what category this command belongs to.
|
||||
*
|
||||
* @return the command category.
|
||||
*/
|
||||
@NotNull
|
||||
CommandCategory getCategory();
|
||||
|
||||
/**
|
||||
* Say what this command does.
|
||||
*
|
||||
* @return a String explaining what this command does.
|
||||
*/
|
||||
@NotNull
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Say how people should use this command.
|
||||
*
|
||||
* @return a String explaining how to use the command, excluding the bot prefix and command name. Null if no parameter is needed
|
||||
*/
|
||||
@Nullable
|
||||
String getUsage();
|
||||
|
||||
|
||||
/**
|
||||
* Run the command logic by parsing the event and replying accordingly.
|
||||
*
|
||||
|
@ -0,0 +1,16 @@
|
||||
package wtf.beatrice.hidekobot.objects.comparators;
|
||||
|
||||
import wtf.beatrice.hidekobot.objects.fun.TriviaCategory;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* This class gets two trivia categories, and compares them by their name.
|
||||
*/
|
||||
public class TriviaCategoryComparator implements Comparator<TriviaCategory> {
|
||||
|
||||
@Override
|
||||
public int compare(TriviaCategory o1, TriviaCategory o2) {
|
||||
return CharSequence.compare(o1.categoryName(), o2.categoryName());
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package wtf.beatrice.hidekobot.objects.comparators;
|
||||
|
||||
import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* This class gets two trivia scores, and compares their score.
|
||||
*/
|
||||
public class TriviaScoreComparator implements Comparator<TriviaScore> {
|
||||
|
||||
@Override
|
||||
public int compare(TriviaScore o1, TriviaScore o2) {
|
||||
return Integer.compare(o2.getScore(), o1.getScore()); // inverted, because higher number should come first
|
||||
}
|
||||
}
|
45
src/main/java/wtf/beatrice/hidekobot/objects/fun/Dice.java
Normal file
45
src/main/java/wtf/beatrice/hidekobot/objects/fun/Dice.java
Normal file
@ -0,0 +1,45 @@
|
||||
package wtf.beatrice.hidekobot.objects.fun;
|
||||
|
||||
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Dice
|
||||
{
|
||||
private final int sides;
|
||||
private int value = 0;
|
||||
private final UUID uuid;
|
||||
|
||||
public Dice(int sides)
|
||||
{
|
||||
this.sides = sides;
|
||||
this.uuid = UUID.randomUUID();
|
||||
}
|
||||
|
||||
public Dice(Dice old)
|
||||
{
|
||||
this.sides = old.sides;
|
||||
this.value = old.value;
|
||||
this.uuid = UUID.randomUUID();
|
||||
}
|
||||
|
||||
public int getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public int getSides()
|
||||
{
|
||||
return sides;
|
||||
}
|
||||
|
||||
public void roll()
|
||||
{
|
||||
value = RandomUtil.getRandomNumber(1, sides);
|
||||
}
|
||||
|
||||
public UUID getUUID()
|
||||
{
|
||||
return uuid;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package wtf.beatrice.hidekobot.objects.fun;
|
||||
|
||||
public record TriviaCategory(String categoryName, int categoryId) {
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package wtf.beatrice.hidekobot.objects.fun;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record TriviaQuestion(String question, String correctAnswer,
|
||||
List<String> wrongAnswers) {
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package wtf.beatrice.hidekobot.objects.fun;
|
||||
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
|
||||
public class TriviaScore
|
||||
{
|
||||
|
||||
private final User user;
|
||||
private int score = 0;
|
||||
|
||||
public TriviaScore(User user)
|
||||
{
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public void changeScore(int add)
|
||||
{
|
||||
score += add;
|
||||
}
|
||||
|
||||
public int getScore() { return score; }
|
||||
|
||||
public User getUser() { return user; }
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + user.getAsTag() + "," + score + "]";
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +1,12 @@
|
||||
package wtf.beatrice.hidekobot.runnables;
|
||||
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
||||
import net.dv8tion.jda.api.requests.RestAction;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
|
||||
import wtf.beatrice.hidekobot.util.CommandUtil;
|
||||
import wtf.beatrice.hidekobot.util.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ExpiredMessageTask implements Runnable {
|
||||
@ -63,95 +55,9 @@ public class ExpiredMessageTask implements Runnable {
|
||||
if(now.isAfter(expiryDate))
|
||||
{
|
||||
if(Cache.isVerbose()) logger.log("expired: " + messageId);
|
||||
disableExpired(messageId);
|
||||
CommandUtil.disableExpired(messageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void disableExpired(String messageId)
|
||||
{
|
||||
String channelId = databaseSource.getQueuedExpiringMessageChannel(messageId);
|
||||
|
||||
ChannelType msgChannelType = databaseSource.getTrackedMessageChannelType(messageId);
|
||||
|
||||
MessageChannel textChannel = null;
|
||||
|
||||
|
||||
// this should never happen, but only message channels are supported.
|
||||
if(!msgChannelType.isMessage())
|
||||
{
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
// if this is a DM
|
||||
if(!(msgChannelType.isGuild()))
|
||||
{
|
||||
String userId = databaseSource.getTrackedReplyUserId(messageId);
|
||||
User user = HidekoBot.getAPI().retrieveUserById(userId).complete();
|
||||
if(user == null)
|
||||
{
|
||||
// if user is not found, consider it expired
|
||||
// (deleted profile, or blocked the bot)
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
textChannel = user.openPrivateChannel().complete();
|
||||
}
|
||||
else
|
||||
{
|
||||
String guildId = databaseSource.getQueuedExpiringMessageGuild(messageId);
|
||||
Guild guild = HidekoBot.getAPI().getGuildById(guildId);
|
||||
if(guild == null)
|
||||
{
|
||||
// if guild is not found, consider it expired
|
||||
// (server was deleted or bot was kicked)
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
textChannel = guild.getTextChannelById(channelId);
|
||||
}
|
||||
|
||||
if(textChannel == null)
|
||||
{
|
||||
// if channel is not found, count it as expired
|
||||
// (channel was deleted or bot permissions restricted)
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
RestAction<Message> retrieveAction = textChannel.retrieveMessageById(messageId);
|
||||
|
||||
|
||||
if(Cache.isVerbose()) logger.log("cleaning up: " + messageId);
|
||||
|
||||
retrieveAction.queue(
|
||||
|
||||
message -> {
|
||||
if(message == null)
|
||||
{
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
List<LayoutComponent> components = message.getComponents();
|
||||
List<LayoutComponent> newComponents = new ArrayList<>();
|
||||
for (LayoutComponent component : components)
|
||||
{
|
||||
component = component.asDisabled();
|
||||
newComponents.add(component);
|
||||
}
|
||||
|
||||
message.editMessageComponents(newComponents).queue();
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
},
|
||||
|
||||
(error) -> {
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,12 @@
|
||||
package wtf.beatrice.hidekobot.runnables;
|
||||
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
|
||||
public class RandomSeedTask implements Runnable
|
||||
{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Cache.setRandomSeed(System.currentTimeMillis());
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package wtf.beatrice.hidekobot.runnables;
|
||||
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.util.RandomUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class StatusUpdateTask implements Runnable
|
||||
{
|
||||
|
||||
List<String> statuses = Arrays.asList(
|
||||
"Hatsune Miku: Project DIVA",
|
||||
"Wii Sports",
|
||||
"Excel",
|
||||
"Mii Channel",
|
||||
"Wii Speak",
|
||||
"Minetest",
|
||||
"Mario Kart Wii"
|
||||
);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
int randomPos = RandomUtil.getRandomNumber(0, statuses.size() - 1);
|
||||
String status = statuses.get(randomPos) + " | " + Cache.getBotPrefix() + " help";
|
||||
HidekoBot.getAPI().getPresence().setActivity(Activity.playing(status));
|
||||
}
|
||||
}
|
202
src/main/java/wtf/beatrice/hidekobot/runnables/TriviaTask.java
Normal file
202
src/main/java/wtf/beatrice/hidekobot/runnables/TriviaTask.java
Normal file
@ -0,0 +1,202 @@
|
||||
package wtf.beatrice.hidekobot.runnables;
|
||||
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||
import org.json.JSONObject;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.commands.base.Trivia;
|
||||
import wtf.beatrice.hidekobot.objects.comparators.TriviaScoreComparator;
|
||||
import wtf.beatrice.hidekobot.objects.fun.TriviaCategory;
|
||||
import wtf.beatrice.hidekobot.objects.fun.TriviaQuestion;
|
||||
import wtf.beatrice.hidekobot.objects.fun.TriviaScore;
|
||||
import wtf.beatrice.hidekobot.util.CommandUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
public class TriviaTask implements Runnable
|
||||
{
|
||||
private final User author;
|
||||
private final MessageChannel channel;
|
||||
|
||||
private Message previousMessage = null;
|
||||
|
||||
private final JSONObject triviaJson;
|
||||
private final List<TriviaQuestion> questions;
|
||||
private final TriviaCategory category;
|
||||
|
||||
ScheduledFuture<?> future = null;
|
||||
|
||||
private int iteration = 0;
|
||||
|
||||
public TriviaTask(User author, MessageChannel channel, TriviaCategory category)
|
||||
{
|
||||
this.author = author;
|
||||
this.channel = channel;
|
||||
this.category = category;
|
||||
|
||||
triviaJson = Trivia.fetchJson(Trivia.getTriviaLink(category.categoryId()));
|
||||
questions = Trivia.parseQuestions(triviaJson); //todo: null check, rate limiting...
|
||||
}
|
||||
|
||||
public void setScheduledFuture(ScheduledFuture<?> future)
|
||||
{
|
||||
this.future = future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
|
||||
if(previousMessage != null)
|
||||
{
|
||||
// todo: we shouldn't use this method, since it messes with the database... look at coin reflip
|
||||
CommandUtil.disableExpired(previousMessage.getId());
|
||||
|
||||
String previousCorrectAnswer = questions.get(iteration-1).correctAnswer();
|
||||
|
||||
// we need this to be thread-locking to avoid getting out of sync with the rest of the trivia features
|
||||
previousMessage.reply("The correct answer was: **" + previousCorrectAnswer + "**!").complete();
|
||||
// todo: maybe also add who replied correctly as a list
|
||||
|
||||
// clean the list of people who answered, so they can answer again for the new question
|
||||
Trivia.channelAndWhoResponded.put(previousMessage.getChannel().getId(), new ArrayList<>());
|
||||
}
|
||||
|
||||
if(iteration >= questions.size())
|
||||
{
|
||||
|
||||
String scoreboardText = "\uD83D\uDC23 Trivia session is over!";
|
||||
|
||||
List<String> winners = new ArrayList<>();
|
||||
int topScore = 0;
|
||||
StringBuilder othersBuilder = new StringBuilder();
|
||||
|
||||
LinkedList<TriviaScore> triviaScores = Trivia.channelAndScores.get(channel.getId());
|
||||
if(triviaScores == null) triviaScores = new LinkedList<>();
|
||||
else triviaScores.sort(new TriviaScoreComparator());
|
||||
|
||||
int pos = 0;
|
||||
Integer previousScore = null;
|
||||
for(TriviaScore triviaScore : triviaScores)
|
||||
{
|
||||
if(pos > 10) break; // cap at top 10
|
||||
|
||||
String user = triviaScore.getUser().getAsMention();
|
||||
int score = triviaScore.getScore();
|
||||
if(previousScore == null)
|
||||
{
|
||||
previousScore = score;
|
||||
topScore = score;
|
||||
pos = 1;
|
||||
} else {
|
||||
if(score != previousScore) pos++;
|
||||
}
|
||||
|
||||
if(pos == 1) winners.add(user);
|
||||
else {
|
||||
othersBuilder.append("\n").append(pos)
|
||||
.append(" | ").append(user)
|
||||
.append(": ").append(score).append(" points");
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder winnersBuilder = new StringBuilder();
|
||||
for(int i = 0; i < winners.size(); i++)
|
||||
{
|
||||
String winner = winners.get(i);
|
||||
winnersBuilder.append(winner);
|
||||
if(i + 1 != winners.size())
|
||||
{
|
||||
winnersBuilder.append(", "); // separate with comma except on last run
|
||||
} else {
|
||||
winnersBuilder.append(": ").append(topScore).append(" points \uD83C\uDF89");
|
||||
}
|
||||
}
|
||||
|
||||
String winnersTitle = "\uD83D\uDCAB ";
|
||||
winnersTitle += winners.size() == 1 ? "Winner" : "Winners";
|
||||
|
||||
String winnersString = winnersBuilder.toString();
|
||||
String othersString = othersBuilder.toString();
|
||||
|
||||
EmbedBuilder scoreboardBuilder = new EmbedBuilder();
|
||||
scoreboardBuilder.setColor(Cache.getBotColor());
|
||||
scoreboardBuilder.setTitle("\uD83C\uDF1F Trivia Scoreboard");
|
||||
if(!winnersString.isEmpty()) scoreboardBuilder.addField(winnersTitle, winnersString, false);
|
||||
else scoreboardBuilder.addField("\uD83D\uDE22 Sad Trivia",
|
||||
"No one played \uD83D\uDE2D", false);
|
||||
if(!othersString.isEmpty()) scoreboardBuilder.addField("☁️ Others", othersString, false);
|
||||
|
||||
channel.sendMessage(scoreboardText).addEmbeds(scoreboardBuilder.build()).queue();
|
||||
|
||||
// remove all cached data
|
||||
Trivia.channelsRunningTrivia.remove(channel.getId());
|
||||
Trivia.channelAndWhoResponded.remove(channel.getId());
|
||||
Trivia.channelAndScores.remove(channel.getId());
|
||||
|
||||
future.cancel(false);
|
||||
// we didn't implement null checks on the future on purpose, because we need to know if we were unable
|
||||
// to cancel it (and console errors should make it clear enough).
|
||||
return;
|
||||
}
|
||||
|
||||
TriviaQuestion currentTriviaQuestion = questions.get(iteration);
|
||||
|
||||
List<Button> answerButtons = new ArrayList<>();
|
||||
|
||||
Button correctAnswerButton = Button.primary("trivia_correct", currentTriviaQuestion.correctAnswer());
|
||||
answerButtons.add(correctAnswerButton);
|
||||
|
||||
int i = 0; // we need to add a number because buttons can't have the same id
|
||||
for(String wrongAnswer : currentTriviaQuestion.wrongAnswers())
|
||||
{
|
||||
i++;
|
||||
Button wrongAnswerButton = Button.primary("trivia_wrong_" + i, wrongAnswer);
|
||||
answerButtons.add(wrongAnswerButton);
|
||||
}
|
||||
|
||||
Collections.shuffle(answerButtons);
|
||||
|
||||
List<String> buttonEmojis = Arrays.asList("\uD83D\uDD34", "\uD83D\uDD35",
|
||||
"\uD83D\uDFE2", "\uD83D\uDFE1", "\uD83D\uDFE4", "\uD83D\uDFE3", "\uD83D\uDFE0");
|
||||
|
||||
// add emojis to buttons
|
||||
for(int emojiPos = 0; emojiPos < buttonEmojis.size(); emojiPos++)
|
||||
{
|
||||
if(emojiPos == answerButtons.size()) break;
|
||||
|
||||
String emoji = buttonEmojis.get(emojiPos);
|
||||
Button button = answerButtons.get(emojiPos);
|
||||
|
||||
answerButtons.set(emojiPos, button.withEmoji(Emoji.fromUnicode(emoji)));
|
||||
}
|
||||
|
||||
EmbedBuilder embedBuilder = new EmbedBuilder();
|
||||
|
||||
embedBuilder.setColor(Cache.getBotColor());
|
||||
embedBuilder.setTitle("\uD83C\uDFB2 Trivia - " + category.categoryName() +
|
||||
" (" + (iteration+1) + "/" + questions.size() + ")");
|
||||
|
||||
embedBuilder.addField("❓ Question", currentTriviaQuestion.question(), false);
|
||||
|
||||
previousMessage = channel
|
||||
.sendMessageEmbeds(embedBuilder.build())
|
||||
.setActionRow(answerButtons)
|
||||
.complete();
|
||||
|
||||
|
||||
Cache.getDatabaseSource().trackRanCommandReply(previousMessage, author);
|
||||
// todo: ^ we should get rid of this tracking, since we don't need to know who started the trivia.
|
||||
// todo: however, for now, that's the only way to avoid a thread-locking scenario as some data is
|
||||
// todo: only stored in that table. this should be solved when we merge / fix the two main tables.
|
||||
// todo: then, we can remove this instruction.
|
||||
Cache.getDatabaseSource().queueDisabling(previousMessage);
|
||||
|
||||
iteration++;
|
||||
}
|
||||
}
|
235
src/main/java/wtf/beatrice/hidekobot/util/CommandUtil.java
Normal file
235
src/main/java/wtf/beatrice/hidekobot/util/CommandUtil.java
Normal file
@ -0,0 +1,235 @@
|
||||
package wtf.beatrice.hidekobot.util;
|
||||
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.Command;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
||||
import net.dv8tion.jda.api.requests.RestAction;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.datasources.DatabaseSource;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandUtil
|
||||
{
|
||||
|
||||
private static final Logger logger = new Logger(CommandUtil.class);
|
||||
|
||||
/**
|
||||
* Function to delete a message when a user clicks the "delete" button attached to that message.
|
||||
* This will check in the database if that user ran the command originally.
|
||||
*
|
||||
* @param event the button interaction event.
|
||||
*/
|
||||
public static void delete(ButtonInteractionEvent event)
|
||||
{
|
||||
// check if the user interacting is the same one who ran the command
|
||||
if (!(Cache.getDatabaseSource().isUserTrackedFor(event.getUser().getId(), event.getMessageId()))) {
|
||||
event.reply("❌ You did not run this command!").setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
// delete the message
|
||||
event.getInteraction().getMessage().delete().queue();
|
||||
// no need to manually untrack it from database, it will be purged on the next planned check.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to update slash commands registered on Discord's side.
|
||||
* It runs automatically every time the bot starts, but only updates the commands in case differences
|
||||
* are found, unless forced.
|
||||
*
|
||||
* @param force a boolean specifying if the update should be forced even if no differences were found.
|
||||
*/
|
||||
public static void updateSlashCommands(boolean force)
|
||||
{
|
||||
|
||||
// populate commands list from registered commands
|
||||
List<CommandData> allCommands = new ArrayList<>();
|
||||
for(SlashCommand cmd : Cache.getSlashCommandListener().getRegisteredCommands())
|
||||
{ allCommands.add(cmd.getSlashCommandData()); }
|
||||
|
||||
JDA jdaInstance = HidekoBot.getAPI();
|
||||
|
||||
// get all the already registered commands
|
||||
List<Command> registeredCommands = jdaInstance.retrieveCommands().complete();
|
||||
|
||||
boolean update = false;
|
||||
|
||||
if(force)
|
||||
{
|
||||
update = true;
|
||||
} else
|
||||
{
|
||||
|
||||
// for each command that we have already registered...
|
||||
for(Command currRegCmd : registeredCommands)
|
||||
{
|
||||
boolean found = false;
|
||||
|
||||
// iterate through all "recognized" commands
|
||||
for(CommandData cmdData : allCommands)
|
||||
{
|
||||
// if we find the same command...
|
||||
if(cmdData.getName().equals(currRegCmd.getName()))
|
||||
{
|
||||
// quit the loop since we found it.
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if no match was found, we need to send an updated command list because
|
||||
// an old command was probably removed.
|
||||
if(!found)
|
||||
{
|
||||
update = true;
|
||||
|
||||
// quit the loop since we only need to trigger this once.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if an update is not already queued...
|
||||
if(!update)
|
||||
{
|
||||
// for each "recognized" valid command
|
||||
for(CommandData currCmdData : allCommands)
|
||||
{
|
||||
boolean found = false;
|
||||
|
||||
// iterate through all already registered commands.
|
||||
for(Command cmd : registeredCommands)
|
||||
{
|
||||
// if this command was already registered...
|
||||
if(cmd.getName().equals(currCmdData.getName()))
|
||||
{
|
||||
// quit the loop since we found a match.
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if no match was found, we need to send an updated command list because
|
||||
// a new command was probably added.
|
||||
if(!found)
|
||||
{
|
||||
update = true;
|
||||
|
||||
// quit the loop since we only need to trigger this once.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.log("Found " + registeredCommands.size() + " commands.");
|
||||
|
||||
if(update)
|
||||
{
|
||||
// send updated command list.
|
||||
jdaInstance.updateCommands().addCommands(allCommands).queue();
|
||||
logger.log("Commands updated. New total: " + allCommands.size() + ".");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to disable all buttons from an expired message.
|
||||
*
|
||||
* @param messageId the message id to disable.
|
||||
*/
|
||||
public static void disableExpired(String messageId)
|
||||
{
|
||||
DatabaseSource databaseSource = Cache.getDatabaseSource();
|
||||
|
||||
String channelId = databaseSource.getQueuedExpiringMessageChannel(messageId);
|
||||
|
||||
// todo: warning, the following method + related if check are thread-locking.
|
||||
// todo: we should probably merge the two tables somehow, since they have redundant information.
|
||||
ChannelType msgChannelType = databaseSource.getTrackedMessageChannelType(messageId);
|
||||
|
||||
MessageChannel textChannel = null;
|
||||
|
||||
// this should never happen, but only message channels are supported.
|
||||
if(!msgChannelType.isMessage())
|
||||
{
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
// if this is a DM
|
||||
if(!(msgChannelType.isGuild()))
|
||||
{
|
||||
String userId = databaseSource.getTrackedReplyUserId(messageId);
|
||||
User user = userId == null ? null : HidekoBot.getAPI().retrieveUserById(userId).complete();
|
||||
if(user == null)
|
||||
{
|
||||
// if user is not found, consider it expired
|
||||
// (deleted profile, or blocked the bot)
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
textChannel = user.openPrivateChannel().complete();
|
||||
}
|
||||
else
|
||||
{
|
||||
String guildId = databaseSource.getQueuedExpiringMessageGuild(messageId);
|
||||
Guild guild = guildId == null ? null : HidekoBot.getAPI().getGuildById(guildId);
|
||||
if(guild == null)
|
||||
{
|
||||
// if guild is not found, consider it expired
|
||||
// (server was deleted or bot was kicked)
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
textChannel = guild.getTextChannelById(channelId);
|
||||
}
|
||||
|
||||
if(textChannel == null)
|
||||
{
|
||||
// if channel is not found, count it as expired
|
||||
// (channel was deleted or bot permissions restricted)
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
RestAction<Message> retrieveAction = textChannel.retrieveMessageById(messageId);
|
||||
|
||||
|
||||
if(Cache.isVerbose()) logger.log("cleaning up: " + messageId);
|
||||
|
||||
retrieveAction.queue(
|
||||
message -> {
|
||||
if(message == null)
|
||||
{
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
return;
|
||||
}
|
||||
|
||||
List<LayoutComponent> components = message.getComponents();
|
||||
List<LayoutComponent> newComponents = new ArrayList<>();
|
||||
for (LayoutComponent component : components)
|
||||
{
|
||||
component = component.asDisabled();
|
||||
newComponents.add(component);
|
||||
}
|
||||
|
||||
message.editMessageComponents(newComponents).queue();
|
||||
databaseSource.untrackExpiredMessage(messageId);
|
||||
},
|
||||
|
||||
error -> databaseSource.untrackExpiredMessage(messageId));
|
||||
}
|
||||
}
|
@ -1,29 +1,49 @@
|
||||
package wtf.beatrice.hidekobot.util;
|
||||
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FormatUtil
|
||||
{
|
||||
|
||||
/**
|
||||
* Generate a nicely formatted uptime String that omits unnecessary data
|
||||
* Generate a nicely formatted time-diff String that omits unnecessary data
|
||||
* (e.g. 0 days, 0 hours, 4 minutes, 32 seconds -> 4m 32s)
|
||||
*
|
||||
* @return the formatter String
|
||||
* @return the formatted String
|
||||
*/
|
||||
public static String getNiceUptime()
|
||||
public static String getNiceTimeDiff(LocalDateTime start)
|
||||
{
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
long uptimeSeconds = ChronoUnit.SECONDS.between(Cache.getStartupTime(), now);
|
||||
long uptimeSeconds = ChronoUnit.SECONDS.between(start, now);
|
||||
|
||||
Duration uptime = Duration.ofSeconds(uptimeSeconds);
|
||||
long seconds = uptime.toSecondsPart();
|
||||
long minutes = uptime.toMinutesPart();
|
||||
long hours = uptime.toHoursPart();
|
||||
long days = uptime.toDays();
|
||||
|
||||
return getNiceDuration(uptime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a nicely formatted duration String that omits unnecessary data
|
||||
* (e.g. 0 days, 0 hours, 4 minutes, 32 seconds -> 4m 32s)
|
||||
*
|
||||
* @return the formatted String
|
||||
*/
|
||||
public static String getNiceDuration(Duration duration)
|
||||
{
|
||||
long seconds = duration.toSecondsPart();
|
||||
long minutes = duration.toMinutesPart();
|
||||
long hours = duration.toHoursPart();
|
||||
long days = duration.toDays();
|
||||
|
||||
StringBuilder uptimeStringBuilder = new StringBuilder();
|
||||
if(days == 0)
|
||||
@ -31,7 +51,7 @@ public class FormatUtil
|
||||
if(hours == 0)
|
||||
{
|
||||
if(minutes == 0)
|
||||
{} else {
|
||||
{} else { // i know this if has an empty body but i feel like this reads better
|
||||
uptimeStringBuilder.append(minutes).append("m ");
|
||||
}
|
||||
} else {
|
||||
@ -47,4 +67,81 @@ public class FormatUtil
|
||||
|
||||
return uptimeStringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to parse a string into a duration.
|
||||
* Warning: this only supports up to days; months and longer timeframes are unsupported.
|
||||
*
|
||||
* @param duration the String to parse.
|
||||
* @return a Duration of the parsed timeframe, or null if parsing failed.
|
||||
*/
|
||||
@Nullable
|
||||
public static Duration parseDuration(String duration)
|
||||
{
|
||||
// sanitize a bit to avoid cluttering with garbled strings
|
||||
if(duration.length() > 16) duration = duration.substring(0, 16);
|
||||
duration = duration.replaceAll("[^\\w]", ""); //only keep digits and word characters
|
||||
duration = duration.toLowerCase();
|
||||
|
||||
/* the following regex matches any number followed by any amount of characters, any amount of times.
|
||||
eg: 3d, 33hours, 32048dojg, 3d2h5m22s.
|
||||
it does not match if the [digits and characters] blocks are missing.
|
||||
eg: 33, asd, 3g5hj, 4 asd.
|
||||
*/
|
||||
if(!duration.matches("(\\d+[a-zA-Z]+)+"))
|
||||
return null;
|
||||
|
||||
String[] durationTimes = duration.split("[a-zA-Z]+");
|
||||
String[] durationUnits = duration.split("\\d+");
|
||||
|
||||
// remove first element, because it will always be empty (there's nothing before the first character)
|
||||
durationUnits = Arrays.copyOfRange(durationUnits, 1, durationUnits.length);
|
||||
|
||||
Duration fullDuration = Duration.ZERO;
|
||||
|
||||
for(int i = 0; i < durationTimes.length; i++)
|
||||
{
|
||||
String durationTimeStr = durationTimes[i];
|
||||
String durationUnitStr = durationUnits[i];
|
||||
|
||||
int durationValue = Integer.parseInt(durationTimeStr);
|
||||
TemporalUnit unit = parseTimeUnit(durationUnitStr);
|
||||
|
||||
if(unit != null)
|
||||
fullDuration = fullDuration.plus(durationValue, unit);
|
||||
else return null; // if we failed finding the time unit, instantly quit with failed parsing.
|
||||
}
|
||||
|
||||
return fullDuration;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TemporalUnit parseTimeUnit(@NotNull String unitName)
|
||||
{
|
||||
// we won't do any sanitization, because this is a private method, and
|
||||
// we are only accessing it with things that we know for sure are already sanitized.
|
||||
unitName = unitName.toLowerCase();
|
||||
TemporalUnit timeUnit = null;
|
||||
|
||||
/*
|
||||
parsing table
|
||||
s, se, sec, second, seconds -> SECOND
|
||||
m, mi, min, minute, minutes -> MINUTE
|
||||
h, ho, hr, hour, hours -> HOUR
|
||||
d, day, days -> DAY
|
||||
|
||||
(months and longer timeframes are unsupported due to Discord restrictions)
|
||||
*/
|
||||
|
||||
switch (unitName)
|
||||
{
|
||||
case "s", "se", "sec", "second", "seconds" -> timeUnit = ChronoUnit.SECONDS;
|
||||
case "m", "mi", "min", "minute", "minutes" -> timeUnit = ChronoUnit.MINUTES;
|
||||
case "h", "ho", "hr", "hour", "hours" -> timeUnit = ChronoUnit.HOURS;
|
||||
case "d", "day", "days" -> timeUnit = ChronoUnit.DAYS;
|
||||
}
|
||||
|
||||
return timeUnit;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
package wtf.beatrice.hidekobot.util;
|
||||
|
||||
import java.util.Random;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
|
||||
public class RandomUtil
|
||||
{
|
||||
private static final Random random = new Random();
|
||||
|
||||
|
||||
/**
|
||||
* Returns a random integer picked in a range.
|
||||
*
|
||||
@ -29,7 +26,7 @@ public class RandomUtil
|
||||
int difference = (max - min) + 1;
|
||||
|
||||
// find a number between 0 and our range (eg. 5 -> 8 = 0 -> 3)
|
||||
int randomTemp = random.nextInt(difference);
|
||||
int randomTemp = Cache.getRandom().nextInt(difference);
|
||||
|
||||
// add the minimum value, so we are sure to be in the original range (0->5, 1->6, 2->7, 3->8)
|
||||
return randomTemp + min;
|
||||
|
@ -0,0 +1,39 @@
|
||||
package wtf.beatrice.hidekobot.util;
|
||||
|
||||
import org.apache.commons.lang3.SerializationException;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Base64;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class SerializationUtil
|
||||
{
|
||||
|
||||
public static String serializeBase64(List dataList) {
|
||||
|
||||
try (ByteArrayOutputStream bo = new ByteArrayOutputStream();
|
||||
ObjectOutputStream so = new ObjectOutputStream(bo)) {
|
||||
so.writeObject(dataList);
|
||||
so.flush();
|
||||
return Base64.getEncoder().encodeToString(bo.toByteArray());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new SerializationException("Error during serialization", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LinkedList deserializeBase64(String dataStr) {
|
||||
|
||||
byte[] b = Base64.getDecoder().decode(dataStr);
|
||||
ByteArrayInputStream bi = new ByteArrayInputStream(b);
|
||||
ObjectInputStream si;
|
||||
try {
|
||||
si = new ObjectInputStream(bi);
|
||||
return LinkedList.class.cast(si.readObject());
|
||||
}
|
||||
catch (IOException | ClassNotFoundException e) {
|
||||
throw new SerializationException("Error during deserialization", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
package wtf.beatrice.hidekobot.util;
|
||||
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.interactions.commands.Command;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import wtf.beatrice.hidekobot.Cache;
|
||||
import wtf.beatrice.hidekobot.HidekoBot;
|
||||
import wtf.beatrice.hidekobot.listeners.MessageCommandListener;
|
||||
import wtf.beatrice.hidekobot.objects.commands.SlashCommand;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SlashCommandUtil
|
||||
{
|
||||
|
||||
private static final Logger logger = new Logger(MessageCommandListener.class);
|
||||
|
||||
public static void updateSlashCommands(boolean force)
|
||||
{
|
||||
|
||||
// populate commands list from registered commands
|
||||
List<CommandData> allCommands = new ArrayList<>();
|
||||
for(SlashCommand cmd : Cache.getSlashCommandListener().getRegisteredCommands())
|
||||
{ allCommands.add(cmd.getSlashCommandData()); }
|
||||
|
||||
JDA jdaInstance = HidekoBot.getAPI();
|
||||
|
||||
// get all the already registered commands
|
||||
List<Command> registeredCommands = jdaInstance.retrieveCommands().complete();
|
||||
|
||||
boolean update = false;
|
||||
|
||||
if(force)
|
||||
{
|
||||
update = true;
|
||||
} else
|
||||
{
|
||||
|
||||
// for each command that we have already registered...
|
||||
for(Command currRegCmd : registeredCommands)
|
||||
{
|
||||
boolean found = false;
|
||||
|
||||
// iterate through all "recognized" commands
|
||||
for(CommandData cmdData : allCommands)
|
||||
{
|
||||
// if we find the same command...
|
||||
if(cmdData.getName().equals(currRegCmd.getName()))
|
||||
{
|
||||
// quit the loop since we found it.
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if no match was found, we need to send an updated command list because
|
||||
// an old command was probably removed.
|
||||
if(!found)
|
||||
{
|
||||
update = true;
|
||||
|
||||
// quit the loop since we only need to trigger this once.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if an update is not already queued...
|
||||
if(!update)
|
||||
{
|
||||
// for each "recognized" valid command
|
||||
for(CommandData currCmdData : allCommands)
|
||||
{
|
||||
boolean found = false;
|
||||
|
||||
// iterate through all already registered commands.
|
||||
for(Command cmd : registeredCommands)
|
||||
{
|
||||
// if this command was already registered...
|
||||
if(cmd.getName().equals(currCmdData.getName()))
|
||||
{
|
||||
// quit the loop since we found a match.
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if no match was found, we need to send an updated command list because
|
||||
// a new command was probably added.
|
||||
if(!found)
|
||||
{
|
||||
update = true;
|
||||
|
||||
// quit the loop since we only need to trigger this once.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.log("Found " + registeredCommands.size() + " commands.");
|
||||
|
||||
if(update)
|
||||
{
|
||||
// send updated command list.
|
||||
jdaInstance.updateCommands().addCommands(allCommands).queue();
|
||||
logger.log("Commands updated. New total: " + allCommands.size() + ".");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user