mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-11-22 11:55:54 +01:00
Add console command execution and user/role limiting to custom commands
This commit is contained in:
parent
227138fc94
commit
791abc7888
@ -33,8 +33,7 @@ import net.dv8tion.jda.api.events.interaction.GenericInteractionCreateEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class AbstractInteractionEvent<T extends GenericInteractionCreateEvent> extends
|
||||
AbstractDiscordEvent<T> {
|
||||
public abstract class AbstractInteractionEvent<T extends GenericInteractionCreateEvent> extends AbstractDiscordEvent<T> {
|
||||
|
||||
protected final ComponentIdentifier identifier;
|
||||
protected final DiscordUser user;
|
||||
|
@ -31,11 +31,11 @@ import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifi
|
||||
import net.dv8tion.jda.api.events.interaction.GenericInteractionCreateEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class AbstractDeferrableInteractionEvent<T extends GenericInteractionCreateEvent> extends AbstractInteractionEvent<T> {
|
||||
public class AbstractInteractionWithHookEvent<T extends GenericInteractionCreateEvent> extends AbstractInteractionEvent<T> {
|
||||
|
||||
protected final DiscordInteractionHook hook;
|
||||
|
||||
public AbstractDeferrableInteractionEvent(
|
||||
public AbstractInteractionWithHookEvent(
|
||||
T jdaEvent,
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
@ -30,7 +30,7 @@ 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 class DiscordModalInteractionEvent extends AbstractInteractionWithHookEvent<ModalInteractionEvent> {
|
||||
|
||||
public DiscordModalInteractionEvent(
|
||||
ModalInteractionEvent jdaEvent,
|
||||
|
@ -32,7 +32,7 @@ import com.discordsrv.api.discord.entity.guild.DiscordRole;
|
||||
import com.discordsrv.api.discord.entity.interaction.DiscordInteractionHook;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.events.discord.interaction.AbstractDeferrableInteractionEvent;
|
||||
import com.discordsrv.api.events.discord.interaction.AbstractInteractionWithHookEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.command.GenericCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -40,7 +40,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public abstract class AbstractCommandInteractionEvent<E extends GenericCommandInteractionEvent>
|
||||
extends AbstractDeferrableInteractionEvent<E> {
|
||||
extends AbstractInteractionWithHookEvent<E> {
|
||||
|
||||
private final DiscordSRVApi discordSRV;
|
||||
|
||||
@ -63,6 +63,8 @@ public abstract class AbstractCommandInteractionEvent<E extends GenericCommandIn
|
||||
return reply(message, false);
|
||||
}
|
||||
|
||||
public abstract CompletableFuture<DiscordInteractionHook> deferReply(boolean ephemeral);
|
||||
|
||||
@Nullable
|
||||
public String getOptionAsString(String name) {
|
||||
OptionMapping mapping = jdaEvent.getOption(name);
|
||||
|
@ -28,10 +28,10 @@ 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.events.discord.interaction.AbstractDeferrableInteractionEvent;
|
||||
import com.discordsrv.api.events.discord.interaction.AbstractInteractionWithHookEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||
|
||||
public class DiscordButtonInteractionEvent extends AbstractDeferrableInteractionEvent<ButtonInteractionEvent> {
|
||||
public class DiscordButtonInteractionEvent extends AbstractInteractionWithHookEvent<ButtonInteractionEvent> {
|
||||
|
||||
public DiscordButtonInteractionEvent(
|
||||
ButtonInteractionEvent jdaEvent,
|
||||
@ -39,8 +39,8 @@ public class DiscordButtonInteractionEvent extends AbstractDeferrableInteraction
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook interaction
|
||||
DiscordInteractionHook hook
|
||||
) {
|
||||
super(jdaEvent, identifier, user, member, channel, interaction);
|
||||
super(jdaEvent, identifier, user, member, channel, hook);
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,10 @@ 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.events.discord.interaction.AbstractDeferrableInteractionEvent;
|
||||
import com.discordsrv.api.events.discord.interaction.AbstractInteractionWithHookEvent;
|
||||
import net.dv8tion.jda.api.events.interaction.component.GenericSelectMenuInteractionEvent;
|
||||
|
||||
public class DiscordSelectMenuInteractionEvent extends AbstractDeferrableInteractionEvent<GenericSelectMenuInteractionEvent<?, ?>> {
|
||||
public class DiscordSelectMenuInteractionEvent extends AbstractInteractionWithHookEvent<GenericSelectMenuInteractionEvent<?, ?>> {
|
||||
|
||||
public DiscordSelectMenuInteractionEvent(
|
||||
GenericSelectMenuInteractionEvent<?, ?> jdaEvent,
|
||||
|
@ -91,7 +91,7 @@ public class ExecuteCommand implements Consumer<DiscordChatInputInteractionEvent
|
||||
DiscordCommandConfig.ExecuteConfig config = discordSRV.config().discordCommand.execute;
|
||||
boolean ephemeral = config.ephemeral;
|
||||
if (!config.enabled) {
|
||||
event.reply(SendableDiscordMessage.builder().setContent("The execute command is disabled").build(), ephemeral);
|
||||
event.reply(SendableDiscordMessage.builder().setContent("The execute command is disabled").build(), true); // TODO: translation
|
||||
return;
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ public class ExecuteCommand implements Consumer<DiscordChatInputInteractionEvent
|
||||
}
|
||||
|
||||
if (isNotAcceptableCommand(event.getMember(), event.getUser(), command, false)) {
|
||||
event.reply(SendableDiscordMessage.builder().setContent("You do not have permission to run that command").build(), ephemeral);
|
||||
event.reply(SendableDiscordMessage.builder().setContent("You do not have permission to run that command").build(), true); // TODO: translation
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,12 @@ package com.discordsrv.common.config.main;
|
||||
import com.discordsrv.api.discord.entity.interaction.command.CommandOption;
|
||||
import com.discordsrv.api.discord.entity.message.DiscordMessageEmbed;
|
||||
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.common.config.configurate.annotation.Constants;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Comment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@ConfigSerializable
|
||||
@ -48,21 +51,52 @@ public class CustomCommandConfig {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Comment("The command in Discord, this can be in up-to 3 parts (seperated by spaces).\n"
|
||||
+ "You cannot specify commands on the 2nd and 3rd layer for the same main command at once.\n"
|
||||
+ "You cannot specify a action for the main command if you specify something for the same main command on the 2nd or 3rd layer")
|
||||
public String command = "";
|
||||
|
||||
@Comment("The description of the command, will be shown to the user")
|
||||
public String description = "";
|
||||
|
||||
@Comment("If the command output should only be visible to the user who ran the command")
|
||||
public boolean ephemeral = false;
|
||||
|
||||
public List<OptionConfig> options = new ArrayList<>();
|
||||
|
||||
@Comment("Only one of the constraints has to be true to allow execution")
|
||||
public List<ConstraintConfig> constraints = new ArrayList<>(Collections.singletonList(new ConstraintConfig()));
|
||||
|
||||
@Comment("A list of console commands to run upon this commands execution")
|
||||
public List<String> consoleCommandsToRun = new ArrayList<>();
|
||||
|
||||
public SendableDiscordMessage.Builder response = SendableDiscordMessage.builder().setContent("test");
|
||||
|
||||
@ConfigSerializable
|
||||
public static class OptionConfig {
|
||||
|
||||
@Comment("Acceptable options are: %1")
|
||||
@Constants.Comment("STRING, LONG, DOUBLE, BOOLEAN, USER, CHANNEL, ROLE, MENTIONABLE, ATTACHMENT")
|
||||
public CommandOption.Type type = CommandOption.Type.USER;
|
||||
|
||||
@Comment("The name of this option, will be shown to the user")
|
||||
public String name = "target_user";
|
||||
|
||||
@Comment("The description of this option, will be shown to the user")
|
||||
public String description = "The user to greet";
|
||||
|
||||
@Comment("If this option is required to run the command")
|
||||
public boolean required = true;
|
||||
|
||||
}
|
||||
|
||||
@ConfigSerializable
|
||||
public static class ConstraintConfig {
|
||||
|
||||
@Comment("The role and user ids that should/should not be allowed to run this custom command")
|
||||
public List<Long> roleAndUserIds = new ArrayList<>();
|
||||
|
||||
@Comment("true for blacklisting the specified roles and users, false for whitelisting")
|
||||
public boolean blacklist = true;
|
||||
}
|
||||
}
|
||||
|
@ -54,4 +54,12 @@ public class DiscordChatInputInteractionEventImpl extends DiscordChatInputIntera
|
||||
.thenApply(ih -> new DiscordInteractionHookImpl(discordSRV, ih))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<DiscordInteractionHook> deferReply(boolean ephemeral) {
|
||||
return discordSRV.discordAPI().mapExceptions(
|
||||
() -> jdaEvent.deferReply(ephemeral).submit()
|
||||
.thenApply(ih -> new DiscordInteractionHookImpl(discordSRV, ih))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,9 @@ public class DiscordMessageContextInteractionEventImpl extends DiscordMessageCon
|
||||
ComponentIdentifier identifier,
|
||||
DiscordUser user,
|
||||
DiscordGuildMember member,
|
||||
DiscordMessageChannel channel, DiscordInteractionHook interaction) {
|
||||
DiscordMessageChannel channel,
|
||||
DiscordInteractionHook interaction
|
||||
) {
|
||||
super(discordSRV, jdaEvent, identifier, user, member, channel, interaction);
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
@ -54,4 +56,12 @@ public class DiscordMessageContextInteractionEventImpl extends DiscordMessageCon
|
||||
.thenApply(ih -> new DiscordInteractionHookImpl(discordSRV, ih))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<DiscordInteractionHook> deferReply(boolean ephemeral) {
|
||||
return discordSRV.discordAPI().mapExceptions(
|
||||
() -> jdaEvent.deferReply(ephemeral).submit()
|
||||
.thenApply(ih -> new DiscordInteractionHookImpl(discordSRV, ih))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -54,4 +54,12 @@ public class DiscordUserContextInteractionEventImpl extends DiscordUserContextIn
|
||||
.thenApply(ih -> new DiscordInteractionHookImpl(discordSRV, ih))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<DiscordInteractionHook> deferReply(boolean ephemeral) {
|
||||
return discordSRV.discordAPI().mapExceptions(
|
||||
() -> jdaEvent.deferReply(ephemeral).submit()
|
||||
.thenApply(ih -> new DiscordInteractionHookImpl(discordSRV, ih))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -21,11 +21,13 @@ package com.discordsrv.common.feature.customcommands;
|
||||
import com.discordsrv.api.DiscordSRVApi;
|
||||
import com.discordsrv.api.discord.entity.DiscordUser;
|
||||
import com.discordsrv.api.discord.entity.channel.DiscordChannel;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
|
||||
import com.discordsrv.api.discord.entity.guild.DiscordRole;
|
||||
import com.discordsrv.api.discord.entity.interaction.command.CommandOption;
|
||||
import com.discordsrv.api.discord.entity.interaction.command.DiscordCommand;
|
||||
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
|
||||
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.events.discord.interaction.command.AbstractCommandInteractionEvent;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.main.CustomCommandConfig;
|
||||
import com.discordsrv.common.core.logging.NamedLogger;
|
||||
@ -99,73 +101,8 @@ public class CustomCommandModule extends AbstractModule<DiscordSRV> {
|
||||
);
|
||||
}
|
||||
|
||||
commandBuilder.setEventHandler(event -> {
|
||||
SendableDiscordMessage.Formatter formatter = config.response.toFormatter();
|
||||
|
||||
for (CustomCommandConfig.OptionConfig option : config.options) {
|
||||
String optionName = option.name;
|
||||
|
||||
Object context;
|
||||
String reLookup = null;
|
||||
switch (option.type) {
|
||||
case CHANNEL:
|
||||
context = event.getOptionAsChannel(optionName);
|
||||
reLookup = "channel";
|
||||
break;
|
||||
case USER:
|
||||
context = event.getOptionAsUser(optionName);
|
||||
reLookup = "user";
|
||||
break;
|
||||
case ROLE:
|
||||
context = event.getOptionAsRole(optionName);
|
||||
reLookup = "role";
|
||||
break;
|
||||
case MENTIONABLE:
|
||||
Long id = event.getOptionAsLong(optionName);
|
||||
if (id == null) {
|
||||
context = event.getOptionAsString(optionName);
|
||||
break;
|
||||
}
|
||||
|
||||
DiscordUser user = discordSRV.discordAPI().getUserById(id);
|
||||
if (user != null) {
|
||||
context = user;
|
||||
reLookup = "user";
|
||||
break;
|
||||
}
|
||||
|
||||
DiscordRole role = discordSRV.discordAPI().getRoleById(id);
|
||||
if (role != null) {
|
||||
context = role;
|
||||
reLookup = "role";
|
||||
break;
|
||||
}
|
||||
|
||||
DiscordChannel channel = discordSRV.discordAPI().getChannelById(id);
|
||||
if (channel != null) {
|
||||
context = channel;
|
||||
reLookup = "channel";
|
||||
break;
|
||||
}
|
||||
|
||||
context = event.getOptionAsString(optionName);
|
||||
break;
|
||||
default:
|
||||
context = event.getOptionAsString(optionName);
|
||||
break;
|
||||
}
|
||||
|
||||
formatter = formatter.addPlaceholder("option_" + optionName, context, reLookup);
|
||||
}
|
||||
|
||||
SendableDiscordMessage message = formatter.applyPlaceholderService().build();
|
||||
event.reply(message, config.ephemeral).whenComplete((ih, t) -> {
|
||||
if (t != null) {
|
||||
logger().debug("Failed to reply to custom command: " + config.command, t);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ExecutionHandler handler = new ExecutionHandler(config);
|
||||
commandBuilder.setEventHandler(handler::accept);
|
||||
DiscordCommand command = commandBuilder.build();
|
||||
|
||||
LayerCommand foundLayer = layeredCommands.stream()
|
||||
@ -230,4 +167,115 @@ public class CustomCommandModule extends AbstractModule<DiscordSRV> {
|
||||
return commands;
|
||||
}
|
||||
}
|
||||
|
||||
public class ExecutionHandler implements Consumer<AbstractCommandInteractionEvent<?>> {
|
||||
|
||||
private final CustomCommandConfig config;
|
||||
|
||||
public ExecutionHandler(CustomCommandConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(AbstractCommandInteractionEvent<?> event) {
|
||||
DiscordGuildMember member = event.getMember();
|
||||
if (member == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean anyAllowingConstraint = config.constraints.isEmpty();
|
||||
for (CustomCommandConfig.ConstraintConfig constraint : config.constraints) {
|
||||
boolean included = constraint.roleAndUserIds.contains(member.getUser().getId());
|
||||
if (!included) {
|
||||
for (DiscordRole role : member.getRoles()) {
|
||||
if (constraint.roleAndUserIds.contains(role.getId())) {
|
||||
included = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (included != constraint.blacklist) {
|
||||
anyAllowingConstraint = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!anyAllowingConstraint) {
|
||||
event.reply(SendableDiscordMessage.builder().setContent("You do not have permission to run that command").build(), true); // TODO: translation
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> commandsToRun = config.consoleCommandsToRun;
|
||||
for (String command : commandsToRun) {
|
||||
discordSRV.console().runCommandWithLogging(discordSRV, event.getUser(), command);
|
||||
}
|
||||
|
||||
SendableDiscordMessage.Formatter formatter = config.response.toFormatter();
|
||||
optionsToFormatter(event, formatter);
|
||||
|
||||
SendableDiscordMessage message = formatter.applyPlaceholderService().build();
|
||||
event.reply(message, config.ephemeral).whenComplete((__, t) -> {
|
||||
if (t != null) {
|
||||
logger().debug("Failed to reply to custom command: " + config.command, t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void optionsToFormatter(AbstractCommandInteractionEvent<?> event, SendableDiscordMessage.Formatter formatter) {
|
||||
for (CustomCommandConfig.OptionConfig option : config.options) {
|
||||
String optionName = option.name;
|
||||
|
||||
Object context;
|
||||
String reLookup = null;
|
||||
switch (option.type) {
|
||||
case CHANNEL:
|
||||
context = event.getOptionAsChannel(optionName);
|
||||
reLookup = "channel";
|
||||
break;
|
||||
case USER:
|
||||
context = event.getOptionAsUser(optionName);
|
||||
reLookup = "user";
|
||||
break;
|
||||
case ROLE:
|
||||
context = event.getOptionAsRole(optionName);
|
||||
reLookup = "role";
|
||||
break;
|
||||
case MENTIONABLE:
|
||||
Long id = event.getOptionAsLong(optionName);
|
||||
if (id == null) {
|
||||
context = event.getOptionAsString(optionName);
|
||||
break;
|
||||
}
|
||||
|
||||
DiscordUser user = discordSRV.discordAPI().getUserById(id);
|
||||
if (user != null) {
|
||||
context = user;
|
||||
reLookup = "user";
|
||||
break;
|
||||
}
|
||||
|
||||
DiscordRole role = discordSRV.discordAPI().getRoleById(id);
|
||||
if (role != null) {
|
||||
context = role;
|
||||
reLookup = "role";
|
||||
break;
|
||||
}
|
||||
|
||||
DiscordChannel channel = discordSRV.discordAPI().getChannelById(id);
|
||||
if (channel != null) {
|
||||
context = channel;
|
||||
reLookup = "channel";
|
||||
break;
|
||||
}
|
||||
|
||||
context = event.getOptionAsString(optionName);
|
||||
break;
|
||||
default:
|
||||
context = event.getOptionAsString(optionName);
|
||||
break;
|
||||
}
|
||||
formatter = formatter.addPlaceholder("option_" + optionName, context, reLookup);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user