package tc.oc.commons.bukkit.report; import java.util.Map; import java.util.WeakHashMap; import javax.inject.Inject; import javax.inject.Singleton; import com.sk89q.minecraft.util.commands.Command; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissions; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.TranslatableComponent; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; import java.time.Duration; import java.time.Instant; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerQuitEvent; import tc.oc.api.bukkit.users.BukkitUserStore; import tc.oc.api.docs.Report; import tc.oc.api.docs.Server; import tc.oc.minecraft.scheduler.SyncExecutor; import tc.oc.api.model.QueryService; import tc.oc.api.reports.ReportSearchRequest; import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.chat.HeaderComponent; import tc.oc.commons.bukkit.chat.NameStyle; import tc.oc.commons.bukkit.chat.PlayerComponent; import tc.oc.commons.bukkit.commands.UserFinder; import tc.oc.commons.bukkit.nick.IdentityProvider; import tc.oc.commons.core.chat.Audience; import tc.oc.commons.core.chat.Component; import tc.oc.commons.core.commands.CommandFutureCallback; import tc.oc.commons.core.commands.Commands; import tc.oc.commons.core.commands.TranslatableCommandException; import tc.oc.commons.core.formatting.PeriodFormats; import tc.oc.commons.core.util.Comparables; import tc.oc.minecraft.api.event.Listener; @Singleton public class ReportCommands implements Commands, Listener { private static final int PER_PAGE = 8; private final ReportFormatter reportFormatter; private final QueryService<Report> reportService; private final ReportCreator reportCreator; private final ReportConfiguration reportConfiguration; private final UserFinder userFinder; private final SyncExecutor syncExecutor; private final Server localServer; private final BukkitUserStore userStore; private final Audiences audiences; private final IdentityProvider identities; private final Map<CommandSender, Instant> senderLastReport = new WeakHashMap<>(); @Inject ReportCommands(ReportFormatter reportFormatter, QueryService<Report> reportService, ReportCreator reportCreator, ReportConfiguration reportConfiguration, UserFinder userFinder, SyncExecutor syncExecutor, Server localServer, BukkitUserStore userStore, Audiences audiences, IdentityProvider identities) { this.reportFormatter = reportFormatter; this.reportService = reportService; this.reportCreator = reportCreator; this.reportConfiguration = reportConfiguration; this.userFinder = userFinder; this.syncExecutor = syncExecutor; this.localServer = localServer; this.userStore = userStore; this.audiences = audiences; this.identities = identities; } @EventHandler private void onQuit(PlayerQuitEvent event) { senderLastReport.remove(event.getPlayer()); } private void assertEnabled() throws CommandException { if(!reportConfiguration.enabled()) { throw new TranslatableCommandException("command.reports.notEnabled"); } } @Command( aliases = { "report" }, usage = "<player> <reason>", desc = "Report a player who is breaking the rules", min = 2, max = -1 ) @CommandPermissions(ReportPermissions.CREATE) public void report(final CommandContext args, final CommandSender sender) throws CommandException { assertEnabled(); if(!sender.hasPermission(ReportPermissions.COOLDOWN_EXEMPT)) { final Instant lastReportTime = senderLastReport.get(sender); if(lastReportTime != null) { final Duration timeLeft = reportConfiguration.cooldown().minus(Duration.between(lastReportTime, Instant.now())); if(Comparables.greaterThan(timeLeft, Duration.ZERO)) { throw new TranslatableCommandException("command.report.cooldown", PeriodFormats.briefNaturalApproximate(timeLeft)); } } } syncExecutor.callback( userFinder.findLocalPlayer(sender, args, 0), CommandFutureCallback.onSuccess(sender, args, response -> { if(response.player().hasPermission(ReportPermissions.EXEMPT) && !sender.hasPermission(ReportPermissions.OVERRIDE)) { throw new TranslatableCommandException("command.report.exempt"); } senderLastReport.put(sender, Instant.now()); syncExecutor.callback( reportCreator.createReport( response.user, sender instanceof Player ? userStore.getUser((Player) sender) : null, args.getJoinedStrings(1), sender instanceof ConsoleCommandSender ), CommandFutureCallback.onSuccess(sender, args, report -> { audiences.get(sender).sendMessage( new Component( new Component(new TranslatableComponent("misc.thankYou"), ChatColor.GREEN), new Component(" "), new Component(new TranslatableComponent("command.report.successful.dealtWithMessage"), ChatColor.GOLD) ) ); }) ); }) ); } @Command( aliases = { "reports", "reps" }, usage = "[-a] [-p page] [player]", flags = "ap:", desc = "List recent reports on this server, or all servers, optionally filtering by player.", min = 0, max = 1 ) @CommandPermissions(ReportPermissions.VIEW) public void reports(final CommandContext args, final CommandSender sender) throws CommandException { assertEnabled(); syncExecutor.callback( userFinder.findUser(sender, args.getString(0, null), UserFinder.Scope.ALL, UserFinder.Default.NULL), CommandFutureCallback.onSuccess(sender, args, userResult -> { final int page = args.getFlagInteger('p', 1); final boolean crossServer = args.hasFlag('a'); ReportSearchRequest request = ReportSearchRequest.create(page, PER_PAGE); request = crossServer ? request.forFamilies(reportConfiguration.families()) : request.forServer(localServer); if(userResult != null) { request = request.forPlayer(userResult.user); } syncExecutor.callback( reportService.find(request), CommandFutureCallback.onSuccess(sender, args, reportResult -> { final Component title = new Component(new TranslatableComponent(crossServer ? "command.reports.networkTitle" : "command.reports.serverTitle"), ChatColor.YELLOW); if(userResult != null) { title.extra(" (") .extra(new PlayerComponent(identities.createIdentity(userResult), NameStyle.VERBOSE)) .extra(")"); } title.extra(" ") .extra(new TranslatableComponent("currentPage", String.valueOf(page)), ChatColor.DARK_AQUA); final Audience audience = audiences.get(sender); audience.sendMessage(new HeaderComponent(title)); for(Report report : reportResult.documents()) { if(report.reported() != null) { audience.sendMessages(reportFormatter.format(report, crossServer, true)); } } }) ); }) ); } }