diff --git a/src/main/java/buttondevteam/chat/ChatProcessing.java b/src/main/java/buttondevteam/chat/ChatProcessing.java index 77d1f03..5867e8c 100644 --- a/src/main/java/buttondevteam/chat/ChatProcessing.java +++ b/src/main/java/buttondevteam/chat/ChatProcessing.java @@ -257,14 +257,12 @@ public class ChatProcessing { namesb.append(")"); StringBuilder nicksb = new StringBuilder("(?i)("); boolean addNickFormatter = false; - int index = 0; for (Player p : Bukkit.getOnlinePlayers()) { final String nick = PlayerListener.nicknames.inverse().get(p.getUniqueId()); if (nick != null) { nicksb.append(nick).append("|"); addNickFormatter = true; //Add it even if there's only 1 player online (it was in the if) } - index++; } nicksb.deleteCharAt(nicksb.length() - 1); nicksb.append(")"); diff --git a/src/main/java/buttondevteam/chat/PluginMain.java b/src/main/java/buttondevteam/chat/PluginMain.java index b02f678..de05ca8 100644 --- a/src/main/java/buttondevteam/chat/PluginMain.java +++ b/src/main/java/buttondevteam/chat/PluginMain.java @@ -1,426 +1,427 @@ -package buttondevteam.chat; - -import buttondevteam.chat.commands.YeehawCommand; -import buttondevteam.chat.commands.ucmds.TownColorCommand; -import buttondevteam.chat.listener.PlayerJoinLeaveListener; -import buttondevteam.chat.listener.PlayerListener; -import buttondevteam.chat.listener.TownyListener; -import buttondevteam.lib.TBMCCoreAPI; -import buttondevteam.lib.chat.Channel; -import buttondevteam.lib.chat.Channel.RecipientTestResult; -import buttondevteam.lib.chat.Color; -import buttondevteam.lib.chat.TBMCChatAPI; -import buttondevteam.lib.player.TBMCPlayerBase; -import com.earth2me.essentials.Essentials; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.palmergames.bukkit.towny.Towny; -import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; -import com.palmergames.bukkit.towny.object.Nation; -import com.palmergames.bukkit.towny.object.Resident; -import com.palmergames.bukkit.towny.object.Town; -import com.palmergames.bukkit.towny.object.TownyUniverse; -import lombok.val; -import net.milkbowl.vault.chat.Chat; -import net.milkbowl.vault.economy.Economy; -import net.milkbowl.vault.permission.Permission; -import org.bukkit.Bukkit; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.Player; -import org.bukkit.plugin.RegisteredServiceProvider; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scoreboard.Scoreboard; -import org.dynmap.towny.DTBridge; -import org.dynmap.towny.DynmapTownyPlugin; -import org.htmlcleaner.HtmlCleaner; -import org.htmlcleaner.TagNode; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.net.UnknownHostException; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -public class PluginMain extends JavaPlugin { // Translated to Java: 2015.07.15. - // A user, which flair isn't obtainable: - // https://www.reddit.com/r/thebutton/comments/31c32v/i_pressed_the_button_without_really_thinking/ - public static PluginMain Instance; - public static ConsoleCommandSender Console; - private final static String FlairThreadURL = "https://www.reddit.com/r/Chromagamers/comments/51ys94/flair_thread_for_the_mc_server/"; - - public static Scoreboard SB; - public static TownyUniverse TU; - private static ArrayList Towns; - private static ArrayList Nations; - - public static Channel TownChat; - public static Channel NationChat; - private static Channel RPChannel; - - /** - *

- * This variable is used as a cache for flair state checking when reading the flair thread. - *

- *

- * It's used because normally it has to load all associated player files every time to read the flair state - *

- */ - private Set PlayersWithFlairs = new HashSet<>(); - - // Fired when plugin is first enabled - @Override - public void onEnable() { - Instance = this; - PluginMain.essentials = (Essentials) (Bukkit.getPluginManager().getPlugin("Essentials")); - - TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this); - TBMCCoreAPI.RegisterEventsForExceptions(new PlayerJoinLeaveListener(), this); - TBMCCoreAPI.RegisterEventsForExceptions(new TownyListener(), this); - TBMCChatAPI.AddCommands(this, YeehawCommand.class); - Console = this.getServer().getConsoleSender(); - LoadFiles(); - - SB = getServer().getScoreboardManager().getMainScoreboard(); // Main can be detected with @a[score_...] - TU = ((Towny) Bukkit.getPluginManager().getPlugin("Towny")).getTownyUniverse(); - Towns = new ArrayList<>(TU.getTownsMap().values()); // Creates a snapshot of towns, new towns will be added when needed - Nations = new ArrayList<>(TU.getNationsMap().values()); // Same here but with nations - - TownColors.keySet().removeIf(t -> !TU.getTownsMap().containsKey(t)); // Removes town colors for deleted/renamed towns - NationColor.keySet().removeIf(n -> !TU.getNationsMap().containsKey(n)); // Removes nation colors for deleted/renamed nations - - TBMCChatAPI.RegisterChatChannel( - TownChat = new Channel("§3TC§f", Color.DarkAqua, "tc", s -> checkTownNationChat(s, false))); - TBMCChatAPI.RegisterChatChannel( - NationChat = new Channel("§6NC§f", Color.Gold, "nc", s -> checkTownNationChat(s, true))); - TBMCChatAPI.RegisterChatChannel(RPChannel = new Channel("§7RP§f", Color.Gray, "rp", null)); //Since it's null, it's recognised as global - - Bukkit.getScheduler().runTask(this, () -> { - val dtp = (DynmapTownyPlugin) Bukkit.getPluginManager().getPlugin("Dynmap-Towny"); - if (dtp == null) - return; - for (val entry : TownColors.entrySet()) - setTownColor(dtp, buttondevteam.chat.commands.ucmds.admin.TownColorCommand.getTownNameCased(entry.getKey()), entry.getValue()); - }); - - if (!setupEconomy() || !setupPermissions()) - TBMCCoreAPI.SendException("We're in trouble", new Exception("Failed to set up economy or permissions!")); - - new Thread(this::FlairGetterThreadMethod).start(); - new Thread(new AnnouncerThread()).start(); - } - - /** - * Sets a town's color on Dynmap. - * - * @param dtp A reference for the Dynmap-Towny plugin - * @param town The town's name using the correct casing - * @param colors The town's colors - */ - public static void setTownColor(DynmapTownyPlugin dtp, String town, Color[] colors) { - Function c2i = c -> c.getRed() << 16 | c.getGreen() << 8 | c.getBlue(); - try { - DTBridge.setTownColor(dtp, town, c2i.apply(colors[0]), - c2i.apply(colors.length > 1 ? colors[1] : colors[0])); - } catch (Exception e) { - TBMCCoreAPI.SendException("Failed to set town color for town " + town + "!", e); - } - } - - public Boolean stop = false; - public static Essentials essentials = null; - - // Fired when plugin is disabled - @Override - public void onDisable() { - SaveFiles(); - stop = true; - } - - private void FlairGetterThreadMethod() { - int errorcount = 0; - while (!stop) { - try { - String body = TBMCCoreAPI.DownloadString(FlairThreadURL + ".json?limit=1000"); - JsonArray json = new JsonParser().parse(body).getAsJsonArray().get(1).getAsJsonObject().get("data") - .getAsJsonObject().get("children").getAsJsonArray(); - for (Object obj : json) { - JsonObject item = (JsonObject) obj; - String author = item.get("data").getAsJsonObject().get("author").getAsString(); - String ign = item.get("data").getAsJsonObject().get("body").getAsString(); - int start = ign.indexOf("IGN:") + "IGN:".length(); - if (start == -1 + "IGN:".length()) - continue; - int end = ign.indexOf(' ', start); - if (end == -1 || end == start) - end = ign.indexOf('\n', start); - if (end == -1 || end == start) - ign = ign.substring(start); - else - ign = ign.substring(start, end); - ign = ign.trim(); - if (PlayersWithFlairs.contains(ign)) - continue; - try (ChatPlayer mp = TBMCPlayerBase.getFromName(ign, ChatPlayer.class)) { // Loads player file - if (mp == null) - continue; - /* - * if (!JoinedBefore(mp, 2015, 6, 5)) continue; - */ - if (!mp.UserNames().contains(author)) - mp.UserNames().add(author); - if (mp.FlairState().get().equals(FlairStates.NoComment)) { - mp.FlairState().set(FlairStates.Commented); - ConfirmUserMessage(mp); - } - PlayersWithFlairs.add(ign); // Don't redownload even if flair isn't accepted - } - } - } catch (Exception e) { - errorcount++; - if (errorcount >= 10) { - errorcount = 0; - if (!e.getMessage().contains("Server returned HTTP response code") - && !(e instanceof UnknownHostException)) - TBMCCoreAPI.SendException("Error while getting flairs from Reddit!", e); - } - } - try { - Thread.sleep(10000); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } - - public void DownloadFlair(ChatPlayer mp) throws IOException { - String[] flairdata = TBMCCoreAPI - .DownloadString("http://karmadecay.com/thebutton-data.php?users=" + mp.UserName().get()) - .replace("\"", "").split(":"); - String flair; - if (flairdata.length > 1) - flair = flairdata[1]; - else - flair = ""; - String flairclass; - if (flairdata.length > 2) - flairclass = flairdata[2]; - else - flairclass = "unknown"; - SetFlair(mp, flair, flairclass, mp.UserName().get()); - } - - private void SetFlair(ChatPlayer p, String text, String flairclass, String username) { - p.UserName().set(username); - p.FlairState().set(FlairStates.Recognised); - switch (flairclass) { - case "cheater": - p.SetFlair(Short.parseShort(text), true); - return; - case "unknown": - try { - if (CheckForJoinDate(p)) { - if (text.equals("-1")) // If true, only non-presser/can't press; if false, any flair (but we can still detect can't press) - p.SetFlair(ChatPlayer.FlairTimeNonPresser); - else - p.SetFlair(ChatPlayer.FlairTimeNone); // Flair unknown - } else { - p.SetFlair(ChatPlayer.FlairTimeCantPress); - } - } catch (Exception e) { - p.FlairState().set(FlairStates.Commented); // Flair unknown - p.SetFlair(ChatPlayer.FlairTimeNone); - TBMCCoreAPI.SendException("Error while checking join date for player " + p.PlayerName() + "!", e); - } - return; - default: - break; - } - p.SetFlair(Short.parseShort(text)); - } - - private static boolean CheckForJoinDate(ChatPlayer mp) throws Exception { - return JoinedBefore(mp, 2015, 4, 1); - } - - private static boolean JoinedBefore(ChatPlayer mp, int year, int month, int day) throws Exception { - URL url = new URL("https://www.reddit.com/u/" + mp.UserName()); - URLConnection con = url.openConnection(); - con.setRequestProperty("User-Agent", "TheButtonAutoFlair"); - InputStream in = con.getInputStream(); - HtmlCleaner cleaner = new HtmlCleaner(); - TagNode node = cleaner.clean(in); - - node = node.getElementsByAttValue("class", "age", true, true)[0]; - node = node.getElementsByName("time", false)[0]; - String joindate = node.getAttributeByName("datetime"); - SimpleDateFormat parserSDF = new SimpleDateFormat("yyyy-MM-dd"); - joindate = joindate.split("T")[0]; - Date date = parserSDF.parse(joindate); - return date.before(new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("UTC")).setDate(year, month, day) - .build().getTime()); - } - - public static void ConfirmUserMessage(ChatPlayer mp) { - Player p = Bukkit.getPlayer(mp.getUUID()); - if (mp.FlairState().get().equals(FlairStates.Commented) && p != null) - if (mp.UserNames().size() > 1) - p.sendMessage( - "§9Multiple Reddit users commented your name. You can select with /u accept.§r §6Type /u accept or /u ignore§r"); - else - p.sendMessage("§9A Reddit user commented your name. Is that you?§r §6Type /u accept or /u ignore§r"); - } - - public static ArrayList AnnounceMessages = new ArrayList<>(); - public static int AnnounceTime = 15 * 60 * 1000; - /** - * Names lowercased - */ - public static Map TownColors = new HashMap<>(); - /** - * Names lowercased - nation color gets added to town colors when needed - */ - public static Map NationColor = new HashMap<>(); - - @SuppressWarnings("unchecked") - private static void LoadFiles() { - PluginMain.Instance.getLogger().info("Loading files..."); - try { - File file = new File("TBMC/chatsettings.yml"); - if (file.exists()) { - YamlConfiguration yc = new YamlConfiguration(); - yc.load(file); - PlayerListener.NotificationSound = yc.getString("notificationsound"); - PlayerListener.NotificationPitch = yc.getDouble("notificationpitch"); - AnnounceTime = yc.getInt("announcetime", 15 * 60 * 1000); - AnnounceMessages.addAll(yc.getStringList("announcements")); - PlayerListener.AlphaDeaths = yc.getInt("alphadeaths"); - val cs = yc.getConfigurationSection("towncolors"); - if (cs != null) - TownColors.putAll(cs.getValues(true).entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, v -> ((List) v.getValue()).stream() - .map(Color::valueOf).toArray(Color[]::new)))); - TownColorCommand.ColorCount = (byte) yc.getInt("towncolorcount", 1); - val ncs = yc.getConfigurationSection("nationcolors"); - if (ncs != null) - NationColor.putAll(ncs.getValues(true).entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, v -> Color.valueOf((String) v.getValue())))); - PluginMain.Instance.getLogger().info("Loaded files!"); - } else - PluginMain.Instance.getLogger().info("No files to load, first run probably."); - } catch (Exception e) { - TBMCCoreAPI.SendException("Error while loading chat files!", e); - } - } - - public static void SaveFiles() { - PluginMain.Instance.getLogger().info("Saving files..."); - try { - File file = new File("TBMC/chatsettings.yml"); - YamlConfiguration yc = new YamlConfiguration(); - yc.set("notificationsound", PlayerListener.NotificationSound); - yc.set("notificationpitch", PlayerListener.NotificationPitch); - yc.set("announcetime", AnnounceTime); - yc.set("announcements", AnnounceMessages); - yc.set("alphadeaths", PlayerListener.AlphaDeaths); - yc.createSection("towncolors", TownColors.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, - v -> Arrays.stream(v.getValue()).map(Enum::toString).toArray(String[]::new)))); - yc.set("towncolorcount", TownColorCommand.ColorCount); - yc.createSection("nationcolors", NationColor.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, - v -> v.getValue().toString()))); - yc.save(file); - PluginMain.Instance.getLogger().info("Saved files!"); - } catch (Exception e) { - TBMCCoreAPI.SendException("Error while loading chat files!", e); - } - } - - public static Permission permission = null; - public static Economy economy = null; - public static Chat chat = null; - - private boolean setupPermissions() { - RegisteredServiceProvider permissionProvider = getServer().getServicesManager() - .getRegistration(net.milkbowl.vault.permission.Permission.class); - if (permissionProvider != null) { - permission = permissionProvider.getProvider(); - } - return (permission != null); - } - - private boolean setupChat() { - RegisteredServiceProvider chatProvider = getServer().getServicesManager() - .getRegistration(net.milkbowl.vault.chat.Chat.class); - if (chatProvider != null) { - chat = chatProvider.getProvider(); - } - - return (chat != null); - } - - private boolean setupEconomy() { - RegisteredServiceProvider economyProvider = getServer().getServicesManager() - .getRegistration(net.milkbowl.vault.economy.Economy.class); - if (economyProvider != null) { - economy = economyProvider.getProvider(); - } - - return (economy != null); - } - - /** - * Return the error message for the message sender if they can't send it and the score - */ - private static RecipientTestResult checkTownNationChat(CommandSender sender, boolean nationchat) { - if (!(sender instanceof Player)) - return new RecipientTestResult("§cYou are not a player!"); - Resident resident = PluginMain.TU.getResidentMap().get(sender.getName().toLowerCase()); - RecipientTestResult result = checkTownNationChatInternal(sender, nationchat, resident); - if (result.errormessage != null && resident != null && resident.getModes().contains("spy")) // Only use spy if they wouldn't see it - result = new RecipientTestResult(1000, "allspies"); // There won't be more than a thousand towns/nations probably - return result; - } - - private static RecipientTestResult checkTownNationChatInternal(CommandSender sender, boolean nationchat, - Resident resident) { - try { - /* - * p.sendMessage(String.format("[SPY-%s] - %s: %s", channel.DisplayName, ((Player) sender).getDisplayName(), message)); - */ - Town town = null; - if (resident != null && resident.hasTown()) - town = resident.getTown(); - if (town == null) - return new RecipientTestResult("You aren't in a town."); - Nation nation = null; - int index; - if (nationchat) { - if (town.hasNation()) - nation = town.getNation(); - if (nation == null) - return new RecipientTestResult("Your town isn't in a nation."); - index = PluginMain.Nations.indexOf(nation); - if (index < 0) { - PluginMain.Nations.add(nation); - index = PluginMain.Nations.size() - 1; - } - } else { - index = PluginMain.Towns.indexOf(town); - if (index < 0) { - PluginMain.Towns.add(town); - index = PluginMain.Towns.size() - 1; - } - } - return new RecipientTestResult(index, nationchat ? nation.getName() : town.getName()); - } catch (NotRegisteredException e) { - return new RecipientTestResult("You (probably) aren't knwon by Towny! (Not in a town)"); - } - } -} +package buttondevteam.chat; + +import buttondevteam.chat.commands.YeehawCommand; +import buttondevteam.chat.commands.ucmds.TownColorCommand; +import buttondevteam.chat.listener.PlayerJoinLeaveListener; +import buttondevteam.chat.listener.PlayerListener; +import buttondevteam.chat.listener.TownyListener; +import buttondevteam.lib.TBMCCoreAPI; +import buttondevteam.lib.chat.Channel; +import buttondevteam.lib.chat.Channel.RecipientTestResult; +import buttondevteam.lib.chat.Color; +import buttondevteam.lib.chat.TBMCChatAPI; +import buttondevteam.lib.player.TBMCPlayerBase; +import com.earth2me.essentials.Essentials; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.palmergames.bukkit.towny.Towny; +import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; +import com.palmergames.bukkit.towny.object.Nation; +import com.palmergames.bukkit.towny.object.Resident; +import com.palmergames.bukkit.towny.object.Town; +import com.palmergames.bukkit.towny.object.TownyUniverse; +import lombok.val; +import net.milkbowl.vault.chat.Chat; +import net.milkbowl.vault.economy.Economy; +import net.milkbowl.vault.permission.Permission; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scoreboard.Scoreboard; +import org.dynmap.towny.DTBridge; +import org.dynmap.towny.DynmapTownyPlugin; +import org.htmlcleaner.HtmlCleaner; +import org.htmlcleaner.TagNode; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.net.UnknownHostException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class PluginMain extends JavaPlugin { // Translated to Java: 2015.07.15. + // A user, which flair isn't obtainable: + // https://www.reddit.com/r/thebutton/comments/31c32v/i_pressed_the_button_without_really_thinking/ + public static PluginMain Instance; + public static ConsoleCommandSender Console; + private final static String FlairThreadURL = "https://www.reddit.com/r/Chromagamers/comments/51ys94/flair_thread_for_the_mc_server/"; + + public static Scoreboard SB; + public static TownyUniverse TU; + private static ArrayList Towns; + private static ArrayList Nations; + + public static Channel TownChat; + public static Channel NationChat; + private static Channel RPChannel; + + /** + *

+ * This variable is used as a cache for flair state checking when reading the flair thread. + *

+ *

+ * It's used because normally it has to load all associated player files every time to read the flair state + *

+ */ + private Set PlayersWithFlairs = new HashSet<>(); + + // Fired when plugin is first enabled + @Override + public void onEnable() { + Instance = this; + PluginMain.essentials = (Essentials) (Bukkit.getPluginManager().getPlugin("Essentials")); + + TBMCCoreAPI.RegisterEventsForExceptions(new PlayerListener(), this); + TBMCCoreAPI.RegisterEventsForExceptions(new PlayerJoinLeaveListener(), this); + TBMCCoreAPI.RegisterEventsForExceptions(new TownyListener(), this); + TBMCChatAPI.AddCommands(this, YeehawCommand.class); + Console = this.getServer().getConsoleSender(); + LoadFiles(); + + SB = getServer().getScoreboardManager().getMainScoreboard(); // Main can be detected with @a[score_...] + TU = ((Towny) Bukkit.getPluginManager().getPlugin("Towny")).getTownyUniverse(); + Towns = TU.getTownsMap().values().stream().map(Town::getName).collect(Collectors.toCollection(ArrayList::new)); // Creates a snapshot of towns, new towns will be added when needed + Nations = TU.getNationsMap().values().stream().map(Nation::getName).collect(Collectors.toCollection(ArrayList::new)); // Same here but with nations + + TownColors.keySet().removeIf(t -> !TU.getTownsMap().containsKey(t)); // Removes town colors for deleted/renamed towns + NationColor.keySet().removeIf(n -> !TU.getNationsMap().containsKey(n)); // Removes nation colors for deleted/renamed nations + + TBMCChatAPI.RegisterChatChannel( + TownChat = new Channel("§3TC§f", Color.DarkAqua, "tc", s -> checkTownNationChat(s, false))); + TBMCChatAPI.RegisterChatChannel( + NationChat = new Channel("§6NC§f", Color.Gold, "nc", s -> checkTownNationChat(s, true))); + TBMCChatAPI.RegisterChatChannel(RPChannel = new Channel("§7RP§f", Color.Gray, "rp", null)); //Since it's null, it's recognised as global + + Bukkit.getScheduler().runTask(this, () -> { + val dtp = (DynmapTownyPlugin) Bukkit.getPluginManager().getPlugin("Dynmap-Towny"); + if (dtp == null) + return; + for (val entry : TownColors.entrySet()) + setTownColor(dtp, buttondevteam.chat.commands.ucmds.admin.TownColorCommand.getTownNameCased(entry.getKey()), entry.getValue()); + }); + + if (!setupEconomy() || !setupPermissions()) + TBMCCoreAPI.SendException("We're in trouble", new Exception("Failed to set up economy or permissions!")); + + new Thread(this::FlairGetterThreadMethod).start(); + new Thread(new AnnouncerThread()).start(); + } + + /** + * Sets a town's color on Dynmap. + * + * @param dtp A reference for the Dynmap-Towny plugin + * @param town The town's name using the correct casing + * @param colors The town's colors + */ + public static void setTownColor(DynmapTownyPlugin dtp, String town, Color[] colors) { + Function c2i = c -> c.getRed() << 16 | c.getGreen() << 8 | c.getBlue(); + try { + DTBridge.setTownColor(dtp, town, c2i.apply(colors[0]), + c2i.apply(colors.length > 1 ? colors[1] : colors[0])); + } catch (Exception e) { + TBMCCoreAPI.SendException("Failed to set town color for town " + town + "!", e); + } + } + + public Boolean stop = false; + public static Essentials essentials = null; + + // Fired when plugin is disabled + @Override + public void onDisable() { + SaveFiles(); + stop = true; + } + + private void FlairGetterThreadMethod() { + int errorcount = 0; + while (!stop) { + try { + String body = TBMCCoreAPI.DownloadString(FlairThreadURL + ".json?limit=1000"); + JsonArray json = new JsonParser().parse(body).getAsJsonArray().get(1).getAsJsonObject().get("data") + .getAsJsonObject().get("children").getAsJsonArray(); + for (Object obj : json) { + JsonObject item = (JsonObject) obj; + String author = item.get("data").getAsJsonObject().get("author").getAsString(); + String ign = item.get("data").getAsJsonObject().get("body").getAsString(); + int start = ign.indexOf("IGN:") + "IGN:".length(); + if (start == -1 + "IGN:".length()) + continue; + int end = ign.indexOf(' ', start); + if (end == -1 || end == start) + end = ign.indexOf('\n', start); + if (end == -1 || end == start) + ign = ign.substring(start); + else + ign = ign.substring(start, end); + ign = ign.trim(); + if (PlayersWithFlairs.contains(ign)) + continue; + try (ChatPlayer mp = TBMCPlayerBase.getFromName(ign, ChatPlayer.class)) { // Loads player file + if (mp == null) + continue; + /* + * if (!JoinedBefore(mp, 2015, 6, 5)) continue; + */ + if (!mp.UserNames().contains(author)) + mp.UserNames().add(author); + if (mp.FlairState().get().equals(FlairStates.NoComment)) { + mp.FlairState().set(FlairStates.Commented); + ConfirmUserMessage(mp); + } + PlayersWithFlairs.add(ign); // Don't redownload even if flair isn't accepted + } + } + } catch (Exception e) { + errorcount++; + if (errorcount >= 10) { + errorcount = 0; + if (!e.getMessage().contains("Server returned HTTP response code") + && !(e instanceof UnknownHostException)) + TBMCCoreAPI.SendException("Error while getting flairs from Reddit!", e); + } + } + try { + Thread.sleep(10000); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } + + public void DownloadFlair(ChatPlayer mp) throws IOException { + String[] flairdata = TBMCCoreAPI + .DownloadString("http://karmadecay.com/thebutton-data.php?users=" + mp.UserName().get()) + .replace("\"", "").split(":"); + String flair; + if (flairdata.length > 1) + flair = flairdata[1]; + else + flair = ""; + String flairclass; + if (flairdata.length > 2) + flairclass = flairdata[2]; + else + flairclass = "unknown"; + SetFlair(mp, flair, flairclass, mp.UserName().get()); + } + + private void SetFlair(ChatPlayer p, String text, String flairclass, String username) { + p.UserName().set(username); + p.FlairState().set(FlairStates.Recognised); + switch (flairclass) { + case "cheater": + p.SetFlair(Short.parseShort(text), true); + return; + case "unknown": + try { + if (CheckForJoinDate(p)) { + if (text.equals("-1")) // If true, only non-presser/can't press; if false, any flair (but we can still detect can't press) + p.SetFlair(ChatPlayer.FlairTimeNonPresser); + else + p.SetFlair(ChatPlayer.FlairTimeNone); // Flair unknown + } else { + p.SetFlair(ChatPlayer.FlairTimeCantPress); + } + } catch (Exception e) { + p.FlairState().set(FlairStates.Commented); // Flair unknown + p.SetFlair(ChatPlayer.FlairTimeNone); + TBMCCoreAPI.SendException("Error while checking join date for player " + p.PlayerName() + "!", e); + } + return; + default: + break; + } + p.SetFlair(Short.parseShort(text)); + } + + private static boolean CheckForJoinDate(ChatPlayer mp) throws Exception { + return JoinedBefore(mp, 2015, 4, 1); + } + + private static boolean JoinedBefore(ChatPlayer mp, int year, int month, int day) throws Exception { + URL url = new URL("https://www.reddit.com/u/" + mp.UserName()); + URLConnection con = url.openConnection(); + con.setRequestProperty("User-Agent", "TheButtonAutoFlair"); + InputStream in = con.getInputStream(); + HtmlCleaner cleaner = new HtmlCleaner(); + TagNode node = cleaner.clean(in); + + node = node.getElementsByAttValue("class", "age", true, true)[0]; + node = node.getElementsByName("time", false)[0]; + String joindate = node.getAttributeByName("datetime"); + SimpleDateFormat parserSDF = new SimpleDateFormat("yyyy-MM-dd"); + joindate = joindate.split("T")[0]; + Date date = parserSDF.parse(joindate); + return date.before(new Calendar.Builder().setTimeZone(TimeZone.getTimeZone("UTC")).setDate(year, month, day) + .build().getTime()); + } + + public static void ConfirmUserMessage(ChatPlayer mp) { + Player p = Bukkit.getPlayer(mp.getUUID()); + if (mp.FlairState().get().equals(FlairStates.Commented) && p != null) + if (mp.UserNames().size() > 1) + p.sendMessage( + "§9Multiple Reddit users commented your name. You can select with /u accept.§r §6Type /u accept or /u ignore§r"); + else + p.sendMessage("§9A Reddit user commented your name. Is that you?§r §6Type /u accept or /u ignore§r"); + } + + public static ArrayList AnnounceMessages = new ArrayList<>(); + public static int AnnounceTime = 15 * 60 * 1000; + /** + * Names lowercased + */ + public static Map TownColors = new HashMap<>(); + /** + * Names lowercased - nation color gets added to town colors when needed + */ + public static Map NationColor = new HashMap<>(); + + @SuppressWarnings("unchecked") + private static void LoadFiles() { + PluginMain.Instance.getLogger().info("Loading files..."); + try { + File file = new File("TBMC/chatsettings.yml"); + if (file.exists()) { + YamlConfiguration yc = new YamlConfiguration(); + yc.load(file); + PlayerListener.NotificationSound = yc.getString("notificationsound"); + PlayerListener.NotificationPitch = yc.getDouble("notificationpitch"); + AnnounceTime = yc.getInt("announcetime", 15 * 60 * 1000); + AnnounceMessages.addAll(yc.getStringList("announcements")); + PlayerListener.AlphaDeaths = yc.getInt("alphadeaths"); + val cs = yc.getConfigurationSection("towncolors"); + if (cs != null) + TownColors.putAll(cs.getValues(true).entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, v -> ((List) v.getValue()).stream() + .map(Color::valueOf).toArray(Color[]::new)))); + TownColorCommand.ColorCount = (byte) yc.getInt("towncolorcount", 1); + val ncs = yc.getConfigurationSection("nationcolors"); + if (ncs != null) + NationColor.putAll(ncs.getValues(true).entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, v -> Color.valueOf((String) v.getValue())))); + PluginMain.Instance.getLogger().info("Loaded files!"); + } else + PluginMain.Instance.getLogger().info("No files to load, first run probably."); + } catch (Exception e) { + TBMCCoreAPI.SendException("Error while loading chat files!", e); + } + } + + public static void SaveFiles() { + PluginMain.Instance.getLogger().info("Saving files..."); + try { + File file = new File("TBMC/chatsettings.yml"); + YamlConfiguration yc = new YamlConfiguration(); + yc.set("notificationsound", PlayerListener.NotificationSound); + yc.set("notificationpitch", PlayerListener.NotificationPitch); + yc.set("announcetime", AnnounceTime); + yc.set("announcements", AnnounceMessages); + yc.set("alphadeaths", PlayerListener.AlphaDeaths); + yc.createSection("towncolors", TownColors.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + v -> Arrays.stream(v.getValue()).map(Enum::toString).toArray(String[]::new)))); + yc.set("towncolorcount", TownColorCommand.ColorCount); + yc.createSection("nationcolors", NationColor.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, + v -> v.getValue().toString()))); + yc.save(file); + PluginMain.Instance.getLogger().info("Saved files!"); + } catch (Exception e) { + TBMCCoreAPI.SendException("Error while loading chat files!", e); + } + } + + public static Permission permission = null; + public static Economy economy = null; + public static Chat chat = null; + + private boolean setupPermissions() { + RegisteredServiceProvider permissionProvider = getServer().getServicesManager() + .getRegistration(net.milkbowl.vault.permission.Permission.class); + if (permissionProvider != null) { + permission = permissionProvider.getProvider(); + } + return (permission != null); + } + + private boolean setupChat() { + RegisteredServiceProvider chatProvider = getServer().getServicesManager() + .getRegistration(net.milkbowl.vault.chat.Chat.class); + if (chatProvider != null) { + chat = chatProvider.getProvider(); + } + + return (chat != null); + } + + private boolean setupEconomy() { + RegisteredServiceProvider economyProvider = getServer().getServicesManager() + .getRegistration(net.milkbowl.vault.economy.Economy.class); + if (economyProvider != null) { + economy = economyProvider.getProvider(); + } + + return (economy != null); + } + + /** + * Return the error message for the message sender if they can't send it and the score + */ + private static RecipientTestResult checkTownNationChat(CommandSender sender, boolean nationchat) { + if (!(sender instanceof Player)) + return new RecipientTestResult("§cYou are not a player!"); + Resident resident = PluginMain.TU.getResidentMap().get(sender.getName().toLowerCase()); + RecipientTestResult result = checkTownNationChatInternal(sender, nationchat, resident); + if (result.errormessage != null && resident != null && resident.getModes().contains("spy")) // Only use spy if they wouldn't see it + result = new RecipientTestResult(1000, "allspies"); // There won't be more than a thousand towns/nations probably + return result; + } + + private static RecipientTestResult checkTownNationChatInternal(CommandSender sender, boolean nationchat, + Resident resident) { + try { + /* + * p.sendMessage(String.format("[SPY-%s] - %s: %s", channel.DisplayName, ((Player) sender).getDisplayName(), message)); + */ + Town town = null; + if (resident != null && resident.hasTown()) + town = resident.getTown(); + if (town == null) + return new RecipientTestResult("You aren't in a town."); + Nation nation = null; + int index; + if (nationchat) { + if (town.hasNation()) + nation = town.getNation(); + if (nation == null) + return new RecipientTestResult("Your town isn't in a nation."); + index = getTownNationIndex(nation.getName(), true); + } else + index = getTownNationIndex(town.getName(), false); + return new RecipientTestResult(index, nationchat ? nation.getName() : town.getName()); + } catch (NotRegisteredException e) { + return new RecipientTestResult("You (probably) aren't knwon by Towny! (Not in a town)"); + } + } + + public static int getTownNationIndex(String name, boolean nation) { + val list = nation ? Nations : Towns; + int index = list.indexOf(name); + if (index < 0) { + list.add(name); + index = list.size() - 1; + } + return index; + } +} diff --git a/src/main/java/buttondevteam/chat/components/TownyAnnouncer.java b/src/main/java/buttondevteam/chat/components/TownyAnnouncer.java new file mode 100644 index 0000000..3ad6c2a --- /dev/null +++ b/src/main/java/buttondevteam/chat/components/TownyAnnouncer.java @@ -0,0 +1,56 @@ +package buttondevteam.chat.components; + +import buttondevteam.chat.PluginMain; +import buttondevteam.lib.chat.Channel; +import buttondevteam.lib.chat.TBMCChatAPI; +import com.palmergames.bukkit.towny.TownyLogger; +import lombok.val; + +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.regex.Pattern; + +public class TownyAnnouncer { + private static final Pattern LOG_TYPE_PATTERN = Pattern.compile("\\[(\\w+ (?:Msg|Message))] (\\w+):"); + private static final Handler HANDLER = new Handler() { + @Override + public void publish(LogRecord logRecord) { + if (logRecord.getMessage() == null) return; + val m = LOG_TYPE_PATTERN.matcher(logRecord.getMessage()); + if (!m.find()) return; + String groupID = m.group(2); //The group ID is correctly cased + switch (String.valueOf(m.group(1))) { //valueOf: Handles null + case "Town": + TBMCChatAPI.SendSystemMessage(PluginMain.TownChat, + new Channel.RecipientTestResult(PluginMain.getTownNationIndex(groupID, false), groupID), + logRecord.getMessage()); //TODO: This will also send it in Minecraft + break; + case "Nation": + TBMCChatAPI.SendSystemMessage(PluginMain.NationChat, + new Channel.RecipientTestResult(PluginMain.getTownNationIndex(groupID, true), groupID), + logRecord.getMessage()); //TODO: This will also send it in Minecraft + break; + case "Global": //TODO + break; + } + } + + @Override + public void flush() { + + } + + @Override + public void close() throws SecurityException { + + } + }; + + public static void setup() { + TownyLogger.log.addHandler(HANDLER); + } + + public static void setdown() { + TownyLogger.log.removeHandler(HANDLER); + } +} diff --git a/src/main/java/buttondevteam/chat/components/TownyComponent.java b/src/main/java/buttondevteam/chat/components/TownyComponent.java new file mode 100644 index 0000000..3f1f83e --- /dev/null +++ b/src/main/java/buttondevteam/chat/components/TownyComponent.java @@ -0,0 +1,15 @@ +package buttondevteam.chat.components; + +import buttondevteam.lib.architecture.Component; + +public class TownyComponent extends Component { //TODO: Register component + @Override + protected void enable() { + TownyAnnouncer.setup(); + } + + @Override + protected void disable() { + TownyAnnouncer.setdown(); + } +}