From 9c9116631927d5a97a130d6d3fc2f5ce0462461b Mon Sep 17 00:00:00 2001 From: Allink Date: Thu, 26 May 2022 16:10:00 +0100 Subject: [PATCH] Allow players to report offline players & allow admins to mark reports as completed, deleting them and sending them to an archive channel --- .../totalfreedommod/ChatManager.java | 6 +- .../command/Command_report.java | 29 ++++---- .../command/FreedomCommand.java | 22 ++++-- .../totalfreedommod/config/ConfigEntry.java | 1 + .../totalfreedommod/discord/Discord.java | 67 ++++++++++++++++-- .../discord/MessageReactionListener.java | 68 +++++++++++++++++++ src/main/resources/config.yml | 2 + 7 files changed, 167 insertions(+), 28 deletions(-) create mode 100644 src/main/java/me/totalfreedom/totalfreedommod/discord/MessageReactionListener.java diff --git a/src/main/java/me/totalfreedom/totalfreedommod/ChatManager.java b/src/main/java/me/totalfreedom/totalfreedommod/ChatManager.java index eebefc44..2156af99 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/ChatManager.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/ChatManager.java @@ -175,15 +175,15 @@ public class ChatManager extends FreedomService }); } - public void reportAction(Player reporter, Player reported, String report) + public void reportAction(Player reporter, String reportedName, String report) { for (Player player : server.getOnlinePlayers()) { if (plugin.al.isAdmin(player)) { - playerMsg(player, ChatColor.RED + "[REPORTS] " + ChatColor.GOLD + reporter.getName() + " has reported " + reported.getName() + " for " + report); + playerMsg(player, ChatColor.RED + "[REPORTS] " + ChatColor.GOLD + reporter.getName() + " has reported " + reportedName + " for " + report); } } - FLog.info("[REPORTS] " + reporter.getName() + " has reported " + reported.getName() + " for " + report); + FLog.info("[REPORTS] " + reporter.getName() + " has reported " + reportedName + " for " + report); } } \ No newline at end of file diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_report.java b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_report.java index 086bac6c..554c1f6d 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/Command_report.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/Command_report.java @@ -4,6 +4,7 @@ import me.totalfreedom.totalfreedommod.rank.Rank; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -22,36 +23,40 @@ public class Command_report extends FreedomCommand } Player player = getPlayer(args[0], true); + OfflinePlayer offlinePlayer = getOfflinePlayer(args[0]); - if (player == null) + if (player == null && offlinePlayer == null) { msg(PLAYER_NOT_FOUND); return true; } - - if (sender instanceof Player) + else if (player != null) { - if (player.equals(playerSender)) + if (sender instanceof Player) { - msg(ChatColor.RED + "Please, don't try to report yourself."); + if (player.equals(playerSender)) + { + msg(ChatColor.RED + "Please, don't try to report yourself."); + return true; + } + } + + if (plugin.al.isAdmin(player)) + { + msg(ChatColor.RED + "You can not report admins."); return true; } - } - if (plugin.al.isAdmin(player)) - { - msg(ChatColor.RED + "You can not report admins."); - return true; } String report = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); - plugin.cm.reportAction(playerSender, player, report); + plugin.cm.reportAction(playerSender, (player == null) ? offlinePlayer.getName() : player.getName(), report); boolean logged = false; if (plugin.dc.enabled) { - logged = plugin.dc.sendReport(playerSender, player, report); + logged = (player == null) ? plugin.dc.sendReportOffline(playerSender, offlinePlayer, report) : plugin.dc.sendReport(playerSender, player, report); } msg(ChatColor.GREEN + "Thank you, your report has been successfully logged." diff --git a/src/main/java/me/totalfreedom/totalfreedommod/command/FreedomCommand.java b/src/main/java/me/totalfreedom/totalfreedommod/command/FreedomCommand.java index 017307a4..ed826d46 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/command/FreedomCommand.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/command/FreedomCommand.java @@ -1,14 +1,10 @@ package me.totalfreedom.totalfreedommod.command; import com.google.common.collect.Lists; + import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; +import java.util.*; + import me.totalfreedom.totalfreedommod.TotalFreedomMod; import me.totalfreedom.totalfreedommod.admin.Admin; import me.totalfreedom.totalfreedommod.player.PlayerData; @@ -18,12 +14,14 @@ import me.totalfreedom.totalfreedommod.util.FUtil; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; import org.bukkit.Server; import org.bukkit.command.*; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.util.StringUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public abstract class FreedomCommand implements CommandExecutor, TabCompleter { @@ -229,6 +227,16 @@ public abstract class FreedomCommand implements CommandExecutor, TabCompleter return player; } + @Nullable + protected OfflinePlayer getOfflinePlayer(String name) + { + return Arrays.stream(Bukkit.getOfflinePlayers()) + .filter(player -> player.getName() != null) + .filter(player -> player.getName().equalsIgnoreCase(name)) + .findFirst() + .orElse(null); + } + protected Admin getAdmin(CommandSender sender) { return plugin.al.getAdmin(sender); diff --git a/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java b/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java index 1446e49b..598c3d1f 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/config/ConfigEntry.java @@ -76,6 +76,7 @@ public enum ConfigEntry DISCORD_TOKEN(String.class, "discord.token"), DISCORD_PREFIX(String.class, "discord.prefix"), DISCORD_REPORT_CHANNEL_ID(String.class, "discord.report_channel_id"), + DISCORD_REPORT_ARCHIVE_CHANNEL_ID(String.class, "discord.report_archive_channel_id"), DISCORD_CHAT_CHANNEL_ID(String.class, "discord.chat_channel_id"), DISCORD_ADMINCHAT_CHANNEL_ID(String.class, "discord.adminchat_channel_id"), diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java index 479042be..d2b6e909 100644 --- a/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/Discord.java @@ -47,6 +47,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.WordUtils; import org.apache.commons.lang3.RandomStringUtils; import org.bukkit.GameRule; +import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -64,7 +65,7 @@ public class Discord extends FreedomService public ScheduledThreadPoolExecutor RATELIMIT_EXECUTOR; public List> sentMessages = new ArrayList<>(); public Boolean enabled = false; - private final ImmutableList DISCORD_SUBDOMAINS = ImmutableList.of("discordapp.com", "discord.com", "discord.gg"); + private static final ImmutableList DISCORD_SUBDOMAINS = ImmutableList.of("discordapp.com", "discord.com", "discord.gg"); private final Pattern DISCORD_MENTION_PATTERN = Pattern.compile("(<@!?([0-9]{16,20})>)"); public static String getCode(PlayerData playerData) @@ -180,6 +181,7 @@ public class Discord extends FreedomService .addEventListeners(new PrivateMessageListener(), new DiscordToMinecraftListener(), new DiscordToAdminChatListener(), + new MessageReactionListener(), new ListenerAdapter() { @Override @@ -303,7 +305,7 @@ public class Discord extends FreedomService } } - public String sanitizeChatMessage(String message) + public static String sanitizeChatMessage(String message) { String newMessage = message; @@ -397,12 +399,12 @@ public class Discord extends FreedomService FLog.info("Discord integration has successfully shutdown."); } - public String deformat(String input) + public static String deformat(String input) { return input.replaceAll("([_\\\\`*>|])", "\\\\$1"); } - public boolean sendReport(Player reporter, Player reported, String reason) + public boolean shouldISendReport() { if (ConfigEntry.DISCORD_REPORT_CHANNEL_ID.getString().isEmpty()) { @@ -430,7 +432,54 @@ public class Discord extends FreedomService return false; } - EmbedBuilder embedBuilder = new EmbedBuilder(); + return true; + } + + public boolean sendReportOffline(Player reporter, OfflinePlayer reported, String reason) + { + if (!shouldISendReport()) + { + return false; + } + + final Guild server = bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString()); + final TextChannel channel = server.getTextChannelById(ConfigEntry.DISCORD_REPORT_CHANNEL_ID.getString()); + + final EmbedBuilder embedBuilder = new EmbedBuilder(); + embedBuilder.setTitle("Report for " + reported.getName() + " (offline)"); + embedBuilder.setDescription(reason); + embedBuilder.setFooter("Reported by " + reporter.getName(), "https://minotar.net/helm/" + reporter.getName() + ".png"); + embedBuilder.setTimestamp(Instant.from(ZonedDateTime.now())); + com.earth2me.essentials.User user = plugin.esb.getEssentialsUser(reported.getName()); + String location = "World: " + Objects.requireNonNull(user.getLastLocation().getWorld()).getName() + ", X: " + user.getLastLocation().getBlockX() + ", Y: " + user.getLastLocation().getBlockY() + ", Z: " + user.getLastLocation().getBlockZ(); + embedBuilder.addField("Location", location, true); + embedBuilder.addField("God Mode", WordUtils.capitalizeFully(String.valueOf(user.isGodModeEnabled())), true); + if (user.getNickname() != null) + { + embedBuilder.addField("Nickname", user.getNickname(), true); + } + MessageEmbed embed = embedBuilder.build(); + Message message = channel.sendMessage(embed).complete(); + + if (!ConfigEntry.DISCORD_REPORT_ARCHIVE_CHANNEL_ID.getString().isEmpty()) + { + message.addReaction("\uD83D\uDCCB").complete(); + } + + return true; + } + + public boolean sendReport(Player reporter, Player reported, String reason) + { + if (!shouldISendReport()) + { + return false; + } + + final Guild server = bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString()); + final TextChannel channel = server.getTextChannelById(ConfigEntry.DISCORD_REPORT_CHANNEL_ID.getString()); + + final EmbedBuilder embedBuilder = new EmbedBuilder(); embedBuilder.setTitle("Report for " + reported.getName()); embedBuilder.setDescription(reason); embedBuilder.setFooter("Reported by " + reporter.getName(), "https://minotar.net/helm/" + reporter.getName() + ".png"); @@ -445,7 +494,13 @@ public class Discord extends FreedomService embedBuilder.addField("Nickname", user.getNickname(), true); } MessageEmbed embed = embedBuilder.build(); - channel.sendMessage(embed).complete(); + Message message = channel.sendMessage(embed).complete(); + + if (!ConfigEntry.DISCORD_REPORT_ARCHIVE_CHANNEL_ID.getString().isEmpty()) + { + message.addReaction("\uD83D\uDCCB").complete(); + } + return true; } diff --git a/src/main/java/me/totalfreedom/totalfreedommod/discord/MessageReactionListener.java b/src/main/java/me/totalfreedom/totalfreedommod/discord/MessageReactionListener.java new file mode 100644 index 00000000..9eeb55a6 --- /dev/null +++ b/src/main/java/me/totalfreedom/totalfreedommod/discord/MessageReactionListener.java @@ -0,0 +1,68 @@ +package me.totalfreedom.totalfreedommod.discord; + +import me.totalfreedom.totalfreedommod.config.ConfigEntry; +import me.totalfreedom.totalfreedommod.util.FLog; +import net.dv8tion.jda.api.MessageBuilder; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; + +public class MessageReactionListener extends ListenerAdapter +{ + public void onMessageReactionAdd(MessageReactionAddEvent messageReactionAddEvent) + { + if (!messageReactionAddEvent.isFromGuild()) + { + return; + } + + if (messageReactionAddEvent.getMember() == null) + { + return; + } + + if (messageReactionAddEvent.getMember().getUser().getId().equals(Discord.bot.getSelfUser().getId())) + { + return; + } + + if (!messageReactionAddEvent.getChannel().getId().equals(ConfigEntry.DISCORD_REPORT_CHANNEL_ID.getString())) + { + return; + } + + if (!messageReactionAddEvent.getReactionEmote().getEmoji().equals("\uD83D\uDCCB")) + { + return; + } + + final TextChannel archiveChannel = Discord.bot.getTextChannelById(ConfigEntry.DISCORD_REPORT_ARCHIVE_CHANNEL_ID.getString()); + + if (archiveChannel == null) + { + FLog.warning("Report archive channel is defined in the config, yet doesn't actually exist!"); + return; + } + + final Message message = messageReactionAddEvent.retrieveMessage().complete(); + final Member completer = messageReactionAddEvent.getMember(); + + if (!message.getAuthor().getId().equals(Discord.bot.getSelfUser().getId())) + { + return; + } + + // We don't need other embeds... yet? + final MessageEmbed embed = message.getEmbeds().get(0); + final MessageBuilder archiveMessageBuilder = new MessageBuilder(); + archiveMessageBuilder.setContent("Report completed by " + Discord.deformat(completer.getUser().getAsTag())); + archiveMessageBuilder.setEmbed(embed); + final Message archiveMessage = archiveMessageBuilder.build(); + + archiveChannel.sendMessage(archiveMessage).complete(); + message.delete().complete(); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 9d1bd3bc..d0f9ff4d 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -64,6 +64,8 @@ discord: server_id: '' # Channel to send /report messages to report_channel_id: '' + # Channel to send archived reports to + report_archive_channel_id: '' # Channel for Discord to Minecraft and vice-versa chat_channel_id: '' # Channel for Discord to AdminChat and vice-versa