public static void LoadUsersEmotes() { //LoadEmotesFromApi(); if (loadEmotes == false) { loadEmotes = true; bool isalreadyjoined = false; foreach (var channel in TwitchChannel.Channels) { if (channel.Name.Equals(Account.Username)) { isalreadyjoined = true; break; } } //part and join the users channel if (isalreadyjoined) { Client.WriteConnection.WriteLine("PART #" + Account.Username); } Client.WriteConnection.WriteLine("JOIN #" + Account.Username); if (!isalreadyjoined) { Client.WriteConnection.WriteLine("PART #" + Account.Username); } } Emotes.TriggerEmotesLoaded(); }
public static void LoadUsersEmotes() { try { string userid = Account.UserId, oauth = Account.OauthToken; var request = WebRequest.Create( $"https://api.twitch.tv/kraken/users/{userid}/emotes"); if (AppSettings.IgnoreSystemProxy) { request.Proxy = null; } ((HttpWebRequest)request).Accept = "application/vnd.twitchtv.v5+json"; request.Headers["Client-ID"] = $"{DefaultClientID}"; request.Headers["Authorization"] = $"OAuth {oauth}"; using (var response = request.GetResponse()) { using (var stream = response.GetResponseStream()) { dynamic json = new JsonParser().Parse(stream); //GuiEngine.Current.log(JsonConvert.SerializeObject(json)); Emotes.TwitchEmotes.Clear(); foreach (var set in json["emoticon_sets"]) { int setID; int.TryParse(set.Key, out setID); foreach (var emote in set.Value) { string id; id = emote["id"]; string code = Emotes.GetTwitchEmoteCodeReplacement(emote["code"]); Emotes.TwitchEmotes[code] = new TwitchEmoteValue { ID = id, Set = setID, ChannelName = "<unknown>" }; } } } response.Close(); } } catch (Exception e) { GuiEngine.Current.log("Generic Exception Handler: " + e.ToString()); } Emotes.TriggerEmotesLoaded(); }
public static void LoadEmotesFromApi() { try { string userid = Account.UserId, oauth = Account.OauthToken; var request = WebRequest.Create($"https://api.twitch.tv/helix/users/{userid}/emotes"); if (AppSettings.IgnoreSystemProxy) { request.Proxy = null; } ((HttpWebRequest)request).Accept = "application/vnd.twitchtv.v5+json"; request.Headers["Client-ID"] = $"{DefaultClientID}"; request.Headers["Authorization"] = $"OAuth {oauth}"; using (var response = request.GetResponse()) { using (var stream = response.GetResponseStream()) { dynamic json = new JsonParser().Parse(stream); foreach (var set in json["emoticon_sets"]) { int.TryParse(set.Key, out int setID); foreach (var emote in set.Value) { string id = emote["id"]; string code = Emotes.GetTwitchEmoteCodeReplacement(emote["code"]); Emotes.RecentlyUsedEmotes.TryRemove(code, out LazyLoadedImage image); if (!Emotes.TwitchEmotes.ContainsKey(code)) { Emotes.TwitchEmotes[code] = new TwitchEmoteValue { ID = id, Set = setID, OwnerID = set.Key, ChannelName = "" }; } } } } } } catch (Exception e) { GuiEngine.Current.log("Generic Exception Handler: " + e.ToString()); } }
public Message(IrcMessage data, TwitchChannel channel, bool enableTimestamp = true, bool enablePingSound = true, bool isReceivedWhisper = false, bool isSentWhisper = false, bool includeChannel = false, bool isPastMessage = false) { //var w = Stopwatch.StartNew(); ParseTime = DateTime.Now; Channel = channel; List <Word> words = new List <Word>(); string value; string text = data.Params ?? ""; Params = text; Username = data.PrefixNickname ?? ""; if (string.IsNullOrWhiteSpace(Username)) { string login; if (data.Tags.TryGetValue("login", out login)) { Username = login; } } if (data.Tags.TryGetValue("user-id", out value)) { UserId = value; } var slashMe = false; // Handle /me messages if (text.Length > 8 && text.StartsWith("\u0001ACTION ")) { text = text.Substring("\u0001ACTION ".Length, text.Length - "\u0001ACTION ".Length - 1); slashMe = true; } if (data.Tags.TryGetValue("display-name", out value)) { DisplayName = value; } // Username if (string.IsNullOrWhiteSpace(DisplayName)) { DisplayName = Username; } // Highlights if (!IrcManager.Account.IsAnon) { if ((AppSettings.ChatEnableHighlight || AppSettings.ChatEnableHighlightSound || AppSettings.ChatEnableHighlightTaskbar) && Username != IrcManager.Account.Username.ToLower()) { if (!AppSettings.HighlightIgnoredUsers.ContainsKey(Username)) { if (!IrcManager.IgnoredUsers.Contains(Username)) { if (AppSettings.CustomHighlightRegex != null && AppSettings.CustomHighlightRegex.IsMatch(text)) { if (AppSettings.ChatEnableHighlight) { HighlightType = HighlightType.Highlighted; } if (EnablePings && enablePingSound) { if (AppSettings.ChatEnableHighlightSound) { GuiEngine.Current.PlaySound(NotificationSound.Ping); } if (AppSettings.ChatEnableHighlightTaskbar) { GuiEngine.Current.FlashTaskbar(); } } } else if (AppSettings.HighlightUserNames != null && (AppSettings.HighlightUserNames.ContainsKey(Username) || AppSettings.HighlightUserNames.ContainsKey(DisplayName))) { if (AppSettings.ChatEnableHighlight) { HighlightType = HighlightType.UsernameHighlighted; } } } } } } // Tags if (data.Tags.TryGetValue("color", out value)) { try { if (value.Length == 7 && value[0] == '#') { UsernameColor = HSLColor.FromRGB(Convert.ToInt32(value.Substring(1), 16)); } } catch { } } // Bits string bits = null; data.Tags.TryGetValue("bits", out bits); // Add timestamp string timestampTag; string timestamp = null; string tmiTimestamp; long tmiTimestampInt; DateTime timestampTime = DateTime.Now; if (data.Tags.TryGetValue("tmi-sent-ts", out tmiTimestamp)) { if (long.TryParse(tmiTimestamp, out tmiTimestampInt)) { var dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); var time = dtDateTime.AddSeconds(tmiTimestampInt / 1000).ToLocalTime(); timestampTime = time; //timestamp = time.ToString(AppSettings.ChatShowTimestampSeconds ? "HH:mm:ss" : "HH:mm"); //enableTimestamp = true; } } else if (data.Tags.TryGetValue("timestamp-utc", out timestampTag)) { DateTime time; if (DateTime.TryParseExact(timestampTag, "yyyyMMdd-HHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out time)) { timestampTime = time; //timestamp = time.ToString(AppSettings.ChatShowTimestampSeconds ? "HH:mm:ss" : "HH:mm"); //enableTimestamp = true; } } if (enableTimestamp && AppSettings.ChatShowTimestamps) { string timestampFormat; if (AppSettings.ChatShowTimestampSeconds) { timestampFormat = AppSettings.TimestampsAmPm ? "hh:mm:ss tt" : "HH:mm:ss"; } else { timestampFormat = AppSettings.TimestampsAmPm ? "hh:mm tt" : "HH:mm"; } timestamp = timestampTime.ToString(timestampFormat, enUS); words.Add(new Word { Type = SpanType.Text, Value = timestamp, Color = HSLColor.FromRGB(-8355712), Font = FontType.Small, CopyText = timestamp }); } // add channel name if (includeChannel) { words.Add(new Word { Type = SpanType.Text, Link = new Link(LinkType.ShowChannel, channel.Name), Value = "#" + channel.Name, Color = HSLColor.FromRGB(-8355712), Font = FontType.Medium, CopyText = "#" + channel.Name }); } // add moderation buttons if (channel.IsModOrBroadcaster && !string.Equals(IrcManager.Account.Username, Username)) { string badges; if (data.Tags.TryGetValue("badges", out badges)) { if (badges.Contains("broadcaster")) { goto aftermod; } if (badges.Contains("moderator") && !channel.IsBroadcaster) { goto aftermod; } } if (AppSettings.EnableBanButton) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = new LazyLoadedImage(GuiEngine.Current.GetImage(ImageType.Ban)), Link = new Link(LinkType.BanUser, Tuple.Create(Username, channel.Name)), Tooltip = "Ban User" }); } if (AppSettings.EnableTimeoutButton) { foreach (var amount in AppSettings.TimeoutButtons) { int tmpAmount = amount; string tooltip = ""; if (tmpAmount > 60 * 60 * 24) { tooltip += tmpAmount / (60 * 60 * 24) + " days "; tmpAmount %= 60 * 60 * 24; } if (tmpAmount > 60 * 60) { tooltip += tmpAmount / (60 * 60) + " hours "; tmpAmount %= 60 * 60; } if (tmpAmount > 60) { tooltip += tmpAmount / (60) + " mins "; tmpAmount %= 60; } if (tmpAmount > 0) { tooltip += tmpAmount + " secs "; } Image image; if (AppSettings.TimeoutButtons.Count > 1) { image = ((dynamic)GuiEngine.Current).GetImageForTimeout(amount); } else { image = GuiEngine.Current.GetImage(ImageType.Timeout); } words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = new LazyLoadedImage(image), Link = new Link(LinkType.TimeoutUser, Tuple.Create(Username, channel.Name, amount)), Tooltip = $"Timeout User ({tooltip.Trim()})" }); } } } aftermod: // get badges from tags if (data.Tags.TryGetValue("badges", out value)) { var badges = value.Split(','); LazyLoadedImage image; foreach (var badge in badges) { if (badge.StartsWith("subscriber/")) { try { int n = int.Parse(badge.Substring("subscriber/".Length)); Badges |= MessageBadges.Sub; image = channel.GetSubscriberBadge(n); if (image != null) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = image, Link = new Link(LinkType.Url, Channel.SubLink), Tooltip = image.Tooltip }); } else { image = GuiEngine.Current.GetBadge(badge); if (image != null) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = image, Link = new Link(LinkType.Url, image.click_url), Tooltip = image.Tooltip }); } } } catch { } } else if (badge.Equals("moderator/1") && channel.ModeratorBadge != null) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = channel.ModeratorBadge, Tooltip = channel.ModeratorBadge.Tooltip }); } else if (badge.StartsWith("bits/")) { try { int n = int.Parse(badge.Substring("bits/".Length)); image = channel.GetCheerBadge(n); if (image != null) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = image, Link = new Link(LinkType.Url, image.click_url), Tooltip = image.Tooltip }); } else { image = GuiEngine.Current.GetBadge(badge); if (image != null) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = image, Link = new Link(LinkType.Url, image.click_url), Tooltip = image.Tooltip }); } } } catch {} } else { image = GuiEngine.Current.GetBadge(badge); if (image != null) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = image, Link = new Link(LinkType.Url, image.click_url), Tooltip = image.Tooltip }); } } } } var messageUser = (isSentWhisper ? IrcManager.Account.Username + " -> " : ""); messageUser += DisplayName; if (!isReceivedWhisper && !isSentWhisper) { messageUser += (DisplayName.ToLower() != Username ? $" ({Username})" : ""); } if (isReceivedWhisper) { messageUser += " -> " + IrcManager.Account.Username; } if (!slashMe) { messageUser += ":"; } words.Add(new Word { Type = SpanType.Text, Value = messageUser, Color = UsernameColor, Font = FontType.MediumBold, Link = new Link(LinkType.UserInfo, new UserInfoData { UserName = Username, UserId = UserId, Channel = channel }), CopyText = messageUser }); var twitchEmotes = new List <Tuple <int, LazyLoadedImage> >(); if (data.Tags.TryGetValue("msg-id", out value)) { if (value.Contains("highlighted-message")) { if (!isPastMessage) { Message message; message = new Message("Highlighted Message", HSLColor.Gray, true) { HighlightType = HighlightType.HighlightedMessage }; channel.AddMessage(message); } HighlightType = HighlightType.HighlightedMessage; } } // Twitch Emotes if (AppSettings.ChatEnableTwitchEmotes && data.Tags.TryGetValue("emotes", out value)) { // 93064:0-6,8-14/80481:16-20,22-26 value.Split('/').Do(emote => { if (emote != "") { var x = emote.Split(':'); try { var id = x[0]; foreach (var y in x[1].Split(',')) { var coords = y.Split('-'); var index = int.Parse(coords[0]); var name = text.Substring(index, int.Parse(coords[1]) - index + 1); // ignore ignored emotes if (!AppSettings.ChatIgnoredEmotes.ContainsKey(name)) { var e = Emotes.GetTwitchEmoteById(id, name); twitchEmotes.Add(Tuple.Create(index, e)); } } ; } catch (Exception e) { GuiEngine.Current.log("Generic Exception Handler: " + " " + emote + " " + x[0] + " " + e.ToString()); } } }); twitchEmotes.Sort((e1, e2) => e1.Item1.CompareTo(e2.Item1)); } var i = 0; var currentTwitchEmoteIndex = 0; var currentTwitchEmote = twitchEmotes.FirstOrDefault(); foreach (var split in text.Split(' ')) { if (currentTwitchEmote != null) { if (currentTwitchEmote.Item1 == i) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = currentTwitchEmote.Item2, Link = new Link(LinkType.Url, currentTwitchEmote.Item2.Url), Tooltip = currentTwitchEmote.Item2.Tooltip, CopyText = currentTwitchEmote.Item2.Name }); i += split.Length + 1; currentTwitchEmoteIndex++; currentTwitchEmote = currentTwitchEmoteIndex == twitchEmotes.Count ? null : twitchEmotes[currentTwitchEmoteIndex]; continue; } } foreach (var o in Emojis.ParseEmojis(split)) { var s = o as string; if (s != null) { Match m = Regex.Match(s, "([A-Za-z]+)([1-9][0-9]*)"); if (bits != null && m.Success) { try{ int cheer; string prefix = m.Groups[1].Value; string getcheer = m.Groups[2].Value; if (int.TryParse(getcheer, out cheer)) { string color; string bitsLink; bool found = false; HSLColor bitsColor; LazyLoadedImage emote; if (!(found = channel.GetCheerEmote(prefix, cheer, !GuiEngine.Current.IsDarkTheme, out emote, out color))) { found = GuiEngine.Current.GetCheerEmote(prefix, cheer, !GuiEngine.Current.IsDarkTheme, out emote, out color); } if (found) { bitsColor = HSLColor.FromRGBHex(color); bitsLink = emote.Url; words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = Emotes.MiscEmotesByUrl.GetOrAdd(bitsLink, url => new LazyLoadedImage { Name = "cheer", Url = url, Tooltip = "Twitch Bits Badge" }), Tooltip = "Twitch Bits Donation", CopyText = s, Link = new Link(LinkType.Url, "https://blog.twitch.tv/introducing-cheering-celebrate-together-da62af41fac6") }); words.Add(new Word { Type = SpanType.Text, Value = getcheer, Font = FontType.Small, Color = bitsColor }); } continue; } } catch (Exception e) { GuiEngine.Current.log("Generic Exception Handler: " + e.ToString()); } } LazyLoadedImage bttvEmote; if (!AppSettings.ChatIgnoredEmotes.ContainsKey(s) && (AppSettings.ChatEnableBttvEmotes && (Emotes.BttvGlobalEmotes.TryGetValue(s, out bttvEmote) || channel.BttvChannelEmotes.TryGetValue(s, out bttvEmote)) || (AppSettings.ChatEnableFfzEmotes && (Emotes.FfzGlobalEmotes.TryGetValue(s, out bttvEmote) || channel.FfzChannelEmotes.TryGetValue(s, out bttvEmote))))) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = bttvEmote, Color = slashMe ? UsernameColor : new HSLColor?(), Tooltip = bttvEmote.Tooltip, Link = new Link(LinkType.Url, bttvEmote.Url), CopyText = bttvEmote.Name }); } else { var link = _matchLink(split); words.Add(new Word { Type = SpanType.Text, Value = s.Replace('﷽', '?'), Color = slashMe ? UsernameColor : (link == null ? new HSLColor?() : HSLColor.FromRGB(-8355712)), Link = link == null ? null : new Link(LinkType.Url, link), CopyText = s }); } } else { var e = o as LazyLoadedImage; if (e != null) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = e, Link = new Link(LinkType.Url, e.Url), Tooltip = e.Tooltip, CopyText = e.Name, HasTrailingSpace = e.HasTrailingSpace }); } } } var splitLength = 0; for (var j = 0; j < split.Length; j++) { splitLength++; if (char.IsHighSurrogate(split[j])) { j += 1; } } i += splitLength + 1; } Words = words; RawMessage = text; if (!isReceivedWhisper && AppSettings.HighlightIgnoredUsers.ContainsKey(Username)) { HighlightTab = false; } //w.Stop(); //Console.WriteLine("Message parsed in " + w.Elapsed.TotalSeconds.ToString("0.000000") + " seconds"); }
// Connection public static void Connect() { Disconnect(); // Login string username = Account.Username, oauth = Account.OauthToken; try { if (Account.IsAnon) { if (AppSettings.SelectedUser != "") { AppSettings.SelectedUser = ""; AppSettings.Save(); } } else { if (!string.Equals(username, AppSettings.SelectedUser)) { AppSettings.SelectedUser = username; AppSettings.Save(); } } } catch { } LoggedIn?.Invoke(null, EventArgs.Empty); AppSettings.UpdateCustomHighlightRegex(); // fetch ignored users if (!Account.IsAnon) { Task.Run(() => { try { var limit = 100; var count = 0; string nextLink = $"https://api.twitch.tv/kraken/users/{username}/blocks?limit={limit}&client_id={Account.ClientId}"; var request = WebRequest.Create(nextLink + $"&oauth_token={oauth}"); using (var response = request.GetResponse()) using (var stream = response.GetResponseStream()) { dynamic json = new JsonParser().Parse(stream); dynamic _links = json["_links"]; nextLink = _links["next"]; dynamic blocks = json["blocks"]; count = blocks.Count; foreach (var block in blocks) { dynamic user = block["user"]; string name = user["name"]; string display_name = user["display_name"]; twitchBlockedUsers[name] = null; } } } catch { } }); } // fetch available twitch emotes if (!Account.IsAnon) { Task.Run(() => { try { var request = WebRequest.Create( $"https://api.twitch.tv/kraken/users/{username}/emotes?oauth_token={oauth}&client_id={Account.ClientId}"); using (var response = request.GetResponse()) using (var stream = response.GetResponseStream()) { dynamic json = new JsonParser().Parse(stream); Emotes.TwitchEmotes.Clear(); foreach (var set in json["emoticon_sets"]) { int setID; int.TryParse(set.Key, out setID); foreach (var emote in set.Value) { int id; int.TryParse(emote["id"], out id); string code = Emotes.GetTwitchEmoteCodeReplacement(emote["code"]); Emotes.TwitchEmotes[code] = new TwitchEmoteValue { ID = id, Set = setID, ChannelName = "<unknown>" }; } } } } catch { } Emotes.TriggerEmotesLoaded(); }); } // connect read Task.Run(() => { Client = new IrcClient(Account.IsAnon); Client.ReadConnection.Connected += (s, e) => { foreach (var channel in TwitchChannel.Channels) { Client.ReadConnection.WriteLine("JOIN #" + channel.Name); } Connected?.Invoke(null, EventArgs.Empty); }; Client.ReadConnection.Disconnected += (s, e) => { Disconnected?.Invoke(null, EventArgs.Empty); }; if (!Account.IsAnon) { Client.WriteConnection.Connected += (s, e) => { foreach (var channel in TwitchChannel.Channels) { Client.WriteConnection.WriteLine("JOIN #" + channel.Name); } }; } Client.Connect(username, (oauth.StartsWith("oauth:") ? oauth : "oauth:" + oauth)); Client.ReadConnection.MessageReceived += ReadConnection_MessageReceived; Client.WriteConnection.MessageReceived += WriteConnection_MessageReceived; }); // secret telemetry, please ignore :) // anonymously count user on fourtf.com with the first 32 characters of a sha 256 hash of the username if (!Account.IsAnon) { Task.Run(() => { try { string hash; using (var sha = SHA256.Create()) { hash = string.Join("", sha .ComputeHash(Encoding.UTF8.GetBytes(username)) .Select(item => item.ToString("x2"))); } hash = hash.Remove(32); var request = WebRequest.Create($"https://fourtf.com/chatterino/countuser.php?hash={hash}"); using (var response = request.GetResponse()) using (response.GetResponseStream()) { } } catch { } }); } }
private static void UpdateEmotes(IrcMessage msg) { if (loadEmotes && msg.Tags.TryGetValue("emote-sets", out string value)) { //GuiEngine.Current.log("sets: " + value); string[] emote_sets = value.Split(','); string temp = ""; int count = 1; string emote_set; //split into groups of 10 since thats the limit you can request at once from the api https://api.twitch.tv/helix/chat/emotes/set?emote_set_id= for (int i = 0; i < emote_sets.Length; i++) { emote_set = emote_sets[i]; if (count != 1) { temp += "&emote_set_id="; } temp += emote_set; if (count == 10 || i == (emote_sets.Length - 1)) { //api request try { var request = WebRequest.Create($"https://api.twitch.tv/helix/chat/emotes/set?emote_set_id={temp}"); request.Method = "GET"; //GuiEngine.Current.log("url : " + $"https://api.twitch.tv/helix/chat/emotes/set?emote_set_id={temp}"); if (AppSettings.IgnoreSystemProxy) { request.Proxy = null; } ((HttpWebRequest)request).Accept = "application/json"; request.Headers["Client-ID"] = $"{Account.ClientId}"; request.Headers["Authorization"] = $"Bearer {Account.OauthToken}"; //GuiEngine.Current.log("body: Client-ID: " + Account.ClientId + " Authorization: Bearer " + Account.OauthToken); using (var response = request.GetResponse()) { using (var stream = response.GetResponseStream()) { dynamic json = new JsonParser().Parse(stream); if (json != null) { dynamic data = json["data"]; foreach (var emote in data) { string id = emote["id"]; string owner_id = emote["owner_id"]; if (owner_id == "twitch") { owner_id = "0"; } string emote_type = emote["emote_type"]; string name = emote["name"]; int emote_set_id = Int32.Parse(emote["emote_set_id"]); //GuiEngine.Current.log("name : " + name + " set_id " + emote_set_id + " owner_id " + owner_id + " emotetype " + emote_type ); string code = Emotes.GetTwitchEmoteCodeReplacement(name); Emotes.RecentlyUsedEmotes.TryRemove(code, out LazyLoadedImage image); if (!emote_type.Equals("limitedtime") && !emote_type.Equals("owl2019")) { Emotes.TwitchEmotes[code] = new TwitchEmoteValue { OwnerID = owner_id, Type = emote_type, Name = name, ID = id, Set = emote_set_id, ChannelName = "" }; } } } } response.Close(); } } catch (Exception exc) { GuiEngine.Current.log("Generic Exception Handler: " + exc.ToString()); } temp = ""; count = 1; } else { count++; } } } loadEmotes = false; Emotes.TriggerEmotesLoaded(); }
public void ReloadEmotes() { var channelName = Name; var bttvChannelEmotesCache = Path.Combine(Util.GetUserDataPath(), "Cache", $"bttv_channel_{channelName}"); var ffzChannelEmotesCache = Path.Combine(Util.GetUserDataPath(), "Cache", $"ffz_channel_{channelName}"); // bttv channel emotes Task.Run(() => { try { var parser = new JsonParser(); //if (!File.Exists(bttvChannelEmotesCache)) { try { if (Util.IsLinux) { Util.LinuxDownloadFile("https://api.betterttv.net/2/channels/" + channelName, bttvChannelEmotesCache); } else { using (var webClient = new WebClient()) using (var readStream = webClient.OpenRead("https://api.betterttv.net/2/channels/" + channelName)) using (var writeStream = File.OpenWrite(bttvChannelEmotesCache)) { readStream.CopyTo(writeStream); } } } catch (Exception e) { e.Message.Log("emotes"); } } using (var stream = File.OpenRead(bttvChannelEmotesCache)) { dynamic json = parser.Parse(stream); var template = "https:" + json["urlTemplate"]; //{{id}} {{image}} BttvChannelEmotes.Clear(); foreach (var e in json["emotes"]) { string id = e["id"]; string code = e["code"]; string channel = e["channel"]; LazyLoadedImage emote; if (Emotes.BttvChannelEmotesCache.TryGetValue(id, out emote)) { BttvChannelEmotes[code] = emote; } else { string imageType = e["imageType"]; string url = template.Replace("{{id}}", id); double scale; url = Emotes.GetBttvEmoteLink(url, out scale); Emotes.BttvChannelEmotesCache[id] = BttvChannelEmotes[code] = new LazyLoadedImage { Name = code, Url = url, Tooltip = code + "\nBetterTTV Channel Emote\nChannel: " + channel, Scale = scale, IsEmote = true }; } } } updateEmoteNameList(); } catch { } }); // ffz channel emotes Task.Run(() => { try { var parser = new JsonParser(); //if (!File.Exists(ffzChannelEmotesCache)) { try { if (Util.IsLinux) { Util.LinuxDownloadFile("https://api.frankerfacez.com/v1/room/" + channelName, ffzChannelEmotesCache); } else { using (var webClient = new WebClient()) using (var readStream = webClient.OpenRead("https://api.frankerfacez.com/v1/room/" + channelName)) using (var writeStream = File.OpenWrite(ffzChannelEmotesCache)) { readStream.CopyTo(writeStream); } } } catch (Exception e) { e.Message.Log("emotes"); } } using (var stream = File.OpenRead(ffzChannelEmotesCache)) { dynamic json = parser.Parse(stream); dynamic room = json["room"]; try { object moderator; if (room.TryGetValue("moderator_badge", out moderator)) { if (moderator != null && !string.IsNullOrWhiteSpace((string)moderator)) { var url = "https:" + (moderator as string); ModeratorBadge = new LazyLoadedImage { Url = url, Tooltip = "custom moderator badge\nFFZ", LoadAction = () => { try { object img; var request = WebRequest.Create(url); if (AppSettings.IgnoreSystemProxy) { request.Proxy = null; } using (var response = request.GetResponse()) using (var s = response.GetResponseStream()) { img = GuiEngine.Current.ReadImageFromStream(s); } GuiEngine.Current.FreezeImage(img); return(GuiEngine.Current.DrawImageBackground(img, HSLColor.FromRGB(0x45A41E))); } catch { return(null); } } }; } } } catch { } dynamic sets = json["sets"]; FfzChannelEmotes.Clear(); foreach (var set in sets.Values) { string title = set["title"]; dynamic emoticons = set["emoticons"]; foreach (LazyLoadedImage emote in Emotes.GetFfzEmoteFromDynamic(emoticons, false)) { FfzChannelEmotes[emote.Name] = emote; } } } updateEmoteNameList(); } catch { } }); }
public Message(IrcMessage data, TwitchChannel channel, bool enableTimestamp = true, bool enablePingSound = true, bool isReceivedWhisper = false, bool isSentWhisper = false, bool includeChannel = false) { //var w = Stopwatch.StartNew(); ParseTime = DateTime.Now; Channel = channel; var words = new List <Word>(); var text = data.Params ?? ""; Username = data.PrefixNickname ?? ""; if (string.IsNullOrWhiteSpace(Username)) { string login; if (data.Tags.TryGetValue("login", out login)) { Username = login; } } var slashMe = false; // Handle /me messages if (text.Length > 8 && text.StartsWith("\u0001ACTION ")) { text = text.Substring("\u0001ACTION ".Length, text.Length - "\u0001ACTION ".Length - 1); slashMe = true; } // Highlights if (!IrcManager.Account.IsAnon) { if ((AppSettings.ChatEnableHighlight || AppSettings.ChatEnableHighlightSound || AppSettings.ChatEnableHighlightTaskbar) && Username != IrcManager.Account.Username.ToLower()) { if (!AppSettings.HighlightIgnoredUsers.ContainsKey(Username)) { if (!IrcManager.IgnoredUsers.Contains(Username)) { if (AppSettings.CustomHighlightRegex != null && AppSettings.CustomHighlightRegex.IsMatch(text)) { if (AppSettings.ChatEnableHighlight) { HighlightType = HighlightType.Highlighted; } if (EnablePings && enablePingSound) { if (AppSettings.ChatEnableHighlightSound) { GuiEngine.Current.PlaySound(NotificationSound.Ping); } if (AppSettings.ChatEnableHighlightTaskbar) { GuiEngine.Current.FlashTaskbar(); } } } } } } } // Tags string value; if (data.Tags.TryGetValue("color", out value)) { try { if (value.Length == 7 && value[0] == '#') { UsernameColor = HSLColor.FromRGB(Convert.ToInt32(value.Substring(1), 16)); } } catch { } } if (data.Tags.TryGetValue("display-name", out value)) { DisplayName = value; } // Bits string bits = null; data.Tags.TryGetValue("bits", out bits); // Add timestamp string timestampTag; string timestamp = null; string tmiTimestamp; long tmiTimestampInt; DateTime timestampTime = DateTime.Now; if (data.Tags.TryGetValue("tmi-sent-ts", out tmiTimestamp)) { if (long.TryParse(tmiTimestamp, out tmiTimestampInt)) { var dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); var time = dtDateTime.AddSeconds(tmiTimestampInt / 1000).ToLocalTime(); timestampTime = time; //timestamp = time.ToString(AppSettings.ChatShowTimestampSeconds ? "HH:mm:ss" : "HH:mm"); //enableTimestamp = true; } } else if (data.Tags.TryGetValue("timestamp-utc", out timestampTag)) { DateTime time; if (DateTime.TryParseExact(timestampTag, "yyyyMMdd-HHmmss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out time)) { timestampTime = time; //timestamp = time.ToString(AppSettings.ChatShowTimestampSeconds ? "HH:mm:ss" : "HH:mm"); //enableTimestamp = true; } } if (enableTimestamp && AppSettings.ChatShowTimestamps) { string timestampFormat; if (AppSettings.ChatShowTimestampSeconds) { timestampFormat = AppSettings.TimestampsAmPm ? "hh:mm:ss tt" : "HH:mm:ss"; } else { timestampFormat = AppSettings.TimestampsAmPm ? "hh:mm tt" : "HH:mm"; } timestamp = timestampTime.ToString(timestampFormat, enUS); words.Add(new Word { Type = SpanType.Text, Value = timestamp, Color = HSLColor.FromRGB(-8355712), Font = FontType.Small, CopyText = timestamp }); } // add channel name if (includeChannel) { words.Add(new Word { Type = SpanType.Text, Link = new Link(LinkType.ShowChannel, channel.Name), Value = "#" + channel.Name, Color = HSLColor.FromRGB(-8355712), Font = FontType.Medium, CopyText = "#" + channel.Name }); } // get badges from tags if (data.Tags.TryGetValue("badges", out value)) { var badges = value.Split(','); foreach (var badge in badges) { if (badge.StartsWith("bits/")) { int cheer; if (int.TryParse(badge.Substring("bits/".Length), out cheer)) { object image; if (cheer >= 1000000) { image = GuiEngine.Current.GetImage(ImageType.Cheer1000000); } else if (cheer >= 900000) { image = GuiEngine.Current.GetImage(ImageType.Cheer900000); } else if (cheer >= 800000) { image = GuiEngine.Current.GetImage(ImageType.Cheer800000); } else if (cheer >= 700000) { image = GuiEngine.Current.GetImage(ImageType.Cheer700000); } else if (cheer >= 600000) { image = GuiEngine.Current.GetImage(ImageType.Cheer600000); } else if (cheer >= 500000) { image = GuiEngine.Current.GetImage(ImageType.Cheer500000); } else if (cheer >= 400000) { image = GuiEngine.Current.GetImage(ImageType.Cheer400000); } else if (cheer >= 300000) { image = GuiEngine.Current.GetImage(ImageType.Cheer300000); } else if (cheer >= 200000) { image = GuiEngine.Current.GetImage(ImageType.Cheer200000); } else if (cheer >= 100000) { image = GuiEngine.Current.GetImage(ImageType.Cheer100000); } else if (cheer >= 10000) { image = GuiEngine.Current.GetImage(ImageType.Cheer10000); } else if (cheer >= 5000) { image = GuiEngine.Current.GetImage(ImageType.Cheer5000); } else if (cheer >= 1000) { image = GuiEngine.Current.GetImage(ImageType.Cheer1000); } else if (cheer >= 100) { image = GuiEngine.Current.GetImage(ImageType.Cheer100); } else { image = GuiEngine.Current.GetImage(ImageType.Cheer1); } words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = new LazyLoadedImage(image) { Scale = cheer > 100000 ? 0.25 : 1 }, Tooltip = "Twitch Cheer " + cheer }); } } else if (badge.StartsWith("subscriber/")) { try { var n = int.Parse(badge.Substring("subscriber/".Length)); Badges |= MessageBadges.Sub; var e = channel.GetSubscriberBadge(n); words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = e, Link = new Link(LinkType.Url, Channel.SubLink), Tooltip = e.Tooltip }); } catch { } } else { switch (badge) { case "staff/1": Badges |= MessageBadges.Staff; words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = new LazyLoadedImage(GuiEngine.Current.GetImage(ImageType.BadgeStaff)), Tooltip = "Twitch Staff" }); break; case "admin/1": Badges |= MessageBadges.Admin; words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = new LazyLoadedImage(GuiEngine.Current.GetImage(ImageType.BadgeAdmin)), Tooltip = "Twitch Admin" }); break; case "global_mod/1": Badges |= MessageBadges.GlobalMod; words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = GuiEngine.Current.GetImage(ImageType.BadgeGlobalmod), Tooltip = "Global Moderator" }); break; case "moderator/1": Badges |= MessageBadges.Mod; if (channel.ModeratorBadge == null) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = new LazyLoadedImage(GuiEngine.Current.GetImage(ImageType.BadgeModerator)), Tooltip = "Channel Moderator" }); } else { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = channel.ModeratorBadge, Tooltip = channel.ModeratorBadge.Tooltip }); } break; case "turbo/1": Badges |= MessageBadges.Turbo; words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = new LazyLoadedImage(GuiEngine.Current.GetImage(ImageType.BadgeTurbo)), Tooltip = "Turbo Subscriber" }); break; case "broadcaster/1": Badges |= MessageBadges.Broadcaster; words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = new LazyLoadedImage(GuiEngine.Current.GetImage(ImageType.BadgeBroadcaster)), Tooltip = "Channel Broadcaster" }); break; case "premium/1": Badges |= MessageBadges.Broadcaster; words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = new LazyLoadedImage(GuiEngine.Current.GetImage(ImageType.BadgeTwitchPrime)), Tooltip = "Twitch Prime" }); break; } } } } LazyLoadedImage fourtfBadge; if (Common.Badges.FourtfGlobalBadges.TryGetValue(Username, out fourtfBadge)) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = fourtfBadge, Tooltip = fourtfBadge.Tooltip }); } // Username if (string.IsNullOrWhiteSpace(DisplayName)) { DisplayName = Username; } var messageUser = (isSentWhisper ? IrcManager.Account.Username + " -> " : ""); messageUser += DisplayName; if (!isReceivedWhisper && !isSentWhisper) { messageUser += (DisplayName.ToLower() != Username ? $" ({Username})" : ""); } if (isReceivedWhisper) { messageUser += " -> " + IrcManager.Account.Username; } if (!slashMe) { messageUser += ":"; } words.Add(new Word { Type = SpanType.Text, Value = messageUser, Color = UsernameColor, Font = FontType.MediumBold, Link = new Link(LinkType.UserInfo, new UserInfoData { UserName = Username, Channel = channel }), CopyText = messageUser }); var twitchEmotes = new List <Tuple <int, LazyLoadedImage> >(); // Twitch Emotes if (AppSettings.ChatEnableTwitchEmotes && data.Tags.TryGetValue("emotes", out value)) { // 93064:0-6,8-14/80481:16-20,22-26 value.Split('/').Do(emote => { if (emote != "") { var x = emote.Split(':'); var id = int.Parse(x[0]); foreach (var y in x[1].Split(',')) { var coords = y.Split('-'); var index = int.Parse(coords[0]); var name = text.Substring(index, int.Parse(coords[1]) - index + 1); // ignore ignored emotes if (!AppSettings.ChatIgnoredEmotes.ContainsKey(name)) { var e = Emotes.GetTwitchEmoteById(id, name); twitchEmotes.Add(Tuple.Create(index, e)); } } ; } }); twitchEmotes.Sort((e1, e2) => e1.Item1.CompareTo(e2.Item1)); } //if (data.Tags.TryGetValue("id", out value)) //{ //} //if (data.Tags.TryGetValue("mod", out value)) //{ //} //if (data.Tags.TryGetValue("subscriber", out value)) //{ // if (value == "1") // Badges |= MessageBadges.Sub; //} //if (data.Tags.TryGetValue("turbo", out value)) //{ // if (value == "1") // Badges |= MessageBadges.Turbo; //} var i = 0; var currentTwitchEmoteIndex = 0; var currentTwitchEmote = twitchEmotes.FirstOrDefault(); foreach (var split in text.Split(' ')) { if (currentTwitchEmote != null) { if (currentTwitchEmote.Item1 == i) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = currentTwitchEmote.Item2, Link = new Link(LinkType.Url, currentTwitchEmote.Item2.Url), Tooltip = currentTwitchEmote.Item2.Tooltip, CopyText = currentTwitchEmote.Item2.Name }); i += split.Length + 1; currentTwitchEmoteIndex++; currentTwitchEmote = currentTwitchEmoteIndex == twitchEmotes.Count ? null : twitchEmotes[currentTwitchEmoteIndex]; continue; } } foreach (var o in Emojis.ParseEmojis(split)) { var s = o as string; if (s != null) { //foreach (var match in Regex.Matches(@"\b\w+\b", s)) //{ // LazyLoadedImage bttvEmote; // if (AppSettings.ChatEnableBttvEmotes && (Emotes.BttvGlobalEmotes.TryGetValue(s, out bttvEmote) || channel.BttvChannelEmotes.TryGetValue(s, out bttvEmote)) // || (AppSettings.ChatEnableFfzEmotes && Emotes.FfzGlobalEmotes.TryGetValue(s, out bttvEmote))) // { // words.Add(new Word // { // Type = SpanType.LazyLoadedImage, // Value = bttvEmote, // Color = slashMe ? UsernameColor : new int?(), // Tooltip = bttvEmote.Tooltip, // Link = bttvEmote.Url, // CopyText = bttvEmote.Name // }); // } //} if (bits != null && Regex.IsMatch(s, "cheer[1-9][0-9]*")) { int cheer; if (int.TryParse(s.Substring("cheer".Length), out cheer)) { string color = null; HSLColor bitsColor; if (cheer >= 10000) { color = "red"; bitsColor = new HSLColor(0, 1f, 0.5f); } else if (cheer >= 5000) { color = "blue"; bitsColor = new HSLColor(0.61f, 1f, 0.4f); } else if (cheer >= 1000) { color = "green"; bitsColor = new HSLColor(0.5f, 1f, 0.5f); } else if (cheer >= 100) { color = "purple"; bitsColor = new HSLColor(0.8f, 1f, 0.5f); } else { color = "gray"; bitsColor = HSLColor.FromRGB(0.5f, 0.5f, 0.5f); } var bitsLink = $"http://static-cdn.jtvnw.net/bits/{(GuiEngine.Current.IsDarkTheme ? "dark" : "light")}/animated/{color}/1"; words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = Emotes.MiscEmotesByUrl.GetOrAdd(bitsLink, url => new LazyLoadedImage { Name = "cheer", Url = url, Tooltip = "Twitch Bits Badge" }), Tooltip = "Twitch Bits Donation", CopyText = s, Link = new Link(LinkType.Url, "https://blog.twitch.tv/introducing-cheering-celebrate-together-da62af41fac6") }); words.Add(new Word { Type = SpanType.Text, Value = "x" + s.Substring(5), Font = FontType.Small, Color = bitsColor }); continue; } } LazyLoadedImage bttvEmote; if (!AppSettings.ChatIgnoredEmotes.ContainsKey(s) && (AppSettings.ChatEnableBttvEmotes && (Emotes.BttvGlobalEmotes.TryGetValue(s, out bttvEmote) || channel.BttvChannelEmotes.TryGetValue(s, out bttvEmote)) || (AppSettings.ChatEnableFfzEmotes && (Emotes.FfzGlobalEmotes.TryGetValue(s, out bttvEmote) || channel.FfzChannelEmotes.TryGetValue(s, out bttvEmote))) || Emotes.ChatterinoEmotes.TryGetValue(s, out bttvEmote))) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = bttvEmote, Color = slashMe ? UsernameColor : new HSLColor?(), Tooltip = bttvEmote.Tooltip, Link = new Link(LinkType.Url, bttvEmote.Url), CopyText = bttvEmote.Name }); } else { var link = _matchLink(split); words.Add(new Word { Type = SpanType.Text, Value = s, Color = slashMe ? UsernameColor : (link == null ? new HSLColor?() : HSLColor.FromRGB(-8355712)), Link = link == null ? null : new Link(LinkType.Url, link), CopyText = s }); } } else { var e = o as LazyLoadedImage; if (e != null) { words.Add(new Word { Type = SpanType.LazyLoadedImage, Value = e, Link = new Link(LinkType.Url, e.Url), Tooltip = e.Tooltip, CopyText = e.Name, HasTrailingSpace = e.HasTrailingSpace }); } } } var splitLength = 0; for (var j = 0; j < split.Length; j++) { splitLength++; if (char.IsHighSurrogate(split[j])) { j += 1; } } i += splitLength + 1; } Words = words; RawMessage = text; if (!isReceivedWhisper && AppSettings.HighlightIgnoredUsers.ContainsKey(Username)) { HighlightTab = false; } //w.Stop(); //Console.WriteLine("Message parsed in " + w.Elapsed.TotalSeconds.ToString("0.000000") + " seconds"); }