<commit_before>package top.quantic.sentry.discord;

import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.util.EmbedBuilder;
import top.quantic.sentry.config.Constants;
import top.quantic.sentry.discord.core.Command;
import top.quantic.sentry.discord.core.CommandBuilder;
import top.quantic.sentry.discord.module.CommandSupplier;
import top.quantic.sentry.domain.GameServer;
import top.quantic.sentry.domain.Setting;
import top.quantic.sentry.service.GameServerService;
import top.quantic.sentry.service.SettingService;
import top.quantic.sentry.service.util.Monitor;
import top.quantic.sentry.service.util.Result;

import java.awt.*;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import static com.google.common.collect.Multimaps.toMultimap;
import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCause;
import static top.quantic.sentry.discord.util.DiscordUtil.*;
import static top.quantic.sentry.service.util.DateUtil.*;
import static top.quantic.sentry.service.util.DateUtil.humanizeShort;
import static top.quantic.sentry.service.util.MiscUtil.getIPAddress;
import static top.quantic.sentry.service.util.MiscUtil.inflect;

@Component
public class GameServers implements CommandSupplier {

    private static final Logger log = LoggerFactory.getLogger(GameServers.class);

    private final GameServerService gameServerService;
    private final SettingService settingService;

    @Autowired
    public GameServers(GameServerService gameServerService, SettingService settingService) {
        this.gameServerService = gameServerService;
        this.settingService = settingService;
    }

    @Override
    public List<Command> getCommands() {
        return Arrays.asList(rcon(), servers(), server());
    }

