Plan/Plan/common/src/main/java/com/djrapitops/plan/commands/PlanCommand.java

551 lines
24 KiB
Java

/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.commands;
import com.djrapitops.plan.commands.subcommands.*;
import com.djrapitops.plan.commands.use.*;
import com.djrapitops.plan.gathering.importing.ImportSystem;
import com.djrapitops.plan.settings.Permissions;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.WebserverSettings;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.DeepHelpLang;
import com.djrapitops.plan.settings.locale.lang.HelpLang;
import com.djrapitops.plan.storage.database.DBType;
import com.djrapitops.plan.utilities.dev.Untrusted;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
@Singleton
public class PlanCommand {
private final String commandName;
private final ColorScheme colors;
private final Confirmation confirmation;
private final TabCompleteCache tabCompleteCache;
private final LinkCommands linkCommands;
private final RegistrationCommands registrationCommands;
private final PluginStatusCommands statusCommands;
private final DatabaseCommands databaseCommands;
private final DataUtilityCommands dataUtilityCommands;
private final Locale locale;
private final PlanConfig config;
private final ImportSystem importSystem;
private final ErrorLogger errorLogger;
private static final String DB_ARG_OPTIONS = "MySQL/SQLite";
@Inject
public PlanCommand(
@Named("mainCommandName") String commandName,
Locale locale,
ColorScheme colors,
Confirmation confirmation,
TabCompleteCache tabCompleteCache,
ImportSystem importSystem,
LinkCommands linkCommands,
RegistrationCommands registrationCommands,
PluginStatusCommands statusCommands,
DatabaseCommands databaseCommands,
DataUtilityCommands dataUtilityCommands,
PlanConfig config,
ErrorLogger errorLogger
) {
this.commandName = commandName;
this.locale = locale;
this.colors = colors;
this.confirmation = confirmation;
this.tabCompleteCache = tabCompleteCache;
this.importSystem = importSystem;
this.linkCommands = linkCommands;
this.registrationCommands = registrationCommands;
this.statusCommands = statusCommands;
this.databaseCommands = databaseCommands;
this.dataUtilityCommands = dataUtilityCommands;
this.config = config;
this.errorLogger = errorLogger;
}
private void handleException(RuntimeException error, CMDSender sender, @Untrusted Arguments arguments) {
if (error instanceof IllegalArgumentException) {
sender.send("§c" + error.getMessage());
} else {
errorLogger.warn(error, ErrorContext.builder().related(sender, arguments).build());
}
}
public CommandWithSubcommands build() {
CommandWithSubcommands command = CommandWithSubcommands.builder(locale)
.alias(commandName)
.colorScheme(colors)
.subcommand(serverCommand())
.subcommand(serversCommand())
.subcommand(networkCommand())
.subcommand(playerCommand())
.subcommand(playersCommand())
.subcommand(searchCommand())
.subcommand(inGameCommand())
.subcommand(jsonCommand())
.subcommand(registerCommand())
.subcommand(unregisterCommand())
.subcommand(logoutCommand())
.subcommand(webUsersCommand())
.subcommand(groups())
.subcommand(setGroup())
.subcommand(acceptCommand())
.subcommand(cancelCommand())
.subcommand(infoCommand())
.subcommand(reloadCommand())
.subcommand(disableCommand())
.subcommand(databaseCommand())
.subcommand(exportCommand())
.subcommand(importCommand())
.exceptionHandler(this::handleException)
.build();
if (!"plan".equalsIgnoreCase(commandName)) {
command.getAliases().add("planbungee");
command.getAliases().add("planvelocity");
command.getAliases().add("planproxy");
command.getAliases().add("planp");
}
return command;
}
public List<String> serverNames(CMDSender sender, @Untrusted Arguments arguments) {
@Untrusted String asString = arguments.concatenate(" ");
return tabCompleteCache.getMatchingServerIdentifiers(asString);
}
private List<String> playerNames(CMDSender sender, @Untrusted Arguments arguments) {
@Untrusted String asString = arguments.concatenate(" ");
return tabCompleteCache.getMatchingPlayerIdentifiers(asString);
}
private Subcommand serverCommand() {
return Subcommand.builder()
.aliases("server", "analyze", "a", "analyse", "analysis")
.optionalArgument(locale.getString(HelpLang.ARG_SERVER), locale.getString(HelpLang.DESC_ARG_SERVER_IDENTIFIER))
.requirePermission(Permissions.SERVER)
.description(locale.getString(HelpLang.SERVER))
.inDepthDescription(locale.getString(DeepHelpLang.SERVER))
.onCommand(linkCommands::onServerCommand)
.onTabComplete(this::serverNames)
.build();
}
private Subcommand serversCommand() {
return Subcommand.builder()
.aliases("servers", "serverlist", "listservers", "sl", "ls")
.requirePermission(Permissions.SERVERS)
.description(locale.getString(HelpLang.SERVERS))
.inDepthDescription(locale.getString(DeepHelpLang.SERVERS))
.onCommand(linkCommands::onServersCommand)
.build();
}
private Subcommand networkCommand() {
return Subcommand.builder()
.aliases("network", "netw")
.requirePermission(Permissions.NETWORK)
.description(locale.getString(HelpLang.NETWORK))
.inDepthDescription(locale.getString(DeepHelpLang.NETWORK))
.onCommand(linkCommands::onNetworkCommand)
.build();
}
private Subcommand playerCommand() {
return Subcommand.builder()
.aliases("player", "inspect")
.optionalArgument(locale.getString(HelpLang.ARG_NAME_UUID), locale.getString(HelpLang.DESC_ARG_PLAYER_IDENTIFIER))
.requirePermission(Permissions.PLAYER_SELF)
.description(locale.getString(HelpLang.PLAYER))
.inDepthDescription(locale.getString(DeepHelpLang.PLAYER))
.onCommand(linkCommands::onPlayerCommand)
.onTabComplete(this::playerNames)
.build();
}
private Subcommand playersCommand() {
return Subcommand.builder()
.aliases("players", "pl", "playerlist", "list")
.requirePermission(Permissions.PLAYER_OTHER)
.description(locale.getString(HelpLang.PLAYERS))
.inDepthDescription(locale.getString(DeepHelpLang.PLAYERS))
.onCommand(linkCommands::onPlayersCommand)
.build();
}
private Subcommand searchCommand() {
return Subcommand.builder()
.aliases("search")
.requiredArgument(locale.getString(HelpLang.ARG_NAME_UUID), locale.getString(HelpLang.DESC_ARG_PLAYER_IDENTIFIER))
.requirePermission(Permissions.SEARCH)
.description(locale.getString(HelpLang.SEARCH))
.inDepthDescription(locale.getString(DeepHelpLang.SEARCH))
.onCommand(dataUtilityCommands::onSearch)
.build();
}
private Subcommand inGameCommand() {
return Subcommand.builder()
.aliases("ingame", "qinspect")
.optionalArgument(locale.getString(HelpLang.ARG_NAME_UUID), locale.getString(HelpLang.DESC_ARG_PLAYER_IDENTIFIER))
.requirePermission(Permissions.INGAME_SELF)
.description(locale.getString(HelpLang.INGAME))
.inDepthDescription(locale.getString(DeepHelpLang.INGAME))
.onCommand(dataUtilityCommands::onInGame)
.onTabComplete(this::playerNames)
.build();
}
private Subcommand registerCommand() {
if (config.isTrue(WebserverSettings.DISABLED_AUTHENTICATION) || config.isTrue(WebserverSettings.DISABLED_REGISTRATION)) {
return null;
}
return Subcommand.builder()
.aliases("register")
.requirePermission(Permissions.REGISTER_SELF)
.optionalArgument("--code " + locale.getString(HelpLang.ARG_CODE), locale.getString(HelpLang.DESC_ARG_CODE))
.description(locale.getString(HelpLang.REGISTER))
.inDepthDescription(locale.getString(DeepHelpLang.REGISTER))
.onCommand(registrationCommands::onRegister)
.onTabComplete((sender, arguments) -> arguments.isEmpty() ? Collections.singletonList("--code") : Collections.emptyList())
.build();
}
private Subcommand unregisterCommand() {
return Subcommand.builder()
.aliases("unregister")
.requirePermission(Permissions.UNREGISTER_SELF)
.optionalArgument(locale.getString(HelpLang.ARG_USERNAME), locale.getString(HelpLang.DESC_ARG_USERNAME))
.description(locale.getString(HelpLang.UNREGISTER))
.inDepthDescription(locale.getString(DeepHelpLang.UNREGISTER))
.onCommand(registrationCommands::onUnregister)
.onTabComplete(this::webUserNames)
.build();
}
private Subcommand logoutCommand() {
if (config.isTrue(WebserverSettings.DISABLED_AUTHENTICATION)) {
return null;
}
return Subcommand.builder()
.aliases("logout")
.requirePermission(Permissions.LOGOUT_OTHER)
.requiredArgument(locale.getString(HelpLang.ARG_USERNAME), locale.getString(HelpLang.DESC_ARG_USERNAME))
.description(locale.getString(HelpLang.LOGOUT))
.inDepthDescription(locale.getString(DeepHelpLang.LOGOUT))
.onArgsOnlyCommand(registrationCommands::onLogoutCommand)
.onTabComplete(this::webUserNames)
.build();
}
private List<String> webUserNames(CMDSender sender, @Untrusted Arguments arguments) {
if (!sender.hasPermission(Permissions.UNREGISTER_OTHER)) {
return Collections.emptyList();
}
@Untrusted String username = arguments.concatenate(" ");
return tabCompleteCache.getMatchingUserIdentifiers(username);
}
private Subcommand acceptCommand() {
return Subcommand.builder()
.aliases("accept", "yes", "y")
.onCommand(confirmation::onAcceptCommand)
.build();
}
private Subcommand cancelCommand() {
return Subcommand.builder()
.aliases("cancel", "deny", "no", "n")
.onCommand(confirmation::onCancelCommand)
.build();
}
private Subcommand infoCommand() {
return Subcommand.builder()
.aliases("info")
.requirePermission(Permissions.INFO)
.description(locale.getString(HelpLang.INFO))
.inDepthDescription(locale.getString(DeepHelpLang.INFO))
.onCommand(statusCommands::onInfo)
.build();
}
private Subcommand reloadCommand() {
return Subcommand.builder()
.aliases("reload")
.requirePermission(Permissions.RELOAD)
.description(locale.getString(HelpLang.RELOAD))
.inDepthDescription(locale.getString(DeepHelpLang.RELOAD))
.onCommand(statusCommands::onReload)
.build();
}
private Subcommand disableCommand() {
return Subcommand.builder()
.aliases("disable")
.requirePermission(Permissions.DISABLE)
.optionalArgument(locale.getString(HelpLang.ARG_FEATURE), locale.getString(HelpLang.DESC_ARG_FEATURE, "kickcount"))
.description(locale.getString(HelpLang.DISABLE))
.inDepthDescription(locale.getString(DeepHelpLang.DISABLE))
.onCommand(statusCommands::onDisable)
.onTabComplete((sender, arguments) ->
arguments.isEmpty() ? Collections.singletonList("kickcount") : Collections.emptyList()
).build();
}
private Subcommand webUsersCommand() {
return Subcommand.builder()
.aliases("users", "webusers", "web")
.requirePermission(Permissions.USERS)
.description(locale.getString(HelpLang.USERS))
.inDepthDescription(locale.getString(DeepHelpLang.USERS))
.onCommand(linkCommands::onWebUsersCommand)
.build();
}
private Subcommand databaseCommand() {
return CommandWithSubcommands.builder(locale)
.aliases("db", "database")
.optionalArgument(locale.getString(HelpLang.ARG_SUBCOMMAND), locale.getString(HelpLang.DESC_ARG_SUBCOMMAND))
.colorScheme(colors)
.subcommand(backupCommand())
.subcommand(restoreCommand())
.subcommand(moveCommand())
.subcommand(hotswapCommand())
.subcommand(clearCommand())
.subcommand(removeCommand())
.subcommand(uninstalledCommand())
.subcommand(removeJoinAddressesCommand())
.subcommand(onlineUuidMigration())
.requirePermission(Permissions.DATA_BASE)
.description(locale.getString(HelpLang.DB))
.inDepthDescription(locale.getString(DeepHelpLang.DB))
.build();
}
private Subcommand onlineUuidMigration() {
return Subcommand.builder()
.aliases("migrate_to_online_uuids", "migratetoonlineuuids")
.requirePermission(Permissions.DATA_CLEAR)
.optionalArgument("--remove_offline", "Remove offline players if given")
.description(locale.getString(HelpLang.ONLINE_UUID_MIGRATION))
.inDepthDescription("Moves and combines offline uuid data to online uuids where possible. Leaves offline-only players to database.")
.onCommand(databaseCommands::onOnlineConversion)
.build();
}
private Subcommand removeJoinAddressesCommand() {
return Subcommand.builder()
.aliases("remove_join_addresses", "removejoinaddresses")
.requirePermission(Permissions.DATA_CLEAR)
.requiredArgument(locale.getString(HelpLang.ARG_SERVER), locale.getString(HelpLang.DESC_ARG_SERVER_IDENTIFIER))
.description(locale.getString(HelpLang.JOIN_ADDRESS_REMOVAL))
.onCommand(databaseCommands::onFixFabricJoinAddresses)
.onTabComplete(this::serverNames)
.build();
}
private Subcommand backupCommand() {
return Subcommand.builder()
.aliases("backup")
.requirePermission(Permissions.DATA_BACKUP)
.optionalArgument(DB_ARG_OPTIONS, locale.getString(HelpLang.DESC_ARG_DB_BACKUP))
.description(locale.getString(HelpLang.DB_BACKUP))
.inDepthDescription(locale.getString(DeepHelpLang.DB_BACKUP))
.onCommand(databaseCommands::onBackup)
.onTabComplete((sender, arguments) ->
arguments.isEmpty() ? DBType.names() : Collections.emptyList()
).build();
}
private Subcommand restoreCommand() {
return Subcommand.builder()
.aliases("restore")
.requirePermission(Permissions.DATA_RESTORE)
.requiredArgument(locale.getString(HelpLang.ARG_BACKUP_FILE), locale.getString(HelpLang.DESC_ARG_BACKUP_FILE))
.optionalArgument(DB_ARG_OPTIONS, locale.getString(HelpLang.DESC_ARG_DB_RESTORE))
.description(locale.getString(HelpLang.DB_RESTORE))
.inDepthDescription(locale.getString(DeepHelpLang.DB_RESTORE))
.onCommand(databaseCommands::onRestore)
.onTabComplete(this::getBackupFilenames)
.build();
}
private List<String> getBackupFilenames(CMDSender sender, @Untrusted Arguments arguments) {
if (arguments.get(1).isPresent()) {
return DBType.names();
}
@Untrusted Optional<String> firstArgument = arguments.get(0);
if (firstArgument.isEmpty()) {
return tabCompleteCache.getMatchingBackupFilenames(null);
}
@Untrusted String part = firstArgument.get();
return tabCompleteCache.getMatchingBackupFilenames(part);
}
private Subcommand moveCommand() {
return Subcommand.builder()
.aliases("move")
.requirePermission(Permissions.DATA_MOVE)
.requiredArgument(DB_ARG_OPTIONS, locale.getString(HelpLang.DESC_ARG_DB_MOVE_FROM))
.requiredArgument(DB_ARG_OPTIONS, locale.getString(HelpLang.DESC_ARG_DB_MOVE_TO))
.description(locale.getString(HelpLang.DB_MOVE))
.inDepthDescription(locale.getString(DeepHelpLang.DB_MOVE))
.onCommand(databaseCommands::onMove)
.onTabComplete((sender, arguments) -> DBType.names())
.build();
}
private Subcommand hotswapCommand() {
return Subcommand.builder()
.aliases("hotswap")
.requirePermission(Permissions.DATA_HOTSWAP)
.requiredArgument(DB_ARG_OPTIONS, locale.getString(HelpLang.DESC_ARG_DB_HOTSWAP))
.description(locale.getString(HelpLang.DB_HOTSWAP))
.inDepthDescription(locale.getString(DeepHelpLang.DB_HOTSWAP))
.onCommand(databaseCommands::onHotswap)
.onTabComplete((sender, arguments) ->
arguments.isEmpty() ? DBType.names() : Collections.emptyList()
).build();
}
private Subcommand clearCommand() {
return Subcommand.builder()
.aliases("clear")
.requirePermission(Permissions.DATA_CLEAR)
.requiredArgument(DB_ARG_OPTIONS, locale.getString(HelpLang.DESC_ARG_DB_REMOVE))
.description(locale.getString(HelpLang.DB_CLEAR))
.inDepthDescription(locale.getString(DeepHelpLang.DB_CLEAR))
.onCommand(databaseCommands::onClear)
.onTabComplete((sender, arguments) ->
arguments.isEmpty() ? DBType.names() : Collections.emptyList()
).build();
}
private Subcommand removeCommand() {
return Subcommand.builder()
.aliases("remove")
.requirePermission(Permissions.DATA_REMOVE_PLAYER)
.requiredArgument(locale.getString(HelpLang.ARG_NAME_UUID), locale.getString(HelpLang.DESC_ARG_PLAYER_IDENTIFIER_REMOVE))
.description(locale.getString(HelpLang.DB_REMOVE))
.inDepthDescription(locale.getString(DeepHelpLang.DB_REMOVE))
.onCommand(databaseCommands::onRemove)
.onTabComplete(this::playerNames)
.build();
}
private Subcommand uninstalledCommand() {
return Subcommand.builder()
.aliases("uninstalled")
.requirePermission(Permissions.DATA_REMOVE_SERVER)
.requiredArgument(locale.getString(HelpLang.ARG_SERVER), locale.getString(HelpLang.DESC_ARG_SERVER_IDENTIFIER))
.description(locale.getString(HelpLang.DB_UNINSTALLED))
.inDepthDescription(locale.getString(DeepHelpLang.DB_UNINSTALLED))
.onCommand(databaseCommands::onUninstalled)
.onTabComplete(this::serverNames)
.build();
}
private Subcommand exportCommand() {
return Subcommand.builder()
.aliases("export")
.requirePermission(Permissions.DATA_EXPORT)
.optionalArgument(locale.getString(HelpLang.ARG_EXPORT_KIND), "players/server_json")
.description(locale.getString(HelpLang.EXPORT))
.inDepthDescription(locale.getString(DeepHelpLang.EXPORT))
.onCommand(dataUtilityCommands::onExport)
.onTabComplete((sender, arguments) ->
arguments.isEmpty() ? Arrays.asList("players", "server_json") : Collections.emptyList()
).build();
}
private Subcommand importCommand() {
List<String> importerNames = importSystem.getImporterNames();
if (importerNames.isEmpty()) return null;
return Subcommand.builder()
.aliases("import")
.requirePermission(Permissions.DATA_IMPORT)
.optionalArgument(locale.getString(HelpLang.ARG_IMPORT_KIND), importerNames.toString())
.description(locale.getString(HelpLang.IMPORT))
.inDepthDescription(locale.getString(DeepHelpLang.IMPORT))
.onCommand(dataUtilityCommands::onImport)
.onTabComplete((sender, arguments) ->
arguments.isEmpty() ? importerNames : Collections.emptyList()
).build();
}
private Subcommand jsonCommand() {
return Subcommand.builder()
.aliases("json", "raw")
.requirePermission(Permissions.JSON_SELF)
.optionalArgument(locale.getString(HelpLang.ARG_NAME_UUID), locale.getString(HelpLang.DESC_ARG_PLAYER_IDENTIFIER))
.description(locale.getString(HelpLang.JSON))
.inDepthDescription(locale.getString(DeepHelpLang.JSON))
.onCommand(linkCommands::onJson)
.onTabComplete(this::playerNames)
.build();
}
private Subcommand setGroup() {
return Subcommand.builder()
.aliases("setgroup")
.requirePermission(Permissions.SET_GROUP)
.requiredArgument(locale.getString(HelpLang.ARG_USERNAME), locale.getString(HelpLang.DESC_ARG_USERNAME))
.requiredArgument(locale.getString(HelpLang.ARG_GROUP), locale.getString(HelpLang.DESC_ARG_GROUP))
.description(locale.getString(HelpLang.SET_GROUP))
.inDepthDescription(locale.getString(DeepHelpLang.SET_GROUP))
.onCommand(registrationCommands::onChangePermissionGroup)
.onTabComplete(this::webGroupTabComplete)
.build();
}
private List<String> webGroupTabComplete(CMDSender sender, @Untrusted Arguments arguments) {
Optional<String> groupArgument = arguments.get(1);
if (groupArgument.isPresent()) {
return tabCompleteCache.getMatchingWebGroupNames(groupArgument.get());
}
String usernameArgument = arguments.get(0).orElse(null);
return tabCompleteCache.getMatchingUserIdentifiers(usernameArgument);
}
private Subcommand groups() {
return Subcommand.builder()
.aliases("groups")
.requirePermission(Permissions.SET_GROUP)
.description(locale.getString(HelpLang.GROUPS))
.inDepthDescription(locale.getString(DeepHelpLang.GROUPS))
.onCommand(registrationCommands::onListWebGroups)
.build();
}
}