    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}