diff --git a/EnhancedStreamChat/Chat/ChatHandler.cs b/EnhancedStreamChat/Chat/ChatHandler.cs index 1532d70..6ee0a3a 100644 --- a/EnhancedStreamChat/Chat/ChatHandler.cs +++ b/EnhancedStreamChat/Chat/ChatHandler.cs @@ -20,10 +20,11 @@ using EnhancedStreamChat.Images; using StreamCore.YouTube; using StreamCore.Twitch; +using System.Text; namespace EnhancedStreamChat { - public class ChatHandler : MonoBehaviour, ITwitchMessageHandler, IGlobalMessageHandler + public class ChatHandler : MonoBehaviour, ITwitchIntegration, IYouTubeIntegration { public static ChatHandler Instance = null; public static ConcurrentQueue RenderQueue = new ConcurrentQueue(); @@ -56,18 +57,7 @@ public class ChatHandler : MonoBehaviour, ITwitchMessageHandler, IGlobalMessageH private bool _hasDisplayedTwitchStatus = false; private string _lastRoomId = String.Empty; - public bool ChatCallbacksReady { get; set; } = false; - public Action Twitch_OnPrivmsgReceived { get; set; } - public Action Twitch_OnRoomstateReceived { get; set; } - public Action Twitch_OnUsernoticeReceived { get; set; } - public Action Twitch_OnClearchatReceived { get; set; } - public Action Twitch_OnClearmsgReceived { get; set; } - public Action Twitch_OnModeReceived { get; set; } - public Action Twitch_OnJoinReceived { get; set; } - public Action Twitch_OnUserstateReceived { get; set; } - public Action Global_OnMessageReceived { get; set; } - public Action Global_OnSingleMessageDeleted { get; set; } - public Action Global_OnAllMessagesDeleted { get; set; } + public bool IsPluginReady { get; set; } = false; public void Awake() { @@ -88,7 +78,7 @@ public void Awake() ChatConfig.Instance.ConfigChangedEvent += ChatConfigChanged; initialized = true; - ChatCallbacksReady = true; + IsPluginReady = true; Plugin.Log("EnhancedStreamChat initialized"); } @@ -147,7 +137,7 @@ private void HandleStatusMessages() if (!_hasDisplayedTwitchStatus && TwitchWebSocketClient.Initialized) { // If the last room id hasn't been set, allow up to a 30 second timeout before we throw an error - if (_lastRoomId == String.Empty && (DateTime.Now - TwitchWebSocketClient.ConnectionTime).TotalSeconds < 30) + if ((DateTime.Now - TwitchWebSocketClient.ConnectionTime).TotalSeconds < 30) { return; } @@ -275,35 +265,28 @@ private void RegisterMessageHandlers() _hasDisplayedTwitchStatus = false; }; - Global_OnMessageReceived += (genericMessage) => + TwitchMessageHandlers.PRIVMSG += (genericMessage) => { - if(genericMessage is TwitchMessage) - { - var twitchMsg = genericMessage.Twitch; - // Don't show any messages that aren't from the channel in the config - if (twitchMsg.channelName != TwitchLoginConfig.Instance.TwitchChannelName) - return; + var twitchMsg = genericMessage.Twitch; + // Don't show any messages that aren't from the channel in the config + if (twitchMsg.channelName != TwitchLoginConfig.Instance.TwitchChannelName) + return; - MessageParser.Parse(new ChatMessage(Utilities.EscapeHTML(twitchMsg.message), twitchMsg)); - } - else if(genericMessage is YouTubeMessage) - { - var youTubeMsg = genericMessage.YouTube; - MessageParser.Parse(new ChatMessage(Utilities.EscapeHTML(youTubeMsg.message), youTubeMsg)); - } + MessageParser.Parse(new ChatMessage(Utilities.EscapeHTML(twitchMsg.message), twitchMsg)); }; - Twitch_OnRoomstateReceived += (twitchMsg, twitchChannel) => + TwitchMessageHandlers.ROOMSTATE += (twitchMsg, twitchChannel) => { if (_lastRoomId != twitchChannel.roomId) { _lastRoomId = twitchChannel.roomId; RenderQueue.Enqueue(new ChatMessage($"Success joining Twitch channel \"{TwitchLoginConfig.Instance.TwitchChannelName}\"", new GenericChatMessage())); + _hasDisplayedTwitchStatus = true; ImageDownloader.Instance.Init(); } }; - - Twitch_OnUsernoticeReceived += (twitchMsg) => + + TwitchMessageHandlers.USERNOTICE += (twitchMsg) => { string msgId = String.Empty, systemMsg = String.Empty; foreach (Match t in twitchMsg.tags) @@ -337,7 +320,7 @@ private void RegisterMessageHandlers() } }; - Twitch_OnUserstateReceived += (twitchMsg) => + TwitchMessageHandlers.USERSTATE += (twitchMsg) => { if (!(twitchMsg.user.Twitch.isBroadcaster || twitchMsg.user.Twitch.isMod)) { @@ -348,7 +331,7 @@ private void RegisterMessageHandlers() } }; - Twitch_OnClearchatReceived += (twitchMsg) => + TwitchMessageHandlers.CLEARCHAT += (twitchMsg) => { string userId = "!FULLCLEAR!"; foreach (Match t in twitchMsg.tags) @@ -362,7 +345,7 @@ private void RegisterMessageHandlers() PurgeMessagesFromUser(userId); }; - Twitch_OnClearmsgReceived += (twitchMsg) => + TwitchMessageHandlers.CLEARMSG += (twitchMsg) => { string msgId = String.Empty; foreach (Match t in twitchMsg.tags) @@ -376,6 +359,26 @@ private void RegisterMessageHandlers() if (msgId == String.Empty) return; PurgeChatMessageById(msgId); }; + + YouTubeMessageHandlers.OnInitialize += () => + { + RenderQueue.Enqueue(new ChatMessage("Connecting to YouTube chat...", new GenericChatMessage())); + }; + + YouTubeMessageHandlers.OnConnectedToLiveChat += (liveBroadcastInfo) => + { + RenderQueue.Enqueue(new ChatMessage($"Success joining YouTube channel \"{YouTubeLiveBroadcast.channelName}\"", new GenericChatMessage())); + RenderQueue.Enqueue(new ChatMessage($"Current YouTube Broadcast: \"{liveBroadcastInfo.snippet.title}\"", new GenericChatMessage())); + }; + + YouTubeMessageHandlers.OnMessageReceived += (youTubeMsg) => + { + MessageParser.Parse(new ChatMessage(Utilities.EscapeHTML(youTubeMsg.message), youTubeMsg)); + }; + + YouTubeMessageHandlers.OnYouTubeError += (error) => { + RenderQueue.Enqueue(new ChatMessage($"An unhandled YouTube error occurred. {error}", new GenericChatMessage())); + }; } private void InitializeChatUI() @@ -448,31 +451,68 @@ private void InitializeChatUI() _testMessage.enabled = false; } - private IEnumerator AddNewChatMessage(string msg, ChatMessage messageInfo) + private static readonly Regex _htmlTagRegex = new Regex(@"<(?[a-z]+)=?(?[^>=]+)?>", RegexOptions.Compiled | RegexOptions.Multiline); + private IEnumerator AddNewChatMessage(string origMsg, ChatMessage messageInfo) { _messageRendering = true; CustomText currentMessage = null; - _testMessage.text = msg; - _testMessage.cachedTextGenerator.Populate(msg, _testMessage.GetGenerationSettings(_testMessage.rectTransform.rect.size)); + _testMessage.text = origMsg; + _testMessage.cachedTextGenerator.Populate(origMsg, _testMessage.GetGenerationSettings(_testMessage.rectTransform.rect.size)); yield return null; + Dictionary openTags = new Dictionary(); for (int i = 0; i < _testMessage.cachedTextGenerator.lineCount; i++) { int index = ChatConfig.Instance.ReverseChatOrder ? _testMessage.cachedTextGenerator.lineCount - 1 - i : i; - msg = _testMessage.text.Substring(_testMessage.cachedTextGenerator.lines[index].startCharIdx); - if(msg.IsAllWhitespace()) + string msg; + if (index < _testMessage.cachedTextGenerator.lineCount - 1) + msg = _testMessage.text.Substring(_testMessage.cachedTextGenerator.lines[index].startCharIdx, _testMessage.cachedTextGenerator.lines[index + 1].startCharIdx - _testMessage.cachedTextGenerator.lines[index].startCharIdx); + else + msg = _testMessage.text.Substring(_testMessage.cachedTextGenerator.lines[index].startCharIdx); + + if (msg.IsAllWhitespace()) { continue; } - if (index < _testMessage.cachedTextGenerator.lineCount - 1) - msg = msg.Substring(0, _testMessage.cachedTextGenerator.lines[index + 1].startCharIdx - _testMessage.cachedTextGenerator.lines[index].startCharIdx); + if(openTags.Count > 0) + { + foreach(var tag in openTags.ToArray()) + { + msg = msg.Insert(0, $"<{tag.Key}{(tag.Value != null? $"={tag.Value}" : "")}>"); + var closingTag = $""; + if (msg.Contains(closingTag)) + { + openTags.Remove(tag.Key); + } + else + { + msg += closingTag; + } + } + } + + var matches = _htmlTagRegex.Matches(msg).Cast().Reverse(); + foreach (Match m in matches) + { + var tag = m.Groups["Tag"].Value; + if (openTags.ContainsKey(tag)) + continue; - // Italicize action messages and make the whole message the color of the users name - if (messageInfo.isActionMessage) - msg = $"{msg}"; + var closingTag = $""; + if (msg.Contains(closingTag)) + continue; + + string value = null; + if (m.Groups["Value"].Success) + { + value = m.Groups["Value"].Value; + } + openTags.Add(tag, value); + msg += closingTag; + } currentMessage = _chatMessages.Dequeue(); currentMessage.hasRendered = false; diff --git a/EnhancedStreamChat/Chat/ChatMessage.cs b/EnhancedStreamChat/Chat/ChatMessage.cs index 08eb548..7038e51 100644 --- a/EnhancedStreamChat/Chat/ChatMessage.cs +++ b/EnhancedStreamChat/Chat/ChatMessage.cs @@ -17,7 +17,6 @@ public class ChatMessage public GenericChatMessage origMessage; public List parsedEmotes = new List(); public List parsedBadges = new List(); - public bool isActionMessage = false; public ChatMessage(string msg, GenericChatMessage messageInfo) { diff --git a/EnhancedStreamChat/Chat/MessageParser.cs b/EnhancedStreamChat/Chat/MessageParser.cs index 701f076..531f218 100644 --- a/EnhancedStreamChat/Chat/MessageParser.cs +++ b/EnhancedStreamChat/Chat/MessageParser.cs @@ -287,11 +287,17 @@ public static async void Parse(ChatMessage newChatMessage) badgeStr.Append("\u00A0"); badgeStr.Append(newChatMessage.displayMsg); + // Italicize action messages and make the whole message the color of the users name + if (isActionMessage) { + badgeStr.Insert(0, $""); + badgeStr.Append(""); + } + // Finally, store our final message, parsedEmotes and parsedBadges; then render the message newChatMessage.displayMsg = badgeStr.ToString(); newChatMessage.parsedEmotes = parsedEmotes; newChatMessage.parsedBadges = parsedBadges; - newChatMessage.isActionMessage = isActionMessage; + ChatHandler.RenderQueue.Enqueue(newChatMessage); } }; diff --git a/EnhancedStreamChat/Plugin.cs b/EnhancedStreamChat/Plugin.cs index 5c307fc..c2e1ddc 100644 --- a/EnhancedStreamChat/Plugin.cs +++ b/EnhancedStreamChat/Plugin.cs @@ -22,7 +22,7 @@ public class Plugin : IPlugin { public static readonly string ModuleName = "Enhanced Stream Chat"; public string Name => ModuleName; - public string Version => "2.1.1"; + public string Version => "2.1.2"; public static Plugin Instance { get; private set; } diff --git a/EnhancedStreamChat/Properties/AssemblyInfo.cs b/EnhancedStreamChat/Properties/AssemblyInfo.cs index 597bec8..b555997 100644 --- a/EnhancedStreamChat/Properties/AssemblyInfo.cs +++ b/EnhancedStreamChat/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.1.1.0")] -[assembly: AssemblyFileVersion("2.1.1.0")] +[assembly: AssemblyVersion("2.1.2.0")] +[assembly: AssemblyFileVersion("2.1.2.0")] diff --git a/StreamCore b/StreamCore index d48ecef..857de2d 160000 --- a/StreamCore +++ b/StreamCore @@ -1 +1 @@ -Subproject commit d48ecef99ee7b003e379939333898c2b4e9ade0d +Subproject commit 857de2dff5e032abcbdc95f520f7136c48c308c6