    private Command server() {
        return CommandBuilder.of("server")
            .describedAs("Perform operations on UGC managed GameServers")
            .in("Integrations")
            .withExamples(serverExamples())
            .nonParsed()
            .secured()
            .onExecute(context -> {
                IMessage message = context.getMessage();
                String[] args = safeSplit(context.getArgs(), 3);
                if (args == null || args.length < 2) {
                    answer(message, "Please specify at least two arguments: action, server");
                    return;
                }
                String action = args[0];
                String serverQuery = args[1];
                List<GameServer> targets = gameServerService.findServersMultiple(Arrays.asList(serverQuery.split(",|;")));
                if ("restart".equals(action)) {
                    String response = "Restarting servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        response = resultLine(target, gameServerService.tryRestart(target));
                    }
                    answer(message, response);
                } else if ("stop".equals(action)) {
                    String response = "Stopping servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        response = resultLine(target, gameServerService.tryStop(target));
                    }
                    answer(message, response);
                } else if ("update".equals(action)) {
                    String response = "Updating game version on servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        response = resultLine(target, gameServerService.tryUpdate(target));
                    }
                    answer(message, response);
                } else if ("install-mod".equals(action)) {
                    if (args.length < 3) {
                        answer(message, "You must add an extra argument with the mod ID or name");
                        return;
                    }
                    String mod = args[2];
                    Optional<Setting> setting = settingService.findMostRecentByGuildAndKey(Constants.ANY, mod);
                    String modName = setting.isPresent() ? setting.get().getValue() : mod;
                    String response = "Installing mod " + modName + " on servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        response = resultLine(target, gameServerService.tryModInstall(target, modName));
                    }
                    answerPrivately(message, response);
                } else if ("status".equals(action)) {
                    answerPrivately(message, "Retrieving status for servers matching " + serverQuery);
                    int latest = gameServerService.getLatestVersion();
                    for (GameServer target : targets) {
                        GameServer server = gameServerService.refreshStatus(target);
                        EmbedBuilder builder = new EmbedBuilder()
                            .setLenient(true)
                            .withAuthorIcon("https://quantic.top/sentry.png")
                            .withAuthorName("Sentry")
                            .withAuthorUrl("https://sentry.quantic.top")
                            .withTitle("Status of " + target.getShortNameAndAddress())
                            .withImage("http://cache.gametracker.com/server_info/" + target.getAddress() +
                                "/b_350_20_692108_381007_FFFFFF_000000.png");
                        if (gameServerService.getState(server) == Monitor.State.BAD) {
                            builder.withColor(new Color(0xaa0000))
                                .withDescription("Server appears to be down since " + formatRelative(server.getLastValidPing()));
                        } else {
                            if (server.isUpdating()) {
                                builder.withColor(new Color(0x0000aa))
                                    .withDescription("Server is being updated")
                                    .appendField("Attempts", "" + target.getUpdateAttempts(), true)
                                    .appendField("Started", formatRelative(target.getLastUpdateStart()), false);
                            } else if (server.getPing() > 1000) {
                                builder.withColor(new Color(0xaaaa00))
                                    .withDescription("Server is experiencing connectivity issues")
                                    .appendField("Last Ping", formatRelativeWithNow(target.getLastValidPing()), false);
                            } else {
                                builder.withColor(new Color(0x00aa00))
                                    .withDescription(targets.size() > 1 ? "Server is online" : "Get more details with `.rcon " + target.getShortName() + " status`")
                                    .appendField("Map", target.getMap(), true)
                                    .appendField("Players", target.getPlayers() + " / " + target.getMaxPlayers(), true)
                                    .appendField("Connect", "steam://connect/" + target.getAddress() + "/" + target.getSvPassword(), false);
                                if (target.getTvPort() != null && target.getTvPort() > 0) {
                                    builder.appendField("SourceTV", "steam://connect/" + getIPAddress(target.getAddress()) + ":" + target.getTvPort() + "/", false);
                                }
                                if (target.getExpirationDate() != null && target.getExpirationDate().isAfter(ZonedDateTime.now())) {
                                    builder.appendField("Expires", withRelative(target.getExpirationDate()), false);
                                }
                            }
                            builder.appendField("Version", "v" + target.getVersion(), true);
                            if (target.getVersion() != latest) {
                                builder.appendField("Status", "Outdated (Latest: " + latest + ")", true);
                            } else {
                                builder.appendField("Status", "Up-to-date", true);
                            }
                        }
                        sendMessage(message.getChannel(), builder.build());
                    }
                    if (targets.size() == 1) {
                        GameServer target = targets.get(0);
                        Result<String> result = gameServerService.tryRcon(target, "status");
                        if (result.isSuccessful()) {
                            answer(message, "**" + target.getShortNameAndAddress() + "**\n```\n" +
                                result.getContent() + "\n```");
                        } else {
                            log.debug("Unsuccessful response from {} for command status: {}", target, result.getContent());
                            sendMessage(message.getChannel(), new EmbedBuilder()
                                .setLenient(true)
                                .withTitle("Status of " + target.getShortNameAndAddress())
                                .appendField("Response", "Could not connect via RCON" +
                                    (result.getError() != null ? " (" +
                                        getRootCause(result.getError()).getClass().getSimpleName() + ")" : ""), false)
                                .withColor(new Color(0xaa0000))
                                .build());
                        }
                    }
                } else if ("console".equals(action)) {
                    String response = "Retrieving console for servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        Result<String> result = gameServerService.tryGetConsole(target);
                        if (result.isSuccessful()) {
                            response = "• [**" + target.getShortName() + "**] (" + target.getAddress() + ")\n" + result.getContent() + "\n";
                        } else {
                            response = resultLine(target, result);
                        }
                    }
                    answerPrivately(message, response);
                } else {
                    answer(message, "Invalid action, must be one of: restart, stop, update, install-mod, status or console.");
                }
            })
            .onAuthorDenied(CommandBuilder.noPermission())
            .build();
    }

    private String resultLine(GameServer server, Result<?> result) {
        return "• [**" + server.getShortName() + "**] (" + server.getAddress() + ") " + result.getMessage() + "\n";
    }<commit_msg>Fix issue with multiple server commands<commit_after>package top.quantic.sentry.discord;

