diff --git a/.gitignore b/.gitignore
index 6ec0c54..ba2c6b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@ build_move-BAT.bat
.project
.classpath
.idea/
-*.iml
\ No newline at end of file
+*.iml
+*~
diff --git a/pom.xml b/pom.xml
index f6b3ba8..5746007 100644
--- a/pom.xml
+++ b/pom.xml
@@ -137,6 +137,6 @@
- Bungee plugin which handles ban, mute and kick management
+ Bungee plugin which handles ban, mute, watch and kick management
1.4.2
diff --git a/src/main/java/fr/Alphart/BAT/Configuration.java b/src/main/java/fr/Alphart/BAT/Configuration.java
index be7fb32..e6f63c0 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 d9d26ad..e3c94b9 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 has never been muted."));
}
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 a85bd1d..e4ad357 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()))
@@ -193,8 +210,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("{comments_number}", String.valueOf(commentsNumber))
.replace("{name_history_list}", name_history_list).replaceAll("\\{last_comments:\\d\\}", last_comments)
.replace("{player}", pName).replace("{uuid}", Core.getUUID(pName))
@@ -433,8 +452,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();
@@ -528,7 +643,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..b954bbe
--- /dev/null
+++ b/src/main/java/fr/Alphart/BAT/Modules/Watch/Watch.java
@@ -0,0 +1,851 @@
+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.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);
+ }
+ }
+ }
+ } 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 (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());
+ } 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);
+ }
+ 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))){
+ }
+ }
+ 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", Action.WATCH_BROADCAST.getPermission());
+ 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();
+ 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
+ BAT.getInstance().getProxy().getScheduler().schedule(BAT.getInstance(), new Runnable() {
+ @Override
+ 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)){
+ 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, Action.WATCH_BROADCAST.getPermission());
+ }
+ }
+
+
+ @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 95d7061..5eb9a74 100644
--- a/src/main/java/fr/Alphart/BAT/database/SQLQueries.java
+++ b/src/main/java/fr/Alphart/BAT/database/SQLQueries.java
@@ -145,6 +145,113 @@ public static class SQLite {
+ "WHERE ban_state = 1 AND (ban_end != 0 AND (ban_end / 1000) < CAST(strftime('%s', 'now') as integer));";
}
}
+ 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";
diff --git a/src/main/resources/messages_en.language b/src/main/resources/messages_en.language
index 111649b..4cef1c6 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----