diff --git a/.idea/copyright/GeyserDiscordBot.xml b/.idea/copyright/GeyserDiscordBot.xml
index 510a393f..070e01ea 100644
--- a/.idea/copyright/GeyserDiscordBot.xml
+++ b/.idea/copyright/GeyserDiscordBot.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/src/main/java/org/geysermc/discordbot/GeyserBot.java b/src/main/java/org/geysermc/discordbot/GeyserBot.java
index 376a5a7a..54fc76d8 100644
--- a/src/main/java/org/geysermc/discordbot/GeyserBot.java
+++ b/src/main/java/org/geysermc/discordbot/GeyserBot.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2022 GeyserMC. http://geysermc.org
+ * Copyright (c) 2020-2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -27,6 +27,7 @@
import com.jagrosh.jdautilities.command.Command;
import com.jagrosh.jdautilities.command.CommandClientBuilder;
+import com.jagrosh.jdautilities.command.ContextMenu;
import com.jagrosh.jdautilities.command.SlashCommand;
import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
import io.sentry.Sentry;
@@ -78,6 +79,7 @@ public class GeyserBot {
public static final Logger LOGGER = LoggerFactory.getLogger(GeyserBot.class);
public static final List COMMANDS;
public static final List SLASH_COMMANDS;
+ public static final List CONTEXT_MENUS;
public static AbstractStorageManager storageManager;
@@ -96,8 +98,9 @@ public class GeyserBot {
Set> subTypes = reflections.getSubTypesOf(Command.class);
for (Class extends Command> theClass : subTypes) {
// Don't load SubCommands
- if (theClass.getName().contains("SubCommand"))
+ if (theClass.getName().contains("SubCommand")) {
continue;
+ }
try {
commands.add(theClass.getDeclaredConstructor().newInstance());
LoggerFactory.getLogger(theClass).debug("Loaded Command Successfully!");
@@ -109,8 +112,9 @@ public class GeyserBot {
Set> slashSubTypes = reflections.getSubTypesOf(SlashCommand.class);
for (Class extends SlashCommand> theClass : slashSubTypes) {
// Don't load SubCommands
- if (theClass.getName().contains("SubCommand"))
+ if (theClass.getName().contains("SubCommand")) {
continue;
+ }
slashCommands.add(theClass.getDeclaredConstructor().newInstance());
LoggerFactory.getLogger(theClass).debug("Loaded SlashCommand Successfully!");
}
@@ -119,9 +123,26 @@ public class GeyserBot {
}
COMMANDS = commands;
SLASH_COMMANDS = slashCommands;
+
+ // Gathers all context menu items from "context_menu" package.
+ List contextMenus = new ArrayList<>();
+ try {
+ Reflections reflections = new Reflections("org.geysermc.discordbot.context_menus");
+ Set> subTypes = reflections.getSubTypesOf(ContextMenu.class);
+ for (Class extends ContextMenu> theClass : subTypes) {
+ if (!theClass.getPackageName().startsWith("org.geysermc.discordbot.context_menus")) {
+ continue;
+ }
+ contextMenus.add(theClass.getDeclaredConstructor().newInstance());
+ LoggerFactory.getLogger(theClass).debug("Loaded ContextMenu Successfully!");
+ }
+ } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ LOGGER.error("Unable to load context menus", e);
+ }
+ CONTEXT_MENUS = contextMenus;
}
- public static void main(String[] args) throws IOException, LoginException {
+ public static void main(String[] args) throws IOException {
// Load properties into the PropertiesManager
Properties prop = new Properties();
prop.load(new FileInputStream("bot.properties"));
@@ -169,6 +190,7 @@ public static void main(String[] args) throws IOException, LoginException {
client.useHelpBuilder(false);
client.addCommands(COMMANDS.toArray(new Command[0]));
client.addSlashCommands(SLASH_COMMANDS.toArray(new SlashCommand[0]));
+ client.addContextMenus(CONTEXT_MENUS.toArray(new ContextMenu[0]));
client.setListener(new CommandErrorHandler());
client.setCommandPreProcessBiFunction((event, command) -> !SwearHandler.filteredMessages.contains(event.getMessage().getIdLong()));
diff --git a/src/main/java/org/geysermc/discordbot/commands/moderation/RenameCommand.java b/src/main/java/org/geysermc/discordbot/commands/moderation/RenameCommand.java
index cb1ceab0..fdb64b6b 100644
--- a/src/main/java/org/geysermc/discordbot/commands/moderation/RenameCommand.java
+++ b/src/main/java/org/geysermc/discordbot/commands/moderation/RenameCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2022 GeyserMC. http://geysermc.org
+ * Copyright (c) 2020-2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -81,7 +81,7 @@ protected void execute(CommandEvent event) {
event.getMessage().replyEmbeds(handle(member, event.getMember(), event.getGuild())).queue();
}
- private MessageEmbed handle(Member member, Member moderator, Guild guild) {
+ public static MessageEmbed handle(Member member, Member moderator, Guild guild) {
// Check the user exists
if (member == null) {
return new EmbedBuilder()
diff --git a/src/main/java/org/geysermc/discordbot/context_menus/RenameUserMenu.java b/src/main/java/org/geysermc/discordbot/context_menus/RenameUserMenu.java
new file mode 100644
index 00000000..9722ccdc
--- /dev/null
+++ b/src/main/java/org/geysermc/discordbot/context_menus/RenameUserMenu.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2024-2024 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/GeyserDiscordBot
+ */
+
+package org.geysermc.discordbot.context_menus;
+
+import com.jagrosh.jdautilities.command.UserContextMenu;
+import com.jagrosh.jdautilities.command.UserContextMenuEvent;
+import net.dv8tion.jda.api.Permission;
+import org.geysermc.discordbot.commands.moderation.RenameCommand;
+
+public class RenameUserMenu extends UserContextMenu {
+ public RenameUserMenu() {
+ this.name = "Rename";
+
+ this.userPermissions = new Permission[] { Permission.NICKNAME_MANAGE };
+ this.botPermissions = new Permission[] { Permission.NICKNAME_MANAGE };
+ }
+
+ @Override
+ protected void execute(UserContextMenuEvent event) {
+ event.replyEmbeds(RenameCommand.handle(event.getTargetMember(), event.getMember(), event.getGuild())).setEphemeral(true).queue();
+ }
+}
diff --git a/src/main/java/org/geysermc/discordbot/listeners/CommandErrorHandler.java b/src/main/java/org/geysermc/discordbot/listeners/CommandErrorHandler.java
index 696e5009..6b08612f 100644
--- a/src/main/java/org/geysermc/discordbot/listeners/CommandErrorHandler.java
+++ b/src/main/java/org/geysermc/discordbot/listeners/CommandErrorHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2022 GeyserMC. http://geysermc.org
+ * Copyright (c) 2020-2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,6 +28,12 @@
import com.jagrosh.jdautilities.command.Command;
import com.jagrosh.jdautilities.command.CommandEvent;
import com.jagrosh.jdautilities.command.CommandListener;
+import com.jagrosh.jdautilities.command.MessageContextMenu;
+import com.jagrosh.jdautilities.command.MessageContextMenuEvent;
+import com.jagrosh.jdautilities.command.SlashCommand;
+import com.jagrosh.jdautilities.command.SlashCommandEvent;
+import com.jagrosh.jdautilities.command.UserContextMenu;
+import com.jagrosh.jdautilities.command.UserContextMenuEvent;
import net.dv8tion.jda.api.EmbedBuilder;
import org.geysermc.discordbot.util.BotColors;
import pw.chew.chewbotcca.listeners.BotCommandListener;
@@ -38,28 +44,82 @@
public class CommandErrorHandler extends BotCommandListener implements CommandListener {
@Override
- public void onCommandException(CommandEvent event, Command command, Throwable throwable) {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- throwable.printStackTrace(pw);
- String[] errorStack = sw.toString().split("\n");
- int limit = Math.min(errorStack.length, 5);
+ public void onSlashCommandException(SlashCommandEvent event, SlashCommand command, Throwable throwable) {
+ String errorMessage = getErrorMessage(throwable);
- StringBuilder errorMessage = new StringBuilder();
- for (int i = 0; i < limit; i++) {
- errorMessage.append(errorStack[i]).append("\n");
- }
+ event.replyEmbeds(new EmbedBuilder()
+ .setTitle("Error handling command")
+ .setDescription("An error occurred while handling the command")
+ .addField("Command usage", "/" + command.getName() + (command.getArguments() != null ? " " + command.getArguments() : ""), false)
+ .addField("Command help", command.getHelp(), false)
+ .addField("Error", errorMessage, false)
+ .setTimestamp(Instant.now())
+ .setColor(BotColors.FAILURE.getColor())
+ .build()).setEphemeral(true).queue();
+
+ super.onSlashCommandException(event, command, throwable);
+ }
+
+ @Override
+ public void onCommandException(CommandEvent event, Command command, Throwable throwable) {
+ String errorMessage = getErrorMessage(throwable);
event.getMessage().replyEmbeds(new EmbedBuilder()
.setTitle("Error handling command")
.setDescription("An error occurred while handling the command")
.addField("Command usage", event.getPrefix() + command.getName() + (command.getArguments() != null ? " " + command.getArguments() : ""), false)
.addField("Command help", command.getHelp(), false)
- .addField("Error", errorMessage.toString(), false)
+ .addField("Error", errorMessage, false)
.setTimestamp(Instant.now())
.setColor(BotColors.FAILURE.getColor())
.build()).queue();
super.onCommandException(event, command, throwable);
}
+
+ @Override
+ public void onMessageContextMenuException(MessageContextMenuEvent event, MessageContextMenu menu, Throwable throwable) {
+ String errorMessage = getErrorMessage(throwable);
+
+ event.replyEmbeds(new EmbedBuilder()
+ .setTitle("Error handling context menu option")
+ .setDescription("An error occurred while handling the message context menu option")
+ .addField("Menu option", menu.getName(), false)
+ .addField("Error", errorMessage, false)
+ .setTimestamp(Instant.now())
+ .setColor(BotColors.FAILURE.getColor())
+ .build()).setEphemeral(true).queue();
+
+ super.onMessageContextMenuException(event, menu, throwable);
+ }
+
+ @Override
+ public void onUserContextMenuException(UserContextMenuEvent event, UserContextMenu menu, Throwable throwable) {
+ String errorMessage = getErrorMessage(throwable);
+
+ event.replyEmbeds(new EmbedBuilder()
+ .setTitle("Error handling context menu option")
+ .setDescription("An error occurred while handling the user context menu option")
+ .addField("Menu option", menu.getName(), false)
+ .addField("Error", errorMessage, false)
+ .setTimestamp(Instant.now())
+ .setColor(BotColors.FAILURE.getColor())
+ .build()).setEphemeral(true).queue();
+
+ super.onUserContextMenuException(event, menu, throwable);
+ }
+
+ private String getErrorMessage(Throwable throwable) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ throwable.printStackTrace(pw);
+ String[] errorStack = sw.toString().split("\n");
+ int limit = Math.min(errorStack.length, 5);
+
+ StringBuilder errorMessage = new StringBuilder();
+ for (int i = 0; i < limit; i++) {
+ errorMessage.append(errorStack[i]).append("\n");
+ }
+ return errorMessage.toString();
+ }
}
diff --git a/src/main/java/org/geysermc/discordbot/listeners/SwearHandler.java b/src/main/java/org/geysermc/discordbot/listeners/SwearHandler.java
index f0a4511f..58c360ff 100644
--- a/src/main/java/org/geysermc/discordbot/listeners/SwearHandler.java
+++ b/src/main/java/org/geysermc/discordbot/listeners/SwearHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020-2022 GeyserMC. http://geysermc.org
+ * Copyright (c) 2020-2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -40,6 +40,7 @@
import javax.annotation.Nullable;
import java.io.IOException;
import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.HashMap;
@@ -80,7 +81,7 @@ public static void loadFilters() {
if (fileName.endsWith(".wlist")) {
fileCount++;
// Load the lines
- String[] lines = new String(BotHelpers.bytesFromResource("filters/" + fileName)).split("\n");
+ String[] lines = new String(BotHelpers.bytesFromResource("filters/" + fileName), StandardCharsets.UTF_8).split("\n");
for (String line : lines) {
filterPatterns.add(Pattern.compile("(^| )" + line.trim() + "( |$)", Pattern.CASE_INSENSITIVE));
}
@@ -92,7 +93,7 @@ public static void loadFilters() {
GeyserBot.LOGGER.info("Loaded " + filterPatterns.size() + " filter patterns from " + fileCount + " files");
- nicknames = new String(BotHelpers.bytesFromResource("nicknames.wlist")).trim().split("\n");
+ nicknames = new String(BotHelpers.bytesFromResource("nicknames.wlist"), StandardCharsets.UTF_8).trim().split("\n");
GeyserBot.LOGGER.info("Loaded " + nicknames.length + " nicknames");
}