From ddf6037fc440c8509f074453b25dfaa6ed6e9d88 Mon Sep 17 00:00:00 2001 From: Keir Date: Fri, 4 Dec 2015 23:30:27 +0000 Subject: [PATCH] Wrote standalone chatsnap handler and implemented required changes in Core. --- Plugins/.idea/modules.xml | 1 + .../src/META-INF/MANIFEST.MF | 3 + .../mineplex/chatsnap/ChatSnapManager.java | 49 ++++++++++ .../mineplex/chatsnap/JedisPubSubHandler.java | 97 +++++++++++++++++++ .../core/chatsnap/MessageSnapshotManager.java | 5 - .../core/chatsnap/MessageSnapshotPlugin.java | 2 +- .../src/mineplex/core/report/Report.java | 25 +++++ .../mineplex/core/report/ReportManager.java | 56 +++++++++-- .../core/report/command/ReportCommand.java | 1 - .../report/command/ReportHandleCommand.java | 1 - 10 files changed, 226 insertions(+), 14 deletions(-) create mode 100644 Plugins/Mineplex.ChatSnapManager/src/META-INF/MANIFEST.MF create mode 100644 Plugins/Mineplex.ChatSnapManager/src/mineplex/chatsnap/ChatSnapManager.java create mode 100644 Plugins/Mineplex.ChatSnapManager/src/mineplex/chatsnap/JedisPubSubHandler.java diff --git a/Plugins/.idea/modules.xml b/Plugins/.idea/modules.xml index f7936ba2e..8a6893d95 100644 --- a/Plugins/.idea/modules.xml +++ b/Plugins/.idea/modules.xml @@ -5,6 +5,7 @@ + diff --git a/Plugins/Mineplex.ChatSnapManager/src/META-INF/MANIFEST.MF b/Plugins/Mineplex.ChatSnapManager/src/META-INF/MANIFEST.MF new file mode 100644 index 000000000..b5d0a64ef --- /dev/null +++ b/Plugins/Mineplex.ChatSnapManager/src/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: mineplex.chatsnap.ChatSnapManager + diff --git a/Plugins/Mineplex.ChatSnapManager/src/mineplex/chatsnap/ChatSnapManager.java b/Plugins/Mineplex.ChatSnapManager/src/mineplex/chatsnap/ChatSnapManager.java new file mode 100644 index 000000000..d9d46a45d --- /dev/null +++ b/Plugins/Mineplex.ChatSnapManager/src/mineplex/chatsnap/ChatSnapManager.java @@ -0,0 +1,49 @@ +package mineplex.chatsnap; + +import java.io.File; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +/** + * @author iKeirNez + */ +public class ChatSnapManager +{ + public static final String CHANNEL_DEPLOY = "chatsnap:deploy"; + public static final String CHANNEL_DESTROY = "chatsnap:destroy"; + + public static void main(String[] args) + { + new ChatSnapManager(new JedisPool(new JedisPoolConfig(), "host", 6379)); // TODO host + } + + private JedisPool _jedisPool; + private File dataDirectory = new File("data"); + + public ChatSnapManager(JedisPool jedisPool) + { + _jedisPool = jedisPool; + + if (dataDirectory.exists() && !dataDirectory.isDirectory()) + { + throw new RuntimeException("Not a directory: " + dataDirectory.getPath()); + } + + if (!dataDirectory.exists() && !dataDirectory.mkdir()) + { + throw new RuntimeException("Unable to create directory: " + dataDirectory.getPath()); + } + + registerHandler(); + } + + private void registerHandler() + { + try (Jedis jedis = _jedisPool.getResource()) + { + jedis.subscribe(new JedisPubSubHandler(dataDirectory), CHANNEL_DEPLOY, CHANNEL_DESTROY); + } + } +} diff --git a/Plugins/Mineplex.ChatSnapManager/src/mineplex/chatsnap/JedisPubSubHandler.java b/Plugins/Mineplex.ChatSnapManager/src/mineplex/chatsnap/JedisPubSubHandler.java new file mode 100644 index 000000000..c1feff9a8 --- /dev/null +++ b/Plugins/Mineplex.ChatSnapManager/src/mineplex/chatsnap/JedisPubSubHandler.java @@ -0,0 +1,97 @@ +package mineplex.chatsnap; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Arrays; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import redis.clients.jedis.JedisPubSub; + +/** + * @author iKeirNez + */ +public class JedisPubSubHandler extends JedisPubSub +{ + private File _directory; + + private Gson _gson = new GsonBuilder() + .setPrettyPrinting() + .create(); + + public JedisPubSubHandler(File directory) + { + _directory = directory; + + if (!directory.isDirectory()) + { + throw new IllegalArgumentException("Not a directory."); + } + } + + @Override + public void onMessage(String channel, String dataString) + { + if (channel.equals(ChatSnapManager.CHANNEL_DEPLOY)) + { + JsonObject data = _gson.fromJson(dataString, JsonObject.class); + String token = data.get("token").getAsString(); + JsonArray snapshotArray = data.get("snapshots").getAsJsonArray(); + + File target = new File(_directory, token + ".json"); + String json = _gson.toJson(snapshotArray); + + try + { + Files.write(target.toPath(), Arrays.asList(json.split("\n"))); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + else if (channel.equals(ChatSnapManager.CHANNEL_DESTROY)) + { + // dataString = token + File target = new File(_directory, dataString + ".json"); + + if (target.exists() && !target.delete()) + { + System.out.println("Failed to delete: " + target.getPath()); + } + } + } + + @Override + public void onPMessage(String s, String s1, String s2) + { + + } + + @Override + public void onSubscribe(String s, int i) + { + + } + + @Override + public void onUnsubscribe(String s, int i) + { + + } + + @Override + public void onPUnsubscribe(String s, int i) + { + + } + + @Override + public void onPSubscribe(String s, int i) + { + + } +} diff --git a/Plugins/Mineplex.Core/src/mineplex/core/chatsnap/MessageSnapshotManager.java b/Plugins/Mineplex.Core/src/mineplex/core/chatsnap/MessageSnapshotManager.java index df710766d..8501c2dd5 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/chatsnap/MessageSnapshotManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/chatsnap/MessageSnapshotManager.java @@ -23,11 +23,6 @@ public class MessageSnapshotManager .expireAfterWrite(30, TimeUnit.MINUTES) .build(); - public MessageSnapshotManager() - { - - } - /** * Keeps a snapshot in memory temporarily (30 minutes) and then discards it. * During this time, other modules (such as the Report module) can access it for their own use. diff --git a/Plugins/Mineplex.Core/src/mineplex/core/chatsnap/MessageSnapshotPlugin.java b/Plugins/Mineplex.Core/src/mineplex/core/chatsnap/MessageSnapshotPlugin.java index 19963dc93..ad996cf09 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/chatsnap/MessageSnapshotPlugin.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/chatsnap/MessageSnapshotPlugin.java @@ -38,7 +38,7 @@ public class MessageSnapshotPlugin extends MiniPlugin } @EventHandler(priority = EventPriority.MONITOR) - public void cacheChatMessage(AsyncPlayerChatEvent e) + public void onPlayerChat(AsyncPlayerChatEvent e) { _messageSnapshotManager.cacheSnapshot(getMessageSnap(e)); } diff --git a/Plugins/Mineplex.Core/src/mineplex/core/report/Report.java b/Plugins/Mineplex.Core/src/mineplex/core/report/Report.java index 3f51e9814..049eb92ea 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/report/Report.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/report/Report.java @@ -6,6 +6,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import mineplex.serverdata.data.Data; +import org.apache.commons.lang3.RandomStringUtils; public class Report implements Data { @@ -37,6 +38,8 @@ public class Report implements Data private long _lastActivity; public long getLastActivity() { return _lastActivity; } + private String _token = null; + public Report(int reportId, String playerName, String serverName, ReportCategory category) { _reportId = reportId; @@ -64,6 +67,28 @@ public class Report implements Data _lastActivity = System.currentTimeMillis(); } + public boolean hasChatSnapToken() + { + return _token != null; + } + + /** + * Gets a token in the format of reportId-randomCharacters. + * Currently this is only used for publishing chat abuse reports. + * + * @return the full token + */ + public String getChatSnapToken() + { + // since we don't always use this, only generate a token when we need it + if (_token == null) + { + _token = RandomStringUtils.randomAlphabetic(8); + } + + return _reportId + "-" + _token; + } + @Override public String getDataId() { diff --git a/Plugins/Mineplex.Core/src/mineplex/core/report/ReportManager.java b/Plugins/Mineplex.Core/src/mineplex/core/report/ReportManager.java index 0d2e5aa39..895b0cad1 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/report/ReportManager.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/report/ReportManager.java @@ -3,8 +3,14 @@ package mineplex.core.report; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.Set; +import java.util.UUID; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; import mineplex.core.account.CoreClient; +import mineplex.core.chatsnap.MessageSnapshot; import mineplex.core.chatsnap.MessageSnapshotManager; import mineplex.core.command.CommandCenter; import mineplex.core.common.Rank; @@ -30,6 +36,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.plugin.java.JavaPlugin; +import mineplex.serverdata.servers.ServerManager; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.exceptions.JedisConnectionException; @@ -43,6 +50,11 @@ import redis.clients.jedis.exceptions.JedisConnectionException; public class ReportManager { private static final String NAME = "Report"; + private static final String URL_PREFIX = "http://chatsnap.mineplex.com/"; + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + public static final String CHANNEL_DEPLOY = "chatsnap:deploy"; + public static final String CHANNEL_DESTROY = "chatsnap:destroy"; // statistic constants private static final String STAT_TOTAL_COUNT = "Global.TotalReportsCount"; @@ -53,6 +65,7 @@ public class ReportManager { private StatsManager _statsManager; private MessageSnapshotManager _messageSnapshotManager; private String _serverName; + private JedisPool _jedisPool; // Holds active/open reports in a synchronized database. private DataRepository _reportRepository; @@ -63,13 +76,15 @@ public class ReportManager { // A mapping of PlayerName(String) to the ReportId(Integer) for all active reports on this server. private Map _activeReports; - public ReportManager(JavaPlugin javaPlugin, PreferencesManager preferencesManager, StatsManager statsManager, MessageSnapshotManager messageSnapshotManager, String serverName) + public ReportManager(JavaPlugin javaPlugin, PreferencesManager preferencesManager, StatsManager statsManager, + MessageSnapshotManager messageSnapshotManager, String serverName) { _javaPlugin = javaPlugin; _preferencesManager = preferencesManager; _statsManager = statsManager; _messageSnapshotManager = messageSnapshotManager; _serverName = serverName; + _jedisPool = Utility.generatePool(ServerManager.getMasterConnection()); // TODO is this the best way? _reportRepository = new RedisDataRepository(Region.ALL, Report.class, "reports"); _activeReports = new HashMap(); _reportSqlRepository = new ReportRepository(javaPlugin); @@ -110,6 +125,11 @@ public class ReportManager { CommandCenter.Instance.OnPlayerCommandPreprocess( new PlayerCommandPreprocessEvent(reportCloser, "/punish " + playerName + " " + reason)); } + + if (report.getCategory() == ReportCategory.CHAT_ABUSE) // only chat abuse reports have chat logs published + { + unpublishChatLog(report.getChatSnapToken()); + } } } @@ -182,6 +202,7 @@ public class ReportManager { // only start notifying staff when if (report.getReporters().size() >= category.getNotifyThreshold()) { + publishChatLog(report.getChatSnapToken(), reportedPlayer.getUniqueId()); int reportId = report.getReportId(); String prefix = getReportPrefix(reportId); @@ -206,11 +227,6 @@ public class ReportManager { } else { - message = message.extra("\n" + F.main(prefix, String.format( - "Type /reportclose %d to close this report.", - reportId))) - .click(ClickEvent.SUGGEST_COMMAND, "/reportclose " + reportId); - sendHandlerNotification(report, message); } } @@ -221,6 +237,34 @@ public class ReportManager { return null; } + public String getChatLogUrl(String token) + { + return URL_PREFIX + token; + } + + public void publishChatLog(String token, UUID playerUUID) + { + Set snapshots = _messageSnapshotManager.getSnapshotsFor(playerUUID); + + JsonObject jsonObject = new JsonObject(); + jsonObject.addProperty("token", token); + jsonObject.add("snapshots", GSON.toJsonTree(snapshots)); + String json = GSON.toJson(snapshots); + + try (Jedis jedis = _jedisPool.getResource()) + { + jedis.publish(CHANNEL_DEPLOY, json); + } + } + + public void unpublishChatLog(String token) + { + try (Jedis jedis = _jedisPool.getResource()) + { + jedis.publish(CHANNEL_DESTROY, token); + } + } + public void onPlayerJoin(Player player) { if (hasActiveReport(player)) diff --git a/Plugins/Mineplex.Core/src/mineplex/core/report/command/ReportCommand.java b/Plugins/Mineplex.Core/src/mineplex/core/report/command/ReportCommand.java index b4a0f22f3..8fb26f4c1 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/report/command/ReportCommand.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/report/command/ReportCommand.java @@ -24,7 +24,6 @@ public class ReportCommand extends CommandBase if(args == null || args.length < 2) { UtilPlayer.message(player, F.main(Plugin.getName(), C.cRed + "Your arguments are inappropriate for this command!")); - return; } else { diff --git a/Plugins/Mineplex.Core/src/mineplex/core/report/command/ReportHandleCommand.java b/Plugins/Mineplex.Core/src/mineplex/core/report/command/ReportHandleCommand.java index a7d6ab95a..b83c19576 100644 --- a/Plugins/Mineplex.Core/src/mineplex/core/report/command/ReportHandleCommand.java +++ b/Plugins/Mineplex.Core/src/mineplex/core/report/command/ReportHandleCommand.java @@ -24,7 +24,6 @@ public class ReportHandleCommand extends CommandBase if(args == null || args.length < 1) { UtilPlayer.message(player, F.main(Plugin.getName(), C.cRed + "Your arguments are inappropriate for this command!")); - return; } else {