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}