From a0491ff8144d112be8d3d58358417ccec8593aea Mon Sep 17 00:00:00 2001 From: tduva Date: Mon, 11 Apr 2016 10:48:36 +0200 Subject: [PATCH] Whispers to Main Chat, some other stuff - Hide userlist for Whisper Tabs - Change Whisper display mode to per user by default - Change Whispers to Main Chat - Fix $[whisper] Tab opening inadvertently - Replace WhisperConnection by WhisperManager, using the main connection - Reorganize Settings definition - Increase max reconnection attempts to 40 - Clean up, remove some unused TwitchConnection code - Update help --- src/chatty/SettingsManager.java | 344 ++++++++++------- src/chatty/TwitchClient.java | 38 +- src/chatty/TwitchConnection.java | 165 ++------ src/chatty/WhisperConnection.java | 357 ------------------ src/chatty/WhisperManager.java | 188 +++++++++ src/chatty/gui/Channels.java | 2 +- src/chatty/gui/MainGui.java | 19 +- .../gui/components/help/help-whisper.html | 99 +---- .../components/settings/AdvancedSettings.java | 196 +--------- .../components/settings/SettingsDialog.java | 1 - src/chatty/util/settings/Settings.java | 2 +- 11 files changed, 459 insertions(+), 952 deletions(-) delete mode 100644 src/chatty/WhisperConnection.java create mode 100644 src/chatty/WhisperManager.java diff --git a/src/chatty/SettingsManager.java b/src/chatty/SettingsManager.java index eb7fbd1a6..4de007251 100644 --- a/src/chatty/SettingsManager.java +++ b/src/chatty/SettingsManager.java @@ -70,12 +70,29 @@ void defineSettings() { settings.addFile(historyFile); settings.addFile(statusPresetsFile); - // Global - settings.addBoolean("ontop", false); + //======== + // General + //======== + settings.addBoolean("dontSaveSettings",false); settings.addBoolean("debugCommands", false, false); - settings.addString("laf","default"); + settings.addBoolean("debugLogIrc", false); + settings.addBoolean("debugLogIrcFile", false); + settings.addBoolean("autoRequestMods", false); + // Backup + settings.addLong("backupDelay", 1); + settings.addLong("backupCount", 5); + + // Version/News + settings.addLong("versionLastChecked", 0); + settings.addString("updateAvailable", ""); + settings.addBoolean("checkNewVersion", true); + settings.addBoolean("newsAutoRequest", true); + settings.addLong("newsLastRead", 0); + settings.addString("currentVersion", ""); + + // Hotkeys addDefaultHotkeyAppWide("0.7.3", "dialog.streams", "ctrl L"); addDefaultHotkey("0.7.3", "dialog.toggleEmotes", "ctrl E"); addDefaultHotkey("0.7.3", "dialog.search", "ctrl F"); @@ -92,29 +109,25 @@ void defineSettings() { settings.addList("hotkeys", getDefaultHotkeySettingValue(), Setting.LIST); settings.addBoolean("globalHotkeysEnabled", true); - // Connecting/Login data + + //=========== + // Connecting + //=========== + settings.addString("serverDefault", "irc.chat.twitch.tv"); settings.addString("portDefault", "6697,6667,443,80"); + // Seperate settings for commandline/temp so others can be saved settings.addString("server", "", false); settings.addString("port", "", false); - settings.addList("securedPorts", new LinkedHashSet<>(Arrays.asList((long)6697, (long)443)), Setting.LONG); - settings.addList("securedPortsWhisper", new LinkedHashSet<>(Arrays.asList()), Setting.LONG); - settings.addBoolean("userlistConnection", true); - settings.addList("userlistConnectionBlacklist", new ArrayList(), Setting.STRING); + settings.addList("securedPorts", new LinkedHashSet<>(Arrays.asList((long)6697, (long)443)), Setting.LONG); settings.addBoolean("membershipEnabled", true); - settings.addBoolean("botBadgeEnabled", true); - settings.addBoolean("botNamesBTTV", true); - settings.addBoolean("botNamesFFZ", true); - settings.addList("botNames", new ArrayList<>(Arrays.asList("nightbot", "moobot", "kabukibot", "slowton2", "xanbot")), Setting.STRING); - - settings.addBoolean("debugLogIrcFile", false); - - settings.addBoolean("tc3", false); - + // Auto-join channels settings.addString("channel", ""); + + // Login Data settings.addString("username", ""); settings.setFile("username", loginFile); settings.addString("password", "", false); @@ -126,7 +139,8 @@ void defineSettings() { settings.setFile("token", loginFile); // Don't save setting, login with password isn't possible anymore settings.addBoolean("usePassword", false, false); - // Scopes + + // Token settings.addBoolean("token_editor", false); settings.setFile("token_editor", loginFile); settings.addBoolean("token_commercials", false); @@ -137,31 +151,17 @@ void defineSettings() { settings.setFile("token_subs", loginFile); settings.addBoolean("token_chat", false); settings.setFile("token_chat", loginFile); + + //================= + // Appearance / GUI + //================= - // Chat messages - // Not really used anymore, kept for compatability - settings.addBoolean("ignoreJoinsParts",false); - settings.addBoolean("showJoinsParts", false); - settings.addBoolean("showModMessages", false); - settings.addBoolean("debugLogIrc", false); - settings.addBoolean("showBanMessages", false); - settings.addBoolean("combineBanMessages", true); - settings.addBoolean("deleteMessages", false); - settings.addString("deletedMessagesMode", "keepShortened"); - settings.addLong("deletedMessagesMaxLength", 50); - settings.addBoolean("clearChatOnChannelCleared", false); - settings.addLong("bufferSize", 500); - settings.addBoolean("twitchnotifyAsInfo", true); - settings.addBoolean("printStreamStatus", true); - settings.addLong("filterCombiningCharacters", Helper.FILTER_COMBINING_CHARACTERS_LENIENT); + settings.addBoolean("ontop", false); + settings.addString("laf","default"); - // Chat appearance - settings.addBoolean("emoticonsEnabled",true); - settings.addLong("emoteMaxHeight", 0); - settings.addLong("emoteScale", 100); - settings.addLong("emoteScaleDialog", 100); - settings.addBoolean("closeEmoteDialogOnDoubleClick", false); - settings.addBoolean("usericonsEnabled",true); + settings.addLong("dialogFontSize", -1); + + // Chat Appearance settings.addString("font","Consolas"); settings.addLong("fontSize",14); settings.addString("inputFont", "Dialog 14"); @@ -172,24 +172,32 @@ void defineSettings() { settings.addBoolean("ircv3CapitalizedNames", true); settings.addBoolean("correctlyCapitalizedNames", false); settings.addMap("customNames", new HashMap<>(), Setting.STRING); + settings.addBoolean("actionColored", false); + + // Badges/Emotes + settings.addBoolean("emoticonsEnabled",true); + settings.addLong("emoteMaxHeight", 0); + settings.addLong("emoteScale", 100); + settings.addLong("emoteScaleDialog", 100); + settings.addBoolean("closeEmoteDialogOnDoubleClick", false); settings.addBoolean("ffz", true); settings.addBoolean("ffzModIcon", true); settings.addBoolean("bttvEmotes", true); settings.addBoolean("showAnimatedEmotes", false); settings.addList("ignoredEmotes", new ArrayList(), Setting.STRING); settings.addList("favoriteEmotes", new ArrayList(), Setting.LIST); - settings.addString("tabOrder", "normal"); - settings.addBoolean("tabsMwheelScrolling", false); - settings.addBoolean("tabsMwheelScrollingAnywhere", false); - settings.addBoolean("actionColored", false); - settings.addBoolean("customUsercolors", false); - settings.addList("usercolors", new LinkedList(), Setting.STRING); + settings.addBoolean("usericonsEnabled",true); settings.addList("customUsericons", new ArrayList(), Setting.LIST); settings.addBoolean("customUsericonsEnabled", false); - settings.addLong("dialogFontSize", -1); + settings.addBoolean("botBadgeEnabled", true); + settings.addBoolean("botNamesBTTV", true); + settings.addBoolean("botNamesFFZ", true); + settings.addList("botNames", new LinkedHashSet<>(Arrays.asList( + "nightbot", "moobot", "kabukibot", "slowton2", "xanbot")), + Setting.STRING); settings.addBoolean("imageCache", true); @@ -205,19 +213,53 @@ void defineSettings() { settings.addString("searchResultColor2", "#FFFF80"); settings.addBoolean("colorCorrection", true); + // Usercolors + settings.addBoolean("customUsercolors", false); + settings.addList("usercolors", new LinkedList(), Setting.STRING); + + //==================== + // Other Customization + //==================== - // History and Favorites + // Addressbook + settings.addString("abCommandsChannel", ""); + settings.addString("abCommands", "add,set,remove"); + settings.addString("abUniqueCats", ""); + settings.addBoolean("abAutoImport", false); + settings.addString("abSubMonthsChan", ""); + settings.addList("abSubMonths", new TreeSet(), Setting.LONG); + settings.addString("subNotificationPattern", "([^\\s]+) (?:just )?subscribed(?: for (\\d+) months in a row)?!"); + + // Custom Commands + settings.addList("commands", new ArrayList(), Setting.STRING); + // Default entries, will only be set if setting is not loaded from file + settings.setAdd("commands", "/slap /me slaps $$1- around a bit with a large trout"); + settings.setAdd("commands", "/permit !permit $$1"); + + // Menu Entries + settings.addString("timeoutButtons","/Ban[B], /Unban[U], 5s[1], 2m[2], 10m[3], 30m[4]"); + settings.addString("userContextMenu", ""); + settings.addString("channelContextMenu", ""); + + // History / Favorites settings.addMap("channelHistory",new TreeMap(), Setting.LONG); settings.setFile("channelHistory", historyFile); settings.addList("channelFavorites", new ArrayList(), Setting.STRING); settings.setFile("channelFavorites", historyFile); - settings.addList("gamesFavorites",new ArrayList(), Setting.STRING); - settings.setFile("gamesFavorites", historyFile); settings.addLong("channelHistoryKeepDays", 30); settings.addBoolean("saveChannelHistory", true); settings.addBoolean("historyClear", true); settings.addLong("favoritesSorting", 20); + //======================= + // Channel Admin Features + //======================= + + // Game Presets + settings.addList("gamesFavorites",new ArrayList(), Setting.STRING); + settings.setFile("gamesFavorites", historyFile); + + // Stream Status Presets settings.addList("statusPresets", new ArrayList(), Setting.LIST); settings.setFile("statusPresets", statusPresetsFile); @@ -230,28 +272,17 @@ void defineSettings() { settings.addString("commercialHotkey",""); settings.addBoolean("adDelay", false); settings.addLong("adDelayLength", 300); - - // Other - settings.addBoolean("channelsWarning", true); - settings.addBoolean("autoScroll", true); - settings.addLong("autoScrollTimeout", 30); - settings.addBoolean("pauseChatOnMouseMove", false); - settings.addBoolean("pauseChatOnMouseMoveCtrlRequired", false); - settings.addString("commandOnCtrlClick", ""); - settings.addLong("versionLastChecked", 0); - settings.addString("updateAvailable", ""); - settings.addBoolean("checkNewVersion", true); - settings.addString("liveStreamsSorting", "recent"); - settings.addLong("historyRange", 0); - settings.addString("spamProtection", "18/30"); - settings.addString("currentVersion", ""); + //======= + // Window + //======= + // Open URLs settings.addBoolean("urlPrompt", true); settings.addBoolean("urlCommandEnabled", false); settings.addString("urlCommand", ""); - // Window + // Main Window settings.addBoolean("attachedWindows", false); settings.addBoolean("maximized", false); settings.addBoolean("nod3d", true); @@ -259,44 +290,43 @@ void defineSettings() { settings.addBoolean("bufferStrategy1", false); settings.addBoolean("mainResizable", true); + // Tray settings.addBoolean("closeToTray", false); settings.addBoolean("minimizeToTray", false); + // Window State settings.addMap("windows", new HashMap<>(), Setting.STRING); settings.addLong("restoreMode", WindowStateManager.RESTORE_ON_START); + // Popouts settings.addBoolean("popoutSaveAttributes", true); settings.addBoolean("popoutCloseLastChannel", true); settings.addList("popoutAttributes", new ArrayList(), Setting.STRING); + // Titlebar settings.addBoolean("simpleTitle", false); settings.addBoolean("titleShowUptime", true); settings.addBoolean("titleLongerUptime", true); settings.addBoolean("titleShowViewerCount", true); settings.addBoolean("titleShowChannelState", true); + + // Tabs + settings.addString("tabOrder", "normal"); + settings.addBoolean("tabsMwheelScrolling", false); + settings.addBoolean("tabsMwheelScrollingAnywhere", false); + + // Chat Window settings.addBoolean("chatScrollbarAlways", false); settings.addLong("userlistWidth", 120); settings.addLong("userlistMinWidth", 0); + settings.addLong("bufferSize", 500); - // Highlight - settings.addList("highlight",new ArrayList(), Setting.STRING); - settings.addBoolean("highlightEnabled", true); - settings.addBoolean("highlightUsername", true); - settings.addBoolean("highlightOwnText", false); - settings.addBoolean("highlightNextMessages", false); - settings.addBoolean("highlightIgnored", false); - settings.addList("noHighlightUsers", new ArrayList(), Setting.STRING); - - // Ignore - settings.addList("ignore", new ArrayList(), Setting.STRING); - settings.addBoolean("ignoreEnabled", false); - settings.addBoolean("ignoreOwnText", false); - settings.addLong("ignoreMode", 1); - settings.addBoolean("ignoreShowNotDialog", false); - settings.addList("ignoredUsers", new ArrayList(), Setting.STRING); - settings.addList("ignoredUsersWhisper", new ArrayList(), Setting.STRING); - + settings.addString("liveStreamsSorting", "recent"); + settings.addLong("historyRange", 0); + + //======= // Sounds + //======= settings.addBoolean("sounds", false); settings.addString("highlightSound", "off"); settings.addString("highlightSoundFile", "ding.wav"); @@ -320,7 +350,9 @@ void defineSettings() { settings.addLong("followerSoundVolume",100); settings.addLong("followerSoundDelay", 10); + //============== // Notifications + //============== settings.addString("highlightNotification", "either"); settings.addString("statusNotification", "either"); settings.addBoolean("ignoreOfflineNotifications", false); @@ -340,11 +372,66 @@ void defineSettings() { settings.addList("notificationProperties", new ArrayList<>(), Setting.LIST); - settings.addLong("v0.5", 0); settings.addBoolean("tips", true); settings.addLong("lastTip", 0); - // Logging + + //===================== + // Basic Chat Behaviour + //===================== + + settings.addString("spamProtection", "18/30"); + + settings.addBoolean("autoScroll", true); + settings.addLong("autoScrollTimeout", 30); + settings.addBoolean("pauseChatOnMouseMove", false); + settings.addBoolean("pauseChatOnMouseMoveCtrlRequired", false); + settings.addString("commandOnCtrlClick", ""); + + // Not really used anymore, kept for compatability + settings.addBoolean("ignoreJoinsParts",false); + + // Message Types + settings.addBoolean("showJoinsParts", false); + settings.addBoolean("showModMessages", false); + settings.addBoolean("twitchnotifyAsInfo", true); + settings.addBoolean("printStreamStatus", true); + + // Timeouts/Bans + settings.addBoolean("showBanMessages", false); + settings.addBoolean("combineBanMessages", true); + settings.addBoolean("deleteMessages", false); + settings.addString("deletedMessagesMode", "keepShortened"); + settings.addLong("deletedMessagesMaxLength", 50); + settings.addBoolean("clearChatOnChannelCleared", false); + + // Message filtering + settings.addLong("filterCombiningCharacters", Helper.FILTER_COMBINING_CHARACTERS_LENIENT); + + + //============== + // Chat Features + //============== + + // Highlight + settings.addList("highlight",new ArrayList(), Setting.STRING); + settings.addBoolean("highlightEnabled", true); + settings.addBoolean("highlightUsername", true); + settings.addBoolean("highlightOwnText", false); + settings.addBoolean("highlightNextMessages", false); + settings.addBoolean("highlightIgnored", false); + settings.addList("noHighlightUsers", new ArrayList(), Setting.STRING); + + // Ignore + settings.addList("ignore", new ArrayList(), Setting.STRING); + settings.addBoolean("ignoreEnabled", false); + settings.addBoolean("ignoreOwnText", false); + settings.addLong("ignoreMode", 1); + settings.addBoolean("ignoreShowNotDialog", false); + settings.addList("ignoredUsers", new ArrayList(), Setting.STRING); + settings.addList("ignoredUsersWhisper", new ArrayList(), Setting.STRING); + + // Chat Logging settings.addString("logMode", "always"); settings.addBoolean("logMod", true); settings.addBoolean("logJoinPart", false); @@ -357,74 +444,57 @@ void defineSettings() { settings.addList("logBlacklist",new ArrayList(), Setting.STRING); settings.addString("logPath", ""); settings.addString("logTimestamp", "[HH:mm:ss]"); - - // Addressbook - settings.addString("abCommandsChannel", ""); - settings.addString("abCommands", "add,set,remove"); - settings.addString("abUniqueCats", ""); - settings.addBoolean("abAutoImport", false); - settings.addString("abSubMonthsChan", ""); - settings.addList("abSubMonths", new TreeSet(), Setting.LONG); - settings.addString("subNotificationPattern", "([^\\s]+) (?:just )?subscribed(?: for (\\d+) months in a row)?!"); - - // Backup - settings.addLong("backupDelay", 1); - settings.addLong("backupCount", 5); - - // Livestreamer - settings.addBoolean("livestreamer", false); - settings.addString("livestreamerQualities", "Best, Worst, Select"); - settings.addString("livestreamerCommand", "livestreamer"); - settings.addBoolean("livestreamerUseAuth", false); - settings.addBoolean("livestreamerShowDialog", true); - - // Custom Commands and Menu Customization - settings.addList("commands", new ArrayList(), Setting.STRING); - // Default entries, will only be set if setting is not loaded from file - settings.setAdd("commands", "/slap /me slaps $$1- around a bit with a large trout"); - settings.setAdd("commands", "/permit !permit $$1"); - - settings.addString("timeoutButtons","/Ban[B], /Unban[U], 5s[1], 2m[2], 10m[3], 30m[4]"); - settings.addString("userContextMenu", ""); - settings.addString("channelContextMenu", ""); - - settings.addBoolean("enableStatusWriter", false); - settings.addString("statusWriter", ""); - - settings.addBoolean("autoRequestMods", false); - - settings.addLong("streamChatMessageTimeout", -1); - settings.addList("streamChatChannels", new ArrayList(), Setting.STRING); - settings.addBoolean("streamChatBottom", true); - settings.addBoolean("streamChatResizable", true); - - settings.addString("streamHighlightCommand", "!highlight"); - settings.addString("streamHighlightChannel", ""); - settings.addBoolean("streamHighlightChannelRespond", false); + // TAB Completion settings.addMap("customCompletion", new HashMap(), Setting.STRING); settings.addLong("completionMaxItemsShown", 5); settings.addBoolean("completionShowPopup", true); settings.addBoolean("completionCommonPrefix", false); settings.addString("completionSorting", "predictive"); + // Stream Chat + settings.addLong("streamChatMessageTimeout", -1); + settings.addList("streamChatChannels", new ArrayList(), Setting.STRING); + settings.addBoolean("streamChatBottom", true); + settings.addBoolean("streamChatResizable", true); + + // Whispering settings.addBoolean("whisperEnabled", false); settings.addBoolean("whisperWhitelist", false); - settings.addLong("whisperDisplayMode", WhisperConnection.DISPLAY_IN_CHAT); + settings.addLong("whisperDisplayMode", WhisperManager.DISPLAY_PER_USER); settings.addString("groupChatServer", ""); settings.addString("groupChatPort", ""); settings.addBoolean("whisperAutoRespond", false); - settings.addBoolean("autoUnhost", false); - settings.addList("autoUnhostStreams", new ArrayList(), Setting.STRING); - + // Copy Messages settings.addBoolean("cmEnabled", false); settings.addString("cmChannel", ""); settings.addString("cmTemplate", "{user}: {message}"); settings.addBoolean("cmHighlightedOnly", false); - settings.addBoolean("newsAutoRequest", true); - settings.addLong("newsLastRead", 0); + //=============== + // Other Features + //=============== + + // Livestreamer + settings.addBoolean("livestreamer", false); + settings.addString("livestreamerQualities", "Best, Worst, Select"); + settings.addString("livestreamerCommand", "livestreamer"); + settings.addBoolean("livestreamerUseAuth", false); + settings.addBoolean("livestreamerShowDialog", true); + + // Stream Highlights + settings.addString("streamHighlightCommand", "!highlight"); + settings.addString("streamHighlightChannel", ""); + settings.addBoolean("streamHighlightChannelRespond", false); + + // Stream Status Writer + settings.addBoolean("enableStatusWriter", false); + settings.addString("statusWriter", ""); + + // Auto-Unhost + settings.addBoolean("autoUnhost", false); + settings.addList("autoUnhostStreams", new ArrayList(), Setting.STRING); } /** diff --git a/src/chatty/TwitchClient.java b/src/chatty/TwitchClient.java index bd9ec2385..ac17e7de0 100644 --- a/src/chatty/TwitchClient.java +++ b/src/chatty/TwitchClient.java @@ -10,7 +10,7 @@ import chatty.util.api.ChannelInfo; import chatty.util.api.TwitchApi; import chatty.Version.VersionListener; -import chatty.WhisperConnection.WhisperListener; +import chatty.WhisperManager.WhisperListener; import chatty.gui.GuiUtil; import chatty.gui.MainGui; import chatty.gui.components.Channel; @@ -150,7 +150,7 @@ public class TwitchClient { private final Set refreshRequests = Collections.synchronizedSet(new HashSet()); - private final WhisperConnection w; + private final WhisperManager w; private final IrcLogger ircLogger = new IrcLogger(); private boolean fixServer = false; @@ -238,10 +238,7 @@ public TwitchClient(Map args) { c.addChannelStateListener(new ChannelStateUpdater()); c.setSubNotificationPattern(settings.getString("subNotificationPattern")); - w = new WhisperConnection(new MyWhisperListener(), settings); - w.setUsericonManager(usericonManager); - w.setAddressbook(addressbook); - w.setUsercolorManager(usercolorManager); + w = new WhisperManager(new MyWhisperListener(), settings, c); streamStatusWriter = new StreamStatusWriter(Chatty.getUserDataDirectory(), api); streamStatusWriter.setSetting(settings.getString("statusWriter")); @@ -630,12 +627,10 @@ public boolean prepareConnection(String name, String password, } c.connect(server, ports, name, password, autojoin); - w.connect(name, password); return true; } public boolean disconnect() { - w.disconnect(); return c.disconnect(); } @@ -761,11 +756,7 @@ else if (command.equals("reconnect")) { commandReconnect(); } else if (command.equals("connection")) { - if (!w.isOffline()) { - g.printLine(c.getConnectionInfo()+" {Whisper: "+w.getConnectionInfo()+"}"); - } else { - g.printLine(c.getConnectionInfo()); - } + g.printLine(c.getConnectionInfo()); } else if (command.equals("join")) { commandJoinChannel(parameter); @@ -1890,7 +1881,6 @@ public void exit() { saveSettings(true); logAllViewerstats(); c.disconnect(); - w.disconnect(); g.cleanUp(); chatLog.close(); System.exit(0); @@ -2158,6 +2148,7 @@ public void onChannelCleared(String channel) { @Override public void onWhisper(User user, String message, String emotes) { + w.whisperReceived(user, message, emotes); } @Override @@ -2239,10 +2230,8 @@ private class MyWhisperListener implements WhisperListener { @Override public void whisperReceived(User user, String message, String emotes) { - g.printMessage(WhisperConnection.WHISPER_CHANNEL, user, message, false, emotes); - if (settings.getLong("whisperDisplayMode") == WhisperConnection.DISPLAY_ONE_WINDOW) { - g.updateUser(user); - } + g.printMessage(WhisperManager.WHISPER_CHANNEL, user, message, false, emotes); + g.updateUser(user); } @Override @@ -2252,19 +2241,8 @@ public void info(String message) { @Override public void whisperSent(User to, String message) { - g.printMessage(WhisperConnection.WHISPER_CHANNEL, to, message, true, null); + g.printMessage(WhisperManager.WHISPER_CHANNEL, to, message, true, null); } - - @Override - public void onRawSent(String text) { - ircLogger.onRawSent(text); - } - - @Override - public void onRawReceived(String text) { - ircLogger.onRawReceived(text); - } - } } diff --git a/src/chatty/TwitchConnection.java b/src/chatty/TwitchConnection.java index bd5b7e392..89797b63a 100644 --- a/src/chatty/TwitchConnection.java +++ b/src/chatty/TwitchConnection.java @@ -6,7 +6,6 @@ import chatty.util.settings.Settings; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; @@ -14,7 +13,6 @@ import java.util.Set; import java.util.Timer; import java.util.TimerTask; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -26,13 +24,6 @@ */ public class TwitchConnection { - /** - * Disable userlist connection altogether, since it should not be required - * anymore with the membership CAP, however still keeping the code around - * for now, just never actually connect. - */ - private static final boolean USERLIST_CONNECTION = false; - public enum JoinError { NOT_REGISTERED, ALREADY_JOINED, INVALID_NAME } @@ -54,7 +45,7 @@ public enum JoinError { /** * How many times to try to reconnect */ - private final int maxReconnectionAttempts = 30; + private final int maxReconnectionAttempts = 40; /** * The time in seconds between reconnection attempts. The first entry is the * time for the first attempt, second entry for the second attempt and so @@ -63,8 +54,6 @@ public enum JoinError { private final static int[] RECONNECTION_DELAY = new int[]{1, 5, 5, 10, 10, 60}; private volatile Timer reconnectionTimer; - - private static final int SECONDARY_CONNECTION_UPDATE_DELAY = 10*1000; /** * The username to send to the server. This is stored to reconnect. @@ -87,22 +76,16 @@ public enum JoinError { protected UserManager users = new UserManager(); private final IrcConnection irc; - private final IrcConnection irc2; - private final IrcConnection userlistConnection; private final TwitchCommands twitchCommands; private final SpamProtection spamProtection; private final ChannelStateManager channelStates = new ChannelStateManager(); - private boolean whisperConnection; - private Pattern subNotificationPattern; public TwitchConnection(final ConnectionListener listener, Settings settings, String label) { irc = new IrcConnection(label); - irc2 = new IrcConnection(label+"-secondary"); - userlistConnection = irc; this.listener = listener; this.settings = settings; this.twitchCommands = new TwitchCommands(this); @@ -120,19 +103,6 @@ public void userUpdated(User user) { } } }); - - // Start timer to update secondary connection - TimerTask updateSecondaryConnectionTask = new TimerTask() { - - @Override - public void run() { - updateSecondaryConnection(); - } - }; - final Timer timer = new Timer("update secondary connection", true); - timer.schedule(updateSecondaryConnectionTask, - SECONDARY_CONNECTION_UPDATE_DELAY, - SECONDARY_CONNECTION_UPDATE_DELAY); } private TimerTask getReconnectionTimerTask() { @@ -145,10 +115,6 @@ public void run() { }; } - public void setWhisperConnection(boolean value) { - this.whisperConnection = value; - } - public void setSubNotificationPattern(String text) { try { subNotificationPattern = Pattern.compile(text); @@ -217,7 +183,7 @@ public String getUsername() { } public boolean isUserlistLoaded(String channel) { - return userlistConnection.isRegistered() && userlistConnection.userlistReceived.contains(channel); + return irc.isRegistered() && irc.userlistReceived.contains(channel); } public Set getOpenChannels() { @@ -270,6 +236,9 @@ public boolean isChannelOpen(String channel) { } public void closeChannel(String channel) { + if (channel.equals(WhisperManager.WHISPER_CHANNEL)) { + return; + } partChannel(channel); openChannels.remove(channel); users.clear(channel); @@ -365,8 +334,7 @@ private void connect() { } private Collection getSecuredPorts() { - List setting = settings.getList( - whisperConnection ? "securedPortsWhisper" : "securedPorts"); + List setting = settings.getList("securedPorts"); Collection result = new HashSet<>(); for (Object value : setting) { result.add(((Long)value).intValue()); @@ -401,59 +369,18 @@ public boolean disconnect() { irc.connectionAttempts = 0; } boolean success = irc.disconnect(); - irc2.disconnect(); return success; } public void quit() { irc.disconnect(); - irc2.disconnect(); } - - /** - * Synchronizes the userlist connection with the primary connection, joining - * and leaving channels accordingly. - */ - private void updateSecondaryConnection() { - if (!USERLIST_CONNECTION) { - return; - } - //LOGGER.info("Updating secondary connection.."); - if (irc.isRegistered() && settings.getBoolean("userlistConnection")) { - if (irc2.isOffline()) { - int delay = getReconnectionDelay(irc2.connectionAttempts); - if (irc2.getLastConnectionAttemptAgo() > delay) { - irc2.connect(server, serverPorts, username, password, getSecuredPorts()); - } - } else if (irc2.isRegistered()) { - Set currentChannels = irc.getJoinedChannels(); - Set toLeave = irc2.getJoinedChannels(); - for (String channel : currentChannels) { - if (!settings.listContains("userlistConnectionBlacklist", channel)) { - if (!irc2.onChannel(channel)) { - irc2.joinChannel(channel); - } - toLeave.remove(channel); - } - } - for (String channel : toLeave) { - irc2.partChannel(channel); - } - } - } else if (irc2.isRegistered()) { - irc2.disconnect(); - } - } - + public String getConnectionInfo() { String regular = irc.getConnectionInfo(); - String secondary = irc2.getConnectionInfo(); if (regular == null) { return "Not connected."; } - if (secondary != null) { - return "Connected to: "+regular+" ("+secondary+")"; - } return "Connected to: "+regular; } @@ -470,10 +397,6 @@ public void sendRaw(String text) { } public boolean command(String channel, String command, String parameters) { - if (command.equals("getsecondarychannels")) { - info(irc2.getJoinedChannels().toString()); - return true; - } return twitchCommands.command(channel, command, parameters); } @@ -499,7 +422,7 @@ public void sendCommandMessage(String channel, String message, String echo) { * Tries to send a spam protected message, which will either be send or not, * depending on the status of the spam protection. * - * This doesn't check if you're actually on the channel. + *

This doesn't check if you're actually on the channel.

* * @param channel The channel to send the message to * @param message The message to send @@ -657,7 +580,7 @@ public boolean primaryOnChannel(String channel) { @Override void onUserlist(String channel, String[] nicknames) { channel = channel.toLowerCase(); - if ((this == userlistConnection) && isChannelOpen(channel)) { + if (isChannelOpen(channel)) { /** * Don't clear userlist just yet if only local name is in the @@ -758,8 +681,6 @@ void onDisconnect(int reason, String reasonMessage) { connectionAttempts = 0; } listener.onDisconnect(reason, reasonMessage); - } else if (this == userlistConnection) { - //clearUserlist(null); } } @@ -808,7 +729,7 @@ void onJoin(String channel, String nick, String prefix) { /** * Another user has joined a channel we are currently in. */ - if ((this == userlistConnection) && isChannelOpen(channel)) { + if (isChannelOpen(channel)) { if (!userlistReceived.contains(channel)) { clearUserlist(channel); // Add local user again, must be on this channel but @@ -859,20 +780,17 @@ void onPart(String channel, String nick, String prefix, String message) { listener.onChannelLeft(channel); channelStates.reset(channel); } - if (this == userlistConnection) { - // Leaving the channel on the userlist connection means - // the userlist can no longer be considered as received for - // this channel. - userlistReceived.remove(channel); - } + // Leaving the channel on the userlist connection means + // the userlist can no longer be considered as received for + // this channel. + userlistReceived.remove(channel); debug("PARTED: "+channel); } else { - if ((this == userlistConnection) && isChannelOpen(channel)) { + if (isChannelOpen(channel)) { User user = userOffline(channel, nick); listener.onPart(user); } } - } @Override @@ -960,8 +878,8 @@ private void updateUserFromTags(User user, Map tags) { } } - private boolean checkTagsState(String state, Map tags) { - return "1".equals(tags.get(state)); + private boolean checkTagsState(String key, Map tags) { + return "1".equals(tags.get(key)); } @Override @@ -994,8 +912,6 @@ void onChannelMessage(String channel, String nick, String from, String text, } } } - } else if (nick.equals("jtv")) { - specialMessage(text, channel); } else { User user = userJoined(channel, nick); updateUserFromTags(user, tags); @@ -1020,8 +936,10 @@ void onNotice(String channel, String text, Map tags) { if (this != irc) { return; } - if (onChannel(channel) || whisperConnection) { + if (onChannel(channel)) { infoMessage(channel, text); + } else if (channel.equals("#jtv")) { + listener.onInfo(text); } else { listener.onInfo(String.format("[Info/%s] %s", channel, text)); } @@ -1032,48 +950,11 @@ void onQueryMessage(String nick, String from, String text) { if (this != irc) { return; } - /** - * Any messages from jtv shown directly or used appropriatly, don't think - * there can be any private messages from other users anyway - * (although it might be renamed some time) - */ - if (nick.equals("jtv")) { - specialMessage(text, null); - } if (nick.startsWith("*")) { listener.onSpecialMessage(nick, text); } } - private void specialMessage(String text, String channel) { - String[] split = text.split(" "); - - /** - * This is still returned when changing color (instead of a - * USERSTATE command). - */ - if (split[0].equals("USERCOLOR") && split.length == 3) { - String colorNick = split[1]; - String color = split[2]; - users.setColorForUsername(colorNick, color); - } - - // Decide whether to output the message directly to the user - if (split[0].length() > 2 && Helper.isAllUppercaseLetters(split[0])) { - /** - * Commands are usually all uppercase letters with a length of - * more than two, so don't show those to the user directly. - */ - return; - } else { - /** - * Show anything else, since it's probably a message that's - * useful to the user. - */ - infoMessage(channel, text); - } - } - /** * Any kind of info message. This can be either from jtv (legacy) or the * new NOTICE messages to the channel. @@ -1154,7 +1035,7 @@ private void channelCleared(String channel) { @Override void onWhoResponse(String channel, String nickname) { - + // Not working on Twitch Chat anyway } @Override @@ -1173,8 +1054,6 @@ public boolean isChannelOpen(String channel) { return openChannels.contains(channel); } - - @Override public void raw(String text) { listener.onRawReceived(idPrefix+text); @@ -1307,7 +1186,7 @@ public void onCommand(String nick, String command, String parameter, String text return; } if (command.equals("WHISPER")) { - User user = userJoined(WhisperConnection.WHISPER_CHANNEL, nick); + User user = userJoined(WhisperManager.WHISPER_CHANNEL, nick); updateUserFromTags(user, tags); String emotesTag = tags != null ? tags.get("emotes") : null; listener.onWhisper(user, text, emotesTag); diff --git a/src/chatty/WhisperConnection.java b/src/chatty/WhisperConnection.java deleted file mode 100644 index 4c8b01605..000000000 --- a/src/chatty/WhisperConnection.java +++ /dev/null @@ -1,357 +0,0 @@ - -package chatty; - -import chatty.util.settings.Settings; -import java.util.HashSet; -import java.util.Set; - -/** - * - * @author tduva - */ -public class WhisperConnection { - - public static final String WHISPER_CHANNEL = "$[whisper]"; - - public static final int DISPLAY_IN_CHAT = 0; - public static final int DISPLAY_ONE_WINDOW = 1; - public static final int DISPLAY_PER_USER = 2; - - private final TwitchConnection c; - private final WhisperListener listener; - private final Settings settings; - - private final String AUTO_RESPOND_MESSAGE = "[Auto-Message] This user has " - + "not allowed to receive whispers from you."; - private final Set autoRespondedTo = new HashSet<>(); - - public WhisperConnection(WhisperListener listener, Settings settings) { - this.listener = listener; - this.settings = settings; - c = new TwitchConnection(new Events(), settings, "whisper"); - c.setWhisperConnection(true); - } - - public void disconnect() { - c.disconnect(); - } - - public void setUsericonManager(UsericonManager m) { - c.setUsericonManager(m); - } - - public void setAddressbook(Addressbook a) { - c.setAddressbook(a); - } - - public void setUsercolorManager(UsercolorManager m) { - c.setUsercolorManager(m); - } - - public String getConnectionInfo() { - return c.getConnectionInfo(); - } - - public boolean isOffline() { - return c.isOffline(); - } - - public boolean isAvailable() { - //return !c.isOffline() && c.onChannel("#jtv"); - return c.isRegistered(); - } - - /** - * Check whether the whisper feature is enabled and configured correctly. - * - * @return true if enabled and configured correctly, false otherwise - */ - public boolean isEnabled() { - if (!settings.getBoolean("whisperEnabled")) { - return false; - } - String server = settings.getString("groupChatServer"); - String port = settings.getString("groupChatPort"); - if (server.isEmpty() || port.isEmpty()) { - return false; - } - return true; - } - - /** - * Connect to the Group Chat Server (specified in the settings) using the - * given name and password. If already connected, this will simply store - * the given name/password and not connect. It may use the stored data to - * reconnect. - * - * @param username - * @param password - */ - public void connect(String username, String password) { - if (settings.getBoolean("whisperEnabled")) { - String server = settings.getString("groupChatServer"); - String port = settings.getString("groupChatPort"); - if (server.isEmpty() || port.isEmpty()) { - listener.info("Whisper feature: No server/port defined (read help)"); - return; - } - c.connect(server, port, username, password, new String[]{}); - } - } - - /** - * The whisper command in the form: {@code /w } - * - * @param parameter The target and message in a String - * @param onlyReply Whether to only send a whisper to people you already - * have whispered with this session - */ - public void whisperCommand(String parameter, boolean onlyReply) { - if (parameter == null) { - listener.info("Whisper: Invalid parameters."); - return; - } - String[] split = parameter.split(" ", 2); - if (split.length == 2) { - whisper(split[0], split[1], onlyReply); - } else { - listener.info("Whisper: Invalid parameters."); - } - } - - /** - * Whisper entered directly into the channel input box of a whisper tab, so - * get the target of the whisper. - * - * If a whisper is entered in the combined whisper channel, then the message - * has to be in the format {@code }. In case the user only - * types the message, whispers entered this way will only be send to users - * that the local user has already whispered with this session. - * - * @param channel The channel - * @param message The message entered, which may be the {@code } or - * {@code } depending on the channel - */ - public void whisperChannel(String channel, String message) { - if (channel.equals(WHISPER_CHANNEL)) { - whisperCommand(message, true); - } else if (channel.startsWith("$") && channel.length() > 1) { - String nick = channel.substring(1); - whisper(nick, message, false); - } else { - listener.info("Whisper: Invalid parameters."); - } - } - - /** - * Send a whisper. - * - * @param nick The target of the whisper - * @param message The message - * @param onlyReply If true, it will only send a whisper to people that have - * already whispered with you this session - */ - public void whisper(String nick, String message, boolean onlyReply) { - if (!isEnabled()) { - listener.info("Whisper feature not enabled (
)"); - return; - } - if (onlyReply && c.getExistingUser(WHISPER_CHANNEL, nick) == null) { - listener.info("Didn't receive any whisper from '"+nick+"', use /w command"); - return; - } - if (isAvailable()) { - if (!rawWhisper(nick, message)) { - listener.info("# Whisper not sent (spam protection): " + message); - } else { - User user = c.getUser(WHISPER_CHANNEL, nick); - listener.whisperSent(user, message); - if (isUserIgnored(user)) { - listener.info("You haven't allowed to receive whispers from " + user); - } - } - } else { - listener.info("Can't send whisper: not connected"); - } - } - - private boolean rawWhisper(String nick, String message) { - return c.sendSpamProtectedMessage("#jtv", "/w "+nick+" "+message, false); - } - - /** - * Check if whispers from the given user should be output or ignored. - * @param user The user to check - * @return true if whispers from the given user should be output, false - * otherwise - */ - private boolean isUserAllowed(User user) { - if (user.hasCategory("blockwhisper")) { - return false; - } - if (settings.getBoolean("whisperWhitelist") && !user.hasCategory("whisper")) { - return false; - } - return true; - } - - private boolean isUserIgnored(User user) { - if (settings.listContains("ignoredUsersWhisper", user.nick)) { - return true; - } - return !isUserAllowed(user); - } - - private class Events implements TwitchConnection.ConnectionListener { - - @Override - public void onJoinAttempt(String channel) { - } - - @Override - public void onChannelJoined(String channel) { - } - - @Override - public void onChannelLeft(String channel) { - } - - @Override - public void onJoin(User user) { - } - - @Override - public void onPart(User user) { - } - - @Override - public void onUserAdded(User user) { - } - - @Override - public void onUserRemoved(User user) { - } - - @Override - public void onUserlistCleared(String channel) { - } - - @Override - public void onUserUpdated(User user) { - } - - @Override - public void onChannelMessage(User user, String message, boolean action, String emotes) { - - } - - @Override - public void onNotice(String message) { - } - - @Override - public void onInfo(String channel, String infoMessage) { - if (channel.equals("#jtv")) { - listener.info(infoMessage); - } - } - - @Override - public void onInfo(String message) { - - } - - @Override - public void onGlobalInfo(String message) { - } - - @Override - public void onBan(User user) { - } - - @Override - public void onRegistered() { - listener.info("Whisper feature: connected"); - } - - @Override - public void onDisconnect(int reason, String reasonMessage) { - listener.info("Whisper feature: Disconnected ("+reasonMessage+")"); - } - - @Override - public void onMod(User user) { - } - - @Override - public void onUnmod(User user) { - } - - @Override - public void onConnectionStateChanged(int state) { - } - - @Override - public void onSpecialUserUpdated() { - } - - @Override - public void onConnectError(String message) { - } - - @Override - public void onJoinError(Set toJoin, String errorChannel, TwitchConnection.JoinError error) { - } - - @Override - public void onRawReceived(String text) { - listener.onRawReceived(text); - } - - @Override - public void onRawSent(String text) { - listener.onRawSent(text); - } - - @Override - public void onHost(String channel, String target) { - } - - @Override - public void onChannelCleared(String channel) { - } - - @Override - public void onWhisper(User user, String message, String emotes) { - if (isUserAllowed(user)) { - listener.whisperReceived(user, message, emotes); - } - if (isUserIgnored(user) && settings.getBoolean("whisperAutoRespond")) { - if (!autoRespondedTo.contains(user.nick)) { - rawWhisper(user.nick, AUTO_RESPOND_MESSAGE); - autoRespondedTo.add(user.nick); - } - } else { - autoRespondedTo.remove(user.nick); - } - } - - @Override - public void onSubscriberNotification(String channel, String name, int months) { - } - - @Override - public void onSpecialMessage(String name, String message) { - } - - } - - public interface WhisperListener { - public void whisperReceived(User user, String message, String emotes); - public void whisperSent(User to, String message); - public void info(String message); - public void onRawSent(String text); - public void onRawReceived(String text); - } - -} diff --git a/src/chatty/WhisperManager.java b/src/chatty/WhisperManager.java new file mode 100644 index 000000000..8aff62716 --- /dev/null +++ b/src/chatty/WhisperManager.java @@ -0,0 +1,188 @@ + +package chatty; + +import chatty.util.settings.Settings; +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author tduva + */ +public class WhisperManager { + + public static final String WHISPER_CHANNEL = "$[whisper]"; + + public static final int DISPLAY_IN_CHAT = 0; + public static final int DISPLAY_ONE_WINDOW = 1; + public static final int DISPLAY_PER_USER = 2; + + private final String AUTO_RESPOND_MESSAGE = "[Auto-Message] This user has " + + "not allowed to receive whispers from you."; + private final Set autoRespondedTo = new HashSet<>(); + + //----------- + // References + //----------- + private final WhisperListener listener; + private final Settings settings; + private final TwitchConnection c; + + public WhisperManager(WhisperListener listener, Settings settings, + TwitchConnection c) { + this.listener = listener; + this.settings = settings; + this.c = c; + } + + public boolean isAvailable() { + return c.isRegistered() && isEnabled(); + } + + /** + * Check whether the whisper feature is enabled and configured correctly. + * + * @return true if enabled and configured correctly, false otherwise + */ + public boolean isEnabled() { + return settings.getBoolean("whisperEnabled"); + } + + /** + * The whisper command in the form: {@code /w } + * + * @param parameter The target and message in a String + * @param onlyReply Whether to only send a whisper to people you already + * have whispered with this session + */ + public void whisperCommand(String parameter, boolean onlyReply) { + if (parameter == null) { + listener.info("Whisper: Invalid parameters."); + return; + } + String[] split = parameter.split(" ", 2); + if (split.length == 2) { + whisper(split[0], split[1], onlyReply); + } else { + listener.info("Whisper: Invalid parameters."); + } + } + + /** + * Whisper entered directly into the channel input box of a whisper tab, so + * get the target of the whisper. + * + * If a whisper is entered in the combined whisper channel, then the message + * has to be in the format {@code }. In case the user only + * types the message, whispers entered this way will only be send to users + * that the local user has already whispered with this session. + * + * @param channel The channel + * @param message The message entered, which may be the {@code } or + * {@code } depending on the channel + */ + public void whisperChannel(String channel, String message) { + if (channel.equals(WHISPER_CHANNEL)) { + whisperCommand(message, true); + } else if (channel.startsWith("$") && channel.length() > 1) { + String nick = channel.substring(1); + whisper(nick, message, false); + } else { + listener.info("Whisper: Invalid parameters."); + } + } + + /** + * Send a whisper, while checking some stuff. + * + * @param nick The target of the whisper + * @param message The message + * @param onlyReply If true, it will only send a whisper to people that have + * already whispered with you this session + */ + public void whisper(String nick, String message, boolean onlyReply) { + if (!isEnabled()) { + listener.info("Whisper feature not enabled (
)"); + return; + } + if (onlyReply && c.getExistingUser(WHISPER_CHANNEL, nick) == null) { + listener.info("Didn't receive any whisper from '"+nick+"', use /w command"); + return; + } + if (isAvailable()) { + if (!rawWhisper(nick, message)) { + listener.info("# Whisper not sent (spam protection): " + message); + } else { + User user = c.getUser(WHISPER_CHANNEL, nick); + listener.whisperSent(user, message); + if (isUserIgnored(user)) { + listener.info("You haven't allowed to receive whispers from " + user); + } + } + } else { + listener.info("Can't send whisper: not connected"); + } + } + + /** + * Check if whispers from the given user should be output or ignored. + * + * @param user The user to check + * @return true if whispers from the given user should be output, false + * otherwise + */ + private boolean isUserAllowed(User user) { + if (user.hasCategory("blockwhisper")) { + return false; + } + if (settings.getBoolean("whisperWhitelist") && !user.hasCategory("whisper")) { + return false; + } + return true; + } + + private boolean isUserIgnored(User user) { + if (settings.listContains("ignoredUsersWhisper", user.nick)) { + return true; + } + return !isUserAllowed(user); + } + + public void whisperReceived(User user, String message, String emotes) { + if (!isEnabled()) { + return; + } + if (isUserAllowed(user)) { + listener.whisperReceived(user, message, emotes); + } + if (isUserIgnored(user) && settings.getBoolean("whisperAutoRespond")) { + if (!autoRespondedTo.contains(user.nick)) { + rawWhisper(user.nick, AUTO_RESPOND_MESSAGE); + autoRespondedTo.add(user.nick); + } + } else { + autoRespondedTo.remove(user.nick); + } + } + + /** + * Send a whisper without any checks. + * + * @param nick + * @param message + * @return + */ + private boolean rawWhisper(String nick, String message) { + return c.sendSpamProtectedMessage("#jtv", "/w " + nick + " " + message, false); + } + + + public interface WhisperListener { + + public void whisperReceived(User user, String message, String emotes); + + public void whisperSent(User to, String message); + + public void info(String message); + } +} diff --git a/src/chatty/gui/Channels.java b/src/chatty/gui/Channels.java index e49ca354a..78c834b88 100644 --- a/src/chatty/gui/Channels.java +++ b/src/chatty/gui/Channels.java @@ -160,7 +160,7 @@ private Channel createChannel(String name, Channel.Type type) { channel.setUserlistWidth(defaultUserlistWidth, minUserlistWidth); channel.setMouseClickedListener(mouseClickedListener); channel.setScrollbarAlways(chatScrollbarAlaways); - if (type == Channel.Type.SPECIAL) { + if (type == Channel.Type.SPECIAL || type == Channel.Type.WHISPER) { channel.setUserlistEnabled(false); } return channel; diff --git a/src/chatty/gui/MainGui.java b/src/chatty/gui/MainGui.java index 0b2f6dfaa..440d2b514 100644 --- a/src/chatty/gui/MainGui.java +++ b/src/chatty/gui/MainGui.java @@ -28,7 +28,7 @@ import chatty.StatusHistory; import chatty.UsercolorItem; import chatty.Usericon; -import chatty.WhisperConnection; +import chatty.WhisperManager; import chatty.gui.components.AddressbookDialog; import chatty.gui.components.EmotesDialog; import chatty.gui.components.ErrorMessage; @@ -56,7 +56,6 @@ import chatty.util.api.EmoticonUpdate; import chatty.util.api.Emoticons.TagEmotes; import chatty.util.api.FollowerInfo; -import chatty.util.api.TwitchApi; import chatty.util.api.TwitchApi.RequestResult; import chatty.util.hotkeys.HotkeyManager; import chatty.util.settings.Setting; @@ -65,8 +64,6 @@ import chatty.util.settings.SettingsListener; import java.awt.*; import java.awt.event.*; -import java.time.LocalDateTime; -import java.time.Month; import java.util.*; import java.util.logging.LogRecord; import javax.swing.AbstractAction; @@ -2374,11 +2371,11 @@ public void run() { * Check if special channel and change target according to * settings */ - if (channel.equals(WhisperConnection.WHISPER_CHANNEL)) { + if (channel.equals(WhisperManager.WHISPER_CHANNEL)) { int whisperSetting = (int)client.settings.getLong("whisperDisplayMode"); - if (whisperSetting == WhisperConnection.DISPLAY_ONE_WINDOW) { + if (whisperSetting == WhisperManager.DISPLAY_ONE_WINDOW) { chan = channels.getChannel(channel); - } else if (whisperSetting == WhisperConnection.DISPLAY_PER_USER) { + } else if (whisperSetting == WhisperManager.DISPLAY_PER_USER) { if (!userIgnored(user, true)) { chan = channels.getChannel("$"+user.getNick()); } else { @@ -2753,6 +2750,7 @@ public void addUser(final String channel, final User user) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { + if (!shouldUpdateUser(user)) return; Channel c = channels.getChannel(channel); c.addUser(user); if (channels.getActiveChannel() == c) { @@ -2772,6 +2770,7 @@ public void removeUser(final String channel, final User user) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { + if (!shouldUpdateUser(user)) return; Channel c = channels.getChannel(channel); c.removeUser(user); if (channels.getActiveChannel() == c) { @@ -2791,12 +2790,18 @@ public void updateUser(final User user) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { + if (!shouldUpdateUser(user)) return; channels.getChannel(user.getChannel()).updateUser(user); state.update(); } }); } + private boolean shouldUpdateUser(User user) { + return !user.getChannel().equals(WhisperManager.WHISPER_CHANNEL) + || client.settings.getLong("whisperDisplayMode") == WhisperManager.DISPLAY_ONE_WINDOW; + } + /** * Resort users in the userlist of the given channel. * diff --git a/src/chatty/gui/components/help/help-whisper.html b/src/chatty/gui/components/help/help-whisper.html index 6d0dcdf7e..85bc46fe5 100644 --- a/src/chatty/gui/components/help/help-whisper.html +++ b/src/chatty/gui/components/help/help-whisper.html @@ -7,73 +7,28 @@

Whisper Feature

- Getting started / Settings | + Settings | Usage | Troubleshooting

-

It is recommended that you read and understand this whole section - before enabling this feature.

- -

Chatty implements the new /w command that Twitch Chat +

Chatty implements the /w command that Twitch Chat provides, allowing you to send private messages in chat to other users. - However, it is disabled by default, since it has a few problems:

+ Chatty does not provide the Whisper History as on the website and is + only a very basic implementation in general.

-
    -
  • Twitch is planning to move Whispering to a whole different system - soon, which means in the current state it is not really encouraged - to be used by third-party applications. It is unclear what the new - system will be and whether it will be possible to implement it in - Chatty. Also, due to these circumstances, not too much development - time will be put into the Whisper feature in Chatty at this time.
  • -
  • Whispering currently runs on the Group Chat servers, so Chatty can't - just use irc.twitch.tv to connect as for regular chat. - Since there is no easy official way to get the server data - automatically, you have enter the IP and port yourself if you want - to use this feature.
  • -
+

The Whisper Feature is disabled by default.

+ +

In April 2016 Twitch moved Whispers from the Group Chat servers to the + new AWS Twitch Chat servers. There is no additional server configuration + required anymore.

-

Getting started / Settings

-

You have to change a few settings in order to use this feature - (all under Main - Settings - Advanced):

- -

Required settings

-
    -
  • Whisper Enabled: Connect to the given Group Chat - server to send and receive whispers. Enable when you configured the - server. Disable this it if you run into problems.
  • -
  • Server/Port: You can click on - Select server.. and select a server from the list (all - listed should work). This is using an unsupported Twitch - API which is not guaranteed to stay the same.
  • -
- -

Once these settings are correctly configured, Chatty will connect to - Whispers when you connect to regular chat. It will disconnect when you - disconnect from regular chat via the Main-menu. If the Whisper Feature - is properly connected and ready to use, [W] will be shown - in the titlebar and the /connection command will show the - Whisper connection in addition to the regular chat connection.

- -

If you were already connected when you changed these settings, either - disconnect and connect again (via the Main menu) or restart Chatty.

- -

If Select server.. is not working, configure the server manually:

-
    -
  • Server: The Group Chat server IP to connect to. You can check: -
      -
    • Twitchstatus.com (unofficial site) at the bottom of the page, - "Group Chat" tab, "irc" Protocol (not "ws_irc")
    • -
    • Get an IP from the Twitch API using this guide - (you will need a token and be member of a Group Chat, you won't need to get a channelname)
    • -
  • -
  • Port: The port for the Group Chat server you - are using (you should get it along with the IP).
  • -
- -

Optional settings

+

Settings

    +
  • Whisper Enabled: Allow sending and receiving + Whispers. With this disabled, you won't be able to send Whispers and + any received Whispers will be entirely ignored.
  • Whitelist: If enabled, only users that have the Addressbook category whisper will be able to send whispers to you. @@ -143,34 +98,6 @@

    Usage

    (*), which is usually used to mark action messages (/me).
- -

Troubleshooting

-

If you get a lot of errors like Whisper feature: Disconnected this - may be due to different reasons:

- -
    -
  • A temporary problem with Twitch or your internet connection, in which - case just wait a while.
  • -
  • Wrong server/port entered, follow the Getting Started - guide to check if the data you entered is still correct.
  • -
  • Twitch changed the system and it is not working as implemented in Chatty - anymore (in which case you should disable the feature).
  • -
- -

How to proceed:

-
    -
  • Check Twitchstatus.com (mainly Group Chat Tab), - @ChattyClient and - other usual places to find out if the problem lies with Twitch. - Disable the Whisper Feature if necessary.
  • -
  • If the [W] in the titlebar isn't there, but it also - doesn't output errors anymore, it may have given up trying to connect, - in which case you should connect to regular chat through the Main - menu (disconnect first if necessary). Connecting to whispers is - currently somewhat tied to connecting to regular chat. Disconnecting - from regular chat from the Main menu will also disconnect - from whispers.
  • -
diff --git a/src/chatty/gui/components/settings/AdvancedSettings.java b/src/chatty/gui/components/settings/AdvancedSettings.java index f1273f38b..13e46ae5f 100644 --- a/src/chatty/gui/components/settings/AdvancedSettings.java +++ b/src/chatty/gui/components/settings/AdvancedSettings.java @@ -1,46 +1,21 @@ package chatty.gui.components.settings; -import chatty.WhisperConnection; -import chatty.gui.GuiUtil; +import chatty.WhisperManager; import chatty.gui.components.LinkLabel; -import chatty.util.UrlRequest; -import java.awt.CardLayout; -import java.awt.Dimension; import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.LinkedHashMap; import java.util.Map; -import java.util.logging.Logger; -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JDialog; import javax.swing.JLabel; -import javax.swing.JList; import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.ListModel; -import javax.swing.SwingUtilities; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; /** - * Settings that should only be changed if you know what you're doing, includes - * a warning about that. + * Stuff. * * @author tduva */ public class AdvancedSettings extends SettingsPanel { - private static final Logger LOGGER = Logger.getLogger(AdvancedSettings.class.getName()); - - private final StringSetting groupChatServer; - private final StringSetting groupChatPort; - public AdvancedSettings(final SettingsDialog d) { JPanel connection = addTitledPanel("Connection", 1); @@ -81,177 +56,20 @@ public AdvancedSettings(final SettingsDialog d) { d.makeGbc(3, 0, 1, 1)); Map displayMode = new LinkedHashMap<>(); - displayMode.put(Long.valueOf(WhisperConnection.DISPLAY_IN_CHAT), "Active Chat"); - displayMode.put(Long.valueOf(WhisperConnection.DISPLAY_ONE_WINDOW), "One Window"); - displayMode.put(Long.valueOf(WhisperConnection.DISPLAY_PER_USER), "Per User"); + displayMode.put(Long.valueOf(WhisperManager.DISPLAY_IN_CHAT), "Active Chat"); + displayMode.put(Long.valueOf(WhisperManager.DISPLAY_ONE_WINDOW), "One Window"); + displayMode.put(Long.valueOf(WhisperManager.DISPLAY_PER_USER), "Per User"); ComboLongSetting displayModeSetting = new ComboLongSetting(displayMode); d.addLongSetting("whisperDisplayMode", displayModeSetting); whisper.add(displayModeSetting, d.makeGbc(4, 0, 1, 1)); - groupChatServer = (StringSetting)d.addSimpleStringSetting("groupChatServer", 10, true); - groupChatPort = (StringSetting)d.addSimpleStringSetting("groupChatPort", 4, true); - whisper.add(new JLabel("Server:"), d.makeGbc(0, 1, 1, 1)); - whisper.add((JTextField)groupChatServer, - d.makeGbc(1, 1, 1, 1)); - whisper.add(new JLabel("Port:"), d.makeGbc(2, 1, 1, 1, GridBagConstraints.EAST)); - whisper.add((JTextField)groupChatPort, - d.makeGbc(3, 1, 1, 1, GridBagConstraints.WEST)); - - JButton selectServer = new JButton("Select server.."); - selectServer.setMargin(GuiUtil.SMALL_BUTTON_INSETS); - final GroupChatSelect groupChatSelect = new GroupChatSelect(d); - whisper.add(selectServer, - d.makeGbc(0, 3, 2, 1)); - selectServer.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - groupChatSelect.setLocationRelativeTo(d); - groupChatSelect.showDialog(); - } - }); - whisper.add(new LinkLabel("[help-whisper:top Whisper Help]", d.getLinkLabelListener()), - d.makeGbc(2, 3, 2, 1)); + d.makeGbc(2, 1, 2, 1)); whisper.add(d.addSimpleBooleanSetting("whisperAutoRespond", "Auto-respond to ignored/non-whitelisted users", "Sends an automatic message telling users you didn't receive their message"), - d.makeGbc(0, 4, 5, 1, GridBagConstraints.WEST)); - - } - - private class GroupChatSelect extends JDialog { - - private final JPanel cards; - private final CardLayout cardLayout = new CardLayout(); - private final JList serversList = new JList<>(); - private final DefaultListModel servers = new DefaultListModel<>(); - private final JButton selectButton = new JButton("Close"); - private final LinkLabel info; - private boolean successfullyLoaded; - - public GroupChatSelect(SettingsDialog d) { - super(d); - setModal(true); - setResizable(false); - setTitle("Select server"); - - info = new LinkLabel("", d.getLinkLabelListener()); - - cards = new JPanel(cardLayout); - cards.add(new JScrollPane(serversList), "servers"); - cards.add(info, "info"); - - setLayout(new GridBagLayout()); - serversList.setPreferredSize(new Dimension(200, 50)); - GridBagConstraints gbc = d.makeGbc(0, 0, 1, 1); - gbc.fill = GridBagConstraints.BOTH; - gbc.weightx = 1; - gbc.weighty = 1; - add(cards, - gbc); - - gbc = d.makeGbc(0, 1, 1, 1); - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.weightx = 1; - add(selectButton, gbc); - serversList.setModel(servers); - selectButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - String selected = serversList.getSelectedValue(); - if (selected != null) { - int sep = selected.lastIndexOf(":"); - try { - String server = selected.substring(0, sep); - String port = selected.substring(sep+1, selected.length()); - groupChatServer.setSettingValue(server); - groupChatPort.setSettingValue(port); - } catch (Exception ex) { - LOGGER.warning("Invalid server info"); - } - } - setVisible(false); - } - }); - - pack(); - } - - public void showDialog() { - if (!successfullyLoaded) { - requestList(); - } - pack(); - setVisible(true); - } - - private void requestList() { - showInfo("Loading.."); - UrlRequest request = new UrlRequest("https://tmi.twitch.tv/servers?cluster=group") { - - @Override - public void requestResult(final String result, final int responseCode) { - SwingUtilities.invokeLater(new Runnable() { - - @Override - public void run() { - parseResult(result, responseCode); - } - }); - } - }; - request.setLabel("Group Chat Servers"); - new Thread(request).start(); - } - - private void parseResult(String result, int responseCode) { - servers.clear(); - if (result == null || responseCode != 200) { - error("Request error ("+responseCode+")"); - return; - } - JSONParser parser = new JSONParser(); - try { - JSONObject root = (JSONObject) parser.parse(result); - JSONArray serversData = (JSONArray) root.get("servers"); - for (Object server : serversData) { - servers.addElement((String)server); - } - serversList.setSelectedIndex(0); - showServers(); - successfullyLoaded = true; - selectButton.setText("Use selected server"); - } catch (Exception ex) { - error("Error parsing servers"); - LOGGER.warning("Error parsing servers: " + ex); - } - } - - private void error(String error) { - showInfo(""+error+"

" - + "This may be a temporary problem or the used API may have" - + " changed. Please try again later or consult the [help-whisper:top help] on" - + " how to add the server manually.

" - + "If this keeps happening, check the usual channels" - + " ([url:http://chatty.github.io Chatty website], [url:https://twitter.com/chattyclient @ChattyClient]) for updates."); - selectButton.setText("Close"); - } - - private void showInfo(String infoText) { - info.setText(infoText); - cardLayout.show(cards, "info"); - pack(); - } - - private void showServers() { - cardLayout.show(cards, "servers"); - pack(); - } - + d.makeGbc(0, 2, 5, 1, GridBagConstraints.WEST)); } - } diff --git a/src/chatty/gui/components/settings/SettingsDialog.java b/src/chatty/gui/components/settings/SettingsDialog.java index d29d929fd..bad3615e2 100644 --- a/src/chatty/gui/components/settings/SettingsDialog.java +++ b/src/chatty/gui/components/settings/SettingsDialog.java @@ -46,7 +46,6 @@ public class SettingsDialog extends JDialog implements ActionListener { "userlistWidth", "userlistMinWidth", "tabOrder","bttvEmotes","correctlyCapitalizedNames", "logPath", "logTimestamp", "botNamesBTTV", "botNamesFFZ", "ircv3CapitalizedNames", - "whisperEnabled", "groupChatServer", "groupChatPort", "tabsMwheelScrolling", "inputFont")); private final Set reconnectRequiredDef = new HashSet<>(Arrays.asList( diff --git a/src/chatty/util/settings/Settings.java b/src/chatty/util/settings/Settings.java index 884088b48..f607eec75 100644 --- a/src/chatty/util/settings/Settings.java +++ b/src/chatty/util/settings/Settings.java @@ -487,7 +487,7 @@ public void listClear(String settingName) { */ public boolean setAdd(String settingName, Object value) { synchronized(LOCK) { - Collection settingList = (Collection)get(settingName, Setting.LIST); + Collection settingList = getListInternal(settingName); if (!settingList.contains(value)) { settingList.add(value); return true;