15 Commits

Author SHA1 Message Date
Lorenzo Dellacà
495f164552 Ignore bots interacting with hideko
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 16:54:15 +01:00
Lorenzo Dellacà
fd100649a7 Remove unneeded todo 2022-12-19 16:48:20 +01:00
Lorenzo Dellacà
b3990ff04f Make clear command also delete the sender's message
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 16:47:49 +01:00
Lorenzo Dellacà
f5238ced89 Bump version to 0.5.3
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 01:41:04 +01:00
Lorenzo Dellacà
f0ee565185 Implement basic functional diceroll command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 01:36:43 +01:00
Lorenzo Dellacà
a21d179308 Fix command label being passed as arg in case of no args
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:22:51 +01:00
Lorenzo Dellacà
36ad728bbc Fallback to 0 instead of 1
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:14:30 +01:00
Lorenzo Dellacà
1a6fe6465c Fix console error when int parsing fails in clear message
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:13:14 +01:00
Lorenzo Dellacà
f0004dc555 Re-register accidentally removed invite command
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:10:15 +01:00
Lorenzo Dellacà
8ddf0ab80d Bump JDA version to more stable beta
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:07:02 +01:00
Lorenzo Dellacà
660e18d1f4 Bump version to 0.5.2
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:05:49 +01:00
Lorenzo Dellacà
db943f7e05 Fix messages with newlines not being handled for commands
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-19 00:05:36 +01:00
Lorenzo Dellacà
cb49bda84a Make say support both slash and message commands 2022-12-19 00:05:13 +01:00
Lorenzo Dellacà
b318b9f22b Bump version to 0.5.1
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-18 23:49:00 +01:00
Lorenzo Dellacà
1447f8c177 Make avatar support both slash and message commands
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-18 23:47:54 +01:00
13 changed files with 422 additions and 67 deletions

View File

@@ -6,7 +6,7 @@
<groupId>wtf.beatrice.hidekobot</groupId> <groupId>wtf.beatrice.hidekobot</groupId>
<artifactId>HidekoBot</artifactId> <artifactId>HidekoBot</artifactId>
<version>0.5.0</version> <version>0.5.3</version>
<properties> <properties>
<maven.compiler.source>16</maven.compiler.source> <maven.compiler.source>16</maven.compiler.source>
@@ -18,7 +18,7 @@
<dependency> <dependency>
<groupId>net.dv8tion</groupId> <groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId> <artifactId>JDA</artifactId>
<version>5.0.0-alpha.22</version> <version>5.0.0-beta.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>

View File

@@ -129,10 +129,13 @@ public class HidekoBot
// register message commands // register message commands
MessageCommandListener messageCommandListener = new MessageCommandListener(); MessageCommandListener messageCommandListener = new MessageCommandListener();
messageCommandListener.registerCommand(new HelloCommand()); messageCommandListener.registerCommand(new HelloCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.InviteCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.AvatarCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.BotInfoCommand()); 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.CoinFlipCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.ClearCommand()); messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.ClearCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.DiceRollCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.InviteCommand());
messageCommandListener.registerCommand(new wtf.beatrice.hidekobot.commands.message.SayCommand());
Cache.setMessageCommandListener(messageCommandListener); Cache.setMessageCommandListener(messageCommandListener);
// register listeners // register listeners

View File

@@ -0,0 +1,62 @@
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;
public class Avatar
{
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 MessageEmbed buildEmbed(int resolution, User user)
{
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
EmbedBuilder embedBuilder = new EmbedBuilder();
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++)
{
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(" | ");
}
}
embedBuilder.addField("Available resolutions", links.toString(), false);
embedBuilder.setImage(user.getEffectiveAvatar().getUrl(resolution));
return embedBuilder.build();
}
}

View File

