Add combined Minecraft & Discord commands

This commit is contained in:
Vankka 2023-05-29 00:42:55 +03:00
parent 4700adaaf9
commit df5a253b48
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
23 changed files with 545 additions and 135 deletions

View File

@ -179,6 +179,12 @@ public class Command implements JDAEntity<CommandData> {
return Collections.unmodifiableList(subCommandGroups);
}
@NotNull
@Unmodifiable
public List<Command> getSubCommands() {
return Collections.unmodifiableList(subCommands);
}
@NotNull
@Unmodifiable
public List<CommandOption> getOptions() {

View File

@ -29,8 +29,9 @@ import com.discordsrv.common.channel.ChannelConfigHelper;
import com.discordsrv.common.channel.ChannelLockingModule;
import com.discordsrv.common.channel.ChannelUpdaterModule;
import com.discordsrv.common.channel.GlobalChannelLookupModule;
import com.discordsrv.common.command.discord.DiscordCommandModule;
import com.discordsrv.common.command.game.GameCommandModule;
import com.discordsrv.common.command.game.command.subcommand.reload.ReloadResults;
import com.discordsrv.common.command.game.commands.subcommand.reload.ReloadResults;
import com.discordsrv.common.component.ComponentFactory;
import com.discordsrv.common.config.connection.ConnectionConfig;
import com.discordsrv.common.config.connection.UpdateConfig;
@ -562,6 +563,7 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
// Modules
registerModule(ChannelLockingModule::new);
registerModule(ChannelUpdaterModule::new);
registerModule(DiscordCommandModule::new);
registerModule(GameCommandModule::new);
registerModule(GlobalChannelLookupModule::new);
registerModule(DiscordAPIEventModule::new);
@ -755,6 +757,11 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
results.addAll(moduleManager.reload());
}
if (1 == 1) {
discordAPI().commandRegistry().registerCommandsFromEvent();
discordAPI().commandRegistry().registerCommandsToDiscord();
}
if (!initial) {
eventBus().publish(new DiscordSRVReloadedEvent(flags));
logger().info("Reload complete.");

View File

@ -0,0 +1,31 @@
package com.discordsrv.common.command.combined.abstraction;
import com.discordsrv.api.discord.events.interaction.command.DiscordChatInputInteractionEvent;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
import com.discordsrv.common.command.game.sender.ICommandSender;
import java.util.function.Consumer;
public abstract class CombinedCommand implements GameCommandExecutor, Consumer<DiscordChatInputInteractionEvent> {
protected final DiscordSRV discordSRV;
public CombinedCommand(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
}
@Override
public void execute(ICommandSender sender, GameCommandArguments arguments) {
execute(new GameCommandExecution(discordSRV, sender, arguments));
}
@Override
public void accept(DiscordChatInputInteractionEvent event) {
execute(new DiscordCommandExecution(discordSRV, event));
}
public abstract void execute(CommandExecution execution);
}

View File

@ -0,0 +1,18 @@
package com.discordsrv.common.command.combined.abstraction;
import java.util.Arrays;
import java.util.Collection;
public interface CommandExecution {
void setEphemeral(boolean ephemeral);
String getArgument(String label);
default void send(Text... texts) {
send(Arrays.asList(texts));
}
void send(Collection<Text> texts);
void runAsync(Runnable runnable);
}

View File

@ -0,0 +1,80 @@
package com.discordsrv.common.command.combined.abstraction;
import com.discordsrv.api.discord.events.interaction.command.DiscordChatInputInteractionEvent;
import com.discordsrv.common.DiscordSRV;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
import java.util.EnumMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
public class DiscordCommandExecution implements CommandExecution {
private final DiscordSRV discordSRV;
private final DiscordChatInputInteractionEvent event;
private final AtomicBoolean isEphemeral = new AtomicBoolean(true);
private final AtomicReference<InteractionHook> hook = new AtomicReference<>();
public DiscordCommandExecution(DiscordSRV discordSRV, DiscordChatInputInteractionEvent event) {
this.discordSRV = discordSRV;
this.event = event;
}
@Override
public void setEphemeral(boolean ephemeral) {
isEphemeral.set(ephemeral);
}
@Override
public String getArgument(String label) {
OptionMapping mapping = event.asJDA().getOption(label);
return mapping != null ? mapping.getAsString() : null;
}
@Override
public void send(Collection<Text> texts) {
StringBuilder builder = new StringBuilder();
EnumMap<Text.Formatting, Boolean> formats = new EnumMap<>(Text.Formatting.class);
for (Text text : texts) {
if (StringUtils.isEmpty(text.content())) continue;
verifyStyle(builder, formats, text);
builder.append(text.content());
}
verifyStyle(builder, formats, null);
InteractionHook interactionHook = hook.get();
boolean ephemeral = isEphemeral.get();
if (interactionHook != null) {
interactionHook.sendMessage(builder.toString()).setEphemeral(ephemeral).queue();
} else {
event.asJDA().reply(builder.toString()).setEphemeral(ephemeral).queue();
}
}
private void verifyStyle(StringBuilder builder, EnumMap<Text.Formatting, Boolean> formats, Text text) {
for (Text.Formatting format : Text.Formatting.values()) {
boolean is = formats.computeIfAbsent(format, key -> false);
boolean thisIs = text != null && text.discordFormatting().contains(format);
if (is != thisIs) {
// should end or start
builder.append(format.discord());
formats.put(format, thisIs);
}
}
}
@Override
public void runAsync(Runnable runnable) {
event.asJDA().deferReply(isEphemeral.get()).queue(ih -> {
hook.set(ih);
discordSRV.scheduler().run(runnable);
});
}
}

View File

@ -0,0 +1,50 @@
package com.discordsrv.common.command.combined.abstraction;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
import com.discordsrv.common.command.game.sender.ICommandSender;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import java.util.Collection;
public class GameCommandExecution implements CommandExecution {
private final DiscordSRV discordSRV;
private final ICommandSender sender;
private final GameCommandArguments arguments;
public GameCommandExecution(DiscordSRV discordSRV, ICommandSender sender, GameCommandArguments arguments) {
this.discordSRV = discordSRV;
this.sender = sender;
this.arguments = arguments;
}
@Override
public void setEphemeral(boolean ephemeral) {
// NO-OP
}
@Override
public String getArgument(String label) {
return arguments.getString(label);
}
@Override
public void send(Collection<Text> texts) {
TextComponent.Builder builder = Component.text();
for (Text text : texts) {
builder.append(
Component.text(text.content())
.color(text.gameColor())
.decorations(text.gameFormatting(), true)
);
}
sender.sendMessage(builder);
}
@Override
public void runAsync(Runnable runnable) {
discordSRV.scheduler().run(runnable);
}
}

View File

@ -0,0 +1,87 @@
package com.discordsrv.common.command.combined.abstraction;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import java.util.Arrays;
import java.util.EnumSet;
public class Text {
private final String content;
private TextColor gameColor;
private EnumSet<TextDecoration> gameFormatting;
private EnumSet<Formatting> discordFormatting;
public Text(String content) {
this.content = content;
this.gameFormatting = EnumSet.noneOf(TextDecoration.class);
this.discordFormatting = EnumSet.noneOf(Formatting.class);
}
public String content() {
return content;
}
public Text withGameColor(TextColor color) {
this.gameColor = color;
return this;
}
public TextColor gameColor() {
return gameColor;
}
public Text withFormatting(Formatting... formatting) {
EnumSet<TextDecoration> game = EnumSet.noneOf(TextDecoration.class);
for (Formatting format : formatting) {
game.add(format.game);
}
this.gameFormatting = game;
this.discordFormatting = EnumSet.copyOf(Arrays.asList(formatting));
return this;
}
public Text withGameFormatting(Formatting... formatting) {
EnumSet<TextDecoration> game = EnumSet.noneOf(TextDecoration.class);
for (Formatting format : formatting) {
game.add(format.game);
}
this.gameFormatting = game;
return this;
}
public EnumSet<TextDecoration> gameFormatting() {
return gameFormatting;
}
public Text withDiscordFormatting(Formatting... formatting) {
this.discordFormatting = EnumSet.copyOf(Arrays.asList(formatting));
return this;
}
public EnumSet<Formatting> discordFormatting() {
return discordFormatting;
}
public enum Formatting {
BOLD(TextDecoration.BOLD, "**"),
ITALICS(TextDecoration.ITALIC, "*"),
UNDERLINED(TextDecoration.UNDERLINED, "__"),
STRIKETHROUGH(TextDecoration.STRIKETHROUGH, "~~");
private final TextDecoration game;
private final String discord;
Formatting(TextDecoration game, String discord) {
this.game = game;
this.discord = discord;
}
public String discord() {
return discord;
}
}
}

View File

@ -16,20 +16,21 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.command.game.command.subcommand;
package com.discordsrv.common.command.combined.commands;
import com.discordsrv.api.discord.entity.interaction.command.Command;
import com.discordsrv.api.discord.entity.interaction.command.CommandOption;
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.combined.abstraction.CombinedCommand;
import com.discordsrv.common.command.combined.abstraction.CommandExecution;
import com.discordsrv.common.command.combined.abstraction.Text;
import com.discordsrv.common.command.game.abstraction.GameCommand;
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
import com.discordsrv.common.command.game.sender.ICommandSender;
import com.discordsrv.common.debug.DebugReport;
import com.discordsrv.common.paste.Paste;
import com.discordsrv.common.paste.PasteService;
import com.discordsrv.common.paste.service.AESEncryptedPasteService;
import com.discordsrv.common.paste.service.BytebinPasteService;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import java.nio.charset.StandardCharsets;
@ -38,54 +39,76 @@ import java.util.Base64;
import java.util.Collections;
import java.util.Locale;
public class DebugCommand implements GameCommandExecutor {
public class DebugCommand extends CombinedCommand {
private static GameCommand INSTANCE;
private static DebugCommand INSTANCE;
private static GameCommand GAME;
private static Command DISCORD;
public static GameCommand get(DiscordSRV discordSRV) {
if (INSTANCE == null) {
DebugCommand debugCommand = new DebugCommand(discordSRV);
INSTANCE = GameCommand.literal("debug")
private static DebugCommand getInstance(DiscordSRV discordSRV) {
return INSTANCE != null ? INSTANCE : (INSTANCE = new DebugCommand(discordSRV));
}
public static GameCommand getGame(DiscordSRV discordSRV) {
if (GAME == null) {
DebugCommand command = getInstance(discordSRV);
GAME = GameCommand.literal("debug")
.requiredPermission("discordsrv.admin.debug")
.executor(debugCommand)
.executor(command)
.then(
GameCommand.stringWord("zip")
GameCommand.stringWord("format")
.suggester((sender, previousArguments, currentInput) ->
"zip".startsWith(currentInput.toLowerCase(Locale.ROOT))
? Collections.singletonList("zip") : Collections.emptyList())
.executor(debugCommand)
.executor(command)
);
}
return INSTANCE;
return GAME;
}
public static Command getDiscord(DiscordSRV discordSRV) {
if (DISCORD == null) {
DebugCommand command = getInstance(discordSRV);
DISCORD = Command.chatInput(ComponentIdentifier.of("DiscordSRV", "debug"), "debug", "Create a debug report")
.addOption(
CommandOption.builder(CommandOption.Type.STRING, "format", "The format to generate the debug report")
.addChoice(".zip", "zip")
.setRequired(false)
.build()
)
.setEventHandler(command)
.build();
}
return DISCORD;
}
private static final String URL_FORMAT = "https://discordsrv.vankka.dev/debug/%s#%s";
private static final Base64.Encoder KEY_ENCODER = Base64.getUrlEncoder().withoutPadding();
private final DiscordSRV discordSRV;
private final PasteService pasteService;
public DebugCommand(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
super(discordSRV);
this.pasteService = new AESEncryptedPasteService(new BytebinPasteService(discordSRV, "https://bytebin.lucko.me") /* TODO: final store tbd */, 128);
}
@Override
public void execute(ICommandSender sender, GameCommandArguments arguments) {
boolean usePaste = !"zip".equals(arguments.getString("zip"));
public void execute(CommandExecution execution) {
boolean usePaste = !"zip".equals(execution.getArgument("format"));
discordSRV.scheduler().run(() -> {
execution.runAsync(() -> {
DebugReport report = new DebugReport(discordSRV);
report.generate();
Throwable pasteError = usePaste ? paste(sender, report) : null;
Throwable pasteError = usePaste ? paste(execution, report) : null;
if (usePaste && pasteError == null) {
// Success
return;
}
Throwable zipError = zip(sender, report);
Throwable zipError = zip(execution, report);
if (zipError == null) {
// Success
if (usePaste) {
@ -98,35 +121,38 @@ public class DebugCommand implements GameCommandExecutor {
zipError.addSuppressed(pasteError);
}
discordSRV.logger().error(usePaste ? "Failed to upload & zip debug" : "Failed to zip debug", zipError);
sender.sendMessage(Component.text(
usePaste
? "Failed to upload debug report to paste & failed to generate zip"
: "Failed to create debug zip",
NamedTextColor.DARK_RED
));
execution.send(
new Text(usePaste
? "Failed to upload debug report to paste & failed to generate zip"
: "Failed to create debug zip"
).withGameColor(NamedTextColor.DARK_RED)
);
});
}
private Throwable paste(ICommandSender sender, DebugReport report) {
private Throwable paste(CommandExecution execution, DebugReport report) {
try {
Paste paste = report.upload(pasteService);
String key = new String(KEY_ENCODER.encode(paste.decryptionKey()), StandardCharsets.UTF_8);
String url = String.format(URL_FORMAT, paste.id(), key);
sender.sendMessage(Component.text(url).clickEvent(ClickEvent.openUrl(url)));
execution.send(new Text(url));
return null;
} catch (Throwable e) {
return e;
}
}
private Throwable zip(ICommandSender sender, DebugReport report) {
private Throwable zip(CommandExecution execution, DebugReport report) {
try {
Path zip = report.zip();
Path relative = discordSRV.dataDirectory().resolve("../..").relativize(zip);
sender.sendMessage(
Component.text("Debug generated to zip: ", NamedTextColor.GRAY)
.append(Component.text(relative.toString(), NamedTextColor.GREEN))
execution.send(
new Text("Debug generated to zip ")
.withGameColor(NamedTextColor.GRAY),
new Text(relative.toString())
.withGameColor(NamedTextColor.GREEN)
.withDiscordFormatting(Text.Formatting.BOLD)
);
return null;
} catch (Throwable e) {

View File

@ -0,0 +1,95 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2023 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.command.combined.commands;
import com.discordsrv.api.color.Color;
import com.discordsrv.api.discord.entity.interaction.command.Command;
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.combined.abstraction.CombinedCommand;
import com.discordsrv.common.command.combined.abstraction.CommandExecution;
import com.discordsrv.common.command.combined.abstraction.Text;
import com.discordsrv.common.command.game.abstraction.GameCommand;
import com.discordsrv.common.debug.data.VersionInfo;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
public class VersionCommand extends CombinedCommand {
private static VersionCommand INSTANCE;
private static GameCommand GAME;
private static Command DISCORD;
private static VersionCommand getInstance(DiscordSRV discordSRV) {
return INSTANCE != null ? INSTANCE : (INSTANCE = new VersionCommand(discordSRV));
}
public static GameCommand getGame(DiscordSRV discordSRV) {
if (GAME == null) {
VersionCommand command = getInstance(discordSRV);
GAME = GameCommand.literal("version")
.requiredPermission("discordsrv.admin.version")
.executor(command);
}
return GAME;
}
public static Command getDiscord(DiscordSRV discordSRV) {
if (DISCORD == null) {
VersionCommand command = getInstance(discordSRV);
DISCORD = Command.chatInput(ComponentIdentifier.of("DiscordSRV", "version"), "version", "Get the DiscordSRV version")
.setEventHandler(command)
.build();
}
return DISCORD;
}
public VersionCommand(DiscordSRV discordSRV) {
super(discordSRV);
}
@Override
public void execute(CommandExecution execution) {
VersionInfo versionInfo = discordSRV.versionInfo();
List<Text> text = new ArrayList<>();
text.add(
new Text("Running DiscordSRV ")
.withGameColor(TextColor.color(Color.BLURPLE.rgb()))
);
text.add(
new Text("v" + versionInfo.version())
.withGameColor(NamedTextColor.GRAY)
.withDiscordFormatting(Text.Formatting.BOLD)
);
if (versionInfo.isSnapshot()) {
String rev = StringUtils.substring(versionInfo.gitRevision(), 0, 6);
text.add(new Text(" (" + rev + "/" + versionInfo.gitBranch() + ")").withGameColor(NamedTextColor.AQUA));
}
execution.send(text);
}
}

View File

@ -0,0 +1,19 @@
package com.discordsrv.common.command.discord;
import com.discordsrv.api.discord.events.interaction.command.CommandRegisterEvent;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.discord.commands.DiscordSRVDiscordCommand;
import com.discordsrv.common.module.type.AbstractModule;
public class DiscordCommandModule extends AbstractModule<DiscordSRV> {
public DiscordCommandModule(DiscordSRV discordSRV) {
super(discordSRV);
}
@Subscribe
public void onCommandRegister(CommandRegisterEvent event) {
event.registerCommands(DiscordSRVDiscordCommand.get(discordSRV));
}
}

View File

@ -0,0 +1,27 @@
package com.discordsrv.common.command.discord.commands;
import com.discordsrv.api.discord.entity.interaction.command.Command;
import com.discordsrv.api.discord.entity.interaction.component.ComponentIdentifier;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.combined.commands.DebugCommand;
import com.discordsrv.common.command.combined.commands.VersionCommand;
public class DiscordSRVDiscordCommand {
private static final ComponentIdentifier IDENTIFIER = ComponentIdentifier.of("DiscordSRV", "discordsrv");
private static Command INSTANCE;
public static Command get(DiscordSRV discordSRV) {
if (INSTANCE == null) {
INSTANCE = Command.chatInput(IDENTIFIER, "discordsrv", "DiscordSRV related commands")
.addSubCommand(DebugCommand.getDiscord(discordSRV))
.addSubCommand(VersionCommand.getDiscord(discordSRV))
.setGuildOnly(false)
.setDefaultPermission(Command.DefaultPermission.ADMINISTRATOR)
.build();
}
return INSTANCE;
}
}

View File

@ -20,8 +20,8 @@ package com.discordsrv.common.command.game;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.game.abstraction.GameCommand;
import com.discordsrv.common.command.game.command.DiscordSRVCommand;
import com.discordsrv.common.command.game.command.subcommand.LinkCommand;
import com.discordsrv.common.command.game.commands.DiscordSRVGameCommand;
import com.discordsrv.common.command.game.commands.subcommand.LinkCommand;
import com.discordsrv.common.command.game.handler.ICommandHandler;
import com.discordsrv.common.config.main.CommandConfig;
import com.discordsrv.common.module.type.AbstractModule;
@ -39,8 +39,8 @@ public class GameCommandModule extends AbstractModule<DiscordSRV> {
public GameCommandModule(DiscordSRV discordSRV) {
super(discordSRV);
this.primaryCommand = DiscordSRVCommand.get(discordSRV, "discordsrv");
this.discordAlias = DiscordSRVCommand.get(discordSRV, "discord");
this.primaryCommand = DiscordSRVGameCommand.get(discordSRV, "discordsrv");
this.discordAlias = DiscordSRVGameCommand.get(discordSRV, "discord");
this.linkCommand = LinkCommand.get(discordSRV);
registerCommand(primaryCommand);

View File

@ -1,74 +0,0 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2023 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.command.game.command.subcommand;
import com.discordsrv.api.color.Color;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.game.abstraction.GameCommand;
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
import com.discordsrv.common.command.game.sender.ICommandSender;
import com.discordsrv.common.debug.data.VersionInfo;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.apache.commons.lang3.StringUtils;
public class VersionCommand implements GameCommandExecutor {
private static GameCommand INSTANCE;
public static GameCommand get(DiscordSRV discordSRV) {
if (INSTANCE == null) {
INSTANCE = GameCommand.literal("version")
.requiredPermission("discordsrv.admin.version")
.executor(new VersionCommand(discordSRV));
}
return INSTANCE;
}
private final DiscordSRV discordSRV;
public VersionCommand(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
}
@Override
public void execute(ICommandSender sender, GameCommandArguments arguments) {
VersionInfo versionInfo = discordSRV.versionInfo();
TextComponent.Builder builder =
Component.text()
.content("Running DiscordSRV ")
.color(TextColor.color(Color.BLURPLE.rgb()))
.append(Component.text("v" + versionInfo.version(), NamedTextColor.GRAY));
if (versionInfo.isSnapshot()) {
String rev = StringUtils.substring(versionInfo.gitRevision(), 0, 6);
builder.append(
Component.text()
.content(" (" + rev + "/" + versionInfo.gitBranch() + ")")
.color(NamedTextColor.AQUA)
);
}
sender.sendMessage(builder);
}
}

View File

@ -16,29 +16,31 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.command.game.command;
package com.discordsrv.common.command.game.commands;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.combined.commands.DebugCommand;
import com.discordsrv.common.command.combined.commands.VersionCommand;
import com.discordsrv.common.command.game.abstraction.GameCommand;
import com.discordsrv.common.command.game.abstraction.GameCommandArguments;
import com.discordsrv.common.command.game.abstraction.GameCommandExecutor;
import com.discordsrv.common.command.game.command.subcommand.*;
import com.discordsrv.common.command.game.command.subcommand.reload.ReloadCommand;
import com.discordsrv.common.command.game.commands.subcommand.*;
import com.discordsrv.common.command.game.commands.subcommand.reload.ReloadCommand;
import com.discordsrv.common.command.game.sender.ICommandSender;
import com.discordsrv.common.component.util.ComponentUtil;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class DiscordSRVCommand implements GameCommandExecutor {
public class DiscordSRVGameCommand implements GameCommandExecutor {
private static final Map<String, GameCommand> INSTANCES = new ConcurrentHashMap<>();
private static DiscordSRVCommand COMMAND;
private static DiscordSRVGameCommand COMMAND;
public static GameCommand get(DiscordSRV discordSRV, String alias) {
if (COMMAND == null) {
COMMAND = new DiscordSRVCommand(discordSRV);
COMMAND = new DiscordSRVGameCommand(discordSRV);
}
return INSTANCES.computeIfAbsent(alias, key ->
GameCommand.literal(alias)
@ -47,17 +49,17 @@ public class DiscordSRVCommand implements GameCommandExecutor {
.then(BroadcastCommand.discord(discordSRV))
.then(BroadcastCommand.minecraft(discordSRV))
.then(BroadcastCommand.json(discordSRV))
.then(DebugCommand.get(discordSRV))
.then(DebugCommand.getGame(discordSRV))
.then(LinkCommand.get(discordSRV))
.then(ReloadCommand.get(discordSRV))
.then(ResyncCommand.get(discordSRV))
.then(VersionCommand.get(discordSRV))
.then(VersionCommand.getGame(discordSRV))
);
}
private final DiscordSRV discordSRV;
public DiscordSRVCommand(DiscordSRV discordSRV) {
public DiscordSRVGameCommand(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
}

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.command.game.command.subcommand;
package com.discordsrv.common.command.game.commands.subcommand;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.command.game.command.subcommand;
package com.discordsrv.common.command.game.commands.subcommand;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.game.abstraction.GameCommand;

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.command.game.command.subcommand;
package com.discordsrv.common.command.game.commands.subcommand;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.game.abstraction.GameCommand;

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.command.game.command.subcommand.reload;
package com.discordsrv.common.command.game.commands.subcommand.reload;
import com.discordsrv.api.DiscordSRVApi;
import com.discordsrv.common.DiscordSRV;

View File

@ -1,4 +1,4 @@
package com.discordsrv.common.command.game.command.subcommand.reload;
package com.discordsrv.common.command.game.commands.subcommand.reload;
import com.discordsrv.api.DiscordSRVApi;

View File

@ -23,6 +23,7 @@ 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.command.SubCommandGroup;
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.*;
@ -169,7 +170,7 @@ public class DiscordAPIEventModule extends AbstractModule<DiscordSRV> {
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);
.getActiveCommand(guild, CommandType.MESSAGE, name).orElse(null);
if (command == null) {
return;
}
@ -190,7 +191,7 @@ public class DiscordAPIEventModule extends AbstractModule<DiscordSRV> {
}
} else if (event instanceof UserContextInteractionEvent) {
com.discordsrv.api.discord.entity.interaction.command.Command command = discordSRV.discordAPI()
.getActiveCommand(guild, CommandType.MESSAGE, name).orElse(null);
.getActiveCommand(guild, CommandType.USER, name).orElse(null);
if (command == null) {
return;
}
@ -211,11 +212,35 @@ public class DiscordAPIEventModule extends AbstractModule<DiscordSRV> {
}
} else if (event instanceof SlashCommandInteractionEvent) {
com.discordsrv.api.discord.entity.interaction.command.Command command = discordSRV.discordAPI()
.getActiveCommand(guild, CommandType.USER, name).orElse(null);
.getActiveCommand(guild, CommandType.CHAT_INPUT, name).orElse(null);
if (command == null) {
return;
}
String subCommandGroupName = ((SlashCommandInteractionEvent) event).getSubcommandGroup();
String subCommandName = ((SlashCommandInteractionEvent) event).getSubcommandName();
if (subCommandGroupName != null) {
for (SubCommandGroup group : command.getSubCommandGroups()) {
if (group.getName().equals(subCommandGroupName)) {
for (com.discordsrv.api.discord.entity.interaction.command.Command subCommand : group.getCommands()) {
if (subCommand.getName().equals(subCommandName)) {
command = subCommand;
break;
}
}
break;
}
}
} else if (subCommandName != null) {
for (com.discordsrv.api.discord.entity.interaction.command.Command subCommand : command.getSubCommands()) {
if (subCommandName.equals(subCommand.getName())) {
command = subCommand;
break;
}
}
}
DiscordChatInputInteractionEvent interactionEvent = new DiscordChatInputInteractionEvent(
(SlashCommandInteractionEvent) event,
command.getId(),

View File

@ -485,6 +485,10 @@ public class DiscordAPIImpl implements DiscordAPI {
return Optional.ofNullable(commandRegistry.getActive(guild != null ? guild.getIdLong() : null, type, name));
}
public DiscordCommandRegistry commandRegistry() {
return commandRegistry;
}
private class WebhookCacheLoader implements AsyncCacheLoader<Long, WebhookClient> {
@Override

View File

@ -91,9 +91,13 @@ public class DiscordCommandRegistry {
@Nullable
public Command getActive(Long guildId, CommandType type, String name) {
return registries
Registry registry = registries
.computeIfAbsent(guildId != null ? guildId : GLOBAL_ID, key -> Collections.emptyMap())
.get(type).getActive(name);
.get(type);
if (registry == null) {
return null;
}
return registry.getActive(name);
}
public void registerCommandsToDiscord() {
@ -149,7 +153,10 @@ public class DiscordCommandRegistry {
action.addCommands(allCommands.stream().map(JDAEntity::asJDA).collect(Collectors.toList()))
.queue(v -> {
for (CommandType value : CommandType.values()) {
commandsByType.get(value).putActiveCommands(commandsToRegister.get(value));
Registry registry = commandsByType.get(value);
if (registry != null) {
registry.putActiveCommands(commandsToRegister.get(value));
}
}
});
}

View File

@ -27,7 +27,7 @@ import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.lifecycle.DiscordSRVShuttingDownEvent;
import com.discordsrv.api.module.type.Module;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.game.command.subcommand.reload.ReloadResults;
import com.discordsrv.common.command.game.commands.subcommand.reload.ReloadResults;
import com.discordsrv.common.debug.DebugGenerateEvent;
import com.discordsrv.common.debug.file.TextDebugFile;
import com.discordsrv.common.discord.connection.jda.JDAConnectionManager;