diff --git a/Assets/HOTK/Twitch/TwitchChatTester.cs b/Assets/HOTK/Twitch/TwitchChatTester.cs index 444a5af..f693873 100644 --- a/Assets/HOTK/Twitch/TwitchChatTester.cs +++ b/Assets/HOTK/Twitch/TwitchChatTester.cs @@ -46,8 +46,6 @@ public TwitchIRC IRC } private TwitchIRC _irc; - private readonly Dictionary _userColors = new Dictionary(); - private readonly List _userChat = new List(); private bool _connected; @@ -132,38 +130,27 @@ private void OnChatMsg(string msg) switch (channel) { case "System-Green": - AddMsg(nickname, ColorToHex(new Color(0f, 1f, 0f)), chat); + AddMsg(nickname, TwitchIRC.ColorToHex(new Color(0f, 1f, 0f)), chat); break; case "System-Red": - AddMsg(nickname, ColorToHex(new Color(1f, 0f, 0f)), chat); + AddMsg(nickname, TwitchIRC.ColorToHex(new Color(1f, 0f, 0f)), chat); break; case "System-Blue": - AddMsg(nickname, ColorToHex(new Color(0f, 0.4f, 1f)), chat); + AddMsg(nickname, TwitchIRC.ColorToHex(new Color(0f, 0.4f, 1f)), chat); break; case "System-Yellow": - AddMsg(nickname, ColorToHex(new Color(1f, 1f, 0f)), chat); + AddMsg(nickname, TwitchIRC.ColorToHex(new Color(1f, 1f, 0f)), chat); break; case "System-Purple": - AddMsg(nickname, ColorToHex(new Color(1f, 0f, 1f)), chat); + AddMsg(nickname, TwitchIRC.ColorToHex(new Color(1f, 0f, 1f)), chat); break; default: - AddMsg(nickname, ColorToHex(new Color(1f, 1f, 1f)), chat); + AddMsg(nickname, TwitchIRC.ColorToHex(new Color(1f, 1f, 1f)), chat); break; } break; case "PRIVMSG": - nickname = FirstLetterToUpper(nickname); - if (!_userColors.ContainsKey(nickname)) - { - var r = Mathf.Max(0.25f, Random.value); - var g = Mathf.Max(0.25f, Random.value); - var b = Mathf.Max(0.25f, Random.value); - _userColors.Add(nickname, ColorToHex(new Color(r, g, b))); - } - - string hex; - if (_userColors.TryGetValue(nickname, out hex)) - AddMsg(nickname, hex, chat); + AddMsg(FirstLetterToUpper(nickname), TwitchIRC.GetUserColor(nickname), chat); break; } } @@ -188,50 +175,29 @@ private void WordWrapText(List messages) var lines = new List(); TextMesh.text = ""; var ren = TextMesh.GetComponent(); - var rowLimit = 0.9f; //find the sweet spot + var rowLimit = 0.975f; //find the sweet spot foreach (var m in messages) { - TextMesh.text = string.Format("{0}: ", m.Name); - var builder = string.Format("{1}: ", m.Color, m.Name); + TextMesh.text = string.Format("{1}: ", m.Color, m.Name); + string builder = ""; var parts = m.Message.Split(' '); foreach (var t in parts) { + builder = TextMesh.text; TextMesh.text += t + " "; if (ren.bounds.extents.x > rowLimit) { lines.Add(builder.TrimEnd() + System.Environment.NewLine); - TextMesh.text = ""; - builder = ""; + TextMesh.text = t + " "; } - builder += t + " "; + builder = TextMesh.text; } - - if (builder != "") - lines.Add(builder.TrimEnd() + System.Environment.NewLine); + lines.Add(builder.TrimEnd() + System.Environment.NewLine); } TextMesh.text = lines.Aggregate("", (current, t) => current + t); } - public static string ColorToHex(Color32 color) - { - return color.r.ToString("X2") + color.g.ToString("X2") + color.b.ToString("X2"); - } - - public static Color HexToColor(string hex) - { - hex = hex.Replace("0x", "");//in case the string is formatted 0xFFFFFF - hex = hex.Replace("#", "");//in case the string is formatted #FFFFFF - byte a = 255;//assume fully visible unless specified in hex - var r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); - var g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); - var b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); - //Only use alpha if the string has enough characters - if (hex.Length == 8) - a = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); - return new Color32(r, g, b, a); - } - public static string FirstLetterToUpper(string str) { if (str == null) diff --git a/Assets/HOTK/Twitch/TwitchIRC.cs b/Assets/HOTK/Twitch/TwitchIRC.cs index dde2fd7..65a5e62 100644 --- a/Assets/HOTK/Twitch/TwitchIRC.cs +++ b/Assets/HOTK/Twitch/TwitchIRC.cs @@ -6,6 +6,7 @@ public class TwitchIRC : MonoBehaviour { + public const string DefaultChatNameColor = "#FFFFFFFF"; public string Oauth; public string NickName; public string ChannelName; @@ -21,7 +22,10 @@ public class MsgEvent : UnityEngine.Events.UnityEvent { } private readonly Queue _commandQueue = new Queue(); private readonly List _recievedMsgs = new List(); private System.Threading.Thread _inProc, _outProc; - + + private static readonly Dictionary UserColors = new Dictionary(); + private static System.Random Random = new System.Random(); + private bool _connected; private bool _loggedin; @@ -32,7 +36,7 @@ public void StartIRC() sock.Connect(Server, Port); if (!sock.Connected) { - Debug.Log("Failed to connect!"); + ToNotice("System", "Failed to connect!", NoticeColor.Red); return; } var networkStream = sock.GetStream(); @@ -54,7 +58,7 @@ public void StartIRC() _inProc.Start(); CancelInvoke("CheckConnection"); - Invoke("CheckConnection", 5f); + Invoke("CheckConnection", 10f); } private void CheckConnection() @@ -77,48 +81,109 @@ private void IRCInputProcedure(System.IO.TextReader input, System.Net.Sockets.Ne { while (!_stopThreads) { - if (!networkStream.DataAvailable) + try { - Thread.Sleep(20); - continue; - } + if (!networkStream.DataAvailable) + { + Thread.Sleep(20); + continue; + } - _buffer = input.ReadLine(); - if (_buffer == null) continue; - var tokens = _buffer.Split(' '); - switch (tokens[1]) - { - case "PRIVMSG": - case "NOTICE": - lock (_recievedMsgs) - { - _recievedMsgs.Add(_buffer); - } - break; - case "001": - lock (_recievedMsgs) - { - _recievedMsgs.Add(ToTwitchNotice("Logged in! Connecting to chat..")); - _loggedin = true; - } - SendCommand("JOIN #" + ChannelName); - break; - case "JOIN": - lock (_recievedMsgs) - { - _recievedMsgs.Add(ToTwitchNotice(string.Format("Connected to {0}!", tokens[2]))); - _connected = true; - } - break; - default: - if (_buffer.StartsWith("PING ")) + _buffer = input.ReadLine(); + if (_buffer == null) continue; + //Debug.Log(_buffer); + + string[] tokens; + string message; + if (_buffer.StartsWith("@")) + { + var split = _buffer.IndexOf(' '); + var userstate = _buffer.Substring(0, split); + message = _buffer.Substring(split + 1); + tokens = message.Split(' '); + + var username = tokens[0].Split('!')[0].Substring(1); + var keys = userstate.Split(';'); + + foreach (var k in keys) { - SendCommand(_buffer.Replace("PING", "PONG")); + if (k.StartsWith("color=")) + { + if (GetUserColor(username) != DefaultChatNameColor) continue; + var color = (k != "color=") ? k.Substring(7) : null; + if (string.IsNullOrEmpty(color)) + { + var r = Mathf.Max(0.25f, Random.Next(0, 100)/100f); + var g = Mathf.Max(0.25f, Random.Next(0, 100)/100f); + var b = Mathf.Max(0.25f, Random.Next(0, 100)/100f); + color = ColorToHex(new Color(r, g, b)); + } + lock (UserColors) + { + UserColors.Add(username, color); + } + } } - break; + } + else + { + message = _buffer; + tokens = _buffer.Split(' '); + } + + switch (tokens[1]) + { + case "PRIVMSG": + case "NOTICE": + lock (_recievedMsgs) + { + _recievedMsgs.Add(message); + } + break; + case "JOIN": + lock (_recievedMsgs) + { + _recievedMsgs.Add(ToTwitchNotice(string.Format("Connected to {0}!", tokens[2]))); + _connected = true; + } + break; + case "001": + lock (_recievedMsgs) + { + _recievedMsgs.Add(ToTwitchNotice("Logged in! Connecting to chat..")); + _loggedin = true; + } + SendCommand("CAP REQ :twitch.tv/tags"); + SendCommand("CAP REQ :twitch.tv/commands"); + SendCommand("JOIN #" + ChannelName); + break; + case "CAP": + lock (_recievedMsgs) + { + _recievedMsgs.Add(ToTwitchNotice("Acknowledging Client Capabilities!")); + _connected = true; + } + break; + case "USERSTATE": + break; + default: + if (_buffer.StartsWith("PING ")) + { + SendCommand(_buffer.Replace("PING", "PONG")); + } + break; + } + } + catch (Exception e) + { + lock (_recievedMsgs) + { + _recievedMsgs.Add(ToNotice("EXCEPTION", e.ToString(), NoticeColor.Red)); + } } } } + private void IRCOutputProcedure(System.IO.TextWriter output) { System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch(); @@ -202,6 +267,35 @@ public static string ToNotice(string nickname, string msgIn, NoticeColor colorEn return string.Format(":{0} NOTICE {1} :{2}", nickname, NoticeColorToString(colorEnum), msgIn); } + public static string GetUserColor(string username) + { + lock (UserColors) + { + string hex; + return UserColors.TryGetValue(username, out hex) ? hex : DefaultChatNameColor; + } + } + + public static string ColorToHex(Color32 color) + { + return color.r.ToString("X2") + color.g.ToString("X2") + color.b.ToString("X2"); + } + + public static Color HexToColor(string hex) + { + hex = hex.Replace("0x", "");//in case the string is formatted 0xFFFFFF + hex = hex.Replace("#", "");//in case the string is formatted #FFFFFF + byte a = 255;//assume fully visible unless specified in hex + var r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber); + var g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber); + var b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); + //Only use alpha if the string has enough characters + if (hex.Length == 8) + a = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber); + return new Color32(r, g, b, a); + } + + public static string NoticeColorToString(NoticeColor colorEnum) { switch (colorEnum)