@@ -3,6 +3,7 @@ package wtf.beatrice.hidekobot.commands.base;
import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageHistory; import net.dv8tion.jda.api.entities.MessageHistory;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.Channel;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
@@ -46,7 +47,9 @@ public class ClearChat
return null; 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 to keep track of how many messages we actually deleted.
int deleted = 0; int deleted = 0;
@@ -155,6 +158,7 @@ public class ClearChat
public static String parseAmount(int deleted) public static String parseAmount(int deleted)
{ {
if(deleted < 1) if(deleted < 1)
{ {
return "\uD83D\uDE22 Couldn't clear any message!"; return "\uD83D\uDE22 Couldn't clear any message!";

View 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;
}
}

View File

@@ -0,0 +1,81 @@
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.Nullable;
import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.HidekoBot;
import wtf.beatrice.hidekobot.commands.base.Avatar;
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;
}
@Override
public void runCommand(MessageReceivedEvent event, String label, String[] args)
{
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
User user = null;
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 = Avatar.parseResolution(givenRes);
resFound = true;
break;
} catch (NumberFormatException ignored) {
}
}
// fallback in case we didn't find any specified resolution
if(!resFound) resolution = Avatar.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
MessageEmbed embed = Avatar.buildEmbed(resolution, user);
event.getMessage().replyEmbeds(embed).queue();
}
}

View File

@@ -43,7 +43,15 @@ public class ClearCommand implements MessageCommand
// get the amount from the command args. // get the amount from the command args.
Integer toDeleteAmount; Integer toDeleteAmount;
if (args.length == 0) toDeleteAmount = 1; if (args.length == 0) toDeleteAmount = 1;
else toDeleteAmount = Integer.parseInt(args[0]); else
{
try {
toDeleteAmount = Integer.parseInt(args[0]);
} catch (NumberFormatException e)
{
toDeleteAmount = 0;
}
}
error = ClearChat.checkDeleteAmount(toDeleteAmount); error = ClearChat.checkDeleteAmount(toDeleteAmount);
if (error != null) { if (error != null) {
@@ -64,14 +72,16 @@ public class ClearCommand implements MessageCommand
// edit the message text and attach a button. // edit the message text and attach a button.
Button dismiss = ClearChat.getDismissButton(); Button dismiss = ClearChat.getDismissButton();
// ^ todo: maybe the dismiss button should also delete the original message sent by the user? Message finalMessage = event.getChannel().sendMessage(content).setActionRow(dismiss).complete();
// 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();
// add the message to database. // add the message to database.
Cache.getDatabaseSource().queueDisabling(botMessage); Cache.getDatabaseSource().queueDisabling(finalMessage);
Cache.getDatabaseSource().trackRanCommandReply(botMessage, event.getAuthor()); Cache.getDatabaseSource().trackRanCommandReply(finalMessage, event.getAuthor());
// delete the sender's message.
event.getMessage().delete().queue();
// delete the "clearing" info message.
botMessage.delete().queue();
} }

View File

@@ -0,0 +1,128 @@
package wtf.beatrice.hidekobot.commands.message;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.Nullable;
import wtf.beatrice.hidekobot.objects.Dice;
import wtf.beatrice.hidekobot.objects.commands.MessageCommand;
import java.util.*;
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;
}
@Override
public void runCommand(MessageReceivedEvent event, String label, String[] args)
{
LinkedHashMap<Dice, Integer> dicesToRoll = new LinkedHashMap<Dice, Integer>();
String diceRegex = "d[0-9]+";
String amountRegex = "[0-9]+";
Dice currentDice = null;
int currentAmount;
UUID lastPushedDice = null;
for(String arg : args)
{
if(arg.matches(amountRegex))
{
currentAmount = Integer.parseInt(arg);
if(currentDice == null)
{
currentDice = new Dice(6);
} else {
currentDice = new Dice(currentDice);
}
lastPushedDice = currentDice.getUUID();
dicesToRoll.put(currentDice, currentAmount);
}
else if(arg.matches(diceRegex))
{
int sides = Integer.parseInt(arg.substring(1));
if(args.length == 1)
{
dicesToRoll.put(new Dice(sides), 1);
} else
{
if(currentDice != null)
{
if(lastPushedDice == null || !lastPushedDice.equals(currentDice.getUUID()))
{
dicesToRoll.put(currentDice, 1);
lastPushedDice = currentDice.getUUID();
}
}
currentDice = new Dice(sides);
}
}
}
if(lastPushedDice == null)
{
if(currentDice != null)
{
dicesToRoll.put(currentDice, 1);
}
} else
{
if(!lastPushedDice.equals(currentDice.getUUID()))
{
dicesToRoll.put(new Dice(currentDice), 1);
}
}
LinkedList<Dice> rolledDices = new LinkedList<>();
for(Dice dice : dicesToRoll.keySet())
{
for(int roll = 0; roll < dicesToRoll.get(dice); roll++)
{
dice.roll();
rolledDices.add(new Dice(dice));
}
}
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(dice.getValue());
total += dice.getValue();
}
message.append("\n\n").append("**Total**: ").append(total);
event.getMessage().reply(message.toString()).queue();
}
}

