001package co.aikar.commands; 002 003import co.aikar.commands.apachecommonslang.ApacheCommonsExceptionUtil; 004import net.dv8tion.jda.core.AccountType; 005import net.dv8tion.jda.core.JDA; 006import net.dv8tion.jda.core.entities.ChannelType; 007import net.dv8tion.jda.core.entities.Message; 008import net.dv8tion.jda.core.events.message.MessageReceivedEvent; 009import org.jetbrains.annotations.NotNull; 010 011import java.util.Arrays; 012import java.util.HashMap; 013import java.util.Collection; 014import java.util.Collections; 015import java.util.List; 016import java.util.Map; 017import java.util.logging.Level; 018import java.util.logging.Logger; 019 020public class JDACommandManager extends CommandManager< 021 MessageReceivedEvent, 022 JDACommandEvent, 023 String, 024 MessageFormatter<String>, 025 JDACommandExecutionContext, 026 JDAConditionContext 027 > { 028 029 private final JDA jda; 030 protected JDACommandCompletions completions; 031 protected JDACommandContexts contexts; 032 protected JDALocales locales; 033 protected Map<String, JDARootCommand> commands = new HashMap<>(); 034 private Logger logger; 035 private CommandConfig defaultConfig; 036 private CommandConfigProvider configProvider; 037 private CommandPermissionResolver permissionResolver; 038 private long botOwner = 0L; 039 040 public JDACommandManager(JDA jda) { 041 this(jda, null); 042 } 043 044 public JDACommandManager(JDA jda, JDAOptions options) { 045 if (options == null) { 046 options = new JDAOptions(); 047 } 048 this.jda = jda; 049 this.permissionResolver = options.permissionResolver; 050 jda.addEventListener(new JDAListener(this)); 051 this.defaultConfig = options.defaultConfig == null ? new JDACommandConfig() : options.defaultConfig; 052 this.configProvider = options.configProvider; 053 this.defaultFormatter = new JDAMessageFormatter(); 054 this.completions = new JDACommandCompletions(this); 055 this.logger = Logger.getLogger(this.getClass().getSimpleName()); 056 057 getCommandConditions().addCondition("owneronly", context -> { 058 if (context.getIssuer().getEvent().getAuthor().getIdLong() != getBotOwnerId()) { 059 throw new ConditionFailedException("Only the bot owner can use this command."); // TODO: MessageKey 060 } 061 }); 062 063 getCommandConditions().addCondition("guildonly", context -> { 064 if (context.getIssuer().getEvent().getChannelType() != ChannelType.TEXT) { 065 throw new ConditionFailedException("This command must be used in guild chat."); // TODO: MessageKey 066 } 067 }); 068 069 getCommandConditions().addCondition("privateonly", context -> { 070 if (context.getIssuer().getEvent().getChannelType() != ChannelType.PRIVATE) { 071 throw new ConditionFailedException("This command must be used in private chat."); // TODO: MessageKey 072 } 073 }); 074 075 getCommandConditions().addCondition("grouponly", context -> { 076 if (context.getIssuer().getEvent().getChannelType() != ChannelType.GROUP) { 077 throw new ConditionFailedException("This command must be used in group chat."); // TODO: MessageKey 078 } 079 }); 080 } 081 082 public static JDAOptions options() { 083 return new JDAOptions(); 084 } 085 086 void initializeBotOwner() { 087 if (botOwner == 0L) { 088 if (jda.getAccountType() == AccountType.BOT) { 089 botOwner = jda.asBot().getApplicationInfo().complete().getOwner().getIdLong(); 090 } else { 091 botOwner = jda.getSelfUser().getIdLong(); 092 } 093 } 094 } 095 096 public long getBotOwnerId() { 097 // Just in case initialization on ReadyEvent fails. 098 initializeBotOwner(); 099 return botOwner; 100 } 101 102 public JDA getJDA() { 103 return jda; 104 } 105 106 public Logger getLogger() { 107 return logger; 108 } 109 110 public void setLogger(Logger logger) { 111 this.logger = logger; 112 } 113 114 public CommandConfig getDefaultConfig() { 115 return defaultConfig; 116 } 117 118 public void setDefaultConfig(@NotNull CommandConfig defaultConfig) { 119 this.defaultConfig = defaultConfig; 120 } 121 122 public CommandConfigProvider getConfigProvider() { 123 return configProvider; 124 } 125 126 public void setConfigProvider(CommandConfigProvider configProvider) { 127 this.configProvider = configProvider; 128 } 129 130 public CommandPermissionResolver getPermissionResolver() { 131 return permissionResolver; 132 } 133 134 public void setPermissionResolver(CommandPermissionResolver permissionResolver) { 135 this.permissionResolver = permissionResolver; 136 } 137 138 @Override 139 public CommandContexts<?> getCommandContexts() { 140 if (this.contexts == null) { 141 this.contexts = new JDACommandContexts(this); 142 } 143 return this.contexts; 144 } 145 146 @Override 147 public CommandCompletions<?> getCommandCompletions() { 148 return this.completions; 149 } 150 151 @Override 152 public void registerCommand(BaseCommand command) { 153 command.onRegister(this); 154 for (Map.Entry<String, RootCommand> entry : command.registeredCommands.entrySet()) { 155 String commandName = entry.getKey().toLowerCase(); 156 JDARootCommand cmd = (JDARootCommand) entry.getValue(); 157 if (!cmd.isRegistered) { 158 cmd.isRegistered = true; 159 commands.put(commandName, cmd); 160 } 161 } 162 } 163 164 public void unregisterCommand(BaseCommand command) { 165 for (Map.Entry<String, RootCommand> entry : command.registeredCommands.entrySet()) { 166 String jdaCommandName = entry.getKey().toLowerCase(); 167 JDARootCommand jdaCommand = (JDARootCommand) entry.getValue(); 168 jdaCommand.getSubCommands().values().removeAll(command.subCommands.values()); 169 if (jdaCommand.isRegistered && jdaCommand.getSubCommands().isEmpty()) { 170 jdaCommand.isRegistered = false; 171 commands.remove(jdaCommandName); 172 } 173 } 174 } 175 176 @Override 177 public boolean hasRegisteredCommands() { 178 return !this.commands.isEmpty(); 179 } 180 181 @Override 182 public boolean isCommandIssuer(Class<?> type) { 183 return JDACommandEvent.class.isAssignableFrom(type); 184 } 185 186 @Override 187 public JDACommandEvent getCommandIssuer(Object issuer) { 188 if (!(issuer instanceof MessageReceivedEvent)) { 189 throw new IllegalArgumentException(issuer.getClass().getName() + " is not a Message Received Event."); 190 } 191 return new JDACommandEvent(this, (MessageReceivedEvent) issuer); 192 } 193 194 @Override 195 public RootCommand createRootCommand(String cmd) { 196 return new JDARootCommand(this, cmd); 197 } 198 199 @Override 200 public Collection<RootCommand> getRegisteredRootCommands() { 201 return Collections.unmodifiableCollection(commands.values()); 202 } 203 204 @Override 205 public Locales getLocales() { 206 if (this.locales == null) { 207 this.locales = new JDALocales(this); 208 this.locales.loadLanguages(); 209 } 210 return this.locales; 211 } 212 213 @Override 214 public CommandExecutionContext createCommandContext(RegisteredCommand command, CommandParameter parameter, CommandIssuer sender, List<String> args, int i, Map<String, Object> passedArgs) { 215 return new JDACommandExecutionContext(command, parameter, (JDACommandEvent) sender, args, i, passedArgs); 216 } 217 218 @Override 219 public CommandCompletionContext createCompletionContext(RegisteredCommand command, CommandIssuer sender, String input, String config, String[] args) { 220 // Not really going to be used; 221 //noinspection unchecked 222 return new CommandCompletionContext(command, sender, input, config, args); 223 } 224 225 @Override 226 public void log(LogLevel level, String message, Throwable throwable) { 227 Level logLevel = level == LogLevel.INFO ? Level.INFO : Level.SEVERE; 228 logger.log(logLevel, LogLevel.LOG_PREFIX + message); 229 if (throwable != null) { 230 for (String line : ACFPatterns.NEWLINE.split(ApacheCommonsExceptionUtil.getFullStackTrace(throwable))) { 231 logger.log(logLevel, LogLevel.LOG_PREFIX + line); 232 } 233 } 234 } 235 236 void dispatchEvent(MessageReceivedEvent event) { 237 Message message = event.getMessage(); 238 String msg = message.getContentRaw(); 239 240 CommandConfig config = getCommandConfig(event); 241 242 String prefixFound = null; 243 for (String prefix : config.getCommandPrefixes()) { 244 if (msg.startsWith(prefix)) { 245 prefixFound = prefix; 246 break; 247 } 248 } 249 if (prefixFound == null) { 250 return; 251 } 252 253 String[] args = ACFPatterns.SPACE.split(msg.substring(prefixFound.length()), -1); 254 if (args.length == 0) { 255 return; 256 } 257 String cmd = args[0].toLowerCase(); 258 JDARootCommand rootCommand = this.commands.get(cmd); 259 if (rootCommand == null) { 260 return; 261 } 262 if (args.length > 1) { 263 args = Arrays.copyOfRange(args, 1, args.length); 264 } else { 265 args = new String[0]; 266 } 267 rootCommand.execute(this.getCommandIssuer(event), cmd, args); 268 } 269 270 private CommandConfig getCommandConfig(MessageReceivedEvent event) { 271 CommandConfig config = this.defaultConfig; 272 if (this.configProvider != null) { 273 CommandConfig provided = this.configProvider.provide(event); 274 if (provided != null) { 275 config = provided; 276 } 277 } 278 return config; 279 } 280 281 282 @Override 283 public String getCommandPrefix(CommandIssuer issuer) { 284 MessageReceivedEvent event = ((JDACommandEvent) issuer).getEvent(); 285 CommandConfig commandConfig = getCommandConfig(event); 286 List<String> prefixes = commandConfig.getCommandPrefixes(); 287 return prefixes.isEmpty() ? "" : prefixes.get(0); 288 } 289}