// Send Messages public static void SendMessage(TwitchChannel channel, string _message, bool isMod) { if (channel != null) { if (!_message.StartsWith(".color ")) { if (!isMod && nextMessageSendTime > DateTime.Now) { Task.Run(async() => { await Task.Delay(300); channel.AddMessage(new Message("Sending messages too fast, message not sent.", HSLColor.Gray, false)); }); return; } nextMessageSendTime = DateTime.Now.AddSeconds(1.1); } var message = Commands.ProcessMessage(_message, channel, true); if (!Client.Say(message, channel.Name.TrimStart('#'), isMod)) { if (nextProtectMessageSendTime < DateTime.Now) { channel.AddMessage(new Message($"Message not sent to protect you from a global ban. (try again in {Client.GetTimeUntilNextMessage(isMod).Seconds} seconds)", HSLColor.Gray, false)); nextProtectMessageSendTime = DateTime.Now.AddSeconds(1); } } } }
public static bool IsMessageIgnored(Message msg, TwitchChannel c) { // check if message has an ignored keyword if (AppSettings.IgnoredKeywordsRegex != null && AppSettings.IgnoredKeywordsRegex.IsMatch(msg.Params)) { return(true); } // check if message user is on the ignore list if (IsIgnoredUser(msg.Username)) { //check if message is somewhere that the user has mod or broadcaster switch (AppSettings.ChatShowIgnoredUsersMessages) { case 1: if (!c.IsModOrBroadcaster) { return(true); } break; case 2: if (!c.IsBroadcaster) { return(true); } break; default: return(true); } } return(false); }
private static void WriteConnection_MessageReceived(object sender, MessageEventArgs e) { var msg = e.Message; if (msg.Command == "NOTICE") { TwitchChannel.GetChannel((msg.Middle ?? "").TrimStart('#')).Process(c => { string tmp; if (msg.Tags.TryGetValue("msg-id", out tmp) && tmp == "timeout_success") { return; } if (AppSettings.Rainbow && tmp == "color_changed") { return; } var message = new Message(msg.Params, null, true) { HighlightTab = false }; c.AddMessage(message); }); } else if (msg.Command == "USERSTATE") { string value; if (msg.Tags.TryGetValue("mod", out value)) { TwitchChannel.GetChannel((msg.Middle ?? "").TrimStart('#')) .Process(c => { c.IsMod = value == "1"; }); } if (msg.Tags.TryGetValue("badges", out value)) { if (value.Contains("vip")) { TwitchChannel.GetChannel((msg.Middle ?? "").TrimStart('#')) .Process(c => { c.IsVip = true; }); } } } }
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"); }
// public public static string ProcessMessage(string text, TwitchChannel channel, bool executeCommands) { string _command = null; string args = null; if (text.Length > 1) { if (text[0] == '/') { var index = text.IndexOf(' '); _command = index == -1 ? text.Substring(1) : text.Substring(1, index - 1); args = index == -1 ? "" : text.Substring(index + 1); } else if (AppSettings.ChatAllowCommandsAtEnd) { var index = text.LastIndexOf(' '); if (index != -1) { var s = text.Substring(index + 1); if (s.Length > 0 && s[0] == '/') { _command = s.Substring(1); args = text.Remove(index); } } } } if (_command != null) { Func <string, TwitchChannel, bool, string> command; if (ChatCommands.TryGetValue(_command, out command)) { text = command(args, channel, executeCommands); } else { lock (CustomCommandsLock) { foreach (var c in CustomCommands) { if (c.Name == _command) { text = c.Execute(args); break; } } } } } if (text != null) { text = Emojis.ReplaceShortCodes(text); text = Regex.Replace(text, " +", " "); text = text.Trim(); return(text); } else { return(null); } }
private static void ReadConnection_MessageReceived(object sender, MessageEventArgs e) { var msg = e.Message; if (msg.Command == "PRIVMSG") { TwitchChannel.GetChannel(msg.Middle.TrimStart('#')).Process(c => { if (msg.PrefixUser == "twitchnotify") { c.AddMessage(new Message(msg.Params ?? "", HSLColor.Gray, true) { HighlightType = HighlightType.Resub }); } else { // check if ignore keyword is triggered if (AppSettings.IgnoredKeywordsRegex == null || !AppSettings.IgnoredKeywordsRegex.IsMatch(e.Message.Params)) { var message = new Message(msg, c); // check if user is on the ignore list if (AppSettings.EnableTwitchUserIgnores && IsIgnoredUser(message.Username)) { switch (AppSettings.ChatShowIgnoredUsersMessages) { case 1: if (!c.IsModOrBroadcaster) { return; } break; case 2: if (!c.IsBroadcaster) { return; } break; default: return; } } { c.Users[message.Username.ToUpper()] = message.DisplayName; if (message.HasAnyHighlightType(HighlightType.Highlighted)) { var mentionMessage = new Message(msg, c, enablePingSound: false, includeChannel: true) { HighlightType = HighlightType.None }; TwitchChannel.MentionsChannel.AddMessage(mentionMessage); } c.AddMessage(message); } } } }); } else if (msg.Command == "CLEARCHAT") { var channel = msg.Middle; var user = msg.Params; string reason; msg.Tags.TryGetValue("ban-reason", out reason); string _duration; var duration = 0; if (msg.Tags.TryGetValue("ban-duration", out _duration)) { int.TryParse(_duration, out duration); } TwitchChannel.GetChannel((msg.Middle ?? "").TrimStart('#')).Process(c => c.ClearChat(user, reason, duration)); //} } else if (msg.Command == "ROOMSTATE") { TwitchChannel.GetChannel((msg.Middle ?? "").TrimStart('#')).Process(c => { var state = c.RoomState; string value; if (msg.Tags.TryGetValue("emote-only", out value)) { if (value == "1") { state |= RoomState.EmoteOnly; } else { state &= ~RoomState.EmoteOnly; } } if (msg.Tags.TryGetValue("subs-only", out value)) { if (value == "1") { state |= RoomState.SubOnly; } else { state &= ~RoomState.SubOnly; } } if (msg.Tags.TryGetValue("slow", out value)) { if (value == "0") { state &= ~RoomState.SlowMode; } else { int time; if (!int.TryParse(value, out time)) { time = -1; } c.SlowModeTime = time; state |= RoomState.SlowMode; } } if (msg.Tags.TryGetValue("r9k", out value)) { if (value == "1") { state |= RoomState.R9k; } else { state &= ~RoomState.R9k; } } //if (e.Data.Tags.TryGetValue("broadcaster-lang", out value)) c.RoomState = state; }); } else if (msg.Command == "USERSTATE") { string value; if (msg.Tags.TryGetValue("mod", out value)) { TwitchChannel.GetChannel((msg.Middle ?? "").TrimStart('#')).Process(c => c.IsMod = value == "1"); } } else if (msg.Command == "WHISPER") { TwitchChannel.WhisperChannel.AddMessage(new Message(msg, TwitchChannel.WhisperChannel, true, false, isReceivedWhisper: true)); LastReceivedWhisperUser = msg.PrefixUser; if (AppSettings.ChatEnableInlineWhispers) { var inlineMessage = new Message(msg, TwitchChannel.WhisperChannel, true, false, true) { HighlightTab = false, HighlightType = HighlightType.Whisper }; foreach (var channel in TwitchChannel.Channels) { channel.AddMessage(inlineMessage); } } } else if (msg.Command == "USERNOTICE") { string sysMsg; msg.Tags.TryGetValue("system-msg", out sysMsg); TwitchChannel.GetChannel((msg.Middle ?? "").TrimStart('#')).Process(c => { try { var sysMessage = new Message(sysMsg, HSLColor.Gray, true) { HighlightType = HighlightType.Resub }; c.AddMessage(sysMessage); if (!string.IsNullOrEmpty(msg.Params)) { var message = new Message(msg, c) { HighlightType = HighlightType.Resub }; c.AddMessage(message); } } 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"); }