import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.util.EmbedBuilder;
import top.quantic.sentry.config.Constants;
import top.quantic.sentry.discord.core.Command;
import top.quantic.sentry.discord.core.CommandBuilder;
import top.quantic.sentry.discord.module.CommandSupplier;
import top.quantic.sentry.domain.GameServer;
import top.quantic.sentry.domain.Setting;
import top.quantic.sentry.service.GameServerService;
import top.quantic.sentry.service.SettingService;
import top.quantic.sentry.service.util.Monitor;
import top.quantic.sentry.service.util.Result;

import java.awt.*;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import static com.google.common.collect.Multimaps.toMultimap;
import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCause;
import static top.quantic.sentry.discord.util.DiscordUtil.*;
import static top.quantic.sentry.service.util.DateUtil.*;
import static top.quantic.sentry.service.util.DateUtil.humanizeShort;
import static top.quantic.sentry.service.util.MiscUtil.getIPAddress;
import static top.quantic.sentry.service.util.MiscUtil.inflect;

@Component
public class GameServers implements CommandSupplier {

    private static final Logger log = LoggerFactory.getLogger(GameServers.class);

    private final GameServerService gameServerService;
    private final SettingService settingService;

    @Autowired
    public GameServers(GameServerService gameServerService, SettingService settingService) {
        this.gameServerService = gameServerService;
        this.settingService = settingService;
    }

    @Override
    public List<Command> getCommands() {
        return Arrays.asList(rcon(), servers(), server());
    }

