Пример #1
0
        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();
        }
Пример #2
0
        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();
        }
Пример #3
0
        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());
            }
        }
Пример #4
0
        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");
        }
Пример #5
0
        // 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 { }
                });
            }
        }
Пример #6
0
 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();
 }
Пример #7
0
        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 { }
            });
        }
Пример #8
0
        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");
        }