From fc68648d5c822174360c20cac95432415d924a82 Mon Sep 17 00:00:00 2001 From: woolwind Date: Fri, 12 Feb 2016 14:35:40 -0500 Subject: [PATCH 1/5] Insert the Watch module and associated changes to data, core, entity, lookup, permissions, and messages --- .gitignore | 3 +- pom.xml | 2 +- .../java/fr/Alphart/BAT/Configuration.java | 2 +- .../Alphart/BAT/Modules/Core/CoreCommand.java | 12 + .../Alphart/BAT/Modules/Core/EntityEntry.java | 10 + .../BAT/Modules/Core/LookupFormatter.java | 127 ++- .../BAT/Modules/Core/PermissionManager.java | 3 + .../Alphart/BAT/Modules/ModulesManager.java | 10 + .../fr/Alphart/BAT/Modules/Watch/Watch.java | 840 ++++++++++++++++++ .../BAT/Modules/Watch/WatchCommand.java | 424 +++++++++ .../Alphart/BAT/Modules/Watch/WatchEntry.java | 21 + .../Alphart/BAT/Modules/Watch/WatchTask.java | 47 + .../java/fr/Alphart/BAT/Utils/RedisUtils.java | 24 +- .../BAT/database/DataSourceHandler.java | 2 +- .../fr/Alphart/BAT/database/SQLQueries.java | 108 +++ src/main/resources/messages_en.language | 21 + 16 files changed, 1645 insertions(+), 11 deletions(-) create mode 100644 src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java create mode 100644 src/main/java/fr/Alphart/BAT/Modules/Watch/WatchCommand.java create mode 100644 src/main/java/fr/Alphart/BAT/Modules/Watch/WatchEntry.java create mode 100644 src/main/java/fr/Alphart/BAT/Modules/Watch/WatchTask.java diff --git a/.gitignore b/.gitignore index 76bb6cf..bb6c31e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ dependency-reduced-pom.xml .project .classpath .idea -BungeeAdminTools.iml \ No newline at end of file +BungeeAdminTools.iml +*~ diff --git a/pom.xml b/pom.xml index 1e29cc5..8755711 100644 --- a/pom.xml +++ b/pom.xml @@ -140,6 +140,6 @@ - Bungee plugin which handles ban, mute and kick management + Bungee plugin which handles ban, mute, watch and kick management 1.3.4 diff --git a/src/main/java/fr/Alphart/BAT/Configuration.java b/src/main/java/fr/Alphart/BAT/Configuration.java index b3b0dfc..347b504 100644 --- a/src/main/java/fr/Alphart/BAT/Configuration.java +++ b/src/main/java/fr/Alphart/BAT/Configuration.java @@ -28,7 +28,7 @@ public Configuration(){ private String language = "en"; private String prefix = "&6[&4BAT&6]&e "; - @Comment("Force players to give reason when /ban /unban /kick /mute /unmute etc.") + @Comment("Force players to give reason when /ban /unban /kick /watch /mute /unmute etc.") private boolean mustGiveReason= false; @Comment("Enable /bat confirm, to confirm command such as action on unknown player.") private boolean confirmCommand = true; diff --git a/src/main/java/fr/Alphart/BAT/Modules/Core/CoreCommand.java b/src/main/java/fr/Alphart/BAT/Modules/Core/CoreCommand.java index 86f209f..b7df402 100644 --- a/src/main/java/fr/Alphart/BAT/Modules/Core/CoreCommand.java +++ b/src/main/java/fr/Alphart/BAT/Modules/Core/CoreCommand.java @@ -45,6 +45,7 @@ import fr.Alphart.BAT.Modules.Core.Importer.MinecraftUUIDImporter; import fr.Alphart.BAT.Modules.Core.Importer.SQLiteMigrater; import fr.Alphart.BAT.Modules.Kick.KickEntry; +import fr.Alphart.BAT.Modules.Watch.WatchEntry; import fr.Alphart.BAT.Modules.Mute.MuteEntry; import fr.Alphart.BAT.Utils.CallbackUtils.Callback; import fr.Alphart.BAT.Utils.CallbackUtils.ProgressCallback; @@ -304,6 +305,17 @@ public void onCommand(final CommandSender sender, final String[] args, final boo : "&eThe IP &a" + entity + "&e wasn't ever mute.")); } break; + case "watch": + final List watches = modules.getWatchModule().getWatchData(entity); + if(!watches.isEmpty()){ + message = lookupFormatter.formatWatchLookup(entity, watches, page, false); + }else{ + message = new ArrayList(); + message.add(BAT.__((!Utils.validIP(entity)) + ? "&eThe player &a" + entity + "&e wasn't ever watched." + : "&eThe IP &a" + entity + "&e wasn't ever watched.")); + } + break; case "kick": final List kicks = modules.getKickModule().getKickData(entity); if(!kicks.isEmpty()){ diff --git a/src/main/java/fr/Alphart/BAT/Modules/Core/EntityEntry.java b/src/main/java/fr/Alphart/BAT/Modules/Core/EntityEntry.java index 463ffe5..b40ce34 100644 --- a/src/main/java/fr/Alphart/BAT/Modules/Core/EntityEntry.java +++ b/src/main/java/fr/Alphart/BAT/Modules/Core/EntityEntry.java @@ -17,6 +17,7 @@ import fr.Alphart.BAT.Modules.Comment.CommentEntry; import fr.Alphart.BAT.Modules.Kick.KickEntry; import fr.Alphart.BAT.Modules.Mute.MuteEntry; +import fr.Alphart.BAT.Modules.Watch.WatchEntry; import fr.Alphart.BAT.Utils.UUIDNotFoundException; import fr.Alphart.BAT.Utils.Utils; import fr.Alphart.BAT.database.DataSourceHandler; @@ -34,6 +35,7 @@ public class EntityEntry { private final List bans = new ArrayList(); private final List mutes = new ArrayList(); + private final List watches = new ArrayList(); private final List kicks = new ArrayList(); private final List comments = new ArrayList(); @@ -121,6 +123,10 @@ public EntityEntry(final String entity) { if (modules.isLoaded("mute")) { mutes.addAll(modules.getMuteModule().getMuteData(entity)); } + + if (modules.isLoaded("watch")) { + watches.addAll(modules.getWatchModule().getWatchData(entity)); + } // No ip kick if (modules.isLoaded("kick") && ipUsers.isEmpty()) { kicks.addAll(modules.getKickModule().getKickData(entity)); @@ -148,6 +154,10 @@ public List getMutes() { return mutes; } + public List getWatches() { + return watches; + } + public List getKicks() { return kicks; } diff --git a/src/main/java/fr/Alphart/BAT/Modules/Core/LookupFormatter.java b/src/main/java/fr/Alphart/BAT/Modules/Core/LookupFormatter.java index 2897f2d..529935a 100644 --- a/src/main/java/fr/Alphart/BAT/Modules/Core/LookupFormatter.java +++ b/src/main/java/fr/Alphart/BAT/Modules/Core/LookupFormatter.java @@ -29,6 +29,7 @@ import fr.Alphart.BAT.Modules.Comment.CommentEntry.Type; import fr.Alphart.BAT.Modules.Kick.KickEntry; import fr.Alphart.BAT.Modules.Mute.MuteEntry; +import fr.Alphart.BAT.Modules.Watch.WatchEntry; import fr.Alphart.BAT.Utils.FormatUtils; import fr.Alphart.BAT.Utils.MojangAPIProvider; import fr.Alphart.BAT.Utils.Utils; @@ -56,16 +57,25 @@ public List getSummaryLookupPlayer(final String pName, final bo } final EntityEntry ipDetails = new EntityEntry(Core.getPlayerIP(pName)); + boolean isBan = false; boolean isBanIP = false; int bansNumber = 0; final List banServers = Lists.newArrayList(); final List banIPServers = Lists.newArrayList(); + boolean isMute = false; boolean isMuteIP = false; int mutesNumber = 0; final List muteServers = Lists.newArrayList(); final List muteIPServers = Lists.newArrayList(); + + boolean isWatched = false; + boolean isWatchedIP = false; + int watchesNumber = 0; + final List watchServers = Lists.newArrayList(); + final List watchIPServers = Lists.newArrayList(); + int kicksNumber = 0; // Compute player's state (as well as his ip) concerning ban and mute for (final BanEntry banEntry : pDetails.getBans()) { @@ -80,10 +90,10 @@ public List getSummaryLookupPlayer(final String pName, final bo banIPServers.add(banEntry.getServer()); } } - for (final MuteEntry muteEntry : pDetails.getMutes()) { - if (muteEntry.isActive()) { - isMute = true; - muteServers.add(muteEntry.getServer()); + for (final WatchEntry watchEntry : pDetails.getWatches()) { + if (watchEntry.isActive()) { + isWatched = true; + watchServers.add(watchEntry.getServer()); } } for (final MuteEntry muteEntry : ipDetails.getMutes()) { @@ -94,6 +104,7 @@ public List getSummaryLookupPlayer(final String pName, final bo } bansNumber = pDetails.getBans().size() + ipDetails.getBans().size(); mutesNumber = pDetails.getMutes().size() + ipDetails.getMutes().size(); + watchesNumber = pDetails.getWatches().size() + ipDetails.getWatches().size(); kicksNumber = pDetails.getKicks().size(); // Load the lookup pattern @@ -132,6 +143,12 @@ public List getSummaryLookupPlayer(final String pName, final bo final String muteip_servers = !muteIPServers.isEmpty() ? Joiner.on(joinChar).join(muteIPServers).toLowerCase() : _("none"); + final String watch_servers = !watchServers.isEmpty() + ? Joiner.on(joinChar).join(watchServers).toLowerCase() + : _("none"); + final String watchip_servers = !watchIPServers.isEmpty() + ? Joiner.on(joinChar).join(watchIPServers).toLowerCase() + : _("none"); final String first_login = pDetails.getFirstLogin() != EntityEntry.noDateFound ? Core.defaultDF.format(new Date(pDetails.getFirstLogin().getTime())) @@ -192,8 +209,10 @@ public List getSummaryLookupPlayer(final String pName, final bo .replace("{connection_state}", connection_state) .replace("{ban_servers}", ban_servers).replace("{banip_servers}", banip_servers) .replace("{mute_servers}", mute_servers).replace("{muteip_servers}", muteip_servers) + .replace("{watch_servers}", watch_servers).replace("{watchip_servers}", watchip_servers) .replace("{first_login}", first_login).replace("{last_login}", last_login).replace("{last_ip}", last_ip) .replace("{bans_number}", String.valueOf(bansNumber)).replace("{mutes_number}", String.valueOf(mutesNumber)) + .replace("{watches_number}", String.valueOf(watchesNumber)) .replace("{kicks_number}", String.valueOf(kicksNumber)) .replace("{name_history_list}", name_history_list).replaceAll("\\{last_comments:\\d\\}", last_comments) .replace("{player}", pName).replace("{uuid}", Core.getUUID(pName)) @@ -432,8 +451,104 @@ public List formatBanLookup(final String entity, final List formatWatchLookup(final String entity, final List watches, + int page, final boolean staffLookup) throws InvalidModuleException { + final StringBuilder msg = new StringBuilder(); + + int totalPages = (int) Math.ceil((double)watches.size()/entriesPerPage); + if(watches.size() > entriesPerPage){ + if(page > totalPages){ + page = totalPages; + } + int beginIndex = (page - 1) * entriesPerPage; + int endIndex = (beginIndex + entriesPerPage < watches.size()) ? beginIndex + entriesPerPage : watches.size(); + for(int i=watches.size() -1; i > 0; i--){ + if(i >= beginIndex && i < endIndex){ + continue; + } + watches.remove(i); + } + } + msg.append(lookupHeader.replace("{entity}", entity).replace("{module}", "Watch") + .replace("{page}", page + "/" + totalPages)); + + boolean isWatched = false; + for (final WatchEntry watchEntry : watches) { + if (watchEntry.isActive()) { + isWatched = true; + } + } + + // We begin with active ban + if(isWatched){ + msg.append("&6&lActive watches: &e"); + final Iterator it = watches.iterator(); + while(it.hasNext()){ + final WatchEntry watch = it.next(); + if(!watch.isActive()){ + break; + } + final String begin = Core.defaultDF.format(watch.getBeginDate()); + final String server = watch.getServer(); + final String reason = watch.getReason(); + final String end; + if(watch.getEndDate() == null){ + end = "permanent watch"; + }else{ + end = Core.defaultDF.format(watch.getEndDate()); + } + + msg.append("\n"); + if(staffLookup){ + msg.append(_("activeStaffWatchLookupRow", + new String[] { watch.getEntity(), begin, server, reason, end})); + }else{ + msg.append(_("activeWatchLookupRow", + new String[] { begin, server, reason, watch.getStaff(), end})); + } + it.remove(); + } + } + + if(!watches.isEmpty()){ + msg.append("\n&7&lArchive watches: &e"); + for(final WatchEntry watch : watches){ + final String begin = Core.defaultDF.format(watch.getBeginDate()); + final String server = watch.getServer(); + final String reason = watch.getReason(); + + final String unwatchDate; + if(watch.getUnwatchDate() == null){ + unwatchDate = Core.defaultDF.format(watch.getEndDate()); + }else{ + unwatchDate = Core.defaultDF.format(watch.getUnwatchDate()); + } + final String unwatchReason = watch.getUnwatchReason(); + String unwatchStaff = watch.getUnwatchStaff(); + if(unwatchStaff == "null"){ + unwatchStaff = "temporary watch"; + } + + msg.append("\n"); + if(staffLookup){ + msg.append(_("archiveStaffWatchLookupRow", + new String[] { watch.getEntity(), begin, server, reason, unwatchDate, unwatchReason, unwatchStaff})); + }else{ + msg.append(_("archiveWatchLookupRow", + new String[] { begin, server, reason, watch.getStaff(), unwatchDate, unwatchReason, unwatchStaff})); + } + } + } + + msg.append(lookupFooter.replace("{entity}", entity).replace("{module}", "Watch") + .replace("{page}", page + "/" + totalPages)); + + return FormatUtils.formatNewLine(ChatColor.translateAlternateColorCodes('&', msg.toString())); + } - public List formatMuteLookup(final String entity, final List mutes, + public List formatMuteLookup(final String entity, final List mutes, int page, final boolean staffLookup) throws InvalidModuleException { final StringBuilder msg = new StringBuilder(); @@ -527,7 +642,7 @@ public List formatMuteLookup(final String entity, final List formatKickLookup(final String entity, final List kicks, int page, final boolean staffLookup) throws InvalidModuleException { final StringBuilder msg = new StringBuilder(); diff --git a/src/main/java/fr/Alphart/BAT/Modules/Core/PermissionManager.java b/src/main/java/fr/Alphart/BAT/Modules/Core/PermissionManager.java index d999371..d454ff8 100644 --- a/src/main/java/fr/Alphart/BAT/Modules/Core/PermissionManager.java +++ b/src/main/java/fr/Alphart/BAT/Modules/Core/PermissionManager.java @@ -15,6 +15,9 @@ public static enum Action { MUTE("mute"), MUTEIP("muteip"), TEMPMUTE("tempmute"), TEMPMUTEIP("tempmuteip"), UNMUTE("unmute"), UNMUTEIP( "unmuteip"), MUTE_BROADCAST("mute.broadcast"), + WATCH("watch"), WATCHIP("watchip"), TEMPWATCH("tempwatch"), TEMPWATCHIP("tempwatchip"), UNWATCH("unwatch"), UNWATCHIP( + "unwatchip"), WATCH_BROADCAST("watch.broadcast"), + KICK("kick"), WARN("warn"), WARN_BROADCAST("warn.broadcast"), KICK_BROADCAST("kick.broadcast"), LOOKUP("lookup"); diff --git a/src/main/java/fr/Alphart/BAT/Modules/ModulesManager.java b/src/main/java/fr/Alphart/BAT/Modules/ModulesManager.java index 34aaf2c..73e5f7b 100644 --- a/src/main/java/fr/Alphart/BAT/Modules/ModulesManager.java +++ b/src/main/java/fr/Alphart/BAT/Modules/ModulesManager.java @@ -20,6 +20,7 @@ import fr.Alphart.BAT.Modules.Core.Core; import fr.Alphart.BAT.Modules.Kick.Kick; import fr.Alphart.BAT.Modules.Mute.Mute; +import fr.Alphart.BAT.Modules.Watch.Watch; public class ModulesManager { private final Logger log; @@ -67,6 +68,7 @@ public void loadModules() { modules.put(new Core(), IModule.OFF_STATE); modules.put(new Ban(), IModule.OFF_STATE); modules.put(new Mute(), IModule.OFF_STATE); + modules.put(new Watch(), IModule.OFF_STATE); modules.put(new Kick(), IModule.OFF_STATE); modules.put(new Comment(), IModule.OFF_STATE); cmdsModules = new HashMap(); @@ -165,6 +167,14 @@ public Mute getMuteModule() throws InvalidModuleException { return null; } + public Watch getWatchModule() throws InvalidModuleException { + final IModule module = getModule("watch"); + if (module != null) { + return (Watch) module; + } + return null; + } + public Kick getKickModule() throws InvalidModuleException { final IModule module = getModule("kick"); if (module != null) { diff --git a/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java b/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java new file mode 100644 index 0000000..cf1f5ce --- /dev/null +++ b/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java @@ -0,0 +1,840 @@ +package fr.Alphart.BAT.Modules.Watch; + +import static fr.Alphart.BAT.I18n.I18n._; +import static fr.Alphart.BAT.I18n.I18n.__; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Timestamp; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import lombok.Getter; +import net.cubespace.Yamler.Config.Comment; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.ChatEvent; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.event.ServerConnectedEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.api.scheduler.ScheduledTask; +import net.md_5.bungee.event.EventHandler; +import net.md_5.bungee.event.EventPriority; + +import com.imaginarycode.minecraft.redisbungee.RedisBungee; + +import fr.Alphart.BAT.BAT; +import fr.Alphart.BAT.Modules.BATCommand; +import fr.Alphart.BAT.Modules.BATCommand.RunAsync; +import fr.Alphart.BAT.Modules.CommandHandler; +import fr.Alphart.BAT.Modules.IModule; +import fr.Alphart.BAT.Modules.ModuleConfiguration; +import fr.Alphart.BAT.Modules.Core.Core; +import fr.Alphart.BAT.Modules.Core.PermissionManager; +import fr.Alphart.BAT.Modules.Core.PermissionManager.Action; +import fr.Alphart.BAT.Utils.FormatUtils; +import fr.Alphart.BAT.Utils.UUIDNotFoundException; +import fr.Alphart.BAT.Utils.Utils; +import fr.Alphart.BAT.database.DataSourceHandler; +import fr.Alphart.BAT.database.SQLQueries; + +/** + * This module handles all the watch list.
+ * The watch status of online players are cached in order to avoid lag. + */ +public class Watch implements IModule, Listener { + private final String name = "watch"; + private ConcurrentHashMap watchedPlayers; + private CommandHandler commandHandler; + private ScheduledTask task; + private final WatchConfig config; + + public Watch() { + config = new WatchConfig(); + } + + @Override + public List getCommands() { + return commandHandler.getCmds(); + } + + @Override + public String getMainCommand() { + return "watch"; + } + + @Override + public ModuleConfiguration getConfig() { + return config; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean load() { + // Init table + Statement statement = null; + try (Connection conn = BAT.getConnection()) { + statement = conn.createStatement(); + if (DataSourceHandler.isSQLite()) { + for (final String query : SQLQueries.Watch.SQLite.createTable) { + statement.executeUpdate(query); + } + } else { + statement.executeUpdate(SQLQueries.Watch.createTable); + } + statement.close(); + } catch (final SQLException e) { + DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement); + } + + // Register commands + commandHandler = new WatchCommand(this); + commandHandler.loadCmds(); + + watchedPlayers = new ConcurrentHashMap(); + + final WatchTask watchTask = new WatchTask(this); + task = ProxyServer.getInstance().getScheduler().schedule(BAT.getInstance(), watchTask, 0, 10, TimeUnit.SECONDS); + return true; + } + + @Override + public boolean unload() { + task.cancel(); + watchedPlayers.clear(); + return true; + } + + public class WatchConfig extends ModuleConfiguration { + public WatchConfig() { + init(name); + } + + @Comment("Forbidden commands when a player is watched") + @Getter + private List forbiddenCmds = new ArrayList(){ + private static final long serialVersionUID = 1L; + + { // not currently forbidding any commands to watched players + // add("msg"); + }}; + } + + public void loadWatchMessage(final String pName, final String server){ + if(!watchedPlayers.containsKey(pName)){ + return; + } + String reason = ""; + Timestamp expiration = null; + Timestamp begin = null; + String staff = null; + + PreparedStatement statement = null; + ResultSet resultSet = null; + try (Connection conn = BAT.getConnection()) { + statement = conn.prepareStatement(DataSourceHandler.isSQLite() + ? SQLQueries.Watch.SQLite.getWatchMessage + : SQLQueries.Watch.getWatchMessage); + try{ + statement.setString(1, Core.getUUID(pName)); + statement.setString(2, Core.getPlayerIP(pName)); + statement.setString(3, server); + }catch(final UUIDNotFoundException e){ + BAT.getInstance().getLogger().severe("Error during retrieving of the UUID of " + pName + ". Please report this error :"); + e.printStackTrace(); + } + resultSet = statement.executeQuery(); + + if(resultSet.next()) { + if(DataSourceHandler.isSQLite()){ + begin = new Timestamp(resultSet.getLong("strftime('%s',watch_begin)") * 1000); + String endStr = resultSet.getString("watch_end"); + expiration = (endStr == null) ? null : new Timestamp(Long.parseLong(endStr)); + }else{ + begin = resultSet.getTimestamp("watch_begin"); + expiration = resultSet.getTimestamp("watch_end"); + } + reason = (resultSet.getString("watch_reason") != null) ? resultSet.getString("watch_reason") : IModule.NO_REASON; + staff = resultSet.getString("watch_staff"); + }else{ + throw new SQLException("No active watch found."); + } + } catch (final SQLException e) { + DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement, resultSet); + } + if(expiration != null){ + watchedPlayers.get(pName).setWatchMessage(_("isWatchedTemp", + new String[]{ reason , "{expiration}", Core.defaultDF.format(begin), staff }), expiration); + }else{ + watchedPlayers.get(pName).setWatchMessage(_("isWatched", + new String[]{ reason, Core.defaultDF.format(begin), staff }), null); + } + + } + + /** + * Check if both ip and name of this player are watched
+ * Use cached data + * + * @param player + * @param server + * @return
    + *
  • 1 if the player is watched from this server
  • + *
  • 0 if he's not watched from this server
  • + *
  • -1 if the data are loading
  • + *
+ */ + public int isWatched(final ProxiedPlayer player, final String server) { + final PlayerWatchData pWatchData = watchedPlayers.get(player.getName()); + if (pWatchData != null) { + if (pWatchData.isWatched(server)) { + return 1; + } + return 0; + } + + return -1; + } + + /** + * Check if this entity (player or ip) is watched
+ * Use uncached data. Use {@link #isWatched(ProxiedPlayer, String)} instead + * of this method if the player is available + * + * @param watchdEntity + * | can be an ip or a player name + * @param server + * | if server equals to (any) check if the player is watch on a + * server + * @param forceUseUncachedData + * | use uncached data, for example to handle player or player's + * ip related watch + * @return + */ + public boolean isWatched(final String watchdEntity, final String server, final boolean forceUseUncachedData) { + // Check if the entity is an online player, in this case we're going to + // use the cached method + final ProxiedPlayer player = ProxyServer.getInstance().getPlayer(watchdEntity); + if (!forceUseUncachedData && player != null) { + final int result = isWatched(player, server); + // If the data aren't loading + if (result != -1) { + return (result == 1); + } + } + + PreparedStatement statement = null; + ResultSet resultSet = null; + try (Connection conn = BAT.getConnection()) { + // If this is an ip which may be watchd + if (Utils.validIP(watchdEntity)) { + final String ip = watchdEntity; + statement = conn.prepareStatement((ANY_SERVER.equals(server)) ? SQLQueries.Watch.isWatchedIP + : SQLQueries.Watch.isWatchedServerIP); + statement.setString(1, ip); + if (!ANY_SERVER.equals(server)) { + statement.setString(2, server); + } + } + // If this is a player which may be watchd + else { + final String pName = watchdEntity; + statement = conn.prepareStatement((ANY_SERVER.equals(server)) ? SQLQueries.Watch.isWatched + : SQLQueries.Watch.isWatchedServer); + statement.setString(1, Core.getUUID(pName)); + if (!ANY_SERVER.equals(server)) { + statement.setString(2, server); + } + } + resultSet = statement.executeQuery(); + + // If there are a result + if (resultSet.next()) { + return true; + } + + } catch (final SQLException e) { + DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement, resultSet); + } + return false; + } + + /** + * Watch this entity (player or ip)
+ * + * @param watchdEntity + * | can be an ip or a player name + * @param server + * ; set to "(global)", to global watch + * @param staff + * @param expirationTimestamp + * ; set to 0 for watch def + * @param reason + * | optional + * @return + */ + public String watch(final String watchdEntity, final String server, final String staff, + final long expirationTimestamp, final String reason) { + PreparedStatement statement = null; + try (Connection conn = BAT.getConnection()) { + if (Utils.validIP(watchdEntity)) { + final String ip = watchdEntity; + statement = conn.prepareStatement(SQLQueries.Watch.createWatchIP); + statement.setString(1, ip); + statement.setString(2, staff); + statement.setString(3, server); + statement.setTimestamp(4, (expirationTimestamp > 0) ? new Timestamp(expirationTimestamp) : null); + statement.setString(5, (NO_REASON.equals(reason)) ? null : reason); + statement.executeUpdate(); + statement.close(); + + if (BAT.getInstance().getRedis().isRedisEnabled()) { + for (UUID pUUID : RedisBungee.getApi().getPlayersOnline()) { + if (RedisBungee.getApi().getPlayerIp(pUUID).equals(ip)) { + // The watch task timer will add the player to the bungeecord instance's cache if needed. + if(server.equals(GLOBAL_SERVER) || RedisBungee.getApi().getServerFor(pUUID).getName().equalsIgnoreCase(server)) { + ProxiedPlayer player = ProxyServer.getInstance().getPlayer(pUUID); + /* + if (player != null) { + player.sendMessage(__("wasWatchedNotif", new String[] { reason })); + } else { + BAT.getInstance().getRedis().sendMessagePlayer(pUUID, TextComponent.toLegacyText(__("wasWatchedNotif", new String[] { reason }))); + }*/ + } + } + } + } else { + for (final ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { + if (Utils.getPlayerIP(player).equals(ip)) { + if (server.equals(GLOBAL_SERVER)) { + watchedPlayers.get(player.getName()).setGlobal(); + } else { + watchedPlayers.get(player.getName()).addServer(server); + }/* + if (server.equals(GLOBAL_SERVER) || player.getServer().getInfo().getName().equalsIgnoreCase(server)) { + player.sendMessage(__("wasWatchedNotif", new String[] { reason })); + }*/ + } + } + } + + if (expirationTimestamp > 0) { + return _("watchTempBroadcast", new String[] { ip, FormatUtils.getDuration(expirationTimestamp), + staff, server, reason }); + } else { + return _("watchBroadcast", new String[] { ip, staff, server, reason }); + } + } + + // Otherwise it's a player + else { + final String pName = watchdEntity; + final ProxiedPlayer player = ProxyServer.getInstance().getPlayer(pName); + statement = conn.prepareStatement(SQLQueries.Watch.createWatch); + statement.setString(1, Core.getUUID(pName)); + statement.setString(2, staff); + statement.setString(3, server); + statement.setTimestamp(4, (expirationTimestamp > 0) ? new Timestamp(expirationTimestamp) : null); + statement.setString(5, (NO_REASON.equals(reason)) ? null : reason); + statement.executeUpdate(); + statement.close(); + + if (player != null) { + updateWatchData(player.getName());/* + if(server.equals(GLOBAL_SERVER) || player.getServer().getInfo().getName().equalsIgnoreCase(server)){ + player.sendMessage(__("wasWatchedNotif", new String[] { reason })); + }*/ + } else if (BAT.getInstance().getRedis().isRedisEnabled()) { + //Need to implement a function to get an UUID object instead of a string one. + final UUID pUUID = Core.getUUIDfromString(Core.getUUID(pName)); + BAT.getInstance().getRedis().sendWatchUpdatePlayer(pUUID, server); + BAT.getInstance().getRedis().sendMessagePlayer(pUUID, TextComponent.toLegacyText(__("wasWatchedNotif", new String[] { reason }))); + } + if (expirationTimestamp > 0) { + return _("watchTempBroadcast", new String[] { pName, FormatUtils.getDuration(expirationTimestamp), + staff, server, reason }); + } else { + return _("watchBroadcast", new String[] { pName, staff, server, reason }); + } + + } + } catch (final SQLException e) { + return DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement); + } + } + + /** + * Watch the ip of an online player + * + * @param server + * ; set to "(global)", to global watch + * @param staff + * @param duration + * ; set to 0 for watch def + * @param reason + * | optional + * @param ip + */ + public String watchIP(final ProxiedPlayer player, final String server, final String staff, + final long expirationTimestamp, final String reason) { + watch(Utils.getPlayerIP(player), server, staff, expirationTimestamp, reason); + return _("watchBroadcast", new String[] { player.getName() + "'s IP", staff, server, reason }); + } + + /** + * Unwatch an entity (player or ip) + * + * @param watchdEntity + * | can be an ip or a player name + * @param server + * | if equals to (any), unwatch from all servers | if equals to + * (global), remove global watch + * @param staff + * @param reason + * @param unWatchIP + */ + public String unWatch(final String watchdEntity, final String server, final String staff, final String reason) { + PreparedStatement statement = null; + try (Connection conn = BAT.getConnection()) { + // If the watchdEntity is an ip + if (Utils.validIP(watchdEntity)) { + final String ip = watchdEntity; + if (ANY_SERVER.equals(server)) { + statement = (DataSourceHandler.isSQLite()) ? conn.prepareStatement(SQLQueries.Watch.SQLite.unWatchIP) + : conn.prepareStatement(SQLQueries.Watch.unWatchIP); + statement.setString(1, reason); + statement.setString(2, staff); + statement.setString(3, ip); + } else { + statement = (DataSourceHandler.isSQLite()) ? conn + .prepareStatement(SQLQueries.Watch.SQLite.unWatchIPServer) : conn + .prepareStatement(SQLQueries.Watch.unWatchIPServer); + statement.setString(1, reason); + statement.setString(2, staff); + statement.setString(3, ip); + statement.setString(4, server); + } + statement.executeUpdate(); + statement.close(); + + return _("unWatchBroadcast", new String[] { ip, staff, server, reason }); + } + + // Otherwise it's a player + else { + final String pName = watchdEntity; + if (ANY_SERVER.equals(server)) { + statement = (DataSourceHandler.isSQLite()) ? conn.prepareStatement(SQLQueries.Watch.SQLite.unWatch) + : conn.prepareStatement(SQLQueries.Watch.unWatch); + statement.setString(1, reason); + statement.setString(2, staff); + statement.setString(3, Core.getUUID(pName)); + } else { + statement = (DataSourceHandler.isSQLite()) ? conn + .prepareStatement(SQLQueries.Watch.SQLite.unWatchServer) : conn + .prepareStatement(SQLQueries.Watch.unWatchServer); + statement.setString(1, reason); + statement.setString(2, staff); + statement.setString(3, Core.getUUID(pName)); + statement.setString(4, server); + } + statement.executeUpdate(); + statement.close(); + + final ProxiedPlayer player = ProxyServer.getInstance().getPlayer(pName); + if (player != null) { + updateWatchData(player.getName()); + if(ANY_SERVER.equals(server) || GLOBAL_SERVER.equals(server) || player.getServer().getInfo().getName().equalsIgnoreCase(server)){ + // player.sendMessage(__("wasUnwatchedNotif", new String[] { reason })); + } + } else if (BAT.getInstance().getRedis().isRedisEnabled()) { + final UUID pUUID = Core.getUUIDfromString(Core.getUUID(pName)); + ServerInfo pServer = RedisBungee.getApi().getServerFor(pUUID); + if (ANY_SERVER.equals(server) || GLOBAL_SERVER.equals(server) || (pServer != null && pServer.getName().equalsIgnoreCase(server))){ + BAT.getInstance().getRedis().sendWatchUpdatePlayer(pUUID, server); + BAT.getInstance().getRedis().sendMessagePlayer(pUUID, TextComponent.toLegacyText(__("wasUnwatchedNotif", new String[] { reason }))); + } + } + + return _("unWatchBroadcast", new String[] { pName, staff, server, reason }); + } + } catch (final SQLException e) { + return DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement); + } + } + + /** + * Unwatch the ip of this entity + * + * @param entity + * @param server + * | if equals to (any), unwatch from all servers | if equals to + * (global), remove global watch + * @param staff + * @param reason + * | optional + * @param duration + * ; set to 0 for watch def + */ + public String unWatchIP(final String entity, final String server, final String staff, final String reason) { + if (Utils.validIP(entity)) { + return unWatch(entity, server, staff, reason); + } else { + unWatch(Core.getPlayerIP(entity), server, staff, reason); + updateWatchData(entity); + return _("unWatchBroadcast", new String[] { entity + "'s IP", staff, server, reason }); + } + } + + /** + * Get all watch data of an entity
+ * Should be run async to optimize performance + * + * @param entity + * | can be an ip or a player name + * @return List of WatchEntry of the entity + */ + public List getWatchData(final String entity) { + final List watchList = new ArrayList(); + PreparedStatement statement = null; + ResultSet resultSet = null; + try (Connection conn = BAT.getConnection()) { + // If the entity is an ip + if (Utils.validIP(entity)) { + statement = conn.prepareStatement((DataSourceHandler.isSQLite()) + ? SQLQueries.Watch.SQLite.getWatchIP + : SQLQueries.Watch.getWatchIP); + statement.setString(1, entity); + resultSet = statement.executeQuery(); + } + // Otherwise if it's a player + else { + statement = conn.prepareStatement((DataSourceHandler.isSQLite()) + ? SQLQueries.Watch.SQLite.getWatch + : SQLQueries.Watch.getWatch); + statement.setString(1, Core.getUUID(entity)); + resultSet = statement.executeQuery(); + } + + while (resultSet.next()) { + final Timestamp beginDate; + final Timestamp endDate; + final Timestamp unwatchDate; + if(DataSourceHandler.isSQLite()){ + beginDate = new Timestamp(resultSet.getLong("strftime('%s',watch_begin)") * 1000); + final String endStr = resultSet.getString("watch_end"); + endDate = (endStr == null) ? null : new Timestamp(Long.parseLong(endStr)); + final long unbanLong = resultSet.getLong("strftime('%s',watch_unwatchdate)") * 1000; + unwatchDate = (unbanLong == 0) ? null : new Timestamp(unbanLong); + }else{ + beginDate = resultSet.getTimestamp("watch_begin"); + endDate = resultSet.getTimestamp("watch_end"); + unwatchDate = resultSet.getTimestamp("watch_unwatchdate"); + } + + final String server = resultSet.getString("watch_server"); + String reason = resultSet.getString("watch_reason"); + if(reason == null){ + reason = NO_REASON; + } + final String staff = resultSet.getString("watch_staff"); + final boolean active = (resultSet.getBoolean("watch_state") ? true : false); + String unwatchReason = resultSet.getString("watch_unwatchreason"); + if(unwatchReason == null){ + unwatchReason = NO_REASON; + } + final String unwatchStaff = resultSet.getString("watch_unwatchstaff"); + watchList.add(new WatchEntry(entity, server, reason, staff, beginDate, endDate, unwatchDate, unwatchReason, unwatchStaff, active)); + } + } catch (final SQLException e) { + DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement, resultSet); + } + return watchList; + } + + /** + * This class is used to cache the watch data of a player. + */ + public static class PlayerWatchData { + private final String pName; + private final List servers; + private boolean globalWatch = false; + private Map.Entry watchMessage; + + public PlayerWatchData(final String pName, final List servers) { + this.pName = pName; + // Override the arraylist implementation to make used methods non-case sensitive + this.servers = new ArrayList(){ + @Override + public void add(int index, String element) { + super.add(index, element.toLowerCase()); + } + @Override + public boolean add(String e) { + return super.add(e.toLowerCase()); + } + @Override + public boolean contains(Object o) { + if(o instanceof String){ + return super.contains(((String)o).toLowerCase()); + } + return super.contains(o); + } + }; + for(final String server : servers){ + servers.add(server); + } + } + + public void setGlobal() { + globalWatch = true; + } + + public void unsetGlobal() { + globalWatch = false; + } + + public void addServer(final String server) { + if (!servers.contains(server)) { + servers.add(server); + } + } + + public void removeServer(final String server) { + servers.remove(server); + } + + public void clearServers() { + servers.clear(); + } + + public boolean isWatched(final String server) { + if (globalWatch) { + return true; + }else if( (ANY_SERVER.equals(server) && !servers.isEmpty()) ){ + return true; + }else if(servers.contains(server)){ + return true; + } + return false; + } + + public BaseComponent[] getWatchMessage(final Watch module){ + if(watchMessage != null){ + if(watchMessage.getValue() != null){ + if(watchMessage.getValue().getTime() >= System.currentTimeMillis()){ + return BAT.__(watchMessage.getKey().replace("{expiration}", FormatUtils.getDuration(watchMessage.getValue().getTime()))); + } + // If it's not synchronized with the db, force the update of watch data + else{ + Statement statement = null; + try (Connection conn = BAT.getConnection()) { + statement = conn.createStatement(); + if (DataSourceHandler.isSQLite()) { + statement.executeUpdate(SQLQueries.Watch.SQLite.updateExpiredWatch); + } else { + statement.executeUpdate(SQLQueries.Watch.updateExpiredWatch); + } + } catch (final SQLException e) { + DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement); + } + module.updateWatchData(pName); + } + } + else{ + return BAT.__(watchMessage.getKey()); + } + } + return __("wasUnwatchedNotif", new String[]{ NO_REASON }); + } + + public void setWatchMessage(final String messagePattern, final Timestamp expiration){ + watchMessage = new AbstractMap.SimpleEntry(messagePattern, expiration); + } + } + + public void updateWatchData(final String pName) { + final ProxiedPlayer player = ProxyServer.getInstance().getPlayer(pName); + if (player == null) { + return; + } + PlayerWatchData pWatchData; + if (watchedPlayers.containsKey(pName)) { + pWatchData = watchedPlayers.get(pName); + pWatchData.clearServers(); + pWatchData.unsetGlobal(); + } else { + pWatchData = new PlayerWatchData(pName, new ArrayList()); + } + PreparedStatement statement = null; + ResultSet resultSet = null; + try (Connection conn = BAT.getConnection()) { + statement = conn.prepareStatement("SELECT watch_server FROM `BAT_watch` WHERE UUID = ? AND watch_state = 1;"); + statement.setString(1, Core.getUUID(pName)); + resultSet = statement.executeQuery(); + while (resultSet.next()) { + final String server = resultSet.getString("watch_server"); + if (GLOBAL_SERVER.equals(server)) { + pWatchData.setGlobal(); + } else { + pWatchData.addServer(server); + } + } + resultSet.close(); + statement.close(); + + statement = conn + .prepareStatement("SELECT watch_server FROM `BAT_watch` WHERE watch_ip = ? AND watch_state = 1;"); + statement.setString(1, Core.getPlayerIP(pName)); + resultSet = statement.executeQuery(); + while (resultSet.next()) { + final String server = resultSet.getString("watch_server"); + if (GLOBAL_SERVER.equals(server)) { + pWatchData.setGlobal(); + } else { + pWatchData.addServer(server); + } + } + } catch (final SQLException e) { + DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement, resultSet); + } + watchedPlayers.put(pName, pWatchData); + if(pWatchData.isWatched(GLOBAL_SERVER)){ + loadWatchMessage(pName, GLOBAL_SERVER); + }else if(player.getServer() != null && pWatchData.isWatched(player.getServer().getInfo().getName())){ + loadWatchMessage(pName, player.getServer().getInfo().getName()); + } + } + + public List getManagedWatch(final String staff){ + final List watchList = new ArrayList(); + PreparedStatement statement = null; + ResultSet resultSet = null; + try (Connection conn = BAT.getConnection()) { + statement = conn.prepareStatement((DataSourceHandler.isSQLite()) + ? SQLQueries.Watch.SQLite.getManagedWatch + : SQLQueries.Watch.getManagedWatch); + statement.setString(1, staff); + statement.setString(2, staff); + resultSet = statement.executeQuery(); + + while (resultSet.next()) { + final Timestamp beginDate; + final Timestamp endDate; + final Timestamp unwatchDate; + if(DataSourceHandler.isSQLite()){ + beginDate = new Timestamp(resultSet.getLong("strftime('%s',watch_begin)") * 1000); + String endStr = resultSet.getString("watch_end"); + endDate = (endStr == null) ? null : new Timestamp(Long.parseLong(endStr)); + long unwatchLong = resultSet.getLong("strftime('%s',watch_unwatchdate)") * 1000; + unwatchDate = (unwatchLong == 0) ? null : new Timestamp(unwatchLong); + }else{ + beginDate = resultSet.getTimestamp("watch_begin"); + endDate = resultSet.getTimestamp("watch_end"); + unwatchDate = resultSet.getTimestamp("watch_unwatchdate"); + } + + + // Make it compatible with sqlite (date: get an int with the sfrt and then construct a tiemstamp) + final String server = resultSet.getString("watch_server"); + String reason = resultSet.getString("watch_reason"); + if(reason == null){ + reason = NO_REASON; + } + String entity = (resultSet.getString("watch_ip") != null) + ? resultSet.getString("watch_ip") + : Core.getPlayerName(resultSet.getString("UUID")); + // If the UUID search failed + if(entity == null){ + entity = "UUID:" + resultSet.getString("UUID"); + } + final boolean active = (resultSet.getBoolean("watch_state") ? true : false); + String unwatchReason = resultSet.getString("watch_unwatchreason"); + if(unwatchReason == null){ + unwatchReason = NO_REASON; + } + final String unwatchStaff = resultSet.getString("watch_unwatchstaff"); + watchList.add(new WatchEntry(entity, server, reason, staff, beginDate, endDate, unwatchDate, unwatchReason, unwatchStaff, active)); + } + } catch (final SQLException e) { + DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement, resultSet); + } + return watchList; + } + + public void unloadWatchData(final ProxiedPlayer player, final int watchState) { + if (watchState == 1) + BAT.broadcast( "Watched player " + player + " left \n", "watch.broadcast"); + watchedPlayers.remove(player.getName()); + } + + // Event Listener + @EventHandler + public void onServerConnect(final ServerConnectedEvent e) { + final ProxiedPlayer player = e.getPlayer(); + final String pName = player.getName(); + final int watchState = isWatched(player, e.getServer().getInfo().getName()); + if (watchState == -1) { + // Load watch data with a little bit of delay to handle server switching operations which may take some time + BAT.getInstance().getProxy().getScheduler().schedule(BAT.getInstance(), new Runnable() { + @Override + public void run() { + updateWatchData(pName); + } + }, 250, TimeUnit.MILLISECONDS); + } else if (watchState == 1) { + PlayerWatchData pWatchData = watchedPlayers.get(pName); + if(pWatchData.isWatched(GLOBAL_SERVER)){ + loadWatchMessage(pName, GLOBAL_SERVER); + }else if(pWatchData.isWatched(e.getServer().getInfo().getName())){ + loadWatchMessage(pName, e.getServer().getInfo().getName()); + } + //player.sendMessage(pWatchData.getWatchMessage(this)); Don't notify the player + String msg = _("watchConnectBroadcast", new String[] { pName, e.getServer().getInfo().getName()}); + BAT.broadcast( msg, "watch.broadcast"); + } + } + + + @EventHandler + public void onPlayerDisconnect(final PlayerDisconnectEvent e) { + final ProxiedPlayer player = e.getPlayer(); + final int watchState = isWatched(player, GLOBAL_SERVER); + unloadWatchData(player, watchState); + } + + +} diff --git a/src/main/java/fr/Alphart/BAT/Modules/Watch/WatchCommand.java b/src/main/java/fr/Alphart/BAT/Modules/Watch/WatchCommand.java new file mode 100644 index 0000000..1b3fbcf --- /dev/null +++ b/src/main/java/fr/Alphart/BAT/Modules/Watch/WatchCommand.java @@ -0,0 +1,424 @@ +package fr.Alphart.BAT.Modules.Watch; + +import static com.google.common.base.Preconditions.checkArgument; +import static fr.Alphart.BAT.I18n.I18n._; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import com.google.common.base.Joiner; + +import fr.Alphart.BAT.BAT; +import fr.Alphart.BAT.Modules.BATCommand; +import fr.Alphart.BAT.Modules.BATCommand.RunAsync; +import fr.Alphart.BAT.Modules.CommandHandler; +import fr.Alphart.BAT.Modules.IModule; +import fr.Alphart.BAT.Modules.InvalidModuleException; +import fr.Alphart.BAT.Modules.Core.Core; +import fr.Alphart.BAT.Modules.Core.PermissionManager; +import fr.Alphart.BAT.Modules.Core.PermissionManager.Action; +import fr.Alphart.BAT.Utils.FormatUtils; +import fr.Alphart.BAT.Utils.Utils; + +public class WatchCommand extends CommandHandler { + private static Watch watch; + + public WatchCommand(final Watch watchModule) { + super(watchModule); + watch = watchModule; + } + + @RunAsync + public static class WatchCmd extends BATCommand { + public WatchCmd() { + super("watch", " [server] [reason]", + "Watch the player on username basis on the specified server permanently or until unwatched.", + Action.WATCH.getPermission()); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + if (args[0].equals("help")) { + try { + FormatUtils.showFormattedHelp(BAT.getInstance().getModules().getModule("watch").getCommands(), + sender, "WATCH"); + } catch (final InvalidModuleException e) { + e.printStackTrace(); + } + return; + } + handleWatchCommand(this, false, false, sender, args, confirmedCmd); + } + } + + @RunAsync + public static class WatchIPCmd extends BATCommand { + public WatchIPCmd() { + super( + "watchip", + " [server] [reason]", + "Watch player on an IP basis on the specified server permanently or until unbanned. No player logged in with that IP will be able to speak.", + Action.WATCHIP.getPermission()); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleWatchCommand(this, false, true, sender, args, confirmedCmd); + } + } + + @RunAsync + public static class GWatchCmd extends BATCommand { + public GWatchCmd() { + super( + "gwatch", + " [reason]", + "Watch the player on username basis on all servers (the whole network) permanently or until unbanned. Staff will be notified when the player connects", + Action.WATCH.getPermission() + ".global"); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleWatchCommand(this, true, false, sender, args, confirmedCmd); + } + } + + @RunAsync + public static class GWatchIPCmd extends BATCommand { + public GWatchIPCmd() { + super( + "gwatchip", + " [reason]", + "Watch player on an IP basis on all servers (the whole network) permanently or until unwatched. Staff will be notified when any player connects from that IP.", + Action.WATCHIP.getPermission() + ".global"); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleWatchCommand(this, true, true, sender, args, confirmedCmd); + } + } + + public static void handleWatchCommand(final BATCommand command, final boolean global, final boolean ipWatch, + final CommandSender sender, final String[] args, final boolean confirmedCmd) { + String target = args[0]; + String server = IModule.GLOBAL_SERVER; + final String staff = sender.getName(); + String reason = IModule.NO_REASON; + + final ProxiedPlayer player = ProxyServer.getInstance().getPlayer(target); + + String ip = null; + + String returnedMsg; + + if (global) { + if (args.length > 1) { + reason = Utils.getFinalArg(args, 1); + } + } else { + if (args.length == 1) { + checkArgument(sender instanceof ProxiedPlayer, _("specifyServer")); + server = ((ProxiedPlayer) sender).getServer().getInfo().getName(); + } else { + checkArgument(Utils.isServer(args[1]), _("invalidServer")); + server = args[1]; + reason = (args.length > 2) ? Utils.getFinalArg(args, 2) : IModule.NO_REASON; + } + } + + checkArgument( + !reason.equalsIgnoreCase(IModule.NO_REASON) || !BAT.getInstance().getConfiguration().isMustGiveReason(), + _("noReasonInCommand")); + + // Check if the target isn't an ip and the player is offline + if (!Utils.validIP(target) && player == null) { + ip = Core.getPlayerIP(target); + if (ipWatch) { + checkArgument(!"0.0.0.0".equals(ip), _("ipUnknownPlayer")); + } else { + // If ip = 0.0.0.0, it means the player never connects + if ("0.0.0.0".equals(ip) && !confirmedCmd) { + command.mustConfirmCommand(sender, command.getName() + " " + Joiner.on(' ').join(args), + _("operationUnknownPlayer", new String[] { target })); + return; + } + // Set the ip to null to avoid checking if the ip is banned + ip = null; + } + } + + if (!global) { + checkArgument(PermissionManager.canExecuteAction((ipWatch) ? Action.WATCHIP : Action.WATCH, sender, server), + _("noPerm")); + } + target = (ip == null) ? target : ip; + + checkArgument(!PermissionManager.isExemptFrom(Action.WATCH, target), _("isExempt")); + + checkArgument(!watch.isWatched((ip == null) ? target : ip, server, false), _("alreadyWatch")); + + if (ipWatch && !BAT.getInstance().getRedis().isRedisEnabled() && player != null) { + returnedMsg = watch.watchIP(player, server, staff, 0, reason); + } else { + returnedMsg = watch.watch(target, server, staff, 0, reason); + } + + BAT.broadcast(returnedMsg, Action.WATCH_BROADCAST.getPermission()); + } + + @RunAsync + public static class TempWatchCmd extends BATCommand { + public TempWatchCmd() { + super("tempwatch", " [server] [reason]", + "Temporarily watch the player on username basis on from the specified server for duration.", + Action.TEMPWATCH.getPermission()); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleTempWatchCommand(this, false, false, sender, args, confirmedCmd); + } + } + + @RunAsync + public static class TempWatchIPCmd extends BATCommand { + public TempWatchIPCmd() { + super( + "tempwatchip", + " [server] [reason]", + "Temporarily watch the player on IP basis on the specified server for duration. No player logged in with that IP will be able to speak.", + Action.TEMPWATCHIP.getPermission()); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleTempWatchCommand(this, false, true, sender, args, confirmedCmd); + } + } + + @RunAsync + public static class GTempWatchCmd extends BATCommand { + public GTempWatchCmd() { + super("gtempwatch", " [reason]", + "Temporarily watch the player on username basis on all servers (the whole network) for duration.", + Action.TEMPWATCH.getPermission() + ".global"); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleTempWatchCommand(this, true, false, sender, args, confirmedCmd); + } + } + + @RunAsync + public static class GTempWatchIPCmd extends BATCommand { + public GTempWatchIPCmd() { + super( + "gtempwatchip", + " [reason]", + "Temporarily watch the player on IP basis on all servers (the whole network) for duration. Staff will be notified when any player connects from that IP.", + Action.TEMPWATCHIP.getPermission() + ".global"); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleTempWatchCommand(this, true, true, sender, args, confirmedCmd); + } + } + + public static void handleTempWatchCommand(final BATCommand command, final boolean global, final boolean ipWatch, + final CommandSender sender, final String[] args, final boolean confirmedCmd) { + String target = args[0]; + String server = IModule.GLOBAL_SERVER; + final String staff = sender.getName(); + String reason = IModule.NO_REASON; + final long expirationTimestamp = Utils.parseDuration(args[1]); + + final ProxiedPlayer player = ProxyServer.getInstance().getPlayer(target); + + String ip = null; + + String returnedMsg; + + if (global) { + if (args.length > 2) { + reason = Utils.getFinalArg(args, 2); + } + } else { + if (args.length == 2) { + checkArgument(sender instanceof ProxiedPlayer, _("specifyServer")); + server = ((ProxiedPlayer) sender).getServer().getInfo().getName(); + } else { + checkArgument(Utils.isServer(args[2]), _("invalidServer")); + server = args[2]; + reason = (args.length > 3) ? Utils.getFinalArg(args, 3) : IModule.NO_REASON; + } + } + + checkArgument( + !reason.equalsIgnoreCase(IModule.NO_REASON) || !BAT.getInstance().getConfiguration().isMustGiveReason(), + _("noReasonInCommand")); + + // Check if the target isn't an ip and the player is offline + if (!Utils.validIP(target) && player == null) { + ip = Core.getPlayerIP(target); + if (ipWatch) { + checkArgument(!"0.0.0.0".equals(ip), _("ipUnknownPlayer")); + } else { + // If ip = 0.0.0.0, it means the player never connects + if ("0.0.0.0".equals(ip) && !confirmedCmd) { + command.mustConfirmCommand(sender, command.getName() + " " + Joiner.on(' ').join(args), + _("operationUnknownPlayer", new String[] { target })); + return; + } + // Set the ip to null to avoid checking if the ip is banned + ip = null; + } + } + + if (!global) { + checkArgument( + PermissionManager.canExecuteAction((ipWatch) ? Action.TEMPWATCHIP : Action.TEMPWATCH, sender, server), + _("noPerm")); + } + target = (ip == null) ? target : ip; + + checkArgument(!PermissionManager.isExemptFrom(Action.WATCH, target), _("isExempt")); + + checkArgument(!watch.isWatched((ip == null) ? target : ip, server, false), _("alreadyWatch")); + + if (ipWatch && !BAT.getInstance().getRedis().isRedisEnabled() && player != null) { + returnedMsg = watch.watchIP(player, server, staff, expirationTimestamp, reason); + } else { + returnedMsg = watch.watch(target, server, staff, expirationTimestamp, reason); + } + + BAT.broadcast(returnedMsg, Action.WATCH_BROADCAST.getPermission()); + } + + @RunAsync + public static class UnwatchCmd extends BATCommand { + public UnwatchCmd() { + super("unwatch", " [server] [reason]", + "Unwatch the player on a username basis from the specified server.", Action.UNWATCH.getPermission()); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleUnwatchCommand(this, false, false, sender, args, confirmedCmd); + } + } + + @RunAsync + public static class UnwatchIPCmd extends BATCommand { + public UnwatchIPCmd() { + super("unwatchip", " [server] [reason]", + "Unwatch the player on a username basis from all servers (the whole network).", Action.UNWATCHIP + .getPermission()); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleUnwatchCommand(this, false, true, sender, args, confirmedCmd); + } + } + + @RunAsync + public static class GUnwatchCmd extends BATCommand { + public GUnwatchCmd() { + super("gunwatch", " [reason]", "Unwatch the player on an IP basis from the specified server.", + Action.UNWATCH.getPermission() + ".global"); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleUnwatchCommand(this, true, false, sender, args, confirmedCmd); + } + } + + @RunAsync + public static class GUnwatchIPCmd extends BATCommand { + public GUnwatchIPCmd() { + super("gunwatchip", " [reason]", + "Unwatch the player on an IP basis from all servers (the whole network).", Action.UNWATCHIP + .getPermission() + ".global"); + } + + @Override + public void onCommand(final CommandSender sender, final String[] args, final boolean confirmedCmd) + throws IllegalArgumentException { + handleUnwatchCommand(this, true, true, sender, args, confirmedCmd); + } + } + + public static void handleUnwatchCommand(final BATCommand command, final boolean global, final boolean ipUnwatch, + final CommandSender sender, final String[] args, final boolean confirmedCmd) { + String target = args[0]; + String server = IModule.ANY_SERVER; + final String staff = sender.getName(); + String reason = IModule.NO_REASON; + + String ip = null; + + String returnedMsg; + + if (global) { + if (args.length > 1) { + reason = Utils.getFinalArg(args, 1); + } + } else { + if (args.length == 1) { + checkArgument(sender instanceof ProxiedPlayer, _("specifyServer")); + server = ((ProxiedPlayer) sender).getServer().getInfo().getName(); + } else { + checkArgument(Utils.isServer(args[1]), _("invalidServer")); + server = args[1]; + reason = (args.length > 2) ? Utils.getFinalArg(args, 2) : IModule.NO_REASON; + } + } + + checkArgument( + !reason.equalsIgnoreCase(IModule.NO_REASON) || !BAT.getInstance().getConfiguration().isMustGiveReason(), + _("noReasonInCommand")); + + // Check if the target isn't an ip and the player is offline + if (!Utils.validIP(target) && ipUnwatch) { + ip = Core.getPlayerIP(target); + checkArgument(!"0.0.0.0".equals(ip), _("ipUnknownPlayer")); + } + + if (!global) { + checkArgument( + PermissionManager.canExecuteAction((ipUnwatch) ? Action.UNWATCHIP : Action.UNWATCH, sender, server), + _("noPerm")); + } + target = (ip == null) ? target : ip; + + final String[] formatArgs = { args[0] }; + + checkArgument( + watch.isWatched((ip == null) ? target : ip, server, true), + (IModule.ANY_SERVER.equals(server) ? _("notWatchedAny", formatArgs) : ((ipUnwatch) ? _("notWatchedIP", + formatArgs) : _("notWatched", formatArgs)))); + + if (ipUnwatch) { + returnedMsg = watch.unWatchIP(target, server, staff, reason); + } else { + returnedMsg = watch.unWatch(target, server, staff, reason); + } + + BAT.broadcast(returnedMsg, Action.WATCH_BROADCAST.getPermission()); + } +} \ No newline at end of file diff --git a/src/main/java/fr/Alphart/BAT/Modules/Watch/WatchEntry.java b/src/main/java/fr/Alphart/BAT/Modules/Watch/WatchEntry.java new file mode 100644 index 0000000..a044009 --- /dev/null +++ b/src/main/java/fr/Alphart/BAT/Modules/Watch/WatchEntry.java @@ -0,0 +1,21 @@ +package fr.Alphart.BAT.Modules.Watch; + +import java.sql.Timestamp; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class WatchEntry { + private final String entity; + private final String server; + private final String reason; + private final String staff; + private final Timestamp beginDate; + private final Timestamp endDate; + private final Timestamp unwatchDate; + private final String unwatchReason; + private final String unwatchStaff; + private final boolean active; +} diff --git a/src/main/java/fr/Alphart/BAT/Modules/Watch/WatchTask.java b/src/main/java/fr/Alphart/BAT/Modules/Watch/WatchTask.java new file mode 100644 index 0000000..dfbcf2e --- /dev/null +++ b/src/main/java/fr/Alphart/BAT/Modules/Watch/WatchTask.java @@ -0,0 +1,47 @@ +package fr.Alphart.BAT.Modules.Watch; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import fr.Alphart.BAT.BAT; +import fr.Alphart.BAT.database.DataSourceHandler; +import fr.Alphart.BAT.database.SQLQueries; + +/** + * This task handles the watch related update :
+ * - check in the db for every active watch if it is finished if this is the case + * : set watch_(ip)state to 0
+ * - update the PlayerWatchData of every player on the server
+ * This task must be run asynchronously + */ +public class WatchTask implements Runnable { + private final Watch watch; + + public WatchTask(final Watch watchModule) { + watch = watchModule; + } + + @Override + public void run() { + Statement statement = null; + try (Connection conn = BAT.getConnection()) { + statement = conn.createStatement(); + if (DataSourceHandler.isSQLite()) { + statement.executeUpdate(SQLQueries.Watch.SQLite.updateExpiredWatch); + } else { + statement.executeUpdate(SQLQueries.Watch.updateExpiredWatch); + } + } catch (final SQLException e) { + DataSourceHandler.handleException(e); + } finally { + DataSourceHandler.close(statement); + } + // Update player watch data + for (final ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { + watch.updateWatchData(player.getName()); + } + } +} diff --git a/src/main/java/fr/Alphart/BAT/Utils/RedisUtils.java b/src/main/java/fr/Alphart/BAT/Utils/RedisUtils.java index cca78be..409e608 100644 --- a/src/main/java/fr/Alphart/BAT/Utils/RedisUtils.java +++ b/src/main/java/fr/Alphart/BAT/Utils/RedisUtils.java @@ -59,6 +59,9 @@ public void onPubSubMessage(final PubSubMessageEvent e) { case "muteupdate": recieveMuteUpdatePlayer(message[2], message[3]); break; + case "watchupdate": + recieveWatchUpdatePlayer(message[2], message[3]); + break; case "movedefaultserver": recieveMoveDefaultServerPlayer(message[2]); break; @@ -111,6 +114,10 @@ public void sendMuteUpdatePlayer(UUID pUUID, String server) { if (!redis) return; sendMessage("muteupdate", pUUID.toString() + split + server); } + public void sendWatchUpdatePlayer(UUID pUUID, String server) { + if (!redis) return; + sendMessage("watchupdate", pUUID.toString() + split + server); + } private void recieveMuteUpdatePlayer(String sUUID, String server) { if(BAT.getInstance().getModules().isLoaded("mute")){ ProxiedPlayer player = BAT.getInstance().getProxy().getPlayer(UUID.fromString(sUUID)); @@ -125,7 +132,22 @@ private void recieveMuteUpdatePlayer(String sUUID, String server) { throw new IllegalStateException("The mute module isn't enabled. The mute message can't be handled."); } } - + + private void recieveWatchUpdatePlayer(String sUUID, String server) { + if(BAT.getInstance().getModules().isLoaded("watch")){ + ProxiedPlayer player = BAT.getInstance().getProxy().getPlayer(UUID.fromString(sUUID)); + if (player != null) { + try { + BAT.getInstance().getModules().getWatchModule().updateWatchData(player.getName()); + } catch (InvalidModuleException ignored) { + } + } + } + else{ + throw new IllegalStateException("The watch module isn't enabled. The watch message can't be handled."); + } + } + public void sendMoveDefaultServerPlayer(UUID pUUID) { if (!redis) return; sendMessage("movedefaultserver", pUUID.toString()); diff --git a/src/main/java/fr/Alphart/BAT/database/DataSourceHandler.java b/src/main/java/fr/Alphart/BAT/database/DataSourceHandler.java index 414f5ab..a27bfe6 100644 --- a/src/main/java/fr/Alphart/BAT/database/DataSourceHandler.java +++ b/src/main/java/fr/Alphart/BAT/database/DataSourceHandler.java @@ -191,7 +191,7 @@ public void run() { } } String backupCmd = "mysqldump -u {user} -p --add-drop-database -r {path} {database} {tables}"; - final String tables = Joiner.on(' ').join(Arrays.asList(SQLQueries.Ban.table, SQLQueries.Mute.table, + final String tables = Joiner.on(' ').join(Arrays.asList(SQLQueries.Ban.table, SQLQueries.Mute.table,SQLQueries.Watch.table, SQLQueries.Kick.table, SQLQueries.Comments.table, SQLQueries.Core.table)); String backupPath = backupFile.getAbsolutePath(); if(backupPath.contains(" ")){ diff --git a/src/main/java/fr/Alphart/BAT/database/SQLQueries.java b/src/main/java/fr/Alphart/BAT/database/SQLQueries.java index d8961da..4203651 100644 --- a/src/main/java/fr/Alphart/BAT/database/SQLQueries.java +++ b/src/main/java/fr/Alphart/BAT/database/SQLQueries.java @@ -146,6 +146,114 @@ public static class SQLite { } } + public static class Watch { + public final static String table = "BAT_watch"; + public final static String createTable = "CREATE TABLE IF NOT EXISTS `" + table + "` (" + + "`watch_id` INTEGER PRIMARY KEY AUTO_INCREMENT," + "`UUID` varchar(100) NULL," + + "`watch_ip` varchar(50) NULL," + + + "`watch_staff` varchar(30) NOT NULL," + "`watch_reason` varchar(100) NULL," + + "`watch_server` varchar(30) NOT NULL," + "`watch_begin` timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL," + + "`watch_end` timestamp NULL," + "`watch_state` bool NOT NULL default 1," + + + "`watch_unwatchdate` timestamp NULL," + "`watch_unwatchstaff` varchar(30) NULL," + + "`watch_unwatchreason` varchar(100) NULL," + + + "INDEX(UUID)," + "INDEX(watch_ip)" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;"; + + public static final String isWatched = "SELECT watch_id FROM `" + table + "` WHERE watch_state = 1 AND UUID = ?;"; + public static final String isWatchedServer = "SELECT watch_id FROM `" + table + + "` WHERE watch_state = 1 AND UUID = ? " + "AND watch_server = ?;"; + + public static final String isWatchedIP = "SELECT watch_id FROM `" + table + + "` WHERE watch_state = 1 AND watch_ip = ? AND UUID IS NULL;"; + public static final String isWatchedServerIP = "SELECT watch_id FROM `" + table + + "` WHERE watch_state = 1 AND watch_ip = ? AND watch_server = ? AND UUID IS NULL;"; + + public static final String createWatch = "INSERT INTO `" + table + + "`(UUID, watch_staff, watch_server, watch_end, watch_reason) VALUES (?, ?, ?, ?, ?);"; + + public static final String createWatchIP = "INSERT INTO `" + table + + "`(watch_ip, watch_staff, watch_server, watch_end, watch_reason) VALUES (?, ?, ?, ?, ?);"; + + public static final String unWatch = "UPDATE `" + table + + "` SET watch_state = 0, watch_unwatchreason = ?, watch_unwatchstaff = ?, watch_unwatchdate = NOW() " + + "WHERE UUID = ? AND watch_state = 1;"; + public static final String unWatchServer = "UPDATE `" + table + + "` SET watch_state = 0, watch_unwatchreason = ?, watch_unwatchstaff = ?, watch_unwatchdate = NOW() " + + "WHERE UUID = ? AND watch_server = ? AND watch_state = 1;"; + + public static final String unWatchIP = "UPDATE `" + table + + "` SET watch_state = 0, watch_unwatchreason = ?, watch_unwatchstaff = ?, watch_unwatchdate = NOW() " + + "WHERE watch_ip = ? AND UUID IS NULL;"; + public static final String unWatchIPServer = "UPDATE `" + table + + "` SET watch_state = 0, watch_unwatchreason = ?, watch_unwatchstaff = ?, watch_unwatchdate = NOW() " + + "WHERE watch_ip = ? AND watch_server = ? AND UUID IS NULL;"; + + public static final String getWatch = "SELECT * FROM `" + + table + "`" + " WHERE UUID = ? ORDER BY watch_state DESC, watch_end DESC;"; + public static final String getWatchIP = "SELECT * FROM `" + + table + "`" + " WHERE watch_ip = ? AND UUID IS NULL ORDER BY watch_state DESC, watch_end DESC;"; + + public static final String getManagedWatch = "SELECT * FROM `" + + table + "`" + " WHERE watch_staff = ? OR watch_unwatchstaff = ? ORDER BY watch_state DESC, watch_end DESC;"; + + public static final String getWatchMessage = "SELECT watch_reason, watch_end, watch_staff, watch_begin FROM `" + + table + "` WHERE (UUID = ? OR watch_ip = ?) AND watch_state = 1 AND watch_server = ?;"; + + public static final String updateExpiredWatch = "UPDATE `" + table + "` SET watch_state = 0 " + + "WHERE watch_state = 1 AND (watch_end != 0 AND watch_end < NOW());"; + + public static class SQLite { + public final static String[] createTable = { + "CREATE TABLE IF NOT EXISTS `" + table + "` (" + "`watch_id` INTEGER PRIMARY KEY AUTOINCREMENT," + + "`UUID` varchar(100) NULL," + "`watch_ip` varchar(50) NULL," + + + "`watch_staff` varchar(30) NOT NULL," + "`watch_reason` varchar(100) NULL," + + "`watch_server` varchar(30) NOT NULL," + + "`watch_begin` timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL," + + "`watch_end` timestamp NULL," + "`watch_state` bool NOT NULL default 1," + + + "`watch_unwatchdate` timestamp NULL," + "`watch_unwatchstaff` varchar(30) NULL," + + "`watch_unwatchreason` varchar(100) NULL" + ");", + "CREATE INDEX IF NOT EXISTS `watch.uuid_index` ON " + table + " (`UUID`);", + "CREATE INDEX IF NOT EXISTS `watch.ip_index` ON " + table + " (`watch_ip`);" }; + + public static final String unWatch = "UPDATE `" + + table + + "` SET watch_state = 0, watch_unwatchreason = ?, watch_unwatchstaff = ?, watch_unwatchdate = datetime() " + + "WHERE UUID = ? AND watch_state = 1;"; + public static final String unWatchServer = "UPDATE `" + + table + + "` SET watch_state = 0, watch_unwatchreason = ?, watch_unwatchstaff = ?, watch_unwatchdate = datetime() " + + "WHERE UUID = ? AND watch_server = ? AND watch_state = 1;"; + public static final String unWatchIP = "UPDATE `" + + table + + "` SET watch_state = 0, watch_unwatchreason = ?, watch_unwatchstaff = ?, watch_unwatchdate = datetime() " + + "WHERE watch_ip = ? AND UUID IS NULL;"; + public static final String unWatchIPServer = "UPDATE `" + + table + + "` SET watch_state = 0, watch_unwatchreason = ?, watch_unwatchstaff = ?, watch_unwatchdate = datetime() " + + "WHERE watch_ip = ? AND watch_server = ? AND UUID IS NULL;"; + + public static final String getWatch = "SELECT *, " + + "strftime('%s',watch_begin), strftime('%s',watch_end), strftime('%s',watch_unwatchdate)" + + "FROM `" + table + "`" + " WHERE UUID = ? ORDER BY watch_state DESC, watch_end DESC;"; + public static final String getWatchIP = "SELECT *, " + + "strftime('%s',watch_begin), strftime('%s',watch_end), strftime('%s',watch_unwatchdate)" + + "FROM `" + table + "`" + " WHERE watch_ip = ? AND UUID IS NULL ORDER BY watch_state DESC, watch_end DESC;"; + + public static final String getManagedWatch = "SELECT *, " + + "strftime('%s',watch_begin), strftime('%s',watch_end), strftime('%s',watch_unwatchdate) " + + "FROM `" + table + "`" + " WHERE watch_staff = ? OR watch_unwatchstaff = ? ORDER BY watch_state DESC, watch_end DESC;"; + + public static final String getWatchMessage = "SELECT watch_reason, watch_staff, strftime('%s',watch_begin), watch_end FROM `" + + table + "` WHERE (UUID = ? OR watch_ip = ?) AND watch_state = 1 AND watch_server = ?;"; + + public static final String updateExpiredWatch = "UPDATE `" + table + "` SET watch_state = 0 " + + "WHERE watch_state = 1 AND watch_end != 0 AND (watch_end / 1000) < CAST(strftime('%s', 'now') as integer);"; + } + } public static class Mute { public final static String table = "BAT_mute"; public final static String createTable = "CREATE TABLE IF NOT EXISTS `" + table + "` (" diff --git a/src/main/resources/messages_en.language b/src/main/resources/messages_en.language index 82f4ebb..125ff47 100644 --- a/src/main/resources/messages_en.language +++ b/src/main/resources/messages_en.language @@ -26,6 +26,21 @@ wasMutedNotif=You were muted. Reason : {0} wasUnmutedNotif=You were unmuted. Reason : {0} loadingMutedata=Loading of data in progress: you may speak in a little while. +# Watch module +alreadyWatch=&cThis player is already watched from this server! +isWatched=You were watched on {1} by {2} from this server for: {0} +isWatchTemp=You were temporarily watched on {2} by {3} from this server, you were watched for: Reason: {0} This watch ends in: {1} +watchBroadcast=&a{0}&e was &6watched permanently&e by &a{1}&e from the server &a{2}&e. Reason : {3} +watchTempBroadcast=&a{0}&e was &6watched&e for &a{1}&e by &a{2}&e from the server &a{3}&e. Reason : {4} +notWatched=&c{0} isn''t watched from this server. +notWatchedAny=&c{0} isn''t watched from any server ! +notWatchedIP=&c{0} isn''t watched from this server. +unWatchBroadcast=&a{0}&e was &6unwatched &eby &a{1}&e from the server &a{2}&e. Reason : {3} +wasWatchedNotif=You were watched. Reason : {0} +wasWatchedNotif=You were unwatched. Reason : {0} +loadingWatchdata=Loading of data in progress: you may be unwatched in a little while. +watchConnectBroadcast=watched player &a{0}&e has connected to server &a{1}&e. + # Kick module kickBroadcast=&a{0}&e was &6kicked&e by &a{1}&e from the server &a{2}&e. Reason : {3} gKickBroadcast=&a{0}&e was &6kicked&e by &a{1}&e from the network. Reason : {2} @@ -57,6 +72,8 @@ playerLookup=&f---- &9Lookup &b{player} &f----\n\ &eState : \n\ ¤¤&c&lBanned &efrom:&3 {ban_servers}\n\ ¤¤&c&lIP Banned &efrom:&3 {banip_servers}\n\ +¤¤&c&lWatched &efrom:&3 {watch_servers}\n\ +¤¤&c&lIP Watched &efrom:&3 {watchip_servers}\n\ ¤¤&c&lMuted &efrom:&3 {mute_servers}\n\ ¤¤&c&lIP Muted &efrom:&3 {muteip_servers}\n\ &eFirst login: &a{first_login}\n\ @@ -64,6 +81,7 @@ playerLookup=&f---- &9Lookup &b{player} &f----\n\ &eLast IP: &a{last_ip}\n\ &eHistory: &a{bans_number} bans\n\ ¤¤¤¤¤¤¤¤¤¤¤¤&a{mutes_number} mutes\n\ +¤¤¤¤¤¤¤¤¤¤¤¤&a{watches_number} watches\n\ ¤¤¤¤¤¤¤¤¤¤¤¤&a{kicks_number} kicks\n\ &eLast three comments: \n\ ¤¤&a{last_comments:3}\n\ @@ -81,6 +99,7 @@ staffLookup=&f---- &9&nSTAFF Lookup&r &b{staff} &f----\n\ &eStatistics :\n\ ¤¤&b{staff}&e has issued &a{bans_number}&e bans and &a{unbans_number}&e unbans\n\ ¤¤&b{staff}&e has issued &a{mutes_number}&e mutes and &a{unmutes_number}&e unmutes\n\ +¤¤&b{staff}&e has issued &a{watches_number}&e watches and &a{unwatches_number}&e unwatches\n\ ¤¤&b{staff}&e has issued &a{kicks_number}&e kicks and &a{warnings_number}&e warnings\n\ ¤¤&b{staff}&e has written &a{comments_number}&e comments\n\ &f---- &9&nSTAFF Lookup&r &b{staff} &f---- @@ -93,9 +112,11 @@ ipLookup=&f---- &9Lookup &b{ip} &f----\n\ ¤&3{ip_users}\n\ &eState:\n\ ¤¤&c&lBanned &efrom:&3 {ban_servers}\n\ +¤¤&c&lWatched &efrom:&3 {watch_servers}\n\ ¤¤&c&lMute &efrom:&3 {mute_servers}\n\ &eHistory: &a{bans_number} bans\n\ ¤¤¤¤¤¤¤¤¤¤¤¤&a{mutes_number} mutes\n\ +¤¤¤¤¤¤¤¤¤¤¤¤&a{watches_number} watches\n\ &f---- &9Lookup &b{ip} &f---- From 556375b0945d87e479cb7b357ca70f75f41b0c32 Mon Sep 17 00:00:00 2001 From: woolwind Date: Thu, 19 May 2016 08:25:12 -0400 Subject: [PATCH 2/5] use global var instead of hardcoding string --- src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java b/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java index cf1f5ce..95ffaef 100644 --- a/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java +++ b/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java @@ -24,7 +24,6 @@ import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.ChatEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.ServerConnectedEvent; import net.md_5.bungee.api.plugin.Listener; @@ -797,12 +796,12 @@ public List getManagedWatch(final String staff){ public void unloadWatchData(final ProxiedPlayer player, final int watchState) { if (watchState == 1) - BAT.broadcast( "Watched player " + player + " left \n", "watch.broadcast"); + BAT.broadcast( "Watched player " + player + " left \n", Action.WATCH_BROADCAST.getPermission()); watchedPlayers.remove(player.getName()); } // Event Listener - @EventHandler + @EventHandler(priority = EventPriority.HIGHEST) // we want MONITOR but it doens't exist in bungee public void onServerConnect(final ServerConnectedEvent e) { final ProxiedPlayer player = e.getPlayer(); final String pName = player.getName(); @@ -824,7 +823,7 @@ public void run() { } //player.sendMessage(pWatchData.getWatchMessage(this)); Don't notify the player String msg = _("watchConnectBroadcast", new String[] { pName, e.getServer().getInfo().getName()}); - BAT.broadcast( msg, "watch.broadcast"); + BAT.broadcast( msg, Action.WATCH_BROADCAST.getPermission()); } } From aa531ef2f72327d7ceea5475cefb78812c0fabf2 Mon Sep 17 00:00:00 2001 From: woolwind Date: Fri, 20 May 2016 12:39:22 -0400 Subject: [PATCH 3/5] fixed broadcast when watched players connect to bungee --- .../fr/Alphart/BAT/Modules/Watch/Watch.java | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java b/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java index 95ffaef..1e4bbbc 100644 --- a/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java +++ b/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java @@ -800,11 +800,31 @@ public void unloadWatchData(final ProxiedPlayer player, final int watchState) { watchedPlayers.remove(player.getName()); } + public void postConnect(final ServerConnectedEvent e) + { + final ProxiedPlayer player = e.getPlayer(); + final String pName = player.getName(); + // BAT.getInstance().getLogger().info ("running postConnect for player " +pName); + final int watchState = isWatched(player, e.getServer().getInfo().getName()); + if (watchState == 1) { + PlayerWatchData pWatchData = watchedPlayers.get(pName); + if(pWatchData.isWatched(GLOBAL_SERVER)){ + loadWatchMessage(pName, GLOBAL_SERVER); + }else if(pWatchData.isWatched(e.getServer().getInfo().getName())){ + loadWatchMessage(pName, e.getServer().getInfo().getName()); + } + String msg = _("watchConnectBroadcast", new String[] { pName, e.getServer().getInfo().getName()}); + BAT.broadcast( msg, Action.WATCH_BROADCAST.getPermission()); + } + } // Event Listener @EventHandler(priority = EventPriority.HIGHEST) // we want MONITOR but it doens't exist in bungee public void onServerConnect(final ServerConnectedEvent e) { - final ProxiedPlayer player = e.getPlayer(); - final String pName = player.getName(); + + final ProxiedPlayer player = e.getPlayer(); + final String pName = player.getName(); + BAT.getInstance().getLogger().info ("running onServerConnect for player " +pName); + final int watchState = isWatched(player, e.getServer().getInfo().getName()); if (watchState == -1) { // Load watch data with a little bit of delay to handle server switching operations which may take some time @@ -814,6 +834,14 @@ public void run() { updateWatchData(pName); } }, 250, TimeUnit.MILLISECONDS); + // give the update time to run then see if the player is in watched status and print + BAT.getInstance().getProxy().getScheduler().schedule(BAT.getInstance(), new Runnable() { + @Override + public void run() { + postConnect(e); + } + }, 750, TimeUnit.MILLISECONDS); + } else if (watchState == 1) { PlayerWatchData pWatchData = watchedPlayers.get(pName); if(pWatchData.isWatched(GLOBAL_SERVER)){ From bd5b645b6a488359a8ac22a81f389098a00810a0 Mon Sep 17 00:00:00 2001 From: woolwind Date: Sat, 21 May 2016 11:29:27 -0400 Subject: [PATCH 4/5] some cleanup --- .../fr/Alphart/BAT/Modules/Watch/Watch.java | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java b/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java index 1e4bbbc..b954bbe 100644 --- a/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java +++ b/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java @@ -314,12 +314,6 @@ public String watch(final String watchdEntity, final String server, final String // The watch task timer will add the player to the bungeecord instance's cache if needed. if(server.equals(GLOBAL_SERVER) || RedisBungee.getApi().getServerFor(pUUID).getName().equalsIgnoreCase(server)) { ProxiedPlayer player = ProxyServer.getInstance().getPlayer(pUUID); - /* - if (player != null) { - player.sendMessage(__("wasWatchedNotif", new String[] { reason })); - } else { - BAT.getInstance().getRedis().sendMessagePlayer(pUUID, TextComponent.toLegacyText(__("wasWatchedNotif", new String[] { reason }))); - }*/ } } } @@ -330,10 +324,7 @@ public String watch(final String watchdEntity, final String server, final String watchedPlayers.get(player.getName()).setGlobal(); } else { watchedPlayers.get(player.getName()).addServer(server); - }/* - if (server.equals(GLOBAL_SERVER) || player.getServer().getInfo().getName().equalsIgnoreCase(server)) { - player.sendMessage(__("wasWatchedNotif", new String[] { reason })); - }*/ + } } } } @@ -360,15 +351,11 @@ public String watch(final String watchdEntity, final String server, final String statement.close(); if (player != null) { - updateWatchData(player.getName());/* - if(server.equals(GLOBAL_SERVER) || player.getServer().getInfo().getName().equalsIgnoreCase(server)){ - player.sendMessage(__("wasWatchedNotif", new String[] { reason })); - }*/ + updateWatchData(player.getName()); } else if (BAT.getInstance().getRedis().isRedisEnabled()) { //Need to implement a function to get an UUID object instead of a string one. final UUID pUUID = Core.getUUIDfromString(Core.getUUID(pName)); BAT.getInstance().getRedis().sendWatchUpdatePlayer(pUUID, server); - BAT.getInstance().getRedis().sendMessagePlayer(pUUID, TextComponent.toLegacyText(__("wasWatchedNotif", new String[] { reason }))); } if (expirationTimestamp > 0) { return _("watchTempBroadcast", new String[] { pName, FormatUtils.getDuration(expirationTimestamp), @@ -473,11 +460,8 @@ public String unWatch(final String watchdEntity, final String server, final Stri final UUID pUUID = Core.getUUIDfromString(Core.getUUID(pName)); ServerInfo pServer = RedisBungee.getApi().getServerFor(pUUID); if (ANY_SERVER.equals(server) || GLOBAL_SERVER.equals(server) || (pServer != null && pServer.getName().equalsIgnoreCase(server))){ - BAT.getInstance().getRedis().sendWatchUpdatePlayer(pUUID, server); - BAT.getInstance().getRedis().sendMessagePlayer(pUUID, TextComponent.toLegacyText(__("wasUnwatchedNotif", new String[] { reason }))); } } - return _("unWatchBroadcast", new String[] { pName, staff, server, reason }); } } catch (final SQLException e) { From 4acff47621cf3d0229b308e68a42f3801c34d9c7 Mon Sep 17 00:00:00 2001 From: woolwind Date: Sat, 21 May 2016 11:30:17 -0400 Subject: [PATCH 5/5] *~ files are emacs backups --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 382604f..ba2c6b3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ build_move-BAT.bat .classpath .idea/ *.iml +*~