diff --git a/.idea/compiler.xml b/.idea/compiler.xml index c5e4f014e..77971ef3b 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -8,6 +8,7 @@ + @@ -24,8 +25,6 @@ - - @@ -37,6 +36,7 @@ + diff --git a/.idea/encodings.xml b/.idea/encodings.xml index c0c073901..3c71b41f5 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -17,6 +17,8 @@ + + diff --git a/jda5/pom.xml b/jda5/pom.xml new file mode 100644 index 000000000..9e585ccda --- /dev/null +++ b/jda5/pom.xml @@ -0,0 +1,63 @@ + + + + + 4.0.0 + + + co.aikar + acf-parent + 0.5.1-SNAPSHOT + ../pom.xml + + + acf-jda5 + 0.5.1-SNAPSHOT + + ACF (JDA 5) + + + + co.aikar + acf-core + 0.5.1-SNAPSHOT + compile + + + net.dv8tion + JDA + 5.2.1 + provided + + + + + + + + diff --git a/jda5/src/main/java/co/aikar/commands/CommandConfig.java b/jda5/src/main/java/co/aikar/commands/CommandConfig.java new file mode 100644 index 000000000..382cf6363 --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/CommandConfig.java @@ -0,0 +1,15 @@ +package co.aikar.commands; + +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface CommandConfig extends CommandConfigProvider { + @NotNull List getCommandPrefixes(); + + @Override + default CommandConfig provide(MessageReceivedEvent event) { + return this; + } +} diff --git a/jda5/src/main/java/co/aikar/commands/CommandConfigProvider.java b/jda5/src/main/java/co/aikar/commands/CommandConfigProvider.java new file mode 100644 index 000000000..9a1702c9b --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/CommandConfigProvider.java @@ -0,0 +1,7 @@ +package co.aikar.commands; + +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; + +public interface CommandConfigProvider { + CommandConfig provide(MessageReceivedEvent event); +} diff --git a/jda5/src/main/java/co/aikar/commands/CommandPermissionResolver.java b/jda5/src/main/java/co/aikar/commands/CommandPermissionResolver.java new file mode 100644 index 000000000..ba439965e --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/CommandPermissionResolver.java @@ -0,0 +1,6 @@ +package co.aikar.commands; + + +public interface CommandPermissionResolver { + boolean hasPermission(JDACommandManager manager, JDACommandEvent event, String permission); +} diff --git a/jda5/src/main/java/co/aikar/commands/JDACommandCompletions.java b/jda5/src/main/java/co/aikar/commands/JDACommandCompletions.java new file mode 100644 index 000000000..02e0b95db --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDACommandCompletions.java @@ -0,0 +1,42 @@ +package co.aikar.commands; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; + +public class JDACommandCompletions extends CommandCompletions> { + private boolean initialized; + + public JDACommandCompletions(CommandManager manager) { + super(manager); + this.initialized = true; + } + + @Override + public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler> handler) { + if (initialized) { + throw new UnsupportedOperationException("JDA Doesn't support Command Completions"); + } + return null; + } + + @Override + public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler> handler) { + if (initialized) { + throw new UnsupportedOperationException("JDA Doesn't support Command Completions"); + } + return null; + } + + @NotNull + @Override + List of(RegisteredCommand command, CommandIssuer sender, String[] args, boolean isAsync) { + return Collections.emptyList(); + } + + @Override + List getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { + return Collections.emptyList(); + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDACommandConfig.java b/jda5/src/main/java/co/aikar/commands/JDACommandConfig.java new file mode 100644 index 000000000..6eb97afc7 --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDACommandConfig.java @@ -0,0 +1,19 @@ +package co.aikar.commands; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class JDACommandConfig implements CommandConfig { + protected @NotNull List commandPrefixes = new CopyOnWriteArrayList<>(new String[]{"!"}); + + public JDACommandConfig() { + + } + + @NotNull + public List getCommandPrefixes() { + return commandPrefixes; + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDACommandContexts.java b/jda5/src/main/java/co/aikar/commands/JDACommandContexts.java new file mode 100644 index 000000000..4e1e9b6ba --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDACommandContexts.java @@ -0,0 +1,115 @@ +package co.aikar.commands; + +import co.aikar.commands.annotation.Author; +import co.aikar.commands.annotation.CrossGuild; +import co.aikar.commands.annotation.SelfUser; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; + +import java.util.List; + +// TODO: Message Keys !!! +public class JDACommandContexts extends CommandContexts { + private final JDACommandManager manager; + private final JDA jda; + + public JDACommandContexts(JDACommandManager manager) { + super(manager); + this.manager = manager; + this.jda = this.manager.getJDA(); + this.registerIssuerOnlyContext(JDACommandEvent.class, CommandExecutionContext::getIssuer); + this.registerIssuerOnlyContext(MessageReceivedEvent.class, c -> c.getIssuer().getIssuer()); + this.registerIssuerOnlyContext(Message.class, c -> c.issuer.getIssuer().getMessage()); + this.registerIssuerOnlyContext(ChannelType.class, c -> c.issuer.getIssuer().getChannelType()); + this.registerIssuerOnlyContext(JDA.class, c -> jda); + this.registerIssuerOnlyContext(Guild.class, c -> { + MessageReceivedEvent event = c.getIssuer().getIssuer(); + if (event.isFromType(ChannelType.PRIVATE) && !c.isOptional()) { + throw new InvalidCommandArgument("This command can only be executed in a Guild.", false); + } else { + return event.getGuild(); + } + }); + this.registerIssuerAwareContext(MessageChannel.class, c -> { + if (c.hasAnnotation(Author.class)) { + return c.issuer.getIssuer().getChannel(); + } + boolean isCrossGuild = c.hasAnnotation(CrossGuild.class); + String argument = c.popFirstArg(); // we pop because we are only issuer aware if we are annotated + MessageChannel channel = null; + if (argument.startsWith("<#")) { + String id = argument.substring(2, argument.length() - 1); + channel = isCrossGuild ? jda.getTextChannelById(id) : c.issuer.getIssuer().getGuild().getTextChannelById(id); + } else { + List channelList = isCrossGuild ? jda.getTextChannelsByName(argument, true) : + c.issuer.getEvent().getGuild().getTextChannelsByName(argument, true); + if (channelList.size() > 1) { + throw new InvalidCommandArgument("Too many channels were found with the given name. Try with the `#channelname` syntax.", false); + } else if (channelList.size() == 1) { + channel = channelList.get(0); + } + } + if (channel == null) { + throw new InvalidCommandArgument("Couldn't find a channel with that name or ID."); + } + return channel; + }); + this.registerIssuerAwareContext(User.class, c -> { + if (c.hasAnnotation(SelfUser.class)) { + return jda.getSelfUser(); + } + String arg = c.getFirstArg(); + if (c.isOptional() && (arg == null || arg.isEmpty())) { + return null; + } + arg = c.popFirstArg(); // we pop because we are only issuer aware if we are annotated + User user = null; + if (arg.startsWith("<@!")) { // for some reason a ! is added when @'ing and clicking their name. + user = jda.getUserById(arg.substring(3, arg.length() - 1)); + } else if (arg.startsWith("<@")) { // users can /also/ be mentioned like this... + user = jda.getUserById(arg.substring(2, arg.length() - 1)); + } else { + List users = jda.getUsersByName(arg, true); + if (users.size() > 1) { + throw new InvalidCommandArgument("Too many users were found with the given name. Try with the `@username#0000` syntax.", false); + } + if (!users.isEmpty()) { + user = users.get(0); + } + } + if (user == null) { + throw new InvalidCommandArgument("Could not find a user with that name or ID."); + } + return user; + }); + this.registerContext(Role.class, c -> { + boolean isCrossGuild = c.hasAnnotation(CrossGuild.class); + String arg = c.popFirstArg(); + Role role = null; + if (arg.startsWith("<@&")) { + String id = arg.substring(3, arg.length() - 1); + role = isCrossGuild ? jda.getRoleById(id) : c.issuer.getIssuer().getGuild().getRoleById(id); + } else { + List roles = isCrossGuild ? jda.getRolesByName(arg, true) + : c.issuer.getIssuer().getGuild().getRolesByName(arg, true); + if (roles.size() > 1) { + throw new InvalidCommandArgument("Too many roles were found with the given name. Try with the `@role` syntax.", false); + } + if (!roles.isEmpty()) { + role = roles.get(0); + } + } + if (role == null) { + throw new InvalidCommandArgument("Could not find a role with that name or ID."); + } + return role; + }); + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDACommandEvent.java b/jda5/src/main/java/co/aikar/commands/JDACommandEvent.java new file mode 100644 index 000000000..3c292bb93 --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDACommandEvent.java @@ -0,0 +1,67 @@ +package co.aikar.commands; + +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public class JDACommandEvent implements CommandIssuer { + + private MessageReceivedEvent event; + private JDACommandManager manager; + + public JDACommandEvent(JDACommandManager manager, MessageReceivedEvent event) { + + this.manager = manager; + this.event = event; + } + + public MessageReceivedEvent getEvent() { + return event; + } + + @Override + public MessageReceivedEvent getIssuer() { + return event; + } + + @Override + public CommandManager getManager() { + return this.manager; + } + + @Override + public boolean isPlayer() { + return false; + } + + @Override + public @NotNull UUID getUniqueId() { + // Discord id only have 64 bit width (long) while UUIDs have twice the size. + // In order to keep it unique we use 0L for the first 64 bit. + long authorId = event.getAuthor().getIdLong(); + return new UUID(0, authorId); + } + + @Override + public boolean hasPermission(String permission) { + CommandPermissionResolver permissionResolver = this.manager.getPermissionResolver(); + return permissionResolver == null || permissionResolver.hasPermission(manager, this, permission); + } + + @Override + public void sendMessageInternal(String message) { + this.event.getChannel().sendMessage(message).queue(); + } + + public void sendMessage(Message message) { + this.event.getChannel().sendMessage(MessageCreateData.fromMessage(message)).queue(); + } + + public void sendMessage(MessageEmbed message) { + this.event.getChannel().sendMessage(MessageCreateData.fromEmbeds(message)).queue(); + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDACommandExecutionContext.java b/jda5/src/main/java/co/aikar/commands/JDACommandExecutionContext.java new file mode 100644 index 000000000..e9d4612b3 --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDACommandExecutionContext.java @@ -0,0 +1,10 @@ +package co.aikar.commands; + +import java.util.List; +import java.util.Map; + +public class JDACommandExecutionContext extends CommandExecutionContext { + JDACommandExecutionContext(RegisteredCommand cmd, CommandParameter param, JDACommandEvent sender, List args, int index, Map passedArgs) { + super(cmd, param, sender, args, index, passedArgs); + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDACommandManager.java b/jda5/src/main/java/co/aikar/commands/JDACommandManager.java new file mode 100644 index 000000000..a89ef448f --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDACommandManager.java @@ -0,0 +1,285 @@ +package co.aikar.commands; + +import co.aikar.commands.apachecommonslang.ApacheCommonsExceptionUtil; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class JDACommandManager extends CommandManager< + MessageReceivedEvent, + JDACommandEvent, + String, + MessageFormatter, + JDACommandExecutionContext, + JDAConditionContext + > { + + private final JDA jda; + protected JDACommandCompletions completions; + protected JDACommandContexts contexts; + protected JDALocales locales; + protected Map commands = new HashMap<>(); + private Logger logger; + private CommandConfig defaultConfig; + private CommandConfigProvider configProvider; + private CommandPermissionResolver permissionResolver; + private long botOwner = 0L; + + public JDACommandManager(JDA jda) { + this(jda, null); + } + + public JDACommandManager(JDA jda, JDAOptions options) { + if (options == null) { + options = new JDAOptions(); + } + this.jda = jda; + this.permissionResolver = options.permissionResolver; + jda.addEventListener(new JDAListener(this)); + this.defaultConfig = options.defaultConfig == null ? new JDACommandConfig() : options.defaultConfig; + this.configProvider = options.configProvider; + this.defaultFormatter = new JDAMessageFormatter(); + this.completions = new JDACommandCompletions(this); + this.logger = Logger.getLogger(this.getClass().getSimpleName()); + + getCommandConditions().addCondition("owneronly", context -> { + if (context.getIssuer().getEvent().getAuthor().getIdLong() != getBotOwnerId()) { + throw new ConditionFailedException("Only the bot owner can use this command."); // TODO: MessageKey + } + }); + + getCommandConditions().addCondition("guildonly", context -> { + if (context.getIssuer().getEvent().getChannelType() != ChannelType.TEXT) { + throw new ConditionFailedException("This command must be used in guild chat."); // TODO: MessageKey + } + }); + + getCommandConditions().addCondition("privateonly", context -> { + if (context.getIssuer().getEvent().getChannelType() != ChannelType.PRIVATE) { + throw new ConditionFailedException("This command must be used in private chat."); // TODO: MessageKey + } + }); + + getCommandConditions().addCondition("grouponly", context -> { + if (context.getIssuer().getEvent().getChannelType() != ChannelType.GROUP) { + throw new ConditionFailedException("This command must be used in group chat."); // TODO: MessageKey + } + }); + } + + public static JDAOptions options() { + return new JDAOptions(); + } + + void initializeBotOwner() { + if (botOwner == 0L) { + botOwner = jda.retrieveApplicationInfo().complete().getOwner().getIdLong(); + } + } + + public long getBotOwnerId() { + // Just in case initialization on ReadyEvent fails. + initializeBotOwner(); + return botOwner; + } + + public JDA getJDA() { + return jda; + } + + public Logger getLogger() { + return logger; + } + + public void setLogger(Logger logger) { + this.logger = logger; + } + + public CommandConfig getDefaultConfig() { + return defaultConfig; + } + + public void setDefaultConfig(@NotNull CommandConfig defaultConfig) { + this.defaultConfig = defaultConfig; + } + + public CommandConfigProvider getConfigProvider() { + return configProvider; + } + + public void setConfigProvider(CommandConfigProvider configProvider) { + this.configProvider = configProvider; + } + + public CommandPermissionResolver getPermissionResolver() { + return permissionResolver; + } + + public void setPermissionResolver(CommandPermissionResolver permissionResolver) { + this.permissionResolver = permissionResolver; + } + + @Override + public CommandContexts getCommandContexts() { + if (this.contexts == null) { + this.contexts = new JDACommandContexts(this); + } + return this.contexts; + } + + @Override + public CommandCompletions getCommandCompletions() { + return this.completions; + } + + @Override + public void registerCommand(BaseCommand command) { + command.onRegister(this); + for (Map.Entry entry : command.registeredCommands.entrySet()) { + String commandName = entry.getKey().toLowerCase(Locale.ENGLISH); + JDARootCommand cmd = (JDARootCommand) entry.getValue(); + if (!cmd.isRegistered) { + cmd.isRegistered = true; + commands.put(commandName, cmd); + } + } + } + + public void unregisterCommand(BaseCommand command) { + for (Map.Entry entry : command.registeredCommands.entrySet()) { + String jdaCommandName = entry.getKey().toLowerCase(Locale.ENGLISH); + JDARootCommand jdaCommand = (JDARootCommand) entry.getValue(); + jdaCommand.getSubCommands().values().removeAll(command.subCommands.values()); + if (jdaCommand.isRegistered && jdaCommand.getSubCommands().isEmpty()) { + jdaCommand.isRegistered = false; + commands.remove(jdaCommandName); + } + } + } + + @Override + public boolean hasRegisteredCommands() { + return !this.commands.isEmpty(); + } + + @Override + public boolean isCommandIssuer(Class type) { + return JDACommandEvent.class.isAssignableFrom(type); + } + + @Override + public JDACommandEvent getCommandIssuer(Object issuer) { + if (!(issuer instanceof MessageReceivedEvent)) { + throw new IllegalArgumentException(issuer.getClass().getName() + " is not a Message Received Event."); + } + return new JDACommandEvent(this, (MessageReceivedEvent) issuer); + } + + @Override + public RootCommand createRootCommand(String cmd) { + return new JDARootCommand(this, cmd); + } + + @Override + public Collection getRegisteredRootCommands() { + return Collections.unmodifiableCollection(commands.values()); + } + + @Override + public Locales getLocales() { + if (this.locales == null) { + this.locales = new JDALocales(this); + this.locales.loadLanguages(); + } + return this.locales; + } + + @Override + public CommandExecutionContext createCommandContext(RegisteredCommand command, CommandParameter parameter, CommandIssuer sender, List args, int i, Map passedArgs) { + return new JDACommandExecutionContext(command, parameter, (JDACommandEvent) sender, args, i, passedArgs); + } + + @Override + public CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args) { + // Not really going to be used; + //noinspection unchecked + return new CommandCompletionContext(command, sender, input, config, args); + } + + @Override + public void log(LogLevel level, String message, Throwable throwable) { + Level logLevel = level == LogLevel.INFO ? Level.INFO : Level.SEVERE; + logger.log(logLevel, LogLevel.LOG_PREFIX + message); + if (throwable != null) { + for (String line : ACFPatterns.NEWLINE.split(ApacheCommonsExceptionUtil.getFullStackTrace(throwable))) { + logger.log(logLevel, LogLevel.LOG_PREFIX + line); + } + } + } + + void dispatchEvent(MessageReceivedEvent event) { + Message message = event.getMessage(); + String msg = message.getContentRaw(); + + CommandConfig config = getCommandConfig(event); + + String prefixFound = null; + for (String prefix : config.getCommandPrefixes()) { + if (msg.startsWith(prefix)) { + prefixFound = prefix; + break; + } + } + if (prefixFound == null) { + return; + } + + String[] args = ACFPatterns.SPACE.split(msg.substring(prefixFound.length()), -1); + if (args.length == 0) { + return; + } + String cmd = args[0].toLowerCase(Locale.ENGLISH); + JDARootCommand rootCommand = this.commands.get(cmd); + if (rootCommand == null) { + return; + } + if (args.length > 1) { + args = Arrays.copyOfRange(args, 1, args.length); + } else { + args = new String[0]; + } + rootCommand.execute(this.getCommandIssuer(event), cmd, args); + } + + private CommandConfig getCommandConfig(MessageReceivedEvent event) { + CommandConfig config = this.defaultConfig; + if (this.configProvider != null) { + CommandConfig provided = this.configProvider.provide(event); + if (provided != null) { + config = provided; + } + } + return config; + } + + + @Override + public String getCommandPrefix(CommandIssuer issuer) { + MessageReceivedEvent event = ((JDACommandEvent) issuer).getEvent(); + CommandConfig commandConfig = getCommandConfig(event); + List prefixes = commandConfig.getCommandPrefixes(); + return prefixes.isEmpty() ? "" : prefixes.get(0); + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDACommandPermissionResolver.java b/jda5/src/main/java/co/aikar/commands/JDACommandPermissionResolver.java new file mode 100644 index 000000000..1d0bb4398 --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDACommandPermissionResolver.java @@ -0,0 +1,40 @@ +package co.aikar.commands; + +import net.dv8tion.jda.api.Permission; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public class JDACommandPermissionResolver implements CommandPermissionResolver { + private Map discordPermissionOffsets; + + public JDACommandPermissionResolver() { + discordPermissionOffsets = new HashMap<>(); + for (Permission permission : Permission.values()) { + discordPermissionOffsets.put(permission.name().toLowerCase(Locale.ENGLISH).replaceAll("_", "-"), permission.getOffset()); + } + } + + @Override + public boolean hasPermission(JDACommandManager manager, JDACommandEvent event, String permission) { + // Explicitly return true if the issuer is the bot's owner. They are always allowed. + if (manager.getBotOwnerId() == event.getIssuer().getAuthor().getIdLong()) { + return true; + } + + // Return false on webhook messages, as they cannot have permissions defined. + if (event.getIssuer().isWebhookMessage()) { + return false; + } + + Integer permissionOffset = discordPermissionOffsets.get(permission); + if (permissionOffset == null) { + return false; + } + + return event.getIssuer().getMember().hasPermission( + Permission.getFromOffset(permissionOffset) + ); + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDAConditionContext.java b/jda5/src/main/java/co/aikar/commands/JDAConditionContext.java new file mode 100644 index 000000000..adf3f003c --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDAConditionContext.java @@ -0,0 +1,7 @@ +package co.aikar.commands; + +public class JDAConditionContext extends ConditionContext { + JDAConditionContext(JDACommandEvent issuer, String config) { + super(issuer, config); + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDAListener.java b/jda5/src/main/java/co/aikar/commands/JDAListener.java new file mode 100644 index 000000000..fa89d4ae7 --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDAListener.java @@ -0,0 +1,33 @@ +package co.aikar.commands; + +import net.dv8tion.jda.api.entities.channel.ChannelType; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.events.session.GenericSessionEvent; +import net.dv8tion.jda.api.events.session.SessionState; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import org.jetbrains.annotations.NotNull; + +public class JDAListener extends ListenerAdapter { + + private final JDACommandManager manager; + + JDAListener(JDACommandManager manager) { + + this.manager = manager; + } + + @Override + public void onMessageReceived(MessageReceivedEvent event) { + if (event.isFromType(ChannelType.TEXT) || event.isFromType(ChannelType.PRIVATE)) { + this.manager.dispatchEvent(event); + } + } + + @Override + public void onGenericSession(@NotNull GenericSessionEvent event) { + if (event.getState() == SessionState.READY) { + manager.initializeBotOwner(); + } + } + +} diff --git a/jda5/src/main/java/co/aikar/commands/JDALocales.java b/jda5/src/main/java/co/aikar/commands/JDALocales.java new file mode 100644 index 000000000..84898f4dc --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDALocales.java @@ -0,0 +1,7 @@ +package co.aikar.commands; + +public class JDALocales extends Locales { + public JDALocales(CommandManager manager) { + super(manager); + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDAMessageFormatter.java b/jda5/src/main/java/co/aikar/commands/JDAMessageFormatter.java new file mode 100644 index 000000000..44df3224a --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDAMessageFormatter.java @@ -0,0 +1,14 @@ +package co.aikar.commands; + +public class JDAMessageFormatter extends MessageFormatter { + public JDAMessageFormatter() { + // JDA does not support coloring messages outside of embed fields. + // We pass three empty strings to remove color coded messages from appearing. + super("", "", ""); + } + + @Override + String format(String color, String message) { + return message; + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDAOptions.java b/jda5/src/main/java/co/aikar/commands/JDAOptions.java new file mode 100644 index 000000000..f54083aea --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDAOptions.java @@ -0,0 +1,32 @@ +package co.aikar.commands; + +import net.dv8tion.jda.api.JDA; +import org.jetbrains.annotations.NotNull; + +public class JDAOptions { + CommandConfig defaultConfig = new JDACommandConfig(); + CommandConfigProvider configProvider = null; + CommandPermissionResolver permissionResolver = new JDACommandPermissionResolver(); + + public JDAOptions() { + } + + public JDAOptions defaultConfig(@NotNull CommandConfig defaultConfig) { + this.defaultConfig = defaultConfig; + return this; + } + + public JDAOptions configProvider(@NotNull CommandConfigProvider configProvider) { + this.configProvider = configProvider; + return this; + } + + public JDAOptions permissionResolver(@NotNull CommandPermissionResolver permissionResolver) { + this.permissionResolver = permissionResolver; + return this; + } + + public JDACommandManager create(JDA jda) { + return new JDACommandManager(jda, this); + } +} diff --git a/jda5/src/main/java/co/aikar/commands/JDARootCommand.java b/jda5/src/main/java/co/aikar/commands/JDARootCommand.java new file mode 100644 index 000000000..2e01c9f15 --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/JDARootCommand.java @@ -0,0 +1,56 @@ +package co.aikar.commands; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.SetMultimap; + +import java.util.ArrayList; +import java.util.List; + +public class JDARootCommand implements RootCommand { + + private final String name; + boolean isRegistered = false; + private JDACommandManager manager; + private BaseCommand defCommand; + private SetMultimap subCommands = HashMultimap.create(); + private List children = new ArrayList<>(); + + JDARootCommand(JDACommandManager manager, String name) { + this.manager = manager; + this.name = name; + } + + @Override + public void addChild(BaseCommand command) { + if (this.defCommand == null || !command.subCommands.get(BaseCommand.DEFAULT).isEmpty()) { + this.defCommand = command; + } + addChildShared(this.children, this.subCommands, command); + } + + @Override + public CommandManager getManager() { + return this.manager; + } + + @Override + public SetMultimap getSubCommands() { + return this.subCommands; + } + + @Override + public List getChildren() { + return this.children; + } + + @Override + public String getCommandName() { + return this.name; + } + + @Override + public BaseCommand getDefCommand() { + return defCommand; + } + +} diff --git a/jda5/src/main/java/co/aikar/commands/annotation/Author.java b/jda5/src/main/java/co/aikar/commands/annotation/Author.java new file mode 100644 index 000000000..415c4633a --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/annotation/Author.java @@ -0,0 +1,18 @@ +package co.aikar.commands.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link Author} annotation is to define whether the parameter should be the author object from the event or + * parsed from the user's input. + *

+ * Using this on a User/Member will fetch the author and otherwise it'll parse the input. + *

+ */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface Author { +} diff --git a/jda5/src/main/java/co/aikar/commands/annotation/CrossGuild.java b/jda5/src/main/java/co/aikar/commands/annotation/CrossGuild.java new file mode 100644 index 000000000..40b3a3b7f --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/annotation/CrossGuild.java @@ -0,0 +1,19 @@ +package co.aikar.commands.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link CrossGuild} annotation is to define whether the parameter should be guild-specific or global. + *

+ * If a supported parameter is marked with the CrossGuild annotation, the parameter will be filled from + * a global perspective (i.e., all of the guilds the bot is connected to). Otherwise, the parameter will + * be filled from command input. + *

+ */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface CrossGuild { +} diff --git a/jda5/src/main/java/co/aikar/commands/annotation/SelfUser.java b/jda5/src/main/java/co/aikar/commands/annotation/SelfUser.java new file mode 100644 index 000000000..2fbdc2027 --- /dev/null +++ b/jda5/src/main/java/co/aikar/commands/annotation/SelfUser.java @@ -0,0 +1,15 @@ +package co.aikar.commands.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The {@link SelfUser} annotation is to define whether the parameter should be represented by JDA's user object + * or if it should be parsed from command input. + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface SelfUser { +} diff --git a/pom.xml b/pom.xml index 02ff79dab..1ca7464f2 100644 --- a/pom.xml +++ b/pom.xml @@ -206,6 +206,7 @@ sponge sponge10 jda + jda5 velocity