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"); }