From d4c54a21d165013a6c98916369f457756569b243 Mon Sep 17 00:00:00 2001 From: Vankka Date: Fri, 29 Mar 2024 20:10:28 +0200 Subject: [PATCH] Add unlink command, some fixes & cleanup to other linking commands --- .../abstraction/CommandExecution.java | 6 + .../combined/commands/LinkInitCommand.java | 81 +---- .../combined/commands/LinkedCommand.java | 106 +----- .../combined/commands/UnlinkCommand.java | 150 +++++++++ .../commands/DiscordSRVDiscordCommand.java | 4 +- .../game/commands/DiscordSRVGameCommand.java | 34 +- .../config/messages/MessagesConfig.java | 303 +++++++++++++++--- .../common/permission/Permission.java | 2 + .../common/storage/impl/sql/SQLStorage.java | 10 +- 9 files changed, 462 insertions(+), 234 deletions(-) create mode 100644 common/src/main/java/com/discordsrv/common/command/combined/commands/UnlinkCommand.java diff --git a/common/src/main/java/com/discordsrv/common/command/combined/abstraction/CommandExecution.java b/common/src/main/java/com/discordsrv/common/command/combined/abstraction/CommandExecution.java index f24c5e6b..e9d81df1 100644 --- a/common/src/main/java/com/discordsrv/common/command/combined/abstraction/CommandExecution.java +++ b/common/src/main/java/com/discordsrv/common/command/combined/abstraction/CommandExecution.java @@ -1,5 +1,7 @@ package com.discordsrv.common.command.combined.abstraction; +import com.discordsrv.api.component.MinecraftComponent; +import com.discordsrv.common.component.util.ComponentUtil; import com.discordsrv.common.config.messages.MessagesConfig; import net.kyori.adventure.text.Component; @@ -27,6 +29,10 @@ public interface CommandExecution { void send(Collection texts, Collection extra); + default void send(MinecraftComponent minecraft, String discord) { + send(ComponentUtil.fromAPI(minecraft), discord); + } + void send(Component minecraft, String discord); void runAsync(Runnable runnable); diff --git a/common/src/main/java/com/discordsrv/common/command/combined/commands/LinkInitCommand.java b/common/src/main/java/com/discordsrv/common/command/combined/commands/LinkInitCommand.java index 68d1f135..bd0e97ba 100644 --- a/common/src/main/java/com/discordsrv/common/command/combined/commands/LinkInitCommand.java +++ b/common/src/main/java/com/discordsrv/common/command/combined/commands/LinkInitCommand.java @@ -1,10 +1,8 @@ package com.discordsrv.common.command.combined.commands; -import com.discordsrv.api.discord.entity.DiscordUser; 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.placeholder.provider.SinglePlaceholder; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.command.combined.abstraction.CombinedCommand; import com.discordsrv.common.command.combined.abstraction.CommandExecution; @@ -15,35 +13,32 @@ import com.discordsrv.common.command.game.sender.ICommandSender; import com.discordsrv.common.command.util.CommandUtil; import com.discordsrv.common.component.util.ComponentUtil; import com.discordsrv.common.config.messages.MessagesConfig; -import com.discordsrv.common.future.util.CompletableFutureUtil; import com.discordsrv.common.linking.LinkProvider; import com.discordsrv.common.linking.LinkStore; import com.discordsrv.common.logging.Logger; import com.discordsrv.common.logging.NamedLogger; import com.discordsrv.common.permission.Permission; -import com.discordsrv.common.player.IOfflinePlayer; import com.discordsrv.common.player.IPlayer; import com.github.benmanes.caffeine.cache.Cache; import net.kyori.adventure.text.format.NamedTextColor; import org.apache.commons.lang3.StringUtils; -import java.time.Duration; import java.util.UUID; import java.util.concurrent.CompletableFuture; public class LinkInitCommand extends CombinedCommand { - private static DebugCommand INSTANCE; + private static LinkInitCommand INSTANCE; private static GameCommand GAME; private static DiscordCommand DISCORD; - private static DebugCommand getInstance(DiscordSRV discordSRV) { - return INSTANCE != null ? INSTANCE : (INSTANCE = new DebugCommand(discordSRV)); + private static LinkInitCommand getInstance(DiscordSRV discordSRV) { + return INSTANCE != null ? INSTANCE : (INSTANCE = new LinkInitCommand(discordSRV)); } public static GameCommand getGame(DiscordSRV discordSRV) { if (GAME == null) { - LinkInitCommand command = new LinkInitCommand(discordSRV); + LinkInitCommand command = getInstance(discordSRV); GAME = GameCommand.literal("link") .then( GameCommand.stringWord("player") @@ -62,15 +57,15 @@ public class LinkInitCommand extends CombinedCommand { public static DiscordCommand getDiscord(DiscordSRV discordSRV) { if (DISCORD == null) { - DebugCommand command = getInstance(discordSRV); + LinkInitCommand command = getInstance(discordSRV); DISCORD = DiscordCommand.chatInput(ComponentIdentifier.of("DiscordSRV", "link"), "link", "Link players") .addOption( - CommandOption.builder(CommandOption.Type.STRING, "player", "The player to link") + CommandOption.builder(CommandOption.Type.USER, "user", "The user to link") .setRequired(true) .build() ) .addOption( - CommandOption.builder(CommandOption.Type.USER, "user", "The user to link") + CommandOption.builder(CommandOption.Type.STRING, "player", "The player to link") .setRequired(true) .build() ) @@ -128,69 +123,39 @@ public class LinkInitCommand extends CombinedCommand { playerUUIDFuture.whenComplete((playerUUID, __) -> userIdFuture.whenComplete((userId, ___) -> { if (playerUUID == null) { - execution.send( - execution.messages().minecraft.playerNotFound.asComponent(), - execution.messages().discord.playerNotFound - ); + execution.messages().playerNotFound(execution); return; } - if (userId == null) { - execution.send( - execution.messages().minecraft.userNotFound.asComponent(), - execution.messages().discord.userNotFound - ); + execution.messages().userNotFound(execution); return; } - CompletableFuture playerFuture = CompletableFutureUtil.timeout( - discordSRV, - discordSRV.playerProvider().lookupOfflinePlayer(playerUUID), - Duration.ofSeconds(5) - ); - CompletableFuture userFuture = CompletableFutureUtil.timeout( - discordSRV, - discordSRV.discordAPI().retrieveUserById(userId), - Duration.ofSeconds(5) - ); - linkProvider.queryUserId(playerUUID).whenComplete((linkedUser, t) -> { if (t != null) { logger.error("Failed to check linking status", t); - execution.send( - execution.messages().minecraft.unableToCheckLinkingStatus.asComponent(), - execution.messages().discord.unableToCheckLinkingStatus - ); + execution.messages().unableToCheckLinkingStatus(execution); return; } if (linkedUser.isPresent()) { - execution.send( - execution.messages().minecraft.playerAlreadyLinked3rd.asComponent(), - execution.messages().discord.playerAlreadyLinked3rd - ); + execution.messages().playerAlreadyLinked3rd(execution); return; } linkProvider.queryPlayerUUID(userId).whenComplete((linkedPlayer, t2) -> { if (t2 != null) { logger.error("Failed to check linking status", t2); - execution.send( - execution.messages().minecraft.unableToCheckLinkingStatus.asComponent(), - execution.messages().discord.unableToCheckLinkingStatus - ); + execution.messages().unableToCheckLinkingStatus(execution); return; } if (linkedPlayer.isPresent()) { - execution.send( - execution.messages().minecraft.userAlreadyLinked3rd.asComponent(), - execution.messages().discord.userAlreadyLinked3rd - ); + execution.messages().userAlreadyLinked3rd(execution); return; } ((LinkStore) linkProvider).createLink(playerUUID, userId).whenComplete((v, t3) -> { if (t3 != null) { - logger.error("Failed to check linking status", t3); + logger.error("Failed to create link", t3); execution.send( execution.messages().minecraft.unableToLinkAtThisTime.asComponent(), execution.messages().discord.unableToCheckLinkingStatus @@ -198,23 +163,7 @@ public class LinkInitCommand extends CombinedCommand { return; } - userFuture.whenComplete((user, ____) -> playerFuture.whenComplete((player, _____) -> execution.send( - ComponentUtil.fromAPI( - execution.messages().minecraft.nowLinked3rd.textBuilder() - .applyPlaceholderService() - .addContext(user, player) - .addPlaceholder("user_id", userId) - .addPlaceholder("player_uuid", playerUUID) - .build() - ), - discordSRV.placeholderService().replacePlaceholders( - execution.messages().discord.nowLinked3rd, - user, - player, - new SinglePlaceholder("user_id", userId), - new SinglePlaceholder("player_uuid", playerUUID) - ) - ))); + execution.messages().nowLinked3rd(discordSRV, execution, playerUUID, userId); }); }); }); diff --git a/common/src/main/java/com/discordsrv/common/command/combined/commands/LinkedCommand.java b/common/src/main/java/com/discordsrv/common/command/combined/commands/LinkedCommand.java index 88878abe..95b464c7 100644 --- a/common/src/main/java/com/discordsrv/common/command/combined/commands/LinkedCommand.java +++ b/common/src/main/java/com/discordsrv/common/command/combined/commands/LinkedCommand.java @@ -1,25 +1,18 @@ package com.discordsrv.common.command.combined.commands; -import com.discordsrv.api.discord.entity.DiscordUser; 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.placeholder.provider.SinglePlaceholder; 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.game.abstraction.GameCommand; import com.discordsrv.common.command.util.CommandUtil; -import com.discordsrv.common.component.util.ComponentUtil; -import com.discordsrv.common.future.util.CompletableFutureUtil; import com.discordsrv.common.logging.Logger; import com.discordsrv.common.logging.NamedLogger; import com.discordsrv.common.permission.Permission; -import com.discordsrv.common.player.IOfflinePlayer; -import java.time.Duration; import java.util.UUID; -import java.util.concurrent.CompletableFuture; public class LinkedCommand extends CombinedCommand { @@ -50,7 +43,7 @@ public class LinkedCommand extends CombinedCommand { public static DiscordCommand getDiscord(DiscordSRV discordSRV) { if (DISCORD == null) { LinkedCommand command = getInstance(discordSRV); - DISCORD = DiscordCommand.chatInput(ComponentIdentifier.of("DiscordSRV", "linked"), "linked", "Get the linking status of a given user") + DISCORD = DiscordCommand.chatInput(ComponentIdentifier.of("DiscordSRV", "linked"), "linked", "Check the linking status of accounts") .addOption(CommandOption.builder( CommandOption.Type.USER, "user", @@ -95,73 +88,23 @@ public class LinkedCommand extends CombinedCommand { private void processResult(CommandUtil.TargetLookupResult result, CommandExecution execution) { if (result.isPlayer()) { UUID playerUUID = result.getPlayerUUID(); - CompletableFuture playerFuture = CompletableFutureUtil.timeout( - discordSRV, - discordSRV.playerProvider().lookupOfflinePlayer(playerUUID), - Duration.ofSeconds(5) - ); discordSRV.linkProvider().getUserId(playerUUID).whenComplete((optUserId, t) -> { if (t != null) { logger.error("Failed to check linking status during linked command", t); - execution.send( - execution.messages().minecraft.unableToCheckLinkingStatus.asComponent(), - execution.messages().discord.unableToCheckLinkingStatus - ); + execution.messages().unableToCheckLinkingStatus(execution); return; } if (!optUserId.isPresent()) { - playerFuture.whenComplete((player, ___) -> execution.send( - ComponentUtil.fromAPI( - execution.messages().minecraft.minecraftPlayerUnlinked - .textBuilder() - .applyPlaceholderService() - .addContext(player) - .addPlaceholder("player_uuid", playerUUID) - .build() - ), - discordSRV.placeholderService().replacePlaceholders( - execution.messages().discord.minecraftPlayerUnlinked, - player, - new SinglePlaceholder("player_uuid", playerUUID) - ) - )); + execution.messages().minecraftPlayerUnlinked(discordSRV, execution, playerUUID); return; } long userId = optUserId.get(); - CompletableFuture userFuture = CompletableFutureUtil.timeout( - discordSRV, - discordSRV.discordAPI().retrieveUserById(userId), - Duration.ofSeconds(5) - ); - - playerFuture.whenComplete((player, __) -> userFuture.whenComplete((user, ___) -> execution.send( - ComponentUtil.fromAPI( - execution.messages().minecraft.minecraftPlayerLinkedTo - .textBuilder() - .applyPlaceholderService() - .addContext(player, user) - .addPlaceholder("player_uuid", playerUUID) - .addPlaceholder("user_id", userId) - .build() - ), - discordSRV.placeholderService().replacePlaceholders( - execution.messages().discord.minecraftPlayerLinkedTo, - player, - user, - new SinglePlaceholder("player_uuid", playerUUID), - new SinglePlaceholder("user_id", userId) - ) - ))); + execution.messages().minecraftPlayerLinkedTo(discordSRV, execution, playerUUID, userId); }); } else { long userId = result.getUserId(); - CompletableFuture userFuture = CompletableFutureUtil.timeout( - discordSRV, - discordSRV.discordAPI().retrieveUserById(userId), - Duration.ofSeconds(5) - ); discordSRV.linkProvider().getPlayerUUID(userId).whenComplete((optPlayerUUID, t) -> { if (t != null) { @@ -173,49 +116,12 @@ public class LinkedCommand extends CombinedCommand { return; } if (!optPlayerUUID.isPresent()) { - userFuture.whenComplete((user, ___) -> execution.send( - ComponentUtil.fromAPI( - execution.messages().minecraft.discordUserUnlinked - .textBuilder() - .applyPlaceholderService() - .addContext(user) - .addPlaceholder("user_id", userId) - .build() - ), - discordSRV.placeholderService().replacePlaceholders( - execution.messages().discord.discordUserUnlinked, - user, - new SinglePlaceholder("user_id", userId) - ) - )); + execution.messages().discordUserUnlinked(discordSRV, execution, userId); return; } UUID playerUUID = optPlayerUUID.get(); - CompletableFuture playerFuture = CompletableFutureUtil.timeout( - discordSRV, - discordSRV.playerProvider().lookupOfflinePlayer(playerUUID), - Duration.ofSeconds(5) - ); - - userFuture.whenComplete((user, __) -> playerFuture.whenComplete((player, ___) -> execution.send( - ComponentUtil.fromAPI( - execution.messages().minecraft.discordUserLinkedTo - .textBuilder() - .applyPlaceholderService() - .addContext(user, player) - .addPlaceholder("user_id", userId) - .addPlaceholder("player_uuid", playerUUID) - .build() - ), - discordSRV.placeholderService().replacePlaceholders( - execution.messages().discord.discordUserLinkedTo, - user, - player, - new SinglePlaceholder("user_id", userId), - new SinglePlaceholder("player_uuid", playerUUID) - ) - ))); + execution.messages().discordUserLinkedTo(discordSRV, execution, playerUUID, userId); }); } } diff --git a/common/src/main/java/com/discordsrv/common/command/combined/commands/UnlinkCommand.java b/common/src/main/java/com/discordsrv/common/command/combined/commands/UnlinkCommand.java new file mode 100644 index 00000000..1a4b6a67 --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/command/combined/commands/UnlinkCommand.java @@ -0,0 +1,150 @@ +package com.discordsrv.common.command.combined.commands; + +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.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.util.CommandUtil; +import com.discordsrv.common.linking.LinkProvider; +import com.discordsrv.common.linking.LinkStore; +import com.discordsrv.common.logging.Logger; +import com.discordsrv.common.logging.NamedLogger; +import com.discordsrv.common.permission.Permission; +import net.kyori.adventure.text.format.NamedTextColor; + +import java.util.UUID; + +public class UnlinkCommand extends CombinedCommand { + + private static UnlinkCommand INSTANCE; + private static GameCommand GAME; + private static DiscordCommand DISCORD; + + private static UnlinkCommand getInstance(DiscordSRV discordSRV) { + return INSTANCE != null ? INSTANCE : (INSTANCE = new UnlinkCommand(discordSRV)); + } + + public static GameCommand getGame(DiscordSRV discordSRV) { + if (GAME == null) { + UnlinkCommand command = getInstance(discordSRV); + GAME = GameCommand.literal("unlink") + .then( + GameCommand.stringGreedy("target") + .requiredPermission(Permission.COMMAND_UNLINK_OTHER) + .executor(command) + ) + .requiredPermission(Permission.COMMAND_UNLINK) + .executor(command); + } + + return GAME; + } + + public static DiscordCommand getDiscord(DiscordSRV discordSRV) { + if (DISCORD == null) { + UnlinkCommand command = getInstance(discordSRV); + DISCORD = DiscordCommand.chatInput(ComponentIdentifier.of("DiscordSRV", "unlink"), "unlink", "Unlink accounts") + .addOption(CommandOption.builder( + CommandOption.Type.USER, + "user", + "The Discord user to unlink" + ).setRequired(false).build()) + .addOption(CommandOption.builder( + CommandOption.Type.STRING, + "player", + "The Minecraft player username or UUID to unlink" + ).setRequired(false).build()) + .setEventHandler(command) + .build(); + } + + return DISCORD; + } + + private final Logger logger; + + public UnlinkCommand(DiscordSRV discordSRV) { + super(discordSRV); + this.logger = new NamedLogger(discordSRV, "UNLINK_COMMAND"); + } + + @Override + public void execute(CommandExecution execution) { + execution.setEphemeral(true); + + LinkProvider linkProvider = discordSRV.linkProvider(); + if (!(linkProvider instanceof LinkStore)) { + execution.send(new Text("Cannot remove links with this link provider").withGameColor(NamedTextColor.DARK_RED)); + return; + } + + execution.runAsync(() -> CommandUtil.lookupTarget(discordSRV, logger, execution, true, Permission.COMMAND_UNLINK_OTHER) + .whenComplete((result, t) -> { + if (t != null) { + logger.error("Failed to execute linked command", t); + return; + } + if (result.isValid()) { + processResult(result, execution, (LinkStore) linkProvider); + } else { + execution.send(new Text("Invalid target")); + } + }) + ); + } + + private void processResult(CommandUtil.TargetLookupResult result, CommandExecution execution, LinkStore linkStore) { + if (result.isPlayer()) { + UUID playerUUID = result.getPlayerUUID(); + discordSRV.linkProvider().queryUserId(playerUUID) + .whenComplete((user, t) -> { + if (t != null) { + logger.error("Failed to query user", t); + execution.messages().unableToCheckLinkingStatus(execution); + return; + } + if (!user.isPresent()) { + execution.messages().minecraftPlayerUnlinked(discordSRV, execution, playerUUID); + return; + } + + handleUnlinkForPair(playerUUID, user.get(), execution, linkStore); + }); + } else { + long userId = result.getUserId(); + discordSRV.linkProvider().queryPlayerUUID(result.getUserId()) + .whenComplete((player, t) -> { + if (t != null) { + logger.error("Failed to query player", t); + execution.messages().unableToCheckLinkingStatus(execution); + return; + } + if (!player.isPresent()) { + execution.messages().discordUserUnlinked(discordSRV, execution, userId); + return; + } + + handleUnlinkForPair(player.get(), userId, execution, linkStore); + }); + } + } + + private void handleUnlinkForPair(UUID player, Long user, CommandExecution execution, LinkStore linkStore) { + linkStore.removeLink(player, user).whenComplete((v, t2) -> { + if (t2 != null) { + logger.error("Failed to remove link", t2); + execution.send( + execution.messages().minecraft.unableToLinkAtThisTime.asComponent(), + execution.messages().discord.unableToCheckLinkingStatus + ); + return; + } + + execution.messages().unlinked(execution); + }); + } +} diff --git a/common/src/main/java/com/discordsrv/common/command/discord/commands/DiscordSRVDiscordCommand.java b/common/src/main/java/com/discordsrv/common/command/discord/commands/DiscordSRVDiscordCommand.java index 3979ee59..25f73f3a 100644 --- a/common/src/main/java/com/discordsrv/common/command/discord/commands/DiscordSRVDiscordCommand.java +++ b/common/src/main/java/com/discordsrv/common/command/discord/commands/DiscordSRVDiscordCommand.java @@ -28,7 +28,9 @@ public class DiscordSRVDiscordCommand { builder = builder.addSubCommand(ExecuteCommand.get(discordSRV)); } if (discordSRV.linkProvider() instanceof LinkStore) { - builder = builder.addSubCommand(LinkInitCommand.getDiscord(discordSRV)); + builder = builder + .addSubCommand(LinkInitCommand.getDiscord(discordSRV)) + .addSubCommand(UnlinkCommand.getDiscord(discordSRV)); } INSTANCE = builder diff --git a/common/src/main/java/com/discordsrv/common/command/game/commands/DiscordSRVGameCommand.java b/common/src/main/java/com/discordsrv/common/command/game/commands/DiscordSRVGameCommand.java index 13788303..588b40e6 100644 --- a/common/src/main/java/com/discordsrv/common/command/game/commands/DiscordSRVGameCommand.java +++ b/common/src/main/java/com/discordsrv/common/command/game/commands/DiscordSRVGameCommand.java @@ -28,6 +28,7 @@ import com.discordsrv.common.command.game.commands.subcommand.BroadcastCommand; 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 com.discordsrv.common.linking.LinkStore; import com.discordsrv.common.permission.Permission; import java.util.Map; @@ -42,20 +43,25 @@ public class DiscordSRVGameCommand implements GameCommandExecutor { if (COMMAND == null) { COMMAND = new DiscordSRVGameCommand(discordSRV); } - return INSTANCES.computeIfAbsent(alias, key -> - GameCommand.literal(alias) - .requiredPermission(Permission.COMMAND_ROOT) - .executor(COMMAND) - .then(BroadcastCommand.discord(discordSRV)) - .then(BroadcastCommand.minecraft(discordSRV)) - .then(BroadcastCommand.json(discordSRV)) - .then(DebugCommand.getGame(discordSRV)) - .then(LinkInitCommand.getGame(discordSRV)) - .then(LinkedCommand.getGame(discordSRV)) - .then(ReloadCommand.get(discordSRV)) - .then(ResyncCommand.getGame(discordSRV)) - .then(VersionCommand.getGame(discordSRV)) - ); + return INSTANCES.computeIfAbsent(alias, key -> { + GameCommand command = GameCommand.literal(alias) + .requiredPermission(Permission.COMMAND_ROOT) + .executor(COMMAND) + .then(BroadcastCommand.discord(discordSRV)) + .then(BroadcastCommand.minecraft(discordSRV)) + .then(BroadcastCommand.json(discordSRV)) + .then(DebugCommand.getGame(discordSRV)) + .then(LinkInitCommand.getGame(discordSRV)) + .then(LinkedCommand.getGame(discordSRV)) + .then(ReloadCommand.get(discordSRV)) + .then(ResyncCommand.getGame(discordSRV)) + .then(VersionCommand.getGame(discordSRV)); + if (discordSRV.linkProvider() instanceof LinkStore) { + command = command.then(UnlinkCommand.getGame(discordSRV)); + } + + return command; + }); } private final DiscordSRV discordSRV; diff --git a/common/src/main/java/com/discordsrv/common/config/messages/MessagesConfig.java b/common/src/main/java/com/discordsrv/common/config/messages/MessagesConfig.java index 37b25644..fb9fc90b 100644 --- a/common/src/main/java/com/discordsrv/common/config/messages/MessagesConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/messages/MessagesConfig.java @@ -1,12 +1,24 @@ package com.discordsrv.common.config.messages; +import com.discordsrv.api.discord.entity.DiscordUser; +import com.discordsrv.api.placeholder.provider.SinglePlaceholder; +import com.discordsrv.common.DiscordSRV; +import com.discordsrv.common.command.combined.abstraction.CommandExecution; import com.discordsrv.common.config.Config; import com.discordsrv.common.config.configurate.annotation.Constants; import com.discordsrv.common.config.configurate.annotation.Untranslated; import com.discordsrv.common.config.helper.MinecraftMessage; +import com.discordsrv.common.future.util.CompletableFutureUtil; +import com.discordsrv.common.player.IOfflinePlayer; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; +import java.time.Duration; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + @ConfigSerializable public class MessagesConfig implements Config { @@ -17,6 +29,198 @@ public class MessagesConfig implements Config { return FILE_NAME; } + // Helper methods + + private void withPlayer(DiscordSRV discordSRV, UUID playerUUID, Consumer playerConsumer) { + CompletableFuture playerFuture = CompletableFutureUtil.timeout( + discordSRV, + discordSRV.playerProvider().lookupOfflinePlayer(playerUUID), + Duration.ofSeconds(5) + ); + + playerFuture.whenComplete((player, __) -> playerConsumer.accept(player)); + } + + private void withUser(DiscordSRV discordSRV, long userId, Consumer userConsumer) { + CompletableFuture userFuture = CompletableFutureUtil.timeout( + discordSRV, + discordSRV.discordAPI().retrieveUserById(userId), + Duration.ofSeconds(5) + ); + + userFuture.whenComplete((player, __) -> userConsumer.accept(player)); + } + + private void withPlayerAndUser( + DiscordSRV discordSRV, + UUID playerUUID, + long userId, + BiConsumer playerAndUserConsumer + ) { + CompletableFuture playerFuture = CompletableFutureUtil.timeout( + discordSRV, + discordSRV.playerProvider().lookupOfflinePlayer(playerUUID), + Duration.ofSeconds(5) + ); + CompletableFuture userFuture = CompletableFutureUtil.timeout( + discordSRV, + discordSRV.discordAPI().retrieveUserById(userId), + Duration.ofSeconds(5) + ); + + playerFuture.whenComplete((player, __) -> userFuture.whenComplete((user, ___) -> playerAndUserConsumer.accept(player, user))); + } + + // Methods for responding directly to CommandExecutions + + public void playerNotFound(CommandExecution execution) { + execution.send( + minecraft.playerNotFound.asComponent(), + discord.playerNotFound + ); + } + + public void userNotFound(CommandExecution execution) { + execution.send( + minecraft.userNotFound.asComponent(), + discord.userNotFound + ); + } + + public void unableToCheckLinkingStatus(CommandExecution execution) { + execution.send( + minecraft.unableToCheckLinkingStatus.asComponent(), + discord.unableToCheckLinkingStatus + ); + } + + public void playerAlreadyLinked3rd(CommandExecution execution) { + execution.send( + minecraft.playerAlreadyLinked3rd.asComponent(), + discord.playerAlreadyLinked3rd + ); + } + + public void userAlreadyLinked3rd(CommandExecution execution) { + execution.send( + minecraft.userAlreadyLinked3rd.asComponent(), + discord.userAlreadyLinked3rd + ); + } + + public void nowLinked3rd(DiscordSRV discordSRV, CommandExecution execution, UUID playerUUID, long userId) { + withPlayerAndUser(discordSRV, playerUUID, userId, (player, user) -> execution.send( + minecraft.nowLinked3rd.textBuilder() + .applyPlaceholderService() + .addContext(user, player) + .addPlaceholder("user_id", userId) + .addPlaceholder("player_uuid", playerUUID) + .build(), + discordSRV.placeholderService().replacePlaceholders( + execution.messages().discord.nowLinked3rd, + user, + player, + new SinglePlaceholder("user_id", userId), + new SinglePlaceholder("player_uuid", playerUUID) + ) + )); + } + + public void discordUserLinkedTo( + DiscordSRV discordSRV, + CommandExecution execution, + UUID playerUUID, + long userId + ) { + withPlayerAndUser(discordSRV, playerUUID, userId, (player, user) -> execution.send( + minecraft.discordUserLinkedTo + .textBuilder() + .applyPlaceholderService() + .addContext(user, player) + .addPlaceholder("user_id", userId) + .addPlaceholder("player_uuid", playerUUID) + .build(), + discordSRV.placeholderService().replacePlaceholders( + discord.discordUserLinkedTo, + user, + player, + new SinglePlaceholder("user_id", userId), + new SinglePlaceholder("player_uuid", playerUUID) + ) + )); + } + + public void discordUserUnlinked( + DiscordSRV discordSRV, + CommandExecution execution, + long userId + ) { + withUser(discordSRV, userId, (user) -> execution.send( + minecraft.discordUserUnlinked + .textBuilder() + .applyPlaceholderService() + .addContext(user) + .addPlaceholder("user_id", userId) + .build(), + discordSRV.placeholderService().replacePlaceholders( + discord.discordUserUnlinked, + user, + new SinglePlaceholder("user_id", userId) + ) + )); + } + + public void minecraftPlayerLinkedTo( + DiscordSRV discordSRV, + CommandExecution execution, + UUID playerUUID, + long userId + ) { + withPlayerAndUser(discordSRV, playerUUID, userId, (player, user) -> execution.send( + minecraft.minecraftPlayerLinkedTo + .textBuilder() + .applyPlaceholderService() + .addContext(player, user) + .addPlaceholder("player_uuid", playerUUID) + .addPlaceholder("user_id", userId) + .build(), + discordSRV.placeholderService().replacePlaceholders( + discord.minecraftPlayerLinkedTo, + player, + user, + new SinglePlaceholder("player_uuid", playerUUID), + new SinglePlaceholder("user_id", userId) + ) + )); + } + + public void minecraftPlayerUnlinked( + DiscordSRV discordSRV, + CommandExecution execution, + UUID playerUUID + ) { + withPlayer(discordSRV, playerUUID, (player) -> execution.send( + minecraft.minecraftPlayerUnlinked + .textBuilder() + .applyPlaceholderService() + .addContext(player) + .addPlaceholder("player_uuid", playerUUID) + .build(), + discordSRV.placeholderService().replacePlaceholders( + discord.minecraftPlayerUnlinked, + player, + new SinglePlaceholder("player_uuid", playerUUID) + ) + )); + } + + public void unlinked(CommandExecution execution) { + execution.send( + minecraft.unlinked.asComponent(), + discord.unlinked + ); + } + public Minecraft minecraft = new Minecraft(); @ConfigSerializable @@ -45,6 +249,34 @@ public class MessagesConfig implements Config { @Constants(ERROR_COLOR) public MinecraftMessage unableToCheckLinkingStatus = make("%1Unable to check linking status, please try again later"); + @Constants({ + SUCCESS_COLOR + "[hover:show_text:%user_id%][click:copy_to_clipboard:%user_id%]@%user_name%[click][hover]", + NEUTRAL_COLOR, + SUCCESS_COLOR + "[hover:show_text:%player_uuid%][click:copy_to_clipboard:%player_uuid%]%player_name|text:''%[click][hover]" + }) + public MinecraftMessage discordUserLinkedTo = make("%1 %2is linked to %3"); + + @Constants({ + SUCCESS_COLOR + "[hover:show_text:%user_id%][click:copy_to_clipboard:%user_id%]@%user_name%[click][hover]", + NEUTRAL_COLOR, + ERROR_COLOR + }) + public MinecraftMessage discordUserUnlinked = make("%1 %2is %3unlinked"); + + @Constants({ + SUCCESS_COLOR + "[hover:show_text:%player_uuid%][click:copy_to_clipboard:%player_uuid%]%player_name|text:''%[click][hover]", + NEUTRAL_COLOR, + SUCCESS_COLOR + "[hover:show_text:%user_id%][click:copy_to_clipboard:%user_id%]@%user_name%[click][hover]" + }) + public MinecraftMessage minecraftPlayerLinkedTo = make("%1 %2is linked to %3"); + + @Constants({ + SUCCESS_COLOR + "[hover:show_text:%player_uuid%][click:copy_to_clipboard:%player_uuid%]%player_name|text:''%[click][hover]", + NEUTRAL_COLOR, + ERROR_COLOR + }) + public MinecraftMessage minecraftPlayerUnlinked = make("%1 %2is %3unlinked"); + @Untranslated(Untranslated.Type.COMMENT) @Comment("/discord link") @Constants(ERROR_COLOR) @@ -78,36 +310,9 @@ public class MessagesConfig implements Config { public MinecraftMessage minecraftAuthLinking = make("%1Please visit %2 to link your account through %4"); @Untranslated(Untranslated.Type.COMMENT) - @Comment("/discord linked") - @Constants({ - SUCCESS_COLOR + "[hover:show_text:%user_id%][click:copy_to_clipboard:%user_id%]@%user_name%[click][hover]", - NEUTRAL_COLOR, - SUCCESS_COLOR + "[hover:show_text:%player_uuid%][click:copy_to_clipboard:%player_uuid%]%player_name|text:''%[click][hover]" - }) - public MinecraftMessage discordUserLinkedTo = make("%1 %2is linked to %3"); - - @Untranslated(Untranslated.Type.COMMENT) - @Comment("/discord linked") - @Constants({ - SUCCESS_COLOR + "[hover:show_text:%user_id%][click:copy_to_clipboard:%user_id%]@%user_name%[click][hover]", - NEUTRAL_COLOR, - ERROR_COLOR - }) - public MinecraftMessage discordUserUnlinked = make("%1 %2is %3unlinked"); - - @Constants({ - SUCCESS_COLOR + "[hover:show_text:%player_uuid%][click:copy_to_clipboard:%player_uuid%]%player_name|text:''%[click][hover]", - NEUTRAL_COLOR, - SUCCESS_COLOR + "[hover:show_text:%user_id%][click:copy_to_clipboard:%user_id%]@%user_name%[click][hover]" - }) - public MinecraftMessage minecraftPlayerLinkedTo = make("%1 %2is linked to %3"); - - @Constants({ - SUCCESS_COLOR + "[hover:show_text:%player_uuid%][click:copy_to_clipboard:%player_uuid%]%player_name|text:''%[click][hover]", - NEUTRAL_COLOR, - ERROR_COLOR - }) - public MinecraftMessage minecraftPlayerUnlinked = make("%1 %2is %3unlinked"); + @Comment("/discord unlink") + @Constants({SUCCESS_COLOR}) + public MinecraftMessage unlinked = make("%1Accounts unlinked"); } @@ -134,21 +339,6 @@ public class MessagesConfig implements Config { @Constants(ERROR_PREFIX) public String unableToCheckLinkingStatus = "%1Unable to check linking status, please try again later"; - @Untranslated(Untranslated.Type.COMMENT) - @Comment("/discord link") - @Constants(ERROR_PREFIX) - public String playerAlreadyLinked3rd = "%1That Minecraft player is already linked"; - @Constants(ERROR_PREFIX) - public String userAlreadyLinked3rd = "%1That Discord user is already linked"; - @Constants({ - SUCCESS_PREFIX, - "**%player_name%** (%player_uuid%)", - "**%user_name%** (%user_id%)" - }) - public String nowLinked3rd = "%1Link created successfully\n%2 and %3"; - - @Untranslated(Untranslated.Type.COMMENT) - @Comment("/discord linked") @Constants({ SUCCESS_PREFIX, "**%user_name%** (<@%user_id%>)", @@ -156,11 +346,9 @@ public class MessagesConfig implements Config { }) public String discordUserLinkedTo = "%1%2 is linked to %3"; - @Untranslated(Untranslated.Type.COMMENT) - @Comment("/discord linked") @Constants({ ERROR_PREFIX, - "**%user_name%** (%user_id%)" + "**%user_name%** (<@%user_id%>)" }) public String discordUserUnlinked = "%1%2 is __unlinked__"; @@ -176,5 +364,24 @@ public class MessagesConfig implements Config { "**%player_name%** (%player_uuid%)" }) public String minecraftPlayerUnlinked = "%1%2 is __unlinked__"; + + @Untranslated(Untranslated.Type.COMMENT) + @Comment("/discord link") + @Constants(ERROR_PREFIX) + public String playerAlreadyLinked3rd = "%1That Minecraft player is already linked"; + @Constants(ERROR_PREFIX) + public String userAlreadyLinked3rd = "%1That Discord user is already linked"; + @Constants({ + SUCCESS_PREFIX, + "**%player_name%** (%player_uuid%)", + "**%user_name%** (<@%user_id%>)" + }) + public String nowLinked3rd = "%1Link created successfully\n%2 and %3"; + + @Untranslated(Untranslated.Type.COMMENT) + @Comment("/discord unlink") + @Constants({SUCCESS_PREFIX}) + public String unlinked = "%1Accounts unlinked"; + } } diff --git a/common/src/main/java/com/discordsrv/common/permission/Permission.java b/common/src/main/java/com/discordsrv/common/permission/Permission.java index 6652b6a6..5d14bb01 100644 --- a/common/src/main/java/com/discordsrv/common/permission/Permission.java +++ b/common/src/main/java/com/discordsrv/common/permission/Permission.java @@ -11,10 +11,12 @@ public enum Permission { COMMAND_VERSION("command.admin.version"), COMMAND_LINK_OTHER("command.admin.link.other"), COMMAND_LINKED_OTHER("command.admin.linked.other"), + COMMAND_UNLINK_OTHER("command.admin.linked.other"), // Player COMMAND_ROOT("command.player.root"), COMMAND_LINK("command.player.link"), COMMAND_LINKED("command.player.linked"), + COMMAND_UNLINK("command.player.unlink"), // Mentions MENTION_USER("mention.user.base"), diff --git a/common/src/main/java/com/discordsrv/common/storage/impl/sql/SQLStorage.java b/common/src/main/java/com/discordsrv/common/storage/impl/sql/SQLStorage.java index 6063a815..11cd5d06 100644 --- a/common/src/main/java/com/discordsrv/common/storage/impl/sql/SQLStorage.java +++ b/common/src/main/java/com/discordsrv/common/storage/impl/sql/SQLStorage.java @@ -139,7 +139,7 @@ public abstract class SQLStorage implements Storage { @Override public void removeLink(@NotNull UUID player, long userId) { useConnection(connection -> { - try (PreparedStatement statement = connection.prepareStatement("delete " + tablePrefix() + LINKED_ACCOUNTS_TABLE_NAME + " where PLAYER_UUID = ?;")) { + try (PreparedStatement statement = connection.prepareStatement("delete from " + tablePrefix() + LINKED_ACCOUNTS_TABLE_NAME + " where PLAYER_UUID = ?;")) { statement.setString(1, player.toString()); exceptEffectedRows(statement.executeUpdate(), 1); } @@ -168,7 +168,7 @@ public abstract class SQLStorage implements Storage { public UUID getLinkingCode(String code) { return useConnection(connection -> { // Clean expired codes - try (PreparedStatement statement = connection.prepareStatement("DELETE FROM " + tablePrefix() + LINKING_CODES_TABLE_NAME + " where EXPIRY < ?;")) { + try (PreparedStatement statement = connection.prepareStatement("delete from " + tablePrefix() + LINKING_CODES_TABLE_NAME + " where EXPIRY < ?;")) { statement.setLong(1, getTimeMS()); statement.executeUpdate(); } @@ -188,7 +188,7 @@ public abstract class SQLStorage implements Storage { @Override public void removeLinkingCode(@NotNull UUID player) { useConnection(connection -> { - try (PreparedStatement statement = connection.prepareStatement("DELETE FROM " + tablePrefix() + LINKING_CODES_TABLE_NAME + " WHERE PLAYERUUID = ?")) { + try (PreparedStatement statement = connection.prepareStatement("delete from " + tablePrefix() + LINKING_CODES_TABLE_NAME + " WHERE PLAYERUUID = ?")) { statement.setString(1, player.toString()); statement.executeUpdate(); } @@ -199,13 +199,13 @@ public abstract class SQLStorage implements Storage { public void storeLinkingCode(@NotNull UUID player, String code) { useConnection(connection -> { // Remove existing code - try (PreparedStatement statement = connection.prepareStatement("DELETE FROM " + tablePrefix() + LINKING_CODES_TABLE_NAME + " where PLAYERUUID = ?")) { + try (PreparedStatement statement = connection.prepareStatement("delete from " + tablePrefix() + LINKING_CODES_TABLE_NAME + " where PLAYERUUID = ?")) { statement.setString(1, player.toString()); statement.executeUpdate(); } // Insert new code - try (PreparedStatement statement = connection.prepareStatement("INSERT INTO " + tablePrefix() + LINKING_CODES_TABLE_NAME + " (PLAYERUUID, CODE, EXPIRY)")) { + try (PreparedStatement statement = connection.prepareStatement("insert into " + tablePrefix() + LINKING_CODES_TABLE_NAME + " (PLAYERUUID, CODE, EXPIRY)")) { statement.setString(1, player.toString()); statement.setString(2, code); statement.setLong(3, getTimeMS() + LinkStore.LINKING_CODE_EXPIRY_TIME.toMillis());