mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-11-01 08:39:31 +01:00
Interaction events, add handling directly to Command
This commit is contained in:
parent
2f3c9946b5
commit
602c74ba6b
@ -27,6 +27,7 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.*;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordRole;
|
||||
import com.discordsrv.api.discord.entity.interaction.command.Command;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Optional;
|
||||
@ -116,4 +117,16 @@ public interface DiscordAPI {
|
||||
*/
|
||||
@NotNull
|
||||
Optional<DiscordRole> getRoleById(long id);
|
||||
|
||||
/**
|
||||
* Registers a Discord command.
|
||||
* @param command the command to register
|
||||
*/
|
||||
Command.RegistrationResult registerCommand(Command command);
|
||||
|
||||
/**
|
||||
* Unregisters a Discord command.
|
||||
* @param command the command to unregister
|
||||
*/
|
||||
void unregisterCommand(Command command);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ package com.discordsrv.api.discord.entity.interaction.command;
|
||||
|
||||
import com.discordsrv.api.discord.entity.JDAEntity;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import com.discordsrv.api.discord.events.interaction.command.*;
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.*;
|
||||
@ -33,22 +34,34 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A Discord command.
|
||||
*/
|
||||
public class Command implements JDAEntity<CommandData> {
|
||||
|
||||
private static final String CHAT_INPUT_NAME_REGEX = "(?U)[\\w-]{1,32}";
|
||||
public static final Pattern CHAT_INPUT_NAME_PATTERN = Pattern.compile(CHAT_INPUT_NAME_REGEX);
|
||||
|
||||
/**
|
||||
* Creates a chat input or slash command builder.
|
||||
*
|
||||
* @param id a unique identifier for this interaction, used to check if a given event was for this interaction
|
||||
* @param name the name of the command
|
||||
* @param name the name of the command, 1-32 characters alphanumeric and dashes
|
||||
* @param description the description of the command
|
||||
* @return a new chat input command builder
|
||||
* @see com.discordsrv.api.discord.events.interaction.command.DiscordChatInputInteractionEvent
|
||||
*/
|
||||
public static ChatInputBuilder chatInput(ComponentIdentifier id, String name, String description) {
|
||||
public static ChatInputBuilder chatInput(
|
||||
ComponentIdentifier id,
|
||||
@org.intellij.lang.annotations.Pattern(CHAT_INPUT_NAME_REGEX) String name,
|
||||
String description
|
||||
) {
|
||||
if (!CHAT_INPUT_NAME_PATTERN.matcher(name).matches()) {
|
||||
throw new IllegalArgumentException("Name must be alphanumeric (dashes allowed), 1 and 32 characters");
|
||||
}
|
||||
return new ChatInputBuilder(id, name, description);
|
||||
}
|
||||
|
||||
@ -56,46 +69,58 @@ public class Command implements JDAEntity<CommandData> {
|
||||
* Creates a new user context menu command.
|
||||
*
|
||||
* @param id a unique identifier for this interaction, used to check if a given event was for this interaction
|
||||
* @param name the name of the command
|
||||
* @param name the name of the command, 1-32 characters
|
||||
* @return a new command builder
|
||||
* @see com.discordsrv.api.discord.events.interaction.command.DiscordUserContextInteractionEvent
|
||||
*/
|
||||
public static Builder user(ComponentIdentifier id, String name) {
|
||||
return new Builder(id, Type.USER, name);
|
||||
public static Builder<DiscordUserContextInteractionEvent> user(
|
||||
ComponentIdentifier id,
|
||||
@org.intellij.lang.annotations.Pattern(".{1,32}") String name
|
||||
) {
|
||||
return new Builder<>(id, CommandType.USER, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new message context menu command.
|
||||
*
|
||||
* @param id a unique identifier for this interaction, used to check if a given event was for this interaction
|
||||
* @param name the name of the command
|
||||
* @param name the name of the command, 1-32 characters
|
||||
* @return a new command builder
|
||||
* @see com.discordsrv.api.discord.events.interaction.command.DiscordMessageContextInteractionEvent
|
||||
*/
|
||||
public static Builder message(ComponentIdentifier id, String name) {
|
||||
return new Builder(id, Type.MESSAGE, name);
|
||||
public static Builder<DiscordMessageContextInteractionEvent> message(
|
||||
ComponentIdentifier id,
|
||||
@org.intellij.lang.annotations.Pattern(".{1,32}") String name
|
||||
) {
|
||||
return new Builder<>(id, CommandType.MESSAGE, name);
|
||||
}
|
||||
|
||||
private final ComponentIdentifier id;
|
||||
private final Type type;
|
||||
private final CommandType type;
|
||||
private final Map<Locale, String> nameTranslations;
|
||||
private final Map<Locale, String> descriptionTranslations;
|
||||
private final List<SubCommandGroup> subCommandGroups;
|
||||
private final List<Command> subCommands;
|
||||
private final List<CommandOption> options;
|
||||
private final Long guildId;
|
||||
private final boolean guildOnly;
|
||||
private final DefaultPermission defaultPermission;
|
||||
private final Consumer<? extends AbstractCommandInteractionEvent<?>> eventHandler;
|
||||
private final Consumer<DiscordCommandAutoCompleteInteractionEvent> autoCompleteHandler;
|
||||
|
||||
private Command(
|
||||
ComponentIdentifier id,
|
||||
Type type,
|
||||
CommandType type,
|
||||
Map<Locale, String> nameTranslations,
|
||||
Map<Locale, String> descriptionTranslations,
|
||||
List<SubCommandGroup> subCommandGroups,
|
||||
List<Command> subCommands,
|
||||
List<CommandOption> options,
|
||||
Long guildId,
|
||||
boolean guildOnly,
|
||||
DefaultPermission defaultPermission
|
||||
DefaultPermission defaultPermission,
|
||||
Consumer<? extends AbstractCommandInteractionEvent<?>> eventHandler,
|
||||
Consumer<DiscordCommandAutoCompleteInteractionEvent> autoCompleteHandler
|
||||
) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
@ -104,8 +129,11 @@ public class Command implements JDAEntity<CommandData> {
|
||||
this.subCommandGroups = subCommandGroups;
|
||||
this.subCommands = subCommands;
|
||||
this.options = options;
|
||||
this.guildId = guildId;
|
||||
this.guildOnly = guildOnly;
|
||||
this.defaultPermission = defaultPermission;
|
||||
this.eventHandler = eventHandler;
|
||||
this.autoCompleteHandler = autoCompleteHandler;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -114,7 +142,12 @@ public class Command implements JDAEntity<CommandData> {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Type getType() {
|
||||
public Optional<Long> getGuildId() {
|
||||
return Optional.ofNullable(guildId);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CommandType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -161,6 +194,21 @@ public class Command implements JDAEntity<CommandData> {
|
||||
return defaultPermission;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends AbstractCommandInteractionEvent<?>> Optional<Consumer<T>> getEventHandler() {
|
||||
if (eventHandler == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of((Consumer<T>) eventHandler);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Optional<Consumer<DiscordCommandAutoCompleteInteractionEvent>> getAutoCompleteHandler() {
|
||||
return Optional.ofNullable(autoCompleteHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandData asJDA() {
|
||||
CommandData commandData;
|
||||
@ -194,15 +242,16 @@ public class Command implements JDAEntity<CommandData> {
|
||||
return data;
|
||||
}
|
||||
|
||||
public static class ChatInputBuilder extends Builder {
|
||||
public static class ChatInputBuilder extends Builder<DiscordChatInputInteractionEvent> {
|
||||
|
||||
private final Map<Locale, String> descriptionTranslations = new LinkedHashMap<>();
|
||||
private final List<SubCommandGroup> subCommandGroups = new ArrayList<>();
|
||||
private final List<Command> subCommands = new ArrayList<>();
|
||||
private final List<CommandOption> options = new ArrayList<>();
|
||||
private Consumer<DiscordCommandAutoCompleteInteractionEvent> autoCompleteHandler;
|
||||
|
||||
private ChatInputBuilder(ComponentIdentifier id, String name, String description) {
|
||||
super(id, Type.CHAT_INPUT, name);
|
||||
super(id, CommandType.CHAT_INPUT, name);
|
||||
this.descriptionTranslations.put(Locale.ROOT, description);
|
||||
}
|
||||
|
||||
@ -211,11 +260,11 @@ public class Command implements JDAEntity<CommandData> {
|
||||
* @param locale the language
|
||||
* @param translation the translation
|
||||
* @return this builder, useful for chaining
|
||||
* @throws IllegalStateException if this isn't a {@link Type#CHAT_INPUT} command
|
||||
* @throws IllegalStateException if this isn't a {@link CommandType#CHAT_INPUT} command
|
||||
*/
|
||||
@NotNull
|
||||
public ChatInputBuilder addDescriptionTranslation(@NotNull Locale locale, @NotNull String translation) {
|
||||
if (type != Type.CHAT_INPUT) {
|
||||
if (type != CommandType.CHAT_INPUT) {
|
||||
throw new IllegalStateException("Descriptions are only available for CHAT_INPUT commands");
|
||||
}
|
||||
this.descriptionTranslations.put(locale, translation);
|
||||
@ -258,6 +307,17 @@ public class Command implements JDAEntity<CommandData> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the auto complete handler for this command, this can be used instead of listening to the {@link DiscordCommandAutoCompleteInteractionEvent}.
|
||||
* @param autoCompleteHandler the auto complete handler, only receives events for this command
|
||||
* @return this builder, useful for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public ChatInputBuilder setAutoCompleteHandler(Consumer<DiscordCommandAutoCompleteInteractionEvent> autoCompleteHandler) {
|
||||
this.autoCompleteHandler = autoCompleteHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command build() {
|
||||
return new Command(
|
||||
@ -268,21 +328,26 @@ public class Command implements JDAEntity<CommandData> {
|
||||
subCommandGroups,
|
||||
subCommands,
|
||||
options,
|
||||
guildId,
|
||||
guildOnly,
|
||||
defaultPermission
|
||||
defaultPermission,
|
||||
eventHandler,
|
||||
autoCompleteHandler
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
public static class Builder<E extends AbstractCommandInteractionEvent<?>> {
|
||||
|
||||
protected final ComponentIdentifier id;
|
||||
protected final Type type;
|
||||
protected final CommandType type;
|
||||
protected final Map<Locale, String> nameTranslations = new LinkedHashMap<>();
|
||||
protected Long guildId = null;
|
||||
protected boolean guildOnly = true;
|
||||
protected DefaultPermission defaultPermission = DefaultPermission.EVERYONE;
|
||||
protected Consumer<? extends AbstractCommandInteractionEvent<?>> eventHandler;
|
||||
|
||||
private Builder(ComponentIdentifier id, Type type, String name) {
|
||||
private Builder(ComponentIdentifier id, CommandType type, String name) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.nameTranslations.put(Locale.ROOT, name);
|
||||
@ -295,18 +360,28 @@ public class Command implements JDAEntity<CommandData> {
|
||||
* @return this builder, useful for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public Builder addNameTranslation(@NotNull Locale locale, @NotNull String translation) {
|
||||
public Builder<E> addNameTranslation(@NotNull Locale locale, @NotNull String translation) {
|
||||
this.nameTranslations.put(locale, translation);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id of the guild this command should be registered to, or {@code null} to register the command globally.
|
||||
* @param guildId the guild id
|
||||
* @return this builder, useful for chaining
|
||||
*/
|
||||
public Builder<E> setGuildId(Long guildId) {
|
||||
this.guildId = guildId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets if this command is limited to Discord servers.
|
||||
* @param guildOnly if this command is limited to Discord servers
|
||||
* @return this builder, useful for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public Builder setGuildOnly(boolean guildOnly) {
|
||||
public Builder<E> setGuildOnly(boolean guildOnly) {
|
||||
this.guildOnly = guildOnly;
|
||||
return this;
|
||||
}
|
||||
@ -314,13 +389,25 @@ public class Command implements JDAEntity<CommandData> {
|
||||
/**
|
||||
* Sets the permission level required to use the command by default.
|
||||
* @param defaultPermission the permission level
|
||||
* @return this builder, useful for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public Builder setDefaultPermission(@NotNull DefaultPermission defaultPermission) {
|
||||
public Builder<E> setDefaultPermission(@NotNull DefaultPermission defaultPermission) {
|
||||
this.defaultPermission = defaultPermission;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the event handler for this command, this can be used instead of listening to the specific interaction event.
|
||||
* @param eventHandler the event handler, only receives events for this command
|
||||
* @return this builder, useful for chaining
|
||||
*/
|
||||
@NotNull
|
||||
public Builder<E> setEventHandler(Consumer<E> eventHandler) {
|
||||
this.eventHandler = eventHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Command build() {
|
||||
return new Command(
|
||||
id,
|
||||
@ -330,8 +417,11 @@ public class Command implements JDAEntity<CommandData> {
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList(),
|
||||
Collections.emptyList(),
|
||||
guildId,
|
||||
guildOnly,
|
||||
defaultPermission
|
||||
defaultPermission,
|
||||
eventHandler,
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -379,21 +469,24 @@ public class Command implements JDAEntity<CommandData> {
|
||||
}
|
||||
}
|
||||
|
||||
public enum Type implements JDAEntity<net.dv8tion.jda.api.interactions.commands.Command.Type> {
|
||||
public enum RegistrationResult {
|
||||
|
||||
CHAT_INPUT(net.dv8tion.jda.api.interactions.commands.Command.Type.SLASH),
|
||||
USER(net.dv8tion.jda.api.interactions.commands.Command.Type.USER),
|
||||
MESSAGE(net.dv8tion.jda.api.interactions.commands.Command.Type.MESSAGE);
|
||||
/**
|
||||
* Indicates that the command was successfully added to the registry, and will be registered.
|
||||
*/
|
||||
REGISTERED,
|
||||
|
||||
private final net.dv8tion.jda.api.interactions.commands.Command.Type jda;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
NAME_ALREADY_IN_USE,
|
||||
|
||||
Type(net.dv8tion.jda.api.interactions.commands.Command.Type jda) {
|
||||
this.jda = jda;
|
||||
}
|
||||
/**
|
||||
* There are too many commands of the same type,
|
||||
* therefore the command won't be registered unless other commands of the same type are unregistered.
|
||||
*/
|
||||
TOO_MANY_COMMANDS
|
||||
|
||||
@Override
|
||||
public net.dv8tion.jda.api.interactions.commands.Command.Type asJDA() {
|
||||
return jda;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package com.discordsrv.api.discord.entity.interaction.command;
|
||||
|
||||
import com.discordsrv.api.discord.entity.JDAEntity;
|
||||
import net.dv8tion.jda.api.interactions.commands.Command;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
|
||||
public enum CommandType implements JDAEntity<Command.Type> {
|
||||
|
||||
CHAT_INPUT(Command.Type.SLASH, Commands.MAX_SLASH_COMMANDS),
|
||||
USER(Command.Type.USER, Commands.MAX_USER_COMMANDS),
|
||||
MESSAGE(Command.Type.MESSAGE, Commands.MAX_MESSAGE_COMMANDS);
|
||||
|
||||
private final Command.Type jda;
|
||||
private final int maximumCount;
|
||||
|
||||
CommandType(Command.Type jda, int maximumCount) {
|
||||
this.jda = jda;
|
||||
this.maximumCount = maximumCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.Type asJDA() {
|
||||
return jda;
|
||||
}
|
||||
|
||||
public int getMaximumCount() {
|
||||
return maximumCount;
|
||||
}
|
||||
}
|
@ -23,20 +23,23 @@
|
||||
|
||||
package com.discordsrv.api.discord.entity.interaction.component;
|
||||
|
||||
import org.intellij.lang.annotations.Pattern;
|
||||
import org.intellij.lang.annotations.Subst;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* An identifier for commands and components to match up with interaction events, and to avoid conflicts between extensions.
|
||||
*/
|
||||
public class ComponentIdentifier {
|
||||
|
||||
public static final String ID_PREFIX = "DiscordSRV/";
|
||||
private static final String ID_PREFIX = "DiscordSRV/";
|
||||
private static final char PART_SEPARATOR = ':';
|
||||
|
||||
private static final String REGEX = "[\\w\\d-_]{1,40}";
|
||||
private static final java.util.regex.Pattern PATTERN = java.util.regex.Pattern.compile(REGEX);
|
||||
private static final Pattern PATTERN = java.util.regex.Pattern.compile(REGEX);
|
||||
|
||||
/**
|
||||
* Creates a new {@link ComponentIdentifier}.
|
||||
@ -48,8 +51,8 @@ public class ComponentIdentifier {
|
||||
*/
|
||||
@NotNull
|
||||
public static ComponentIdentifier of(
|
||||
@NotNull @Pattern(REGEX) String extensionName,
|
||||
@NotNull @Pattern(REGEX) String identifier
|
||||
@NotNull @org.intellij.lang.annotations.Pattern(REGEX) String extensionName,
|
||||
@NotNull @org.intellij.lang.annotations.Pattern(REGEX) String identifier
|
||||
) {
|
||||
if (!PATTERN.matcher(extensionName).matches()) {
|
||||
throw new IllegalArgumentException("Extension name does not match the required pattern");
|
||||
@ -59,6 +62,26 @@ public class ComponentIdentifier {
|
||||
return new ComponentIdentifier(extensionName, identifier);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Optional<ComponentIdentifier> parseFromDiscord(@NotNull String discordIdentifier) {
|
||||
if (!discordIdentifier.startsWith(ID_PREFIX)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
discordIdentifier = discordIdentifier.substring(ID_PREFIX.length());
|
||||
|
||||
@Subst("Example:Test")
|
||||
String[] parts = discordIdentifier.split(Pattern.quote(ID_PREFIX));
|
||||
if (parts.length != 2) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
return Optional.of(of(parts[0], parts[1]));
|
||||
} catch (IllegalStateException ignored) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private final String extensionName;
|
||||
private final String identifier;
|
||||
|
||||
@ -76,7 +99,7 @@ public class ComponentIdentifier {
|
||||
}
|
||||
|
||||
public String getDiscordIdentifier() {
|
||||
return ID_PREFIX + getExtensionName() + ":" + getIdentifier();
|
||||
return ID_PREFIX + getExtensionName() + PART_SEPARATOR + getIdentifier();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,6 +27,7 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import net.dv8tion.jda.api.events.interaction.GenericInteractionCreateEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -36,12 +37,13 @@ public abstract class AbstractDeferrableInteractionEvent<T extends GenericIntera
|
||||
|
||||
public AbstractDeferrableInteractionEvent(
|
||||
T jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook hook
|
||||
) {
|
||||
super(jdaEvent, user, member, channel);
|
||||
super(jdaEvent, identifier, user, member, channel);
|
||||
this.hook = hook;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import com.discordsrv.api.discord.events.AbstractDiscordEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.GenericInteractionCreateEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -35,17 +36,29 @@ import java.util.Optional;
|
||||
|
||||
public abstract class AbstractInteractionEvent<T extends GenericInteractionCreateEvent> extends AbstractDiscordEvent<T> {
|
||||
|
||||
protected final ComponentIdentifier identifier;
|
||||
protected final DiscordUser user;
|
||||
protected final DiscordGuildMember member;
|
||||
protected final DiscordMessageChannel channel;
|
||||
|
||||
public AbstractInteractionEvent(T jdaEvent, DiscordUser user, DiscordGuildMember member, DiscordMessageChannel channel) {
|
||||
public AbstractInteractionEvent(
|
||||
T jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel
|
||||
) {
|
||||
super(jdaEvent);
|
||||
this.identifier = identifier;
|
||||
this.user = user;
|
||||
this.member = member;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public boolean isFor(ComponentIdentifier identifier) {
|
||||
return this.identifier.equals(identifier);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DiscordUser getUser() {
|
||||
return user;
|
||||
|
@ -27,17 +27,19 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
||||
|
||||
public class DiscordModalInteractionEvent extends AbstractDeferrableInteractionEvent<ModalInteractionEvent> {
|
||||
|
||||
public DiscordModalInteractionEvent(
|
||||
ModalInteractionEvent jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook hook
|
||||
) {
|
||||
super(jdaEvent, user, member, channel, hook);
|
||||
super(jdaEvent, identifier, user, member, channel, hook);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.discordsrv.api.discord.events.interaction.command;
|
||||
|
||||
import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import com.discordsrv.api.discord.events.interaction.AbstractDeferrableInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent;
|
||||
|
||||
public class AbstractCommandInteractionEvent<E extends GenericCommandInteractionEvent>
|
||||
extends AbstractDeferrableInteractionEvent<E> {
|
||||
|
||||
public AbstractCommandInteractionEvent(
|
||||
E jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook interaction
|
||||
) {
|
||||
super(jdaEvent, identifier, user, member, channel, interaction);
|
||||
}
|
||||
}
|
@ -27,18 +27,19 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.events.interaction.AbstractDeferrableInteractionEvent;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
|
||||
public class DiscordChatInputInteractionEvent extends AbstractDeferrableInteractionEvent<SlashCommandInteractionEvent> {
|
||||
public class DiscordChatInputInteractionEvent extends AbstractCommandInteractionEvent<SlashCommandInteractionEvent> {
|
||||
|
||||
public DiscordChatInputInteractionEvent(
|
||||
SlashCommandInteractionEvent jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook interaction
|
||||
) {
|
||||
super(jdaEvent, user, member, channel, interaction);
|
||||
super(jdaEvent, identifier, user, member, channel, interaction);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ package com.discordsrv.api.discord.events.interaction.command;
|
||||
import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import com.discordsrv.api.discord.events.interaction.AbstractInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
|
||||
|
||||
@ -38,11 +39,12 @@ public class DiscordCommandAutoCompleteInteractionEvent extends AbstractInteract
|
||||
|
||||
public DiscordCommandAutoCompleteInteractionEvent(
|
||||
CommandAutoCompleteInteractionEvent jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel
|
||||
) {
|
||||
super(jdaEvent, user, member, channel);
|
||||
super(jdaEvent, identifier, user, member, channel);
|
||||
}
|
||||
|
||||
public void addChoice(String key, String value) {
|
||||
|
@ -27,19 +27,20 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.events.interaction.AbstractDeferrableInteractionEvent;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
|
||||
|
||||
public class DiscordMessageContextInteractionEvent extends
|
||||
AbstractDeferrableInteractionEvent<MessageContextInteractionEvent> {
|
||||
public class DiscordMessageContextInteractionEvent
|
||||
extends AbstractCommandInteractionEvent<MessageContextInteractionEvent> {
|
||||
|
||||
public DiscordMessageContextInteractionEvent(
|
||||
MessageContextInteractionEvent jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook interaction
|
||||
) {
|
||||
super(jdaEvent, user, member, channel, interaction);
|
||||
super(jdaEvent, identifier, user, member, channel, interaction);
|
||||
}
|
||||
}
|
||||
|
@ -27,18 +27,19 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.events.interaction.AbstractDeferrableInteractionEvent;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent;
|
||||
|
||||
public class DiscordUserContextInteractionEvent extends AbstractDeferrableInteractionEvent<UserContextInteractionEvent> {
|
||||
public class DiscordUserContextInteractionEvent extends AbstractCommandInteractionEvent<UserContextInteractionEvent> {
|
||||
|
||||
public DiscordUserContextInteractionEvent(
|
||||
UserContextInteractionEvent jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook interaction
|
||||
) {
|
||||
super(jdaEvent, user, member, channel, interaction);
|
||||
super(jdaEvent, identifier, user, member, channel, interaction);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import com.discordsrv.api.discord.events.interaction.AbstractDeferrableInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
|
||||
@ -34,11 +35,12 @@ public class DiscordButtonInteractionEvent extends AbstractDeferrableInteraction
|
||||
|
||||
public DiscordButtonInteractionEvent(
|
||||
ButtonInteractionEvent jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook interaction
|
||||
) {
|
||||
super(jdaEvent, user, member, channel, interaction);
|
||||
super(jdaEvent, identifier, user, member, channel, interaction);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import com.discordsrv.api.discord.events.interaction.AbstractDeferrableInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent;
|
||||
|
||||
@ -34,11 +35,12 @@ public class DiscordSelectMenuInteractionEvent extends AbstractDeferrableInterac
|
||||
|
||||
public DiscordSelectMenuInteractionEvent(
|
||||
SelectMenuInteractionEvent jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook interaction
|
||||
) {
|
||||
super(jdaEvent, user, member, channel, interaction);
|
||||
super(jdaEvent, identifier, user, member, channel, interaction);
|
||||
}
|
||||
}
|
||||
|
@ -22,11 +22,10 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.entity.interaction.command.CommandType;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import com.discordsrv.api.discord.events.interaction.DiscordModalInteractionEvent;
|
||||
import com.discordsrv.api.discord.events.interaction.command.DiscordChatInputInteractionEvent;
|
||||
import com.discordsrv.api.discord.events.interaction.command.DiscordCommandAutoCompleteInteractionEvent;
|
||||
import com.discordsrv.api.discord.events.interaction.command.DiscordMessageContextInteractionEvent;
|
||||
import com.discordsrv.api.discord.events.interaction.command.DiscordUserContextInteractionEvent;
|
||||
import com.discordsrv.api.discord.events.interaction.command.*;
|
||||
import com.discordsrv.api.discord.events.interaction.component.DiscordButtonInteractionEvent;
|
||||
import com.discordsrv.api.discord.events.interaction.component.DiscordSelectMenuInteractionEvent;
|
||||
import com.discordsrv.api.discord.events.member.role.DiscordMemberRoleAddEvent;
|
||||
@ -40,16 +39,15 @@ import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.discord.api.entity.component.DiscordInteractionHookImpl;
|
||||
import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageImpl;
|
||||
import com.discordsrv.common.module.type.AbstractModule;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleAddEvent;
|
||||
import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleRemoveEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.GenericInteractionCreateEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.command.*;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.GenericComponentInteractionCreateEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.SelectMenuInteractionEvent;
|
||||
import net.dv8tion.jda.api.events.message.MessageDeleteEvent;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
@ -130,9 +128,20 @@ public class DiscordAPIEventModule extends AbstractModule<DiscordSRV> {
|
||||
DiscordGuildMember guildMember = member != null ? api().getGuildMember(member) : null;
|
||||
DiscordMessageChannel channel = api().getMessageChannel(event.getMessageChannel());
|
||||
if (event instanceof CommandAutoCompleteInteractionEvent) {
|
||||
com.discordsrv.api.discord.entity.interaction.command.Command command = discordSRV.discordAPI().getActiveCommand(
|
||||
((CommandAutoCompleteInteractionEvent) event).isGuildCommand() ? event.getGuild() : null,
|
||||
CommandType.CHAT_INPUT,
|
||||
((CommandAutoCompleteInteractionEvent) event).getName()
|
||||
).orElse(null);
|
||||
if (command == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DiscordCommandAutoCompleteInteractionEvent autoComplete = new DiscordCommandAutoCompleteInteractionEvent(
|
||||
(CommandAutoCompleteInteractionEvent) event, user, guildMember, channel);
|
||||
(CommandAutoCompleteInteractionEvent) event, command.getId(), user, guildMember, channel);
|
||||
discordSRV.eventBus().publish(autoComplete);
|
||||
command.getAutoCompleteHandler().ifPresent(handler -> handler.accept(autoComplete));
|
||||
|
||||
List<Command.Choice> choices = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> entry : autoComplete.getChoices().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
@ -150,22 +159,90 @@ public class DiscordAPIEventModule extends AbstractModule<DiscordSRV> {
|
||||
}
|
||||
|
||||
DiscordInteractionHook hook = new DiscordInteractionHookImpl(discordSRV, ((IDeferrableCallback) event).getHook());
|
||||
Event newEvent;
|
||||
if (event instanceof MessageContextInteractionEvent) {
|
||||
newEvent = new DiscordMessageContextInteractionEvent((MessageContextInteractionEvent) event, user, guildMember, channel, hook);
|
||||
} else if (event instanceof UserContextInteractionEvent) {
|
||||
newEvent = new DiscordUserContextInteractionEvent((UserContextInteractionEvent) event, user, guildMember, channel, hook);
|
||||
} else if (event instanceof SlashCommandInteractionEvent) {
|
||||
newEvent = new DiscordChatInputInteractionEvent((SlashCommandInteractionEvent) event, user, guildMember, channel, hook);
|
||||
} else if (event instanceof ButtonInteractionEvent) {
|
||||
newEvent = new DiscordButtonInteractionEvent((ButtonInteractionEvent) event, user, guildMember, channel, hook);
|
||||
} else if (event instanceof SelectMenuInteractionEvent) {
|
||||
newEvent = new DiscordSelectMenuInteractionEvent((SelectMenuInteractionEvent) event, user, guildMember, channel, hook);
|
||||
Event newEvent = null;
|
||||
if (event instanceof GenericCommandInteractionEvent) {
|
||||
Guild guild = ((GenericCommandInteractionEvent) event).isGuildCommand() ? event.getGuild() : null;
|
||||
String name = ((GenericCommandInteractionEvent) event).getName();
|
||||
if (event instanceof MessageContextInteractionEvent) {
|
||||
com.discordsrv.api.discord.entity.interaction.command.Command command = discordSRV.discordAPI()
|
||||
.getActiveCommand(guild, CommandType.CHAT_INPUT, name).orElse(null);
|
||||
if (command == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DiscordMessageContextInteractionEvent interactionEvent = new DiscordMessageContextInteractionEvent(
|
||||
(MessageContextInteractionEvent) event,
|
||||
command.getId(),
|
||||
user,
|
||||
guildMember,
|
||||
channel,
|
||||
hook
|
||||
);
|
||||
|
||||
newEvent = interactionEvent;
|
||||
command.getEventHandler().ifPresent(handler -> handler.accept(interactionEvent));
|
||||
} else if (event instanceof UserContextInteractionEvent) {
|
||||
com.discordsrv.api.discord.entity.interaction.command.Command command = discordSRV.discordAPI()
|
||||
.getActiveCommand(guild, CommandType.MESSAGE, name).orElse(null);
|
||||
if (command == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DiscordUserContextInteractionEvent interactionEvent = new DiscordUserContextInteractionEvent(
|
||||
(UserContextInteractionEvent) event,
|
||||
command.getId(),
|
||||
user,
|
||||
guildMember,
|
||||
channel,
|
||||
hook
|
||||
);
|
||||
|
||||
newEvent = interactionEvent;
|
||||
command.getEventHandler().ifPresent(handler -> handler.accept(interactionEvent));
|
||||
} else if (event instanceof SlashCommandInteractionEvent) {
|
||||
com.discordsrv.api.discord.entity.interaction.command.Command command = discordSRV.discordAPI()
|
||||
.getActiveCommand(guild, CommandType.USER, name).orElse(null);
|
||||
if (command == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
DiscordChatInputInteractionEvent interactionEvent = new DiscordChatInputInteractionEvent(
|
||||
(SlashCommandInteractionEvent) event,
|
||||
command.getId(),
|
||||
user,
|
||||
guildMember,
|
||||
channel,
|
||||
hook
|
||||
);
|
||||
|
||||
newEvent = interactionEvent;
|
||||
command.getEventHandler().ifPresent(handler -> handler.accept(interactionEvent));
|
||||
}
|
||||
} else if (event instanceof GenericComponentInteractionCreateEvent) {
|
||||
ComponentIdentifier identifier = ComponentIdentifier.parseFromDiscord(
|
||||
((GenericComponentInteractionCreateEvent) event).getComponentId()).orElse(null);
|
||||
if (identifier == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event instanceof ButtonInteractionEvent) {
|
||||
newEvent = new DiscordButtonInteractionEvent(
|
||||
(ButtonInteractionEvent) event, identifier, user, guildMember, channel, hook);
|
||||
} else if (event instanceof SelectMenuInteractionEvent) {
|
||||
newEvent = new DiscordSelectMenuInteractionEvent(
|
||||
(SelectMenuInteractionEvent) event, identifier, user, guildMember, channel, hook);
|
||||
}
|
||||
} else if (event instanceof ModalInteractionEvent) {
|
||||
newEvent = new DiscordModalInteractionEvent((ModalInteractionEvent) event, user, guildMember, channel, hook);
|
||||
} else {
|
||||
return;
|
||||
ComponentIdentifier identifier = ComponentIdentifier.parseFromDiscord(
|
||||
((ModalInteractionEvent) event).getModalId()).orElse(null);
|
||||
if (identifier == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
newEvent = new DiscordModalInteractionEvent((ModalInteractionEvent) event, identifier, user, guildMember, channel, hook);
|
||||
}
|
||||
if (newEvent != null) {
|
||||
discordSRV.eventBus().publish(newEvent);
|
||||
}
|
||||
discordSRV.eventBus().publish(newEvent);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.*;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordRole;
|
||||
import com.discordsrv.api.discord.entity.interaction.command.Command;
|
||||
import com.discordsrv.api.discord.entity.interaction.command.CommandType;
|
||||
import com.discordsrv.api.discord.exception.NotReadyException;
|
||||
import com.discordsrv.api.discord.exception.RestErrorResponseException;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
@ -43,7 +45,6 @@ import com.discordsrv.common.future.util.CompletableFutureUtil;
|
||||
import com.github.benmanes.caffeine.cache.AsyncCacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
|
||||
import com.github.benmanes.caffeine.cache.Expiry;
|
||||
import com.github.benmanes.caffeine.cache.RemovalListener;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.*;
|
||||
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
|
||||
@ -64,17 +65,14 @@ import java.util.function.Consumer;
|
||||
public class DiscordAPIImpl implements DiscordAPI {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final DiscordCommandRegistry commandRegistry;
|
||||
private final AsyncLoadingCache<Long, WebhookClient> cachedClients;
|
||||
private final List<ThreadChannelLookup> threadLookups = new CopyOnWriteArrayList<>();
|
||||
|
||||
public DiscordAPIImpl(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.commandRegistry = new DiscordCommandRegistry(discordSRV);
|
||||
this.cachedClients = discordSRV.caffeineBuilder()
|
||||
.removalListener((RemovalListener<Long, WebhookClient>) (id, client, cause) -> {
|
||||
if (client != null) {
|
||||
client.close();
|
||||
}
|
||||
})
|
||||
.expireAfter(new WebhookCacheExpiry())
|
||||
.buildAsync(new WebhookCacheLoader());
|
||||
}
|
||||
@ -301,9 +299,7 @@ public class DiscordAPIImpl implements DiscordAPI {
|
||||
try {
|
||||
return mapExceptions(futureSupplier.get());
|
||||
} catch (Throwable t) {
|
||||
CompletableFuture<T> future = new CompletableFuture<>();
|
||||
future.completeExceptionally(t);
|
||||
return future;
|
||||
return CompletableFutureUtil.failed(t);
|
||||
}
|
||||
}
|
||||
|
||||
@ -452,6 +448,20 @@ public class DiscordAPIImpl implements DiscordAPI {
|
||||
return new DiscordRoleImpl(discordSRV, jda);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command.RegistrationResult registerCommand(Command command) {
|
||||
return commandRegistry.register(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterCommand(Command command) {
|
||||
commandRegistry.unregister(command);
|
||||
}
|
||||
|
||||
public Optional<Command> getActiveCommand(@Nullable Guild guild, CommandType type, String name) {
|
||||
return Optional.ofNullable(commandRegistry.getActive(guild != null ? guild.getIdLong() : null, type, name));
|
||||
}
|
||||
|
||||
private class WebhookCacheLoader implements AsyncCacheLoader<Long, WebhookClient> {
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,192 @@
|
||||
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.common.DiscordSRV;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DiscordCommandRegistry {
|
||||
|
||||
private static final Long GLOBAL_ID = -1L;
|
||||
|
||||
private final Map<Long, Map<CommandType, Registry>> registries = new ConcurrentHashMap<>();
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public DiscordCommandRegistry(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
public Command.RegistrationResult register(Command command) {
|
||||
CommandType type = command.getType();
|
||||
Registry registry = registries
|
||||
.computeIfAbsent(command.getGuildId().orElse(GLOBAL_ID), key -> new EnumMap<>(CommandType.class))
|
||||
.computeIfAbsent(type, key -> new Registry());
|
||||
|
||||
boolean first = registry.register(command);
|
||||
if (!first) {
|
||||
return Command.RegistrationResult.NAME_ALREADY_IN_USE;
|
||||
}
|
||||
if (registry.getInTimeOrder().indexOf(command) >= type.getMaximumCount()) {
|
||||
return Command.RegistrationResult.TOO_MANY_COMMANDS;
|
||||
}
|
||||
return Command.RegistrationResult.REGISTERED;
|
||||
}
|
||||
|
||||
public void unregister(Command command) {
|
||||
Registry registry = registries
|
||||
.computeIfAbsent(command.getGuildId().orElse(GLOBAL_ID), key -> Collections.emptyMap())
|
||||
.get(command.getType());
|
||||
|
||||
if (registry != null) {
|
||||
registry.unregister(command);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Command getActive(Long guildId, CommandType type, String name) {
|
||||
return registries
|
||||
.computeIfAbsent(guildId != null ? guildId : GLOBAL_ID, key -> Collections.emptyMap())
|
||||
.get(type).getActive(name);
|
||||
}
|
||||
|
||||
public void registerCommandsToDiscord() {
|
||||
JDA jda = discordSRV.jda().orElse(null);
|
||||
if (jda == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Long> ids = new ArrayList<>();
|
||||
ids.add(GLOBAL_ID);
|
||||
for (Guild guild : jda.getGuilds()) {
|
||||
ids.add(guild.getIdLong());
|
||||
}
|
||||
|
||||
for (long guildId : ids) {
|
||||
Map<CommandType, Registry> commandsByType = registries.getOrDefault(guildId, Collections.emptyMap());
|
||||
Map<CommandType, Set<Command>> commandsToRegister = new EnumMap<>(CommandType.class);
|
||||
|
||||
boolean updateNeeded = false;
|
||||
for (Map.Entry<CommandType, Registry> entry : commandsByType.entrySet()) {
|
||||
Registry registry = entry.getValue();
|
||||
|
||||
List<Command> commands = registry.getInTimeOrder();
|
||||
Set<Command> currentCommands = new LinkedHashSet<>();
|
||||
int max = Math.min(commands.size(), entry.getKey().getMaximumCount());
|
||||
for (int i = 0; i < max; i++) {
|
||||
Command command = commands.get(i);
|
||||
currentCommands.add(command);
|
||||
}
|
||||
|
||||
commandsToRegister.put(entry.getKey(), currentCommands);
|
||||
|
||||
Collection<Command> activeCommands = registry.activeCommands.values();
|
||||
if (activeCommands.size() != currentCommands.size() || !currentCommands.containsAll(activeCommands)) {
|
||||
updateNeeded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updateNeeded) {
|
||||
CommandListUpdateAction action;
|
||||
if (Objects.equals(guildId, GLOBAL_ID)) {
|
||||
action = jda.updateCommands();
|
||||
} else {
|
||||
Guild guild = jda.getGuildById(guildId);
|
||||
if (guild == null) {
|
||||
continue;
|
||||
}
|
||||
action = guild.updateCommands();
|
||||
}
|
||||
|
||||
List<Command> allCommands = new ArrayList<>();
|
||||
commandsToRegister.values().forEach(allCommands::addAll);
|
||||
action.addCommands(allCommands.stream().map(JDAEntity::asJDA).collect(Collectors.toList()))
|
||||
.queue(v -> {
|
||||
for (CommandType value : CommandType.values()) {
|
||||
commandsByType.get(value).putActiveCommands(commandsToRegister.get(value));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Registry {
|
||||
|
||||
private final Map<String, List<Registration>> registry = new ConcurrentHashMap<>();
|
||||
private final Map<String, Command> activeCommands = new HashMap<>();
|
||||
|
||||
public boolean register(@NotNull Command command) {
|
||||
List<Registration> commands = registry.computeIfAbsent(command.getName(), key -> new CopyOnWriteArrayList<>());
|
||||
boolean empty = commands.isEmpty();
|
||||
commands.add(new Registration(command));
|
||||
return empty;
|
||||
}
|
||||
|
||||
public void unregister(@NotNull Command command) {
|
||||
List<Registration> commands = registry.get(command.getName());
|
||||
if (commands == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
commands.removeIf(reg -> reg.command == command);
|
||||
if (commands.isEmpty()) {
|
||||
registry.remove(command.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void putActiveCommands(Set<Command> commands) {
|
||||
synchronized (activeCommands) {
|
||||
activeCommands.clear();
|
||||
for (Command command : commands) {
|
||||
activeCommands.put(command.getName(), command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Command> getInTimeOrder() {
|
||||
List<Registration> registrations = registry.values().stream()
|
||||
.map(list -> list.get(0))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return registrations.stream()
|
||||
.sorted(Comparator.comparingLong(Registration::getTime))
|
||||
.map(Registration::getCommand)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Command getActive(String name) {
|
||||
synchronized (activeCommands) {
|
||||
return activeCommands.get(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Registration {
|
||||
|
||||
private final Command command;
|
||||
private final long time;
|
||||
|
||||
public Registration(Command command) {
|
||||
this.command = command;
|
||||
this.time = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public Command getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user