public override void Connect() { new Task(() => { try { JsonParser parser = new JsonParser(); var bttvChannelEmotesCache = "./db/twitch." + ChannelName + ".bttv_channel_emotes.json"; if (!File.Exists(bttvChannelEmotesCache) || DateTime.Now - new FileInfo(bttvChannelEmotesCache).LastWriteTime > TimeSpan.FromHours(24)) { 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}} foreach (dynamic e in json["emotes"]) { string id = e["id"]; string code = e["code"]; string imageType = e["imageType"]; string url = template.Replace("{{id}}", id).Replace("{{image}}", "3x"); BttvChannelEmotes[code] = new TwitchEmote { EmoteSet = -1, Height = 112, Width = 112, Name = code, Type = EmoteType.BttvChannel, Url = url, ImageFormat = imageType }; } } } catch { } }).Start(); Bot.TwitchIrc.RfcJoin("#" + ChannelName); messageLimitTimer.Start(); }
public TwitchEmote GetMostUsed() { TwitchEmote TopEmote = null; foreach (TwitchEmote te in this.TwitchEmotes.Emotes) { if (te.Amount > (TopEmote?.Amount ?? -1)) { TopEmote = te; } } return(TopEmote); }
private void textBoxTwitchEmoteSearch_Changed(object sender, EventArgs e) { TwitchEmote te = this.EmoteDatabase.TwitchEmotes.Emotes.Where(x => x.EmoteName == this.textBoxTwitchEmoteSearch.Text).FirstOrDefault(); if (te == null) { this.labelTwitchEmoteSearch.Text = String.Format("Emote Data: NOT FOUND!"); } else { this.labelTwitchEmoteSearch.Text = String.Format("Emote Data: {0}", te.ToString()); } }
public static List <CheerEmote> GetBits(string cacheFolder, string channel_id = "") { List <CheerEmote> cheerEmotes = new List <CheerEmote>(); string bitsFolder = Path.Combine(cacheFolder, "bits"); if (!Directory.Exists(bitsFolder)) { TwitchHelper.CreateDirectory(bitsFolder); } using (WebClient client = new WebClient()) { client.Headers.Add("Accept", "application/vnd.twitchtv.v5+json"); client.Headers.Add("Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko"); JObject globalCheer = JObject.Parse(client.DownloadString("https://api.twitch.tv/kraken/bits/actions?channel_id=" + channel_id)); foreach (JToken emoteToken in globalCheer["actions"]) { string prefix = emoteToken["prefix"].ToString(); List <KeyValuePair <int, TwitchEmote> > tierList = new List <KeyValuePair <int, TwitchEmote> >(); CheerEmote newEmote = new CheerEmote() { prefix = prefix, tierList = tierList }; foreach (JToken tierToken in emoteToken["tiers"]) { try { int minBits = tierToken["min_bits"].ToObject <int>(); string fileName = Path.Combine(bitsFolder, prefix + minBits + "_2x.gif"); byte[] finalBytes = null; if (File.Exists(fileName)) { try { finalBytes = File.ReadAllBytes(fileName); } catch { } } if (finalBytes == null) { byte[] bytes = client.DownloadData(tierToken["images"]["dark"]["animated"]["2"].ToString()); try { File.WriteAllBytes(fileName, bytes); } catch { } finalBytes = bytes; } if (finalBytes != null) { MemoryStream ms = new MemoryStream(finalBytes); TwitchEmote emote = new TwitchEmote(new List <SKBitmap>() { SKBitmap.Decode(finalBytes) }, SKCodec.Create(ms), prefix, "gif", "", 2, finalBytes); tierList.Add(new KeyValuePair <int, TwitchEmote>(minBits, emote)); } } catch { } } cheerEmotes.Add(newEmote); } } return(cheerEmotes); }
public static List <TwitchEmote> GetEmotes(List <Comment> comments, string cacheFolder, Emotes embededEmotes = null, bool deepSearch = false) { List <TwitchEmote> returnList = new List <TwitchEmote>(); List <string> alreadyAdded = new List <string>(); List <string> failedEmotes = new List <string>(); string emoteFolder = Path.Combine(cacheFolder, "emotes"); if (!Directory.Exists(emoteFolder)) { TwitchHelper.CreateDirectory(emoteFolder); } if (embededEmotes != null) { foreach (FirstPartyEmoteData emoteData in embededEmotes.firstParty) { try { MemoryStream ms = new MemoryStream(emoteData.data); SKCodec codec = SKCodec.Create(ms); TwitchEmote newEmote = new TwitchEmote(new List <SKBitmap>() { SKBitmap.Decode(emoteData.data) }, codec, emoteData.id, codec.FrameCount == 0 ? "png" : "gif", emoteData.id, emoteData.imageScale, emoteData.data); returnList.Add(newEmote); alreadyAdded.Add(emoteData.id); } catch { } } } using (WebClient client = new WebClient()) { foreach (var comment in comments) { if (comment.message.fragments == null) { continue; } foreach (var fragment in comment.message.fragments) { if (fragment.emoticon != null) { string id = fragment.emoticon.emoticon_id; if (!alreadyAdded.Contains(id) && !failedEmotes.Contains(id)) { try { string filePath = ""; if (File.Exists(Path.Combine(emoteFolder, id + "_1x.gif"))) { filePath = Path.Combine(emoteFolder, id + "_1x.gif"); } else if (File.Exists(Path.Combine(emoteFolder, id + "_1x.png")) && !id.Contains("emotesv2_")) { filePath = Path.Combine(emoteFolder, id + "_1x.png"); } if (File.Exists(filePath)) { SKBitmap emoteImage = SKBitmap.Decode(filePath); if (emoteImage == null) { try { File.Delete(filePath); } catch { } } else { try { byte[] bytes = File.ReadAllBytes(filePath); MemoryStream ms = new MemoryStream(bytes); SKCodec codec = SKCodec.Create(ms); returnList.Add(new TwitchEmote(new List <SKBitmap>() { SKBitmap.Decode(bytes) }, codec, id, codec.FrameCount == 0 ? "png" : "gif", id, 1, bytes)); alreadyAdded.Add(id); } catch { } } } if (!alreadyAdded.Contains(id)) { byte[] bytes = client.DownloadData(String.Format("https://static-cdn.jtvnw.net/emoticons/v2/{0}/default/dark/1.0", id)); alreadyAdded.Add(id); MemoryStream ms = new MemoryStream(bytes); SKCodec codec = SKCodec.Create(ms); TwitchEmote newEmote = new TwitchEmote(new List <SKBitmap>() { SKBitmap.Decode(bytes) }, codec, id, codec.FrameCount == 0 ? "png" : "gif", id, 1, bytes); returnList.Add(newEmote); try { File.WriteAllBytes(Path.Combine(emoteFolder, newEmote.id + "_1x." + newEmote.imageType), bytes); } catch { } } } catch (WebException) { failedEmotes.Add(id); } } } } } } return(returnList); }
public static List <TwitchEmote> GetThirdPartyEmotes(int streamerId, string cacheFolder, Emotes embededEmotes = null, bool bttv = true, bool ffz = true, bool stv = true) { List <TwitchEmote> returnList = new List <TwitchEmote>(); List <string> alreadyAdded = new List <string>(); string bttvFolder = Path.Combine(cacheFolder, "bttv"); string ffzFolder = Path.Combine(cacheFolder, "ffz"); string stvFolder = Path.Combine(cacheFolder, "stv"); if (embededEmotes != null) { foreach (ThirdPartyEmoteData emoteData in embededEmotes.thirdParty) { try { MemoryStream ms = new MemoryStream(emoteData.data); SKCodec codec = SKCodec.Create(ms); TwitchEmote newEmote = new TwitchEmote(new List <SKBitmap>() { SKBitmap.Decode(emoteData.data) }, codec, emoteData.name, codec.FrameCount == 0 ? "png" : "gif", "", emoteData.imageScale, emoteData.data); returnList.Add(newEmote); alreadyAdded.Add(emoteData.name); } catch { } } } using (WebClient client = new WebClient()) { if (bttv) { if (!Directory.Exists(bttvFolder)) { TwitchHelper.CreateDirectory(bttvFolder); } //Global BTTV Emotes JArray BBTV = JArray.Parse(client.DownloadString("https://api.betterttv.net/3/cached/emotes/global")); foreach (var emote in BBTV) { string id = emote["id"].ToString(); string name = emote["code"].ToString(); if (alreadyAdded.Contains(name)) { continue; } string fileName = Path.Combine(bttvFolder, id + "_2x.png"); string url = String.Format("https://cdn.betterttv.net/emote/{0}/2x", id); TwitchEmote newEmote = GetTwitchEmote(fileName, url, name, emote["imageType"].ToString(), id, 2); if (newEmote != null) { returnList.Add(newEmote); alreadyAdded.Add(name); } } //Channel specific BTTV emotes try { JObject BBTV_channel = JObject.Parse(client.DownloadString("https://api.betterttv.net/3/cached/users/twitch/" + streamerId)); foreach (var emote in BBTV_channel["sharedEmotes"]) { string id = emote["id"].ToString(); string name = emote["code"].ToString(); string mime = emote["imageType"].ToString(); if (alreadyAdded.Contains(name)) { continue; } string fileName = Path.Combine(bttvFolder, id + "_2x." + mime); string url = String.Format("https://cdn.betterttv.net/emote/{0}/2x", id); TwitchEmote newEmote = GetTwitchEmote(fileName, url, name, mime, id, 2); if (newEmote != null) { returnList.Add(newEmote); alreadyAdded.Add(name); } } foreach (var emote in BBTV_channel["channelEmotes"]) { string id = emote["id"].ToString(); string name = emote["code"].ToString(); string mime = emote["imageType"].ToString(); if (alreadyAdded.Contains(name)) { continue; } string fileName = Path.Combine(bttvFolder, id + "_2x." + mime); string url = String.Format("https://cdn.betterttv.net/emote/{0}/2x", id); TwitchEmote newEmote = GetTwitchEmote(fileName, url, name, mime, id, 2); if (newEmote != null) { returnList.Add(newEmote); alreadyAdded.Add(name); } } } catch { } } if (ffz) { if (!Directory.Exists(ffzFolder)) { TwitchHelper.CreateDirectory(ffzFolder); } //Global FFZ emotes JArray FFZ = JArray.Parse(client.DownloadString("https://api.betterttv.net/3/cached/frankerfacez/emotes/global")); foreach (var emote in FFZ) { string id = emote["id"].ToString(); string name = emote["code"].ToString(); string mime = emote["imageType"].ToString(); if (alreadyAdded.Contains(name)) { continue; } string fileName = Path.Combine(ffzFolder, id + "_1x." + mime); string url = String.Format("https://cdn.betterttv.net/frankerfacez_emote/{0}/1", id); TwitchEmote newEmote = GetTwitchEmote(fileName, url, name, mime, id, 2); if (newEmote != null) { returnList.Add(newEmote); alreadyAdded.Add(name); } } //Channel specific FFZ emotes try { JArray FFZ_channel = JArray.Parse(client.DownloadString("https://api.betterttv.net/3/cached/frankerfacez/users/twitch/" + streamerId)); foreach (var emote in FFZ_channel) { string id = emote["id"].ToString(); string name = emote["code"].ToString(); string mime = emote["imageType"].ToString(); if (alreadyAdded.Contains(name)) { continue; } string fileName = Path.Combine(ffzFolder, id + "_2x." + mime); string fileNameLow = Path.Combine(ffzFolder, id + "_1x" + mime); TwitchEmote newEmote = null; if (File.Exists(fileNameLow)) { newEmote = GetTwitchEmote(fileName, String.Format("https://cdn.betterttv.net/frankerfacez_emote/{0}/1", id), name, mime, id, 1); } if (newEmote == null) { newEmote = GetTwitchEmote(fileName, String.Format("https://cdn.betterttv.net/frankerfacez_emote/{0}/2", id), name, mime, id, 2); if (newEmote == null) { newEmote = GetTwitchEmote(fileName, String.Format("https://cdn.betterttv.net/frankerfacez_emote/{0}/1", id), name, mime, id, 1); } } if (newEmote != null) { returnList.Add(newEmote); alreadyAdded.Add(name); } } } catch { } } if (stv) { if (!Directory.Exists(stvFolder)) { TwitchHelper.CreateDirectory(stvFolder); } //Global 7tv Emotes JArray STV = JArray.Parse(client.DownloadString("https://api.7tv.app/v2/emotes/global")); foreach (var emote in STV) { string id = emote["id"].ToString(); string name = emote["name"].ToString(); string mime = emote["mime"].ToString().Split('/')[1]; string url2x = emote["urls"][1][1].ToString(); // 2x if (alreadyAdded.Contains(name)) { continue; } byte[] bytes; string fileName = Path.Combine(stvFolder, id + "_2x." + mime); if (File.Exists(fileName)) { bytes = File.ReadAllBytes(fileName); } else { bytes = client.DownloadData(url2x); File.WriteAllBytes(fileName, bytes); } MemoryStream ms = new MemoryStream(bytes); returnList.Add(new TwitchEmote(new List <SKBitmap>() { SKBitmap.Decode(bytes) }, SKCodec.Create(ms), name, mime, id, 2, bytes)); alreadyAdded.Add(name); } //Channel specific 7tv emotes try { JArray STV_channel = JArray.Parse(client.DownloadString(String.Format("https://api.7tv.app/v2/users/{0}/emotes", streamerId))); foreach (var emote in STV_channel) { string id = emote["id"].ToString(); string name = emote["name"].ToString(); string mime = emote["mime"].ToString().Split('/')[1]; string url2x = emote["urls"][1][1].ToString(); // 2x if (alreadyAdded.Contains(name)) { continue; } byte[] bytes; string fileName = Path.Combine(stvFolder, id + "_2x." + mime); if (File.Exists(fileName)) { bytes = File.ReadAllBytes(fileName); } else { bytes = client.DownloadData(url2x); File.WriteAllBytes(fileName, bytes); } MemoryStream ms = new MemoryStream(bytes); returnList.Add(new TwitchEmote(new List <SKBitmap>() { SKBitmap.Decode(bytes) }, SKCodec.Create(ms), name, mime, id, 2, bytes)); alreadyAdded.Add(name); } } catch { } } } return(returnList); }
public void HandleAnimatedTwitchEmote(TwitchEmote emote) { //throw new NotImplementedException(); }
public void Update(PageChatRender pageChatRender) { try { this.Width = Int32.Parse(pageChatRender.textWidth.Text) + 10; this.Height = Int32.Parse(pageChatRender.textHeight.Text) + 38; imgPreview.Height = Int32.Parse(pageChatRender.textHeight.Text); imgPreview.Width = Int32.Parse(pageChatRender.textWidth.Text); PreviewData previewData = JsonConvert.DeserializeObject<PreviewData>(File.ReadAllText("preview_data.json")); BlockingCollection<TwitchCommentPreview> finalComments = new BlockingCollection<TwitchCommentPreview>(); SKBitmap previewBitmap = new SKBitmap((int)this.Width, (int)imgPreview.Height); SKColor backgroundColor = new SKColor(pageChatRender.colorBackground.SelectedColor.Value.R, pageChatRender.colorBackground.SelectedColor.Value.G, pageChatRender.colorBackground.SelectedColor.Value.B); SKColor messageColor = new SKColor(pageChatRender.colorFont.SelectedColor.Value.R, pageChatRender.colorFont.SelectedColor.Value.G, pageChatRender.colorFont.SelectedColor.Value.B); ChatRenderOptions renderOptions = new ChatRenderOptions() { InputFile = pageChatRender.textJson.Text, OutputFile = "", BackgroundColor = backgroundColor, ChatHeight = Int32.Parse(pageChatRender.textHeight.Text), ChatWidth = Int32.Parse(pageChatRender.textWidth.Text), BttvEmotes = (bool)pageChatRender.checkBTTV.IsChecked, FfzEmotes = (bool)pageChatRender.checkFFZ.IsChecked, Outline = (bool)pageChatRender.checkOutline.IsChecked, Font = (string)pageChatRender.comboFont.SelectedItem, FontSize = Double.Parse(pageChatRender.textFontSize.Text), UpdateRate = Double.Parse(pageChatRender.textUpdateTime.Text), Timestamp = (bool)pageChatRender.checkTimestamp.IsChecked, MessageColor = messageColor, Framerate = Int32.Parse(pageChatRender.textFramerate.Text), InputArgs = Settings.Default.FfmpegInputArgs, OutputArgs = Settings.Default.FfmpegOutputArgs, MessageFontStyle = SKFontStyle.Normal, UsernameFontStyle = SKFontStyle.Bold }; System.Drawing.Size canvasSize = new System.Drawing.Size(renderOptions.ChatWidth, renderOptions.SectionHeight); SKPaint nameFont = new SKPaint() { Typeface = SKTypeface.FromFamilyName(renderOptions.Font, renderOptions.UsernameFontStyle), LcdRenderText = true, SubpixelText = true, TextSize = (float)renderOptions.FontSize, IsAntialias = true, HintingLevel = SKPaintHinting.Full, FilterQuality = SKFilterQuality.High }; SKPaint messageFont = new SKPaint() { Typeface = SKTypeface.FromFamilyName(renderOptions.Font, renderOptions.MessageFontStyle), LcdRenderText = true, SubpixelText = true, TextSize = (float)renderOptions.FontSize, IsAntialias = true, HintingLevel = SKPaintHinting.Full, FilterQuality = SKFilterQuality.High, Color = renderOptions.MessageColor }; List<TwitchEmote> thirdPartyEmotes = new List<TwitchEmote>(); List<TwitchEmote> chatEmotes = new List<TwitchEmote>(); Dictionary<string, SKBitmap> emojiCache = new Dictionary<string, SKBitmap>(); using (SKCanvas previewCanvas = new SKCanvas(previewBitmap)) { previewCanvas.Clear(backgroundColor); foreach (PreviewEmote previewDataEmote in previewData.emotes) { byte[] imageBytes = Convert.FromBase64String(previewDataEmote.image); SKBitmap emoteBitmap = SKBitmap.Decode(imageBytes); SKCodec emoteCodec; using (MemoryStream ms = new MemoryStream(imageBytes)) emoteCodec = SKCodec.Create(ms); TwitchEmote emote = new TwitchEmote(new List<SKBitmap>() { emoteBitmap }, emoteCodec, previewDataEmote.name, ".png", "0", 1, imageBytes); thirdPartyEmotes.Add(emote); } foreach (PreviewComment previewComment in previewData.comments) { int default_x = 2; System.Drawing.Point drawPos = new System.Drawing.Point(default_x, 0); string userName = previewComment.name; SKColor userColor = new SKColor(Convert.ToByte(previewComment.color.Substring(0, 2), 16), Convert.ToByte(previewComment.color.Substring(2, 2), 16), Convert.ToByte(previewComment.color.Substring(4, 2), 16)); List<SKBitmap> imageList = new List<SKBitmap>(); SKBitmap sectionImage = new SKBitmap(canvasSize.Width, canvasSize.Height); List<GifEmote> currentGifEmotes = new List<GifEmote>(); List<SKBitmap> emoteList = new List<SKBitmap>(); List<CheerEmote> cheerEmotes = new List<CheerEmote>(); List<SKRect> emotePositionList = new List<SKRect>(); new SKCanvas(sectionImage).Clear(renderOptions.BackgroundColor); Comment comment = new Comment(); comment.message = new Message(); Fragment msg = new Fragment(); msg.text = previewComment.message; comment.message.fragments = new List<Fragment>(); comment.message.fragments.Add(msg); if (renderOptions.Timestamp) sectionImage = ChatRenderer.DrawTimestamp(sectionImage, imageList, messageFont, renderOptions, comment, canvasSize, ref drawPos, ref default_x); if (previewComment.badges != null) sectionImage = DrawBadges(sectionImage, imageList, renderOptions, canvasSize, ref drawPos, previewComment); sectionImage = ChatRenderer.DrawUsername(sectionImage, imageList, renderOptions, nameFont, userName, userColor, canvasSize, ref drawPos); sectionImage = ChatRenderer.DrawMessage(sectionImage, imageList, renderOptions, currentGifEmotes, messageFont, emojiCache, chatEmotes, thirdPartyEmotes, cheerEmotes, comment, canvasSize, ref drawPos, ref default_x, emoteList, emotePositionList); int finalHeight = 0; foreach (var img in imageList) finalHeight += img.Height; SKBitmap finalImage = new SKBitmap(canvasSize.Width, finalHeight); SKCanvas finalImageCanvas = new SKCanvas(finalImage); finalHeight = 0; foreach (var img in imageList) { finalImageCanvas.DrawBitmap(img, 0, finalHeight); finalHeight += img.Height; img.Dispose(); } finalComments.Add(new TwitchCommentPreview(finalImage, Double.Parse(comment.content_offset_seconds.ToString()), currentGifEmotes, emoteList, emotePositionList)); } int y = 0; int tempHeight = 0; foreach (TwitchCommentPreview twitchCommentPreview in finalComments) tempHeight += twitchCommentPreview.section.Height; SKBitmap tempBitmap = new SKBitmap((int)this.Width, tempHeight); using (SKCanvas tempCanvas = new SKCanvas(tempBitmap)) { foreach (TwitchCommentPreview twitchCommentPreview in finalComments) { tempCanvas.DrawBitmap(twitchCommentPreview.section, 0, y, imagePaint); for (int i = 0; i < twitchCommentPreview.normalEmotes.Count; i++) { SKRect refrenceRect = twitchCommentPreview.normalEmotesPositions[i]; tempCanvas.DrawBitmap(twitchCommentPreview.normalEmotes[i], new SKRect(refrenceRect.Left, refrenceRect.Top + y, refrenceRect.Right, refrenceRect.Bottom + y), imagePaint); } y += twitchCommentPreview.section.Height; } } previewCanvas.DrawBitmap(tempBitmap, 0, previewBitmap.Height - tempBitmap.Height); } using (var stream = new MemoryStream()) { SKImage.FromBitmap(previewBitmap).Encode(SKEncodedImageFormat.Png, 100).SaveTo(stream); var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.StreamSource = stream; bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.EndInit(); bitmap.Freeze(); imgPreview.Source = bitmap; } } catch { try { this.Width = 500; this.Height = 338; imgPreview.Width = 500; imgPreview.Height = 300; SKBitmap errorBitmap = new SKBitmap(500, 300); using (SKCanvas skCanvas = new SKCanvas(errorBitmap)) { skCanvas.DrawText("ERROR, UNABLE TO RENDER CHAT", 40, 150, new SKPaint() { Typeface = SKTypeface.FromFamilyName("Arial", SKFontStyle.Bold), TextSize = 18, IsAntialias = true, FilterQuality = SKFilterQuality.High }); SKBitmap peepo = SKBitmap.Decode(Application .GetResourceStream(new Uri("pack://application:,,,/Images/peepoSad.png")).Stream); skCanvas.DrawBitmap(peepo, 370, 132); } using (var stream = new MemoryStream()) { SKImage.FromBitmap(errorBitmap).Encode(SKEncodedImageFormat.Png, 100).SaveTo(stream); var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.StreamSource = stream; bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.EndInit(); bitmap.Freeze(); imgPreview.Source = bitmap; } } catch { } } }
public static List <CheerEmote> GetBits(string cacheFolder, string channel_id = "") { List <CheerEmote> cheerEmotes = new List <CheerEmote>(); string bitsFolder = Path.Combine(cacheFolder, "bits"); if (!Directory.Exists(bitsFolder)) { TwitchHelper.CreateDirectory(bitsFolder); } using (WebClient client = new WebClient()) { client.Encoding = Encoding.UTF8; client.Headers.Add("Client-ID", "kimne78kx3ncx6brgo4mv6wki5h1ko"); GqlCheerResponse cheerResponse = JsonConvert.DeserializeObject <GqlCheerResponse>(client.UploadString("https://gql.twitch.tv/gql", "{\"query\":\"query{cheerConfig{groups{nodes{id, prefix, tiers{bits}}, templateURL}},user(id:\\\"" + channel_id + "\\\"){cheer{cheerGroups{nodes{id,prefix,tiers{bits}},templateURL}}}}\",\"variables\":{}}")); if (cheerResponse != null && cheerResponse.data != null) { List <CheerGroup> groupList = new List <CheerGroup>(); foreach (CheerGroup group in cheerResponse.data.cheerConfig.groups) { groupList.Add(group); } if (cheerResponse.data.user != null && cheerResponse.data.user.cheer != null && cheerResponse.data.user.cheer.cheerGroups != null) { foreach (var group in cheerResponse.data.user.cheer.cheerGroups) { groupList.Add(group); } } foreach (CheerGroup group in groupList) { string templateURL = group.templateURL; foreach (CheerNode node in group.nodes) { string prefix = node.prefix; List <KeyValuePair <int, TwitchEmote> > tierList = new List <KeyValuePair <int, TwitchEmote> >(); CheerEmote newEmote = new CheerEmote() { prefix = prefix, tierList = tierList }; foreach (Tier tier in node.tiers) { try { int minBits = tier.bits; string fileName = Path.Combine(bitsFolder, prefix + minBits + "_2x.gif"); byte[] finalBytes = null; if (File.Exists(fileName)) { try { finalBytes = File.ReadAllBytes(fileName); } catch { } } if (finalBytes == null) { string url = templateURL.Replace("PREFIX", node.prefix.ToLower()).Replace("BACKGROUND", "dark").Replace("ANIMATION", "animated").Replace("TIER", tier.bits.ToString()).Replace("SCALE.EXTENSION", "2.gif"); byte[] bytes = client.DownloadData(url); try { File.WriteAllBytes(fileName, bytes); } catch { } finalBytes = bytes; } if (finalBytes != null) { MemoryStream ms = new MemoryStream(finalBytes); TwitchEmote emote = new TwitchEmote(new List <SKBitmap>() { SKBitmap.Decode(finalBytes) }, SKCodec.Create(ms), prefix, "gif", "", 2, finalBytes); tierList.Add(new KeyValuePair <int, TwitchEmote>(minBits, emote)); } } catch { } } cheerEmotes.Add(newEmote); } } } } return(cheerEmotes); }