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