diff --git a/api/src/main/java/com/discordsrv/api/discord/entity/interaction/command/Command.java b/api/src/main/java/com/discordsrv/api/discord/entity/interaction/command/Command.java index e66f8007..46c95382 100644 --- a/api/src/main/java/com/discordsrv/api/discord/entity/interaction/command/Command.java +++ b/api/src/main/java/com/discordsrv/api/discord/entity/interaction/command/Command.java @@ -476,6 +476,11 @@ public class Command implements JDAEntity { */ REGISTERED, + /** + * Command is already registered, and was ignored. + */ + ALREADY_REGISTERED, + /** * There was already a command with the same name, * therefor the command won't be registered unless other commands with the same name are unregistered. diff --git a/api/src/main/java/com/discordsrv/api/discord/events/interaction/command/CommandRegisterEvent.java b/api/src/main/java/com/discordsrv/api/discord/events/interaction/command/CommandRegisterEvent.java new file mode 100644 index 00000000..e19b31aa --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/discord/events/interaction/command/CommandRegisterEvent.java @@ -0,0 +1,34 @@ +package com.discordsrv.api.discord.events.interaction.command; + +import com.discordsrv.api.discord.entity.interaction.command.Command; +import com.discordsrv.api.event.events.Event; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * An event for registering {@link com.discordsrv.api.discord.entity.interaction.command.Command}s, + * an alternative to {@link com.discordsrv.api.discord.DiscordAPI#registerCommand(Command)}. + */ +public class CommandRegisterEvent implements Event { + + private final List commands = new ArrayList<>(); + + /** + * Add events to be registered. + * @param commands the commands to be registered, use of the same command instances is recommended + */ + public void registerCommands(@NotNull Command... commands) { + this.commands.addAll(Arrays.asList(commands)); + } + + @NotNull + @Unmodifiable + public List getCommands() { + return Collections.unmodifiableList(commands); + } +} diff --git a/common/src/main/java/com/discordsrv/common/discord/api/DiscordCommandRegistry.java b/common/src/main/java/com/discordsrv/common/discord/api/DiscordCommandRegistry.java index b7738c64..d939407e 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/DiscordCommandRegistry.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/DiscordCommandRegistry.java @@ -3,6 +3,7 @@ package com.discordsrv.common.discord.api; import com.discordsrv.api.discord.entity.JDAEntity; import com.discordsrv.api.discord.entity.interaction.command.Command; import com.discordsrv.api.discord.entity.interaction.command.CommandType; +import com.discordsrv.api.discord.events.interaction.command.CommandRegisterEvent; import com.discordsrv.common.DiscordSRV; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Guild; @@ -13,6 +14,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Predicate; import java.util.stream.Collectors; public class DiscordCommandRegistry { @@ -26,13 +28,28 @@ public class DiscordCommandRegistry { this.discordSRV = discordSRV; } - public Command.RegistrationResult register(Command command) { + public void registerCommandsFromEvent() { + CommandRegisterEvent event = new CommandRegisterEvent(); + discordSRV.eventBus().publish(event); + + List commands = event.getCommands(); + for (Map registryMap : registries.values()) { + registryMap.values().forEach(registry -> registry.removeIf(reg -> reg.isTemporary() && !commands.contains(reg.getCommand()))); + } + + commands.forEach(cmd -> register(cmd, true)); + } + + public Command.RegistrationResult register(Command command, boolean temporary) { CommandType type = command.getType(); Registry registry = registries .computeIfAbsent(command.getGuildId().orElse(GLOBAL_ID), key -> new EnumMap<>(CommandType.class)) .computeIfAbsent(type, key -> new Registry()); + if (registry.contains(command)) { + return Command.RegistrationResult.ALREADY_REGISTERED; + } - boolean first = registry.register(command); + boolean first = registry.register(command, temporary); if (!first) { return Command.RegistrationResult.NAME_ALREADY_IN_USE; } @@ -124,10 +141,31 @@ public class DiscordCommandRegistry { private final Map> registry = new ConcurrentHashMap<>(); private final Map activeCommands = new HashMap<>(); - public boolean register(@NotNull Command command) { + public void removeIf(Predicate commandPredicate) { + List removeKeys = new ArrayList<>(); + for (Map.Entry> entry : registry.entrySet()) { + List registrations = entry.getValue(); + registrations.removeIf(commandPredicate); + if (registrations.isEmpty()) { + removeKeys.add(entry.getKey()); + } + } + removeKeys.forEach(registry::remove); + } + + public boolean contains(@NotNull Command command) { + List commands = registry.get(command.getName()); + if (commands == null) { + return false; + } + + return commands.stream().anyMatch(reg -> reg.getCommand() == command); + } + + public boolean register(@NotNull Command command, boolean temporary) { List commands = registry.computeIfAbsent(command.getName(), key -> new CopyOnWriteArrayList<>()); boolean empty = commands.isEmpty(); - commands.add(new Registration(command)); + commands.add(new Registration(command, temporary)); return empty; } @@ -175,10 +213,12 @@ public class DiscordCommandRegistry { private final Command command; private final long time; + private final boolean temporary; - public Registration(Command command) { + public Registration(Command command, boolean temporary) { this.command = command; this.time = System.currentTimeMillis(); + this.temporary = temporary; } public Command getCommand() { @@ -188,5 +228,9 @@ public class DiscordCommandRegistry { public long getTime() { return time; } + + public boolean isTemporary() { + return temporary; + } } }