View File

@@ -0,0 +1,51 @@
package wtf.beatrice.hidekobot.commands.message;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import org.jetbrains.annotations.Nullable;
import wtf.beatrice.hidekobot.commands.base.ClearChat;
import wtf.beatrice.hidekobot.commands.base.Say;
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;
}
@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();
}
}

View File

@@ -1,6 +1,7 @@
package wtf.beatrice.hidekobot.commands.slash; package wtf.beatrice.hidekobot.commands.slash;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionMapping;
@@ -9,6 +10,7 @@ import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import wtf.beatrice.hidekobot.Cache; import wtf.beatrice.hidekobot.Cache;
import wtf.beatrice.hidekobot.commands.base.Avatar;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
public class AvatarCommand extends SlashCommandImpl public class AvatarCommand extends SlashCommandImpl
@@ -31,9 +33,6 @@ public class AvatarCommand extends SlashCommandImpl
User user; User user;
int resolution; int resolution;
int[] acceptedSizes = Cache.getSupportedAvatarResolutions();
OptionMapping userArg = event.getOption("user"); OptionMapping userArg = event.getOption("user");
if(userArg != null) if(userArg != null)
{ {
@@ -45,57 +44,12 @@ public class AvatarCommand extends SlashCommandImpl
OptionMapping sizeArg = event.getOption("size"); OptionMapping sizeArg = event.getOption("size");
if(sizeArg != null) if(sizeArg != null)
{ {
resolution = sizeArg.getAsInt(); resolution = Avatar.parseResolution(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];
} else { } else {
resolution = 512; resolution = Avatar.parseResolution(512);
} }
EmbedBuilder embedBuilder = new EmbedBuilder(); MessageEmbed embed = Avatar.buildEmbed(resolution, user);
event.getHook().editOriginalEmbeds(embed).queue();
// embed processing
{
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++)
{
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(" | ");
}
}
embedBuilder.addField("Available resolutions", links.toString(), false);
embedBuilder.setImage(user.getEffectiveAvatar().getUrl(resolution));
}
event.getHook().editOriginalEmbeds(embedBuilder.build()).queue();
} }
} }

View File

@@ -9,6 +9,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.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.commands.build.Commands;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import wtf.beatrice.hidekobot.commands.base.Say;
import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl; import wtf.beatrice.hidekobot.objects.commands.SlashCommandImpl;
public class SayCommand extends SlashCommandImpl public class SayCommand extends SlashCommandImpl
@@ -22,7 +23,7 @@ public class SayCommand extends SlashCommandImpl
"The message to send.", "The message to send.",
true, true,
false) false)
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MESSAGE_MANAGE)); .setDefaultPermissions(DefaultMemberPermissions.enabledFor(Say.getPermission()));
} }
@Override @Override

View File

@@ -57,10 +57,14 @@ public class MessageCommandListener extends ListenerAdapter
@Override @Override
public void onMessageReceived(@NotNull MessageReceivedEvent event) 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, ...) // check if the sent message matches the bot activation regex (prefix, name, ...)
if(!eventMessage.toLowerCase().matches(commandRegex + ".*")) if(!eventMessage.toLowerCase().matches(commandRegex + "((.|\\n)*)"))
return; return;
// generate args from the string // generate args from the string
@@ -120,8 +124,9 @@ public class MessageCommandListener extends ListenerAdapter
String[] commandArgs; String[] commandArgs;
if(commandObject.passRawArgs()) if(commandObject.passRawArgs())
{ {
// remove first argument, which is the command label // 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 // pass all other arguments as a single argument as the first array element
commandArgs = new String[]{argsString}; commandArgs = new String[]{argsString};
} }

View File

@@ -0,0 +1,45 @@
package wtf.beatrice.hidekobot.objects;
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;
}
}