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