    private Command server() {
        return CommandBuilder.of("server")
            .describedAs("Perform operations on UGC managed GameServers")
            .in("Integrations")
            .withExamples(serverExamples())
            .nonParsed()
            .secured()
            .onExecute(context -> {
                IMessage message = context.getMessage();
                String[] args = safeSplit(context.getArgs(), 3);
                if (args == null || args.length < 2) {
                    answer(message, "Please specify at least two arguments: action, server");
                    return;
                }
                String action = args[0];
                String serverQuery = args[1];
                List<GameServer> targets = gameServerService.findServersMultiple(Arrays.asList(serverQuery.split(",|;")));
                if ("restart".equals(action)) {
                    String response = "Restarting servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        response += resultLine(target, gameServerService.tryRestart(target));
                    }
                    answer(message, response);
                } else if ("stop".equals(action)) {
                    String response = "Stopping servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        response += resultLine(target, gameServerService.tryStop(target));
                    }
                    answer(message, response);
                } else if ("update".equals(action)) {
                    String response = "Updating game version on servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        response += resultLine(target, gameServerService.tryUpdate(target));
                    }
                    answer(message, response);
                } else if ("install-mod".equals(action)) {
                    if (args.length < 3) {
                        answer(message, "You must add an extra argument with the mod ID or name");
                        return;
                    }
                    String mod = args[2];
                    Optional<Setting> setting = settingService.findMostRecentByGuildAndKey(Constants.ANY, mod);
                    String modName = setting.map(Setting::getValue).orElse(mod);
                    String response = "Installing mod " + modName + " on servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        response += resultLine(target, gameServerService.tryModInstall(target, modName));
                    }
                    answerPrivately(message, response);
                } else if ("status".equals(action)) {
                    answerPrivately(message, "Retrieving status for servers matching " + serverQuery);
                    int latest = gameServerService.getLatestVersion();
                    for (GameServer target : targets) {
                        GameServer server = gameServerService.refreshStatus(target);
                        EmbedBuilder builder = new EmbedBuilder()
                            .setLenient(true)
                            .withAuthorIcon("https://quantic.top/sentry.png")
                            .withAuthorName("Sentry")
                            .withAuthorUrl("https://sentry.quantic.top")
                            .withTitle("Status of " + target.getShortNameAndAddress())
                            .withImage("http://cache.gametracker.com/server_info/" + target.getAddress() +
                                "/b_350_20_692108_381007_FFFFFF_000000.png");
                        if (gameServerService.getState(server) == Monitor.State.BAD) {
                            builder.withColor(new Color(0xaa0000))
                                .withDescription("Server appears to be down since " + formatRelative(server.getLastValidPing()));
                        } else {
                            if (server.isUpdating()) {
                                builder.withColor(new Color(0x0000aa))
                                    .withDescription("Server is being updated")
                                    .appendField("Attempts", "" + target.getUpdateAttempts(), true)
                                    .appendField("Started", formatRelative(target.getLastUpdateStart()), false);
                            } else if (server.getPing() > 1000) {
                                builder.withColor(new Color(0xaaaa00))
                                    .withDescription("Server is experiencing connectivity issues")
                                    .appendField("Last Ping", formatRelativeWithNow(target.getLastValidPing()), false);
                            } else {
                                builder.withColor(new Color(0x00aa00))
                                    .withDescription(targets.size() > 1 ? "Server is online" : "Get more details with `.rcon " + target.getShortName() + " status`")
                                    .appendField("Map", target.getMap(), true)
                                    .appendField("Players", target.getPlayers() + " / " + target.getMaxPlayers(), true)
                                    .appendField("Connect", "steam://connect/" + target.getAddress() + "/" + target.getSvPassword(), false);
                                if (target.getTvPort() != null && target.getTvPort() > 0) {
                                    builder.appendField("SourceTV", "steam://connect/" + getIPAddress(target.getAddress()) + ":" + target.getTvPort() + "/", false);
                                }
                                if (target.getExpirationDate() != null && target.getExpirationDate().isAfter(ZonedDateTime.now())) {
                                    builder.appendField("Expires", withRelative(target.getExpirationDate()), false);
                                }
                            }
                            builder.appendField("Version", "v" + target.getVersion(), true);
                            if (target.getVersion() != latest) {
                                builder.appendField("Status", "Outdated (Latest: " + latest + ")", true);
                            } else {
                                builder.appendField("Status", "Up-to-date", true);
                            }
                        }
                        sendMessage(message.getChannel(), builder.build());
                    }
                    if (targets.size() == 1) {
                        GameServer target = targets.get(0);
                        Result<String> result = gameServerService.tryRcon(target, "status");
                        if (result.isSuccessful()) {
                            answer(message, "**" + target.getShortNameAndAddress() + "**\n```\n" +
                                result.getContent() + "\n```");
                        } else {
                            log.debug("Unsuccessful response from {} for command status: {}", target, result.getContent());
                            sendMessage(message.getChannel(), new EmbedBuilder()
                                .setLenient(true)
                                .withTitle("Status of " + target.getShortNameAndAddress())
                                .appendField("Response", "Could not connect via RCON" +
                                    (result.getError() != null ? " (" +
                                        getRootCause(result.getError()).getClass().getSimpleName() + ")" : ""), false)
                                .withColor(new Color(0xaa0000))
                                .build());
                        }
                    }
                } else if ("console".equals(action)) {
                    String response = "Retrieving console for servers matching " + serverQuery + "\n";
                    for (GameServer target : targets) {
                        Result<String> result = gameServerService.tryGetConsole(target);
                        if (result.isSuccessful()) {
                            response += "• [**" + target.getShortName() + "**] (" + target.getAddress() + ")\n" + result.getContent() + "\n";
                        } else {
                            response += resultLine(target, result);
                        }
                    }
                    answerPrivately(message, response);
                } else {
                    answer(message, "Invalid action, must be one of: restart, stop, update, install-mod, status or console.");
                }
            })
            .onAuthorDenied(CommandBuilder.noPermission())
            .build();
    }

    private String resultLine(GameServer server, Result<?> result) {
        return "• [**" + server.getShortName() + "**] (" + server.getAddress() + ") " + result.getMessage() + "\n";
    }
{'fraction_non_alphanumeric': 0.11012919432980442, 'fraction_numerical': 0.005652251928943118, 'mean_word_length': 1.629201556787357, 'pattern_counts': {'":': 2, '<': 19, '<?xml version=': 0, '>': 23, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '674910', 'n_tokens_mistral': 5481, 'n_tokens_neox': 5180, 'n_words': 1035}