change how events are handled by using a single class to register listeners onto
This commit is contained in:
1 parent
6a869afb1d
commit
fb872f2b0b
24 files changed
+386
-263
No files matched your search
@@ -12,12 +12,7 @@ import me.bobert.netneutrality.core.command.CommandManager;
|
|||||||
import me.bobert.netneutrality.core.db.SessionManager;
|
import me.bobert.netneutrality.core.db.SessionManager;
|
||||||
import me.bobert.netneutrality.core.tasks.TaskManager;
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
import me.bobert.netneutrality.core.thread.ThreadManager;
|
import me.bobert.netneutrality.core.thread.ThreadManager;
|
||||||
import me.bobert.netneutrality.hooks.guilds.ServerJoinHook;
|
import me.bobert.netneutrality.hooks.EventsHandler;
|
||||||
import me.bobert.netneutrality.hooks.interaction.ButtonInteractionHook;
|
|
||||||
import me.bobert.netneutrality.hooks.interaction.ModalInteractionHook;
|
|
||||||
import me.bobert.netneutrality.hooks.interaction.SlashCommandHook;
|
|
||||||
import me.bobert.netneutrality.hooks.message.EmojiReactionHook;
|
|
||||||
import me.bobert.netneutrality.hooks.message.MessageReceivedHook;
|
|
||||||
import me.bobert.netneutrality.http.server.WebApi;
|
import me.bobert.netneutrality.http.server.WebApi;
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
import me.bobert.netneutrality.music.MusicManager;
|
||||||
import me.bobert.netneutrality.thread.ShutdownThread;
|
import me.bobert.netneutrality.thread.ShutdownThread;
|
||||||
@@ -25,7 +20,6 @@ import me.bobert.netneutrality.utils.Settings;
|
|||||||
import net.dv8tion.jda.api.JDA;
|
import net.dv8tion.jda.api.JDA;
|
||||||
import net.dv8tion.jda.api.JDABuilder;
|
import net.dv8tion.jda.api.JDABuilder;
|
||||||
import net.dv8tion.jda.api.entities.Activity;
|
import net.dv8tion.jda.api.entities.Activity;
|
||||||
import net.dv8tion.jda.api.entities.User;
|
|
||||||
import net.dv8tion.jda.api.exceptions.InvalidTokenException;
|
import net.dv8tion.jda.api.exceptions.InvalidTokenException;
|
||||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||||
import org.apache.logging.log4j.Level;
|
import org.apache.logging.log4j.Level;
|
||||||
@@ -67,6 +61,11 @@ public class NetNeutrality {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startup() {
|
private void startup() {
|
||||||
|
String token = Settings.getInstance().get("NETNEUTRALITY_BOT_TOKEN");
|
||||||
|
if (token == null) {
|
||||||
|
throw new RuntimeException("No bot token provided, exiting.");
|
||||||
|
}
|
||||||
|
|
||||||
// DatabaseMigration.migrate();
|
// DatabaseMigration.migrate();
|
||||||
SessionManager.getSessionFactory();
|
SessionManager.getSessionFactory();
|
||||||
CommandManager.init();
|
CommandManager.init();
|
||||||
@@ -74,9 +73,7 @@ public class NetNeutrality {
|
|||||||
TaskManager.getInstance().init();
|
TaskManager.getInstance().init();
|
||||||
initJDA();
|
initJDA();
|
||||||
CommandManager.registerSlashCommands(jda);
|
CommandManager.registerSlashCommands(jda);
|
||||||
jda.addEventListener(new ServerJoinHook());
|
jda.addEventListener(new EventsHandler());
|
||||||
jda.addEventListener(new MessageReceivedHook(), new SlashCommandHook());
|
|
||||||
jda.addEventListener(new ModalInteractionHook(), new ButtonInteractionHook(), new EmojiReactionHook());
|
|
||||||
new WebApi(Integer.parseInt(Settings.getInstance().get("NETNEUTRALITY_API_PORT", "9000")));
|
new WebApi(Integer.parseInt(Settings.getInstance().get("NETNEUTRALITY_API_PORT", "9000")));
|
||||||
jda.getPresence().setActivity(Activity.watching("you"));
|
jda.getPresence().setActivity(Activity.watching("you"));
|
||||||
|
|
||||||
@@ -120,17 +117,9 @@ public class NetNeutrality {
|
|||||||
return jda;
|
return jda;
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getSelf() {
|
|
||||||
return jda.getSelfUser();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void disconnect() {
|
private void disconnect() {
|
||||||
if (jda != null) {
|
if (jda != null) {
|
||||||
jda.shutdown();
|
jda.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ package me.bobert.netneutrality.command.git;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.SlashCommandContext;
|
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
||||||
import me.bobert.netneutrality.core.command.AbstractCommand;
|
import me.bobert.netneutrality.core.command.AbstractCommand;
|
||||||
import me.bobert.netneutrality.core.command.CommandCategory;
|
import me.bobert.netneutrality.core.command.CommandCategory;
|
||||||
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
|
|||||||
+2
-1
@@ -1,10 +1,11 @@
|
|||||||
package me.bobert.netneutrality.context;
|
package me.bobert.netneutrality.context.command;
|
||||||
/*
|
/*
|
||||||
* Copyright (c) anthorob
|
* Copyright (c) anthorob
|
||||||
*
|
*
|
||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.AsyncContext;
|
||||||
import me.bobert.netneutrality.core.command.options.CommandOption;
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
import me.bobert.netneutrality.core.command.options.MessageCommandOption;
|
import me.bobert.netneutrality.core.command.options.MessageCommandOption;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package me.bobert.netneutrality.context.command;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.events.Event;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MessageMenuContext extends CommandContext implements InteractionContext {
|
||||||
|
private final MessageContextInteractionEvent event;
|
||||||
|
|
||||||
|
public MessageMenuContext(MessageContextInteractionEvent event) {
|
||||||
|
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Event getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getType() {
|
||||||
|
return event.getChannelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
||||||
|
return List.of(); // no options for context menu commands
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseCommandName() {
|
||||||
|
return getFullCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullCommand() {
|
||||||
|
return event.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message getTarget() {
|
||||||
|
return event.getTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-7
@@ -1,4 +1,4 @@
|
|||||||
package me.bobert.netneutrality.context;
|
package me.bobert.netneutrality.context.command;
|
||||||
/*
|
/*
|
||||||
* Copyright (c) anthorob
|
* Copyright (c) anthorob
|
||||||
*
|
*
|
||||||
@@ -12,7 +12,6 @@ import me.bobert.netneutrality.core.context.InteractionContext;
|
|||||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
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.build.OptionData;
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
import net.dv8tion.jda.api.interactions.modals.Modal;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -40,11 +39,6 @@ public class SlashCommandContext extends CommandContext implements InteractionCo
|
|||||||
// not implemented
|
// not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reply(Modal modal) {
|
|
||||||
event.replyModal(modal).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
||||||
return event.getOptions().stream().map(SlashCommandOption::new).collect(Collectors.toUnmodifiableList());
|
return event.getOptions().stream().map(SlashCommandOption::new).collect(Collectors.toUnmodifiableList());
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package me.bobert.netneutrality.context.command;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
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.Event;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class UserMenuContext extends CommandContext implements InteractionContext {
|
||||||
|
private final UserContextInteractionEvent event;
|
||||||
|
|
||||||
|
public UserMenuContext(UserContextInteractionEvent event) {
|
||||||
|
super(event.getJDA(), event.getGuild(), (MessageChannel) event.getChannel(), event.getMember(), event.getUser());
|
||||||
|
this.event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Event getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChannelType getType() {
|
||||||
|
return event.getChannelType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
||||||
|
return List.of(); // no options for context menu commands
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String parseCommandName() {
|
||||||
|
return getFullCommand();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFullCommand() {
|
||||||
|
return event.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getTarget() {
|
||||||
|
return event.getTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import net.dv8tion.jda.api.entities.channel.ChannelType;
|
|||||||
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
||||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||||
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
||||||
|
import net.dv8tion.jda.api.interactions.modals.Modal;
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -61,6 +62,11 @@ public class EmojiTaskContext extends TaskContext {
|
|||||||
return event.isFromGuild();
|
return event.isFromGuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reply(Modal modal) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
if (getChannel() != null && getChannel().canTalk()) {
|
if (getChannel() != null && getChannel().canTalk()) {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package me.bobert.netneutrality.core.command;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.command.MessageMenuContext;
|
||||||
|
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
|
public abstract class AbstractMessageMenuCommand extends AbstractCommand {
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSlashCommandOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
// no options for context commands
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.MESSAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
|
if (context instanceof MessageMenuContext mc) {
|
||||||
|
Message message = mc.getTarget();
|
||||||
|
return execute(message, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Wrong context type for this command");
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract CommandExecutionResult execute(Message target, CommandContext context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package me.bobert.netneutrality.core.command;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.command.UserMenuContext;
|
||||||
|
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
|
||||||
|
public abstract class AbstractUserMenuCommand extends AbstractCommand {
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSlashCommandOnly() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
|
// no options for context commands
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
|
if (context instanceof UserMenuContext uc) {
|
||||||
|
User user = uc.getTarget();
|
||||||
|
return execute(user, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Wrong context type for this command");
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract CommandExecutionResult execute(User target, CommandContext context);
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@ public class CommandManager {
|
|||||||
throw new IllegalArgumentException("Command name is null");
|
throw new IllegalArgumentException("Command name is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.getDescription() == null) {
|
if (command.getDescription() == null && command.getType() == Command.Type.SLASH) {
|
||||||
throw new IllegalArgumentException("Command description is null");
|
throw new IllegalArgumentException("Command description is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ public class CommandManager {
|
|||||||
commands.put(command.getCommandName(), container);
|
commands.put(command.getCommandName(), container);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Failed to instantiate command {}: {}", s.getSimpleName(), e.getMessage());
|
logger.error("Failed to load command {}: {}", s.getSimpleName(), e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ package me.bobert.netneutrality.core.command;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import io.sentry.Sentry;
|
import io.sentry.Sentry;
|
||||||
import me.bobert.netneutrality.context.SlashCommandContext;
|
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
||||||
import me.bobert.netneutrality.core.LogHandler;
|
import me.bobert.netneutrality.core.LogHandler;
|
||||||
import me.bobert.netneutrality.core.command.options.CommandOptions;
|
import me.bobert.netneutrality.core.command.options.CommandOptions;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
||||||
import me.bobert.netneutrality.utils.UserFunction;
|
import me.bobert.netneutrality.utils.UserFunction;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
@@ -65,12 +66,12 @@ public class CommandProcessor {
|
|||||||
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild() == null ? "PrivateMessage" : context.getGuild().getId());
|
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild() == null ? "PrivateMessage" : context.getGuild().getId());
|
||||||
configureSentry(command);
|
configureSentry(command);
|
||||||
|
|
||||||
if (!canExecute(command)) {
|
if (command.getType() == Command.Type.SLASH && !canExecute(command)) {
|
||||||
context.reply("You are not powerful enough, scrub");
|
context.reply("You are not powerful enough, scrub");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkUsage(container.getCommandOptions())) {
|
if (command.getType() == Command.Type.SLASH && !checkUsage(container.getCommandOptions())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +97,7 @@ public class CommandProcessor {
|
|||||||
|
|
||||||
String message = commandExecutionResult.getMessage();
|
String message = commandExecutionResult.getMessage();
|
||||||
if (result == ERROR) {
|
if (result == ERROR) {
|
||||||
logger.error("Error from command {}: {}", command.getCommandName(), message, commandExecutionResult.getException());
|
logger.error("Context[{}]: Error from command: {}", context.getId(), message, commandExecutionResult.getException());
|
||||||
}
|
}
|
||||||
|
|
||||||
context.reply(message);
|
context.reply(message);
|
||||||
@@ -175,7 +176,7 @@ public class CommandProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkOptions(String[] args, int skip, List<OptionData> options) {
|
private boolean checkOptions(String[] args, int skip, List<OptionData> options) {
|
||||||
if (context instanceof SlashCommandContext) {
|
if (context instanceof InteractionContext) {
|
||||||
context.setCurrentCommandOptions(args, options);
|
context.setCurrentCommandOptions(args, options);
|
||||||
return true; // slash commands are validated by discord
|
return true; // slash commands are validated by discord
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ package me.bobert.netneutrality.core.context;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.AsyncContext;
|
import me.bobert.netneutrality.context.AsyncContext;
|
||||||
import net.dv8tion.jda.api.events.Event;
|
|
||||||
import net.dv8tion.jda.api.interactions.Interaction;
|
import net.dv8tion.jda.api.interactions.Interaction;
|
||||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||||
|
import net.dv8tion.jda.api.interactions.callbacks.IModalCallback;
|
||||||
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
|
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
|
||||||
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
||||||
|
import net.dv8tion.jda.api.interactions.modals.Modal;
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -66,6 +67,13 @@ public interface InteractionContext extends Context {
|
|||||||
getReplyCallback().reply(messageCreateData).setEphemeral(ephemeral).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
getReplyCallback().reply(messageCreateData).setEphemeral(ephemeral).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void reply(Modal modal) {
|
||||||
|
if (getInteraction() instanceof IModalCallback) {
|
||||||
|
((IModalCallback) getInteraction()).replyModal(modal).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IReplyCallback getReplyCallback() {
|
private IReplyCallback getReplyCallback() {
|
||||||
try {
|
try {
|
||||||
return (IReplyCallback) getEvent();
|
return (IReplyCallback) getEvent();
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import net.dv8tion.jda.api.entities.Guild;
|
|||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
import net.dv8tion.jda.api.entities.User;
|
import net.dv8tion.jda.api.entities.User;
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
import net.dv8tion.jda.api.interactions.modals.Modal;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -28,11 +27,6 @@ public abstract class TaskContext extends AbstractContext {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reply(Modal modal) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract String getId();
|
public abstract String getId();
|
||||||
|
|
||||||
public void addContextData(String key, Object value) {
|
public void addContextData(String key, Object value) {
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package me.bobert.netneutrality.hooks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.command.MessageCommandContext;
|
||||||
|
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
||||||
|
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
|
||||||
|
import me.bobert.netneutrality.context.tasks.EmojiTaskContext;
|
||||||
|
import me.bobert.netneutrality.context.tasks.ModalTaskContext;
|
||||||
|
import me.bobert.netneutrality.core.GuildManager;
|
||||||
|
import me.bobert.netneutrality.core.command.CommandProcessor;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.Context;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
|
import net.dv8tion.jda.api.events.Event;
|
||||||
|
import net.dv8tion.jda.api.events.GenericEvent;
|
||||||
|
import net.dv8tion.jda.api.events.guild.GuildJoinEvent;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
||||||
|
import net.dv8tion.jda.api.hooks.EventListener;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class EventsHandler implements EventListener {
|
||||||
|
private static final Map<Class<? extends Event>, Function<? extends Event, Context>> listeners = new ConcurrentHashMap<>();
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(EventsHandler.class);
|
||||||
|
|
||||||
|
static {
|
||||||
|
addListener(MessageReceivedEvent.class, event -> {
|
||||||
|
if (event.getAuthor().isBot()) return null;
|
||||||
|
if (event.isWebhookMessage()) return null;
|
||||||
|
|
||||||
|
String rawMessage = event.getMessage().getContentDisplay().trim();
|
||||||
|
String prefix = Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$");
|
||||||
|
|
||||||
|
if (rawMessage.startsWith(prefix)) {
|
||||||
|
return new MessageCommandContext(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
addListener(SlashCommandInteractionEvent.class, SlashCommandContext::new);
|
||||||
|
addListener(ModalInteractionEvent.class, ModalTaskContext::new);
|
||||||
|
addListener(ButtonInteractionEvent.class, ButtonTaskContext::new);
|
||||||
|
addListener(MessageReactionAddEvent.class, EmojiTaskContext::new);
|
||||||
|
addGenericListener(GuildJoinEvent.class, event -> GuildManager.getManager(event.getGuild()).removeFromDB());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a listener for an event assocated with a {@link Context}.
|
||||||
|
*
|
||||||
|
* @param eventClass the class type of the event to listen for
|
||||||
|
* @param function the function to handle the event, which takes an event of the specified type and returns a {@link Context}
|
||||||
|
* @param <T> the type of the event that extends the {@link Event} class
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T extends Event> void addListener(Class<T> eventClass, Function<? super T, Context> function) {
|
||||||
|
logger.info("Registering listener for {}", eventClass.getSimpleName());
|
||||||
|
listeners.put(eventClass, (Function<? extends Event, Context>) function);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a generic listener for a specified event type. The consumer will be invoked
|
||||||
|
* whenever an event of the specified type is detected.
|
||||||
|
*
|
||||||
|
* @param eventClass the class type of the event to listen for
|
||||||
|
* @param consumer the consumer to handle the event, which takes an event of the specified type and performs an action
|
||||||
|
* @param <T> the type of the event that extends the {@link Event} class
|
||||||
|
*/
|
||||||
|
public static <T extends Event> void addGenericListener(Class<T> eventClass, Consumer<T> consumer) {
|
||||||
|
addListener(eventClass, t -> {
|
||||||
|
consumer.accept(t);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvent(@NotNull GenericEvent event) {
|
||||||
|
handleEvent((Event) event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void handleEvent(Event event) {
|
||||||
|
Class<?> eventClass = event.getClass();
|
||||||
|
|
||||||
|
while (eventClass != null) {
|
||||||
|
if (listeners.containsKey(eventClass)) {
|
||||||
|
Function<Event, Context> function = (Function<Event, Context>) listeners.get(eventClass);
|
||||||
|
Context context = function.apply(event);
|
||||||
|
|
||||||
|
if (context == null) {
|
||||||
|
logger.debug("Handling event {} by generic listener", event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context instanceof CommandContext commandContext) {
|
||||||
|
handleCommand(commandContext);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context instanceof TaskContext taskContext) {
|
||||||
|
handleTask(taskContext);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventClass = eventClass.getSuperclass(); // Move up the hierarchy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleCommand(CommandContext context) {
|
||||||
|
CommandProcessor processor = new CommandProcessor(context);
|
||||||
|
processor.dispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleTask(TaskContext context) {
|
||||||
|
TaskManager.getInstance().handle(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package me.bobert.netneutrality.hooks.guilds;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import net.dv8tion.jda.api.events.guild.GuildJoinEvent;
|
|
||||||
import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
|
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class ServerJoinHook extends ListenerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGuildJoin(@NotNull GuildJoinEvent event) {
|
|
||||||
GuildManager.getManager(event.getGuild());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGuildLeave(@NotNull GuildLeaveEvent event) {
|
|
||||||
GuildManager.getManager(event.getGuild()).removeFromDB();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package me.bobert.netneutrality.hooks.interaction;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
|
|
||||||
import me.bobert.netneutrality.core.tasks.TaskManager;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class ButtonInteractionHook extends ListenerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onButtonInteraction(@NotNull ButtonInteractionEvent event) {
|
|
||||||
ButtonTaskContext context = new ButtonTaskContext(event);
|
|
||||||
|
|
||||||
TaskManager.getInstance().handle(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package me.bobert.netneutrality.hooks.interaction;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.tasks.ModalTaskContext;
|
|
||||||
import me.bobert.netneutrality.core.tasks.TaskManager;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class ModalInteractionHook extends ListenerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onModalInteraction(@NotNull ModalInteractionEvent event) {
|
|
||||||
ModalTaskContext context = new ModalTaskContext(event);
|
|
||||||
|
|
||||||
TaskManager.getInstance().handle(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package me.bobert.netneutrality.hooks.interaction;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.SlashCommandContext;
|
|
||||||
import me.bobert.netneutrality.core.command.CommandProcessor;
|
|
||||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class SlashCommandHook extends ListenerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) {
|
|
||||||
SlashCommandContext commandContext = new SlashCommandContext(event);
|
|
||||||
CommandProcessor processor = new CommandProcessor(commandContext);
|
|
||||||
processor.dispatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
package me.bobert.netneutrality.hooks.message;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.core.db.SessionManager;
|
|
||||||
import me.bobert.netneutrality.db.entities.OGuild;
|
|
||||||
import me.bobert.netneutrality.db.repository.MessageRepository;
|
|
||||||
import me.bobert.netneutrality.utils.JpaUtils;
|
|
||||||
import me.bobert.netneutrality.utils.MessageFunction;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
|
||||||
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
|
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
|
||||||
import org.hibernate.Session;
|
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class EditHandler extends ListenerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMessageUpdate(MessageUpdateEvent event) {
|
|
||||||
|
|
||||||
if (event.getAuthor().isBot()) return;
|
|
||||||
|
|
||||||
GuildManager guildManager = GuildManager.getManager(event.getGuild());
|
|
||||||
|
|
||||||
if (guildManager.getGuildEntity().getLogging()) {
|
|
||||||
OGuild guildEntity = guildManager.getGuildEntity();
|
|
||||||
|
|
||||||
if (guildEntity.getLogChannel() != null && !guildEntity.getLogChannel().isEmpty()) {
|
|
||||||
JpaUtils.getWithSession(guildEntity, guildEntity.getGuildId(), session -> {
|
|
||||||
int size = guildEntity.getLoggings()
|
|
||||||
.stream()
|
|
||||||
.filter(oLogging -> oLogging.getId().getChannelId().equals(event.getChannel().getId()) && oLogging.getEnabled())
|
|
||||||
.collect(Collectors.toSet())
|
|
||||||
.size();
|
|
||||||
|
|
||||||
if (size > 0) {
|
|
||||||
TextChannel channel = event.getGuild().getTextChannelById(guildEntity.getLogChannel());
|
|
||||||
|
|
||||||
if (channel != null) {
|
|
||||||
MessageRepository repository = new MessageRepository();
|
|
||||||
String originalMessage = repository.getMessageString(event.getMessage().getId());
|
|
||||||
repository.logEdit(event.getMessage().getId(), event.getMessage().getContentDisplay());
|
|
||||||
|
|
||||||
channel.sendMessageEmbeds(MessageFunction.buildEditMessage(event.getMember(), event.getMessage(), originalMessage)).queue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package me.bobert.netneutrality.hooks.message;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.tasks.EmojiTaskContext;
|
|
||||||
import me.bobert.netneutrality.core.tasks.TaskManager;
|
|
||||||
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class EmojiReactionHook extends ListenerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) {
|
|
||||||
EmojiTaskContext context = new EmojiTaskContext(event);
|
|
||||||
TaskManager.getInstance().handle(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package me.bobert.netneutrality.hooks.message;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.MessageCommandContext;
|
|
||||||
import me.bobert.netneutrality.core.command.CommandProcessor;
|
|
||||||
import me.bobert.netneutrality.utils.Settings;
|
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class MessageReceivedHook extends ListenerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMessageReceived(@NotNull MessageReceivedEvent event) {
|
|
||||||
if (event.getAuthor().isBot()) return;
|
|
||||||
if (event.isWebhookMessage()) return;
|
|
||||||
|
|
||||||
String rawMessage = event.getMessage().getContentDisplay().trim();
|
|
||||||
String prefix = Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$");
|
|
||||||
|
|
||||||
if (rawMessage.startsWith(prefix)) {
|
|
||||||
MessageCommandContext context = new MessageCommandContext(event);
|
|
||||||
CommandProcessor processor = new CommandProcessor(context);
|
|
||||||
processor.dispatch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package me.bobert.netneutrality.hooks.voice;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
|
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public class VoiceEventHook extends ListenerAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event) {
|
|
||||||
super.onGuildVoiceUpdate(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@ package me.bobert.netneutrality.utils;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.MusicChannelContext;
|
import me.bobert.netneutrality.context.MusicChannelContext;
|
||||||
import me.bobert.netneutrality.context.SlashCommandContext;
|
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
||||||
import me.bobert.netneutrality.core.context.Context;
|
import me.bobert.netneutrality.core.context.Context;
|
||||||
import me.bobert.netneutrality.exceptions.ContextException;
|
import me.bobert.netneutrality.exceptions.ContextException;
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
import me.bobert.netneutrality.music.MusicManager;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package me.bobert.netneutrality.utils;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.SlashCommandContext;
|
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
||||||
import me.bobert.netneutrality.core.context.Context;
|
import me.bobert.netneutrality.core.context.Context;
|
||||||
import net.dv8tion.jda.api.Permission;
|
import net.dv8tion.jda.api.Permission;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
|||||||
Reference in new issue
Block a user