Search in sources :

Example 1 with SlashCommandInfo

use of com.freya02.botcommands.internal.application.slash.SlashCommandInfo in project BotCommands by freya022.

the class ApplicationCommandsUpdater method computeSlashCommands.

private void computeSlashCommands(List<ApplicationCommandInfo> guildApplicationCommands) {
    guildApplicationCommands.stream().filter(a -> a instanceof SlashCommandInfo).map(a -> (SlashCommandInfo) a).distinct().forEachOrdered(info -> {
        final CommandPath notLocalizedPath = info.getPath();
        try {
            final LocalizedCommandData localizedCommandData = LocalizedCommandData.of(context, guild, info);
            // Put localized option names in order to resolve them when called
            final List<OptionData> localizedMethodOptions = getLocalizedMethodOptions(info, localizedCommandData);
            if (guild != null) {
                info.putLocalizedOptions(guild.getIdLong(), localizedMethodOptions.stream().map(OptionData::getName).collect(Collectors.toList()));
            }
            localizedBaseNameToBaseName.put(localizedCommandData.getLocalizedPath().getName(), notLocalizedPath.getName());
            final CommandPath localizedPath = localizedCommandData.getLocalizedPath();
            final String description = localizedCommandData.getLocalizedDescription();
            Checks.check(localizedPath.getNameCount() == notLocalizedPath.getNameCount(), "Localized path does not have the same name count as the not-localized path");
            final boolean isDefaultEnabled = isDefaultEnabled(info);
            if (localizedPath.getNameCount() == 1) {
                // Standard command
                final SlashCommandData rightCommand = Commands.slash(localizedPath.getName(), description);
                map.put(Command.Type.SLASH, localizedPath, rightCommand);
                rightCommand.addOptions(localizedMethodOptions);
                rightCommand.setDefaultEnabled(isDefaultEnabled);
            } else if (localizedPath.getNameCount() == 2) {
                Checks.notNull(localizedPath.getSubname(), "Subcommand name");
                final SlashCommandData commandData = (SlashCommandData) map.computeIfAbsent(Command.Type.SLASH, localizedPath, x -> {
                    final SlashCommandData tmpData = Commands.slash(localizedPath.getName(), "No description (base name)");
                    tmpData.setDefaultEnabled(isDefaultEnabled);
                    return tmpData;
                });
                // Subcommand of a command
                final SubcommandData subcommandData = new SubcommandData(localizedPath.getSubname(), description);
                subcommandData.addOptions(localizedMethodOptions);
                commandData.addSubcommands(subcommandData);
            } else if (localizedPath.getNameCount() == 3) {
                Checks.notNull(localizedPath.getGroup(), "Command group name");
                Checks.notNull(localizedPath.getSubname(), "Subcommand name");
                final SubcommandGroupData groupData = getSubcommandGroup(Command.Type.SLASH, localizedPath, x -> {
                    final SlashCommandData commandData = Commands.slash(localizedPath.getName(), "No description (base name)");
                    commandData.setDefaultEnabled(isDefaultEnabled);
                    return commandData;
                });
                final SubcommandData subcommandData = new SubcommandData(localizedPath.getSubname(), description);
                subcommandData.addOptions(localizedMethodOptions);
                groupData.addSubcommands(subcommandData);
            } else {
                throw new IllegalStateException("A slash command with more than 4 path components got registered");
            }
            context.addApplicationCommandAlternative(localizedPath, Command.Type.SLASH, info);
            if (!info.isOwnerRequired()) {
                if (ownerOnlyCommands.contains(notLocalizedPath.getName())) {
                    LOGGER.warn("Non owner-only command '{}' is registered as a owner-only command because of another command with the same base name '{}'", notLocalizedPath, notLocalizedPath.getName());
                }
            }
            if (info.isOwnerRequired()) {
                if (info.isGuildOnly()) {
                    ownerOnlyCommands.add(notLocalizedPath.getName());
                } else {
                    LOGGER.warn("Owner-only command '{}' cannot be owner-only as it is a global command", notLocalizedPath);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("An exception occurred while processing command '" + notLocalizedPath + "' at " + Utils.formatMethodShort(info.getMethod()), e);
        }
    });
}
Also used : java.util(java.util) Logging(com.freya02.botcommands.api.Logging) UserCommandInfo(com.freya02.botcommands.internal.application.context.user.UserCommandInfo) Command(net.dv8tion.jda.api.interactions.commands.Command) Function(java.util.function.Function) DebugBuilder(com.freya02.botcommands.api.builder.DebugBuilder) Guild(net.dv8tion.jda.api.entities.Guild) CommandPath(com.freya02.botcommands.api.application.CommandPath) CommandPrivilege(net.dv8tion.jda.api.interactions.commands.privileges.CommandPrivilege) Blocking(org.jetbrains.annotations.Blocking) Path(java.nio.file.Path) SettingsProvider(com.freya02.botcommands.api.SettingsProvider) SlashUtils.getLocalizedMethodOptions(com.freya02.botcommands.internal.application.slash.SlashUtils.getLocalizedMethodOptions) Checks(net.dv8tion.jda.internal.utils.Checks) Logger(org.slf4j.Logger) BContextImpl(com.freya02.botcommands.internal.BContextImpl) Utils(com.freya02.botcommands.internal.utils.Utils) MessageCommandInfo(com.freya02.botcommands.internal.application.context.message.MessageCommandInfo) Files(java.nio.file.Files) StandardOpenOption(java.nio.file.StandardOpenOption) IOException(java.io.IOException) Collectors(java.util.stream.Collectors) net.dv8tion.jda.api.interactions.commands.build(net.dv8tion.jda.api.interactions.commands.build) Nullable(org.jetbrains.annotations.Nullable) SlashUtils.appendCommands(com.freya02.botcommands.internal.application.slash.SlashUtils.appendCommands) SlashCommandInfo(com.freya02.botcommands.internal.application.slash.SlashCommandInfo) CommandListUpdateAction(net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction) NotNull(org.jetbrains.annotations.NotNull) SlashCommandInfo(com.freya02.botcommands.internal.application.slash.SlashCommandInfo) IOException(java.io.IOException) CommandPath(com.freya02.botcommands.api.application.CommandPath)

Example 2 with SlashCommandInfo

use of com.freya02.botcommands.internal.application.slash.SlashCommandInfo in project BotCommands by freya022.

the class LocalizationData method getData.

public static LocalizationData getData(BContext context, @Nullable Guild guild, @NotNull ApplicationCommandInfo info) {
    final CommandPath localizedPath;
    final String localizedDescription;
    final List<LocalizedOption> localizedOptions;
    final List<List<Command.Choice>> localizedChoices;
    final Locale locale = context.getEffectiveLocale(guild);
    final BResourceBundle bundle = BResourceBundle.getBundle("BotCommands", locale);
    if (bundle == null) {
        return null;
    }
    final String prefix;
    if (info instanceof SlashCommandInfo) {
        prefix = "slash";
    } else if (info instanceof UserCommandInfo) {
        prefix = "user";
    } else if (info instanceof MessageCommandInfo) {
        prefix = "message";
    } else {
        throw new IllegalArgumentException("Unknown localization prefix for class: " + info.getClass().getSimpleName());
    }
    final String qualifier = info.getMethod().getName();
    final StringJoiner pathJoiner = new StringJoiner("/");
    pathJoiner.add(tryLocalize(bundle, info.getPath().getName(), prefix, qualifier, "name"));
    if (info instanceof SlashCommandInfo) {
        final String notLocalizedGroup = info.getPath().getGroup();
        final String notLocalizedSubname = info.getPath().getSubname();
        if (notLocalizedGroup != null)
            pathJoiner.add(tryLocalize(bundle, notLocalizedGroup, prefix, qualifier, "group"));
        if (notLocalizedSubname != null)
            pathJoiner.add(tryLocalize(bundle, notLocalizedSubname, prefix, qualifier, "subname"));
    }
    localizedPath = CommandPath.of(pathJoiner.toString());
    if (info instanceof SlashCommandInfo) {
        localizedDescription = tryLocalize(bundle, ((SlashCommandInfo) info).getDescription(), prefix, qualifier, "description");
    } else
        localizedDescription = null;
    if (info instanceof SlashCommandInfo) {
        localizedOptions = new ArrayList<>();
        localizedChoices = new ArrayList<>();
        final List<List<Command.Choice>> notLocalizedChoices = SlashUtils.getNotLocalizedChoices(context, guild, info);
        final List<? extends ApplicationCommandParameter<?>> parameters = info.getOptionParameters();
        for (int optionIndex = 0, parametersSize = parameters.size(); optionIndex < parametersSize; optionIndex++) {
            ApplicationCommandParameter<?> parameter = parameters.get(optionIndex);
            final ApplicationOptionData optionData = parameter.getApplicationOptionData();
            final String optionName = tryLocalize(bundle, optionData.getEffectiveName(), prefix, qualifier, "options", optionIndex, "name");
            final String optionDescription = tryLocalize(bundle, optionData.getEffectiveDescription(), prefix, qualifier, "options", optionIndex, "description");
            localizedOptions.add(new LocalizedOption(optionName, optionDescription));
            final List<Command.Choice> choices = getLocalizedChoices(bundle, prefix, qualifier, notLocalizedChoices, optionIndex, parameter);
            localizedChoices.add(choices);
        }
    } else {
        localizedOptions = null;
        localizedChoices = null;
    }
    return new LocalizationData(localizedPath, localizedDescription, localizedOptions, localizedChoices);
}
Also used : Locale(java.util.Locale) SlashCommandInfo(com.freya02.botcommands.internal.application.slash.SlashCommandInfo) LocalizedOption(com.freya02.botcommands.internal.application.LocalizedCommandData.LocalizedOption) ApplicationOptionData(com.freya02.botcommands.internal.ApplicationOptionData) BResourceBundle(com.freya02.botcommands.internal.utils.BResourceBundle) MessageCommandInfo(com.freya02.botcommands.internal.application.context.message.MessageCommandInfo) Command(net.dv8tion.jda.api.interactions.commands.Command) ArrayList(java.util.ArrayList) List(java.util.List) UserCommandInfo(com.freya02.botcommands.internal.application.context.user.UserCommandInfo) CommandPath(com.freya02.botcommands.api.application.CommandPath) StringJoiner(java.util.StringJoiner)

Example 3 with SlashCommandInfo

use of com.freya02.botcommands.internal.application.slash.SlashCommandInfo in project BotCommands by freya022.

the class BContextImpl method addSlashCommand.

public void addSlashCommand(SlashCommandInfo commandInfo) {
    final CommandPath path = commandInfo.getPath();
    final Map<CommandPath, SlashCommandInfo> slashCommandMap = getSlashCommandsMap();
    // Would be more performant if we used a Trie
    for (Map.Entry<CommandPath, SlashCommandInfo> entry : slashCommandMap.entrySet()) {
        final CommandPath commandPath = entry.getKey();
        final SlashCommandInfo mapInfo = entry.getValue();
        if (commandPath.getNameCount() > path.getNameCount() && commandPath.startsWith(path)) {
            throw new IllegalStateException(String.format("Tried to add a command with path '%s' (at %s) but a equal/longer path already exists: '%s' (at %s)", path, Utils.formatMethodShort(commandInfo.getMethod()), commandPath, Utils.formatMethodShort(mapInfo.getMethod())));
        }
    }
    CommandPath p = path;
    do {
        final SlashCommandInfo mapInfo = slashCommandMap.get(p);
        if (mapInfo != null) {
            throw new IllegalStateException(String.format("Tried to add a command with path '%s' (at %s) but a equal/shorter path already exists: '%s' (at %s)", path, Utils.formatMethodShort(commandInfo.getMethod()), p, Utils.formatMethodShort(mapInfo.getMethod())));
        }
    } while ((p = p.getParent()) != null);
    slashCommandMap.put(path, commandInfo);
}
Also used : SlashCommandInfo(com.freya02.botcommands.internal.application.slash.SlashCommandInfo) CommandPath(com.freya02.botcommands.api.application.CommandPath)

Example 4 with SlashCommandInfo

use of com.freya02.botcommands.internal.application.slash.SlashCommandInfo in project BotCommands by freya022.

the class AutocompletionHandlersBuilder method postProcess.

public void postProcess() {
    for (SlashCommandInfo info : context.getApplicationCommandInfoMap().getSlashCommands().values()) {
        MethodParameters<SlashCommandParameter> parameters = info.getParameters();
        for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) {
            SlashCommandParameter parameter = parameters.get(i);
            if (!parameter.isOption())
                continue;
            final ApplicationOptionData applicationOptionData = parameter.getApplicationOptionData();
            final String autocompleteHandlerName = applicationOptionData.getAutocompletionHandlerName();
            if (autocompleteHandlerName != null) {
                final AutocompletionHandlerInfo handler = context.getAutocompletionHandler(autocompleteHandlerName);
                if (handler == null) {
                    throw new IllegalArgumentException("Slash command parameter #" + i + " at " + Utils.formatMethodShort(info.getMethod()) + " uses autocompletion but has no handler assigned, did you misspell the handler name ? Consider using a constant variable to share with the handler and the option");
                }
                handler.checkParameters(info);
            }
        }
    }
    context.addEventListeners(new AutocompletionListener(context));
}
Also used : SlashCommandInfo(com.freya02.botcommands.internal.application.slash.SlashCommandInfo) ApplicationOptionData(com.freya02.botcommands.internal.ApplicationOptionData) SlashCommandParameter(com.freya02.botcommands.internal.application.slash.SlashCommandParameter)

Example 5 with SlashCommandInfo

use of com.freya02.botcommands.internal.application.slash.SlashCommandInfo in project BotCommands by freya022.

the class ApplicationCommandsBuilder method processSlashCommand.

private void processSlashCommand(ApplicationCommand applicationCommand, Method method) {
    if (method.getAnnotation(JDASlashCommand.class).guildOnly()) {
        if (!ReflectionUtils.hasFirstParameter(method, GlobalSlashEvent.class) && !ReflectionUtils.hasFirstParameter(method, GuildSlashEvent.class))
            throw new IllegalArgumentException("Slash command at " + Utils.formatMethodShort(method) + " must have a GuildSlashEvent or GlobalSlashEvent as first parameter");
        if (!ReflectionUtils.hasFirstParameter(method, GuildSlashEvent.class)) {
            // If type is correct but guild specialization isn't used
            LOGGER.warn("Guild-only slash command {} uses GlobalSlashEvent, consider using GuildSlashEvent to remove warnings related to guild stuff's nullability", Utils.formatMethodShort(method));
        }
    } else {
        if (!ReflectionUtils.hasFirstParameter(method, GlobalSlashEvent.class))
            throw new IllegalArgumentException("Slash command at " + Utils.formatMethodShort(method) + " must have a GlobalSlashEvent as first parameter");
    }
    final SlashCommandInfo info = new SlashCommandInfo(context, applicationCommand, method);
    LOGGER.debug("Adding slash command path {} for method {}", info.getPath(), Utils.formatMethodShort(method));
    context.addSlashCommand(info);
}
Also used : SlashCommandInfo(com.freya02.botcommands.internal.application.slash.SlashCommandInfo) GuildSlashEvent(com.freya02.botcommands.api.application.slash.GuildSlashEvent) GlobalSlashEvent(com.freya02.botcommands.api.application.slash.GlobalSlashEvent) JDASlashCommand(com.freya02.botcommands.api.application.slash.annotations.JDASlashCommand)

Aggregations

SlashCommandInfo (com.freya02.botcommands.internal.application.slash.SlashCommandInfo)6 CommandPath (com.freya02.botcommands.api.application.CommandPath)4 ApplicationOptionData (com.freya02.botcommands.internal.ApplicationOptionData)3 Command (net.dv8tion.jda.api.interactions.commands.Command)3 MessageCommandInfo (com.freya02.botcommands.internal.application.context.message.MessageCommandInfo)2 UserCommandInfo (com.freya02.botcommands.internal.application.context.user.UserCommandInfo)2 ArrayList (java.util.ArrayList)2 List (java.util.List)2 Logging (com.freya02.botcommands.api.Logging)1 SettingsProvider (com.freya02.botcommands.api.SettingsProvider)1 GlobalSlashEvent (com.freya02.botcommands.api.application.slash.GlobalSlashEvent)1 GuildSlashEvent (com.freya02.botcommands.api.application.slash.GuildSlashEvent)1 JDASlashCommand (com.freya02.botcommands.api.application.slash.annotations.JDASlashCommand)1 DebugBuilder (com.freya02.botcommands.api.builder.DebugBuilder)1 BContextImpl (com.freya02.botcommands.internal.BContextImpl)1 LocalizedOption (com.freya02.botcommands.internal.application.LocalizedCommandData.LocalizedOption)1 SlashCommandParameter (com.freya02.botcommands.internal.application.slash.SlashCommandParameter)1 SlashUtils.appendCommands (com.freya02.botcommands.internal.application.slash.SlashUtils.appendCommands)1 SlashUtils.getLocalizedMethodOptions (com.freya02.botcommands.internal.application.slash.SlashUtils.getLocalizedMethodOptions)1 AutocompleteCommandParameter (com.freya02.botcommands.internal.application.slash.autocomplete.AutocompleteCommandParameter)1