Plan/Plan/common/src/main/java/com/djrapitops/plan/commands/subcommands/DataUtilityCommands.java

288 lines
14 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.subcommands;
import com.djrapitops.plan.commands.use.Arguments;
import com.djrapitops.plan.commands.use.CMDSender;
import com.djrapitops.plan.delivery.domain.DateHolder;
import com.djrapitops.plan.delivery.domain.container.PlayerContainer;
import com.djrapitops.plan.delivery.domain.keys.PlayerKeys;
import com.djrapitops.plan.delivery.domain.mutators.ActivityIndex;
import com.djrapitops.plan.delivery.domain.mutators.GeoInfoMutator;
import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator;
import com.djrapitops.plan.delivery.export.Exporter;
import com.djrapitops.plan.delivery.formatting.Formatter;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.exceptions.ExportException;
import com.djrapitops.plan.gathering.domain.GeoInfo;
import com.djrapitops.plan.gathering.domain.event.JoinAddress;
import com.djrapitops.plan.gathering.importing.ImportSystem;
import com.djrapitops.plan.gathering.importing.importers.Importer;
import com.djrapitops.plan.identification.Identifiers;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.processing.Processing;
import com.djrapitops.plan.settings.Permissions;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.ExportSettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.CommandLang;
import com.djrapitops.plan.settings.locale.lang.GenericLang;
import com.djrapitops.plan.settings.locale.lang.HelpLang;
import com.djrapitops.plan.settings.locale.lang.HtmlLang;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.containers.ContainerFetchQueries;
import com.djrapitops.plan.storage.database.queries.objects.UserIdentifierQueries;
import com.djrapitops.plan.utilities.dev.Untrusted;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
import java.util.function.Consumer;
@Singleton
public class DataUtilityCommands {
private final Locale locale;
private final PlanConfig config;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final Identifiers identifiers;
private final Formatters formatters;
private final Exporter exporter;
private final ImportSystem importSystem;
private final Processing processing;
@Inject
public DataUtilityCommands(
Locale locale,
PlanConfig config,
DBSystem dbSystem,
ServerInfo serverInfo,
Identifiers identifiers,
Formatters formatters,
Exporter exporter,
ImportSystem importSystem,
Processing processing
) {
this.locale = locale;
this.config = config;
this.dbSystem = dbSystem;
this.serverInfo = serverInfo;
this.identifiers = identifiers;
this.formatters = formatters;
this.exporter = exporter;
this.importSystem = importSystem;
this.processing = processing;
}
private void ensureDatabaseIsOpen() {
Database.State dbState = dbSystem.getDatabase().getState();
if (dbState != Database.State.OPEN) {
throw new IllegalArgumentException(locale.getString(CommandLang.FAIL_DATABASE_NOT_OPEN, dbState.name()));
}
}
public void onExport(CMDSender sender, @Untrusted Arguments arguments) {
@Untrusted String exportKind = arguments.get(0)
.orElseThrow(() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_ACCEPTS_ARGUMENTS, locale.getString(HelpLang.ARG_EXPORT_KIND), "players, server_json")));
ensureDatabaseIsOpen();
getExportFunction(exportKind).accept(sender);
}
private Consumer<CMDSender> getExportFunction(@Untrusted String exportArg) {
if ("players".equals(exportArg)) {
return this::exportPlayers;
} else if ("server_json".endsWith(exportArg)) {
return this::exportServerJSON;
}
throw new IllegalArgumentException(locale.getString(CommandLang.FAIL_EXPORTER_NOT_FOUND, exportArg));
}
private void exportServerJSON(CMDSender sender) {
if (config.isFalse(ExportSettings.SERVER_JSON)) {
throw new IllegalArgumentException("'" + ExportSettings.SERVER_JSON.getPath() + "': false");
}
processing.submitNonCritical(() -> {
try {
sender.send(locale.getString(CommandLang.PROGRESS_START));
if (exporter.exportServerJSON(serverInfo.getServer())) {
sender.send(locale.getString(CommandLang.PROGRESS_SUCCESS));
} else {
sender.send(locale.getString(
CommandLang.PROGRESS_FAIL,
locale.getString(CommandLang.FAIL_SEE_CONFIG_SETTING, ExportSettings.SERVER_JSON.getPath())
));
}
} catch (ExportException e) {
sender.send(locale.get(CommandLang.PROGRESS_FAIL).toString(e.getMessage()));
}
});
}
private void exportPlayers(CMDSender sender) {
boolean exportPlayerJSON = config.isTrue(ExportSettings.PLAYER_JSON);
boolean exportPlayerHTML = config.isTrue(ExportSettings.PLAYER_PAGES);
boolean exportPlayersHtml = config.isTrue(ExportSettings.PLAYERS_PAGE);
if (!exportPlayerJSON && !exportPlayerHTML) {
throw new IllegalArgumentException("'" + ExportSettings.PLAYER_JSON.getPath() + "' & '" + ExportSettings.PLAYER_PAGES.getPath() + "': false (config.yml)");
}
if (exportPlayersHtml) {
processing.submitNonCritical(exporter::exportPlayersPage);
}
processing.submitNonCritical(() -> performExport(sender, exportPlayerJSON, exportPlayerHTML));
}
private void performExport(CMDSender sender, boolean exportPlayerJSON, boolean exportPlayerHTML) {
sender.send(locale.getString(CommandLang.PROGRESS_START));
Map<UUID, String> players = dbSystem.getDatabase().query(UserIdentifierQueries.fetchAllPlayerNames());
int outOf = players.size();
int failed = 0;
int current = 1;
for (Map.Entry<UUID, String> entry : players.entrySet()) {
try {
if (exportPlayerJSON) exporter.exportPlayerJSON(entry.getKey(), entry.getValue());
if (exportPlayerHTML) exporter.exportPlayerPage(entry.getKey(), entry.getValue());
} catch (ExportException e) {
failed++;
}
current++;
if (current % 1000 == 0) {
sender.send(locale.getString(CommandLang.PROGRESS, current, outOf));
}
}
sender.send(locale.getString(CommandLang.PROGRESS_SUCCESS));
if (failed != 0) {
sender.send(locale.getString(CommandLang.PROGRESS_FAIL));
sender.send(" §2✔: §f" + (current - failed));
sender.send(" §c✕: §f" + failed);
}
}
public void onImport(CMDSender sender, @Untrusted Arguments arguments) {
@Untrusted String importKind = arguments.get(0)
.orElseThrow(() -> new IllegalArgumentException(locale.getString(CommandLang.FAIL_ACCEPTS_ARGUMENTS, locale.getString(HelpLang.ARG_IMPORT_KIND), importSystem.getImporterNames().toString())));
ensureDatabaseIsOpen();
findAndProcessImporter(sender, importKind);
}
private void findAndProcessImporter(CMDSender sender, @Untrusted String importKind) {
Optional<Importer> foundImporter = importSystem.getImporter(importKind);
if (foundImporter.isPresent()) {
Importer importer = foundImporter.get();
processing.submitNonCritical(() -> {
sender.send(locale.getString(CommandLang.PROGRESS_START));
importer.processImport();
sender.send(locale.getString(CommandLang.PROGRESS_SUCCESS));
});
} else {
sender.send(locale.getString(CommandLang.FAIL_IMPORTER_NOT_FOUND, importKind));
}
}
public void onSearch(CMDSender sender, @Untrusted Arguments arguments) {
@Untrusted String searchingFor = arguments.concatenate(" ");
if (searchingFor.trim().isEmpty()) {
throw new IllegalArgumentException(locale.getString(CommandLang.FAIL_EMPTY_SEARCH_STRING));
}
ensureDatabaseIsOpen();
List<String> names = dbSystem.getDatabase().query(UserIdentifierQueries.fetchMatchingPlayerNames(searchingFor));
Collections.sort(names);
sender.send(locale.getString(CommandLang.HEADER_SEARCH, names.isEmpty() ? 0 : names.size(), searchingFor));
StringBuilder asTableString = new StringBuilder();
int i = 0;
for (String name : names) {
asTableString.append(name).append(i != 0 && i % 5 == 0 ? '\n' : "::");
i++;
}
sender.send(sender.getFormatter().table(asTableString.toString(), "::"));
}
public void onInGame(CMDSender sender, @Untrusted Arguments arguments) {
@Untrusted String identifier = arguments.concatenate(" ");
UUID playerUUID = identifiers.getPlayerUUID(identifier);
UUID senderUUID = sender.getUUID().orElse(null);
if (playerUUID == null) playerUUID = senderUUID;
if (playerUUID == null) {
throw new IllegalArgumentException(locale.getString(CommandLang.FAIL_PLAYER_NOT_FOUND, identifier));
}
PlayerContainer player = dbSystem.getDatabase().query(ContainerFetchQueries.fetchPlayerContainer(playerUUID));
if (player.getValue(PlayerKeys.REGISTERED).isEmpty()) {
throw new IllegalArgumentException(locale.getString(CommandLang.FAIL_PLAYER_NOT_FOUND_REGISTER, identifier));
}
if (sender.hasPermission(Permissions.INGAME_OTHER) || playerUUID.equals(senderUUID)) {
sendInGameMessages(sender, player);
} else {
throw new IllegalArgumentException(locale.getString(CommandLang.FAIL_NO_PERMISSION) + " (" + Permissions.INGAME_OTHER.get() + ')');
}
}
private void sendInGameMessages(CMDSender sender, PlayerContainer player) {
long now = System.currentTimeMillis();
com.djrapitops.plan.delivery.formatting.Formatter<DateHolder> timestamp = formatters.year();
Formatter<Long> length = formatters.timeAmount();
String playerName = player.getValue(PlayerKeys.NAME).orElse(locale.getString(GenericLang.UNKNOWN));
ActivityIndex activityIndex = player.getActivityIndex(now, config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD));
Long registered = player.getValue(PlayerKeys.REGISTERED).orElse(0L);
Long lastSeen = player.getValue(PlayerKeys.LAST_SEEN).orElse(0L);
List<GeoInfo> geoInfo = player.getValue(PlayerKeys.GEO_INFO).orElse(new ArrayList<>());
Optional<GeoInfo> mostRecentGeoInfo = new GeoInfoMutator(geoInfo).mostRecent();
String geolocation = mostRecentGeoInfo.isPresent() ? mostRecentGeoInfo.get().getGeolocation() : "-";
SessionsMutator sessionsMutator = SessionsMutator.forContainer(player);
String latestJoinAddress = sessionsMutator.latestSession()
.flatMap(session -> session.getExtraData(JoinAddress.class))
.map(JoinAddress::getAddress)
.orElse("-");
String table = locale.getString(CommandLang.HEADER_INSPECT, playerName) + '\n' +
locale.getString(CommandLang.INGAME_ACTIVITY_INDEX, activityIndex.getFormattedValue(formatters.decimals()), activityIndex.getGroup()) + '\n' +
locale.getString(CommandLang.INGAME_REGISTERED, timestamp.apply(() -> registered)) + '\n' +
locale.getString(CommandLang.INGAME_LAST_SEEN, timestamp.apply(() -> lastSeen)) + '\n' +
locale.getString(CommandLang.INGAME_GEOLOCATION, geolocation) + '\n' +
locale.getString(HtmlLang.TITLE_LATEST_JOIN_ADDRESSES, latestJoinAddress) + '\n' +
locale.getString(CommandLang.INGAME_TIMES_KICKED, player.getValue(PlayerKeys.KICK_COUNT).orElse(0)) + '\n' +
'\n' +
locale.getString(CommandLang.INGAME_PLAYTIME, length.apply(sessionsMutator.toPlaytime())) + '\n' +
locale.getString(CommandLang.INGAME_ACTIVE_PLAYTIME, length.apply(sessionsMutator.toActivePlaytime())) + '\n' +
locale.getString(CommandLang.INGAME_AFK_PLAYTIME, length.apply(sessionsMutator.toAfkTime())) + '\n' +
locale.getString(CommandLang.INGAME_LONGEST_SESSION, length.apply(sessionsMutator.toLongestSessionLength())) + '\n' +
'\n' +
locale.getString(CommandLang.INGAME_PLAYER_KILLS, sessionsMutator.toPlayerKillCount()) + '\n' +
locale.getString(CommandLang.INGAME_MOB_KILLS, sessionsMutator.toMobKillCount()) + '\n' +
locale.getString(CommandLang.INGAME_DEATHS, sessionsMutator.toDeathCount());
sender.send(sender.getFormatter().table(table, ": "));
}
}