private async Task PostAnimeEmbedAsync(JapaneseMedia media, JToken token) { token = token["attributes"]; if (Context.Channel is ITextChannel channel && !channel.IsNsfw && token["nsfw"] != null && token["nsfw"].Value <bool>()) { throw new CommandFailed("The result of your search was NSFW and thus, can only be shown in a NSFW channel."); } var embed = new EmbedBuilder { Title = token["canonicalTitle"].Value <string>(), Color = token["nsfw"] == null ? Color.Green : (token["nsfw"].Value <bool>() ? new Color(255, 20, 147) : Color.Green), Url = "https://kitsu.io/" + (media == JapaneseMedia.ANIME ? "anime" : "manga") + "/" + token["slug"].Value <string>(), Description = token["synopsis"].Value <string>().Length > 1000 ? token["synopsis"].Value <string>().Substring(0, 1000) + " [...]" : token["synopsis"].Value <string>(), // Description that fill the whole screen are a pain ImageUrl = token["posterImage"]["original"].Value <string>() }; if (token["titles"] != null && token["titles"]["en"] != null && !string.IsNullOrEmpty(token["titles"]["en"].Value <string>()) && token["titles"]["en"].Value <string>() != token["canonicalTitle"].Value <string>()) // No use displaying this if it's the same as the embed title { embed.AddField("English Title", token["titles"]["en"].Value <string>()); } if (media == JapaneseMedia.ANIME && token["episodeCount"] != null) { embed.AddField("Episode Count", token["episodeCount"].Value <string>() + (token["episodeLength"] != null ? $" ({token["episodeLength"].Value<string>()} minutes per episode)" : ""), true); } if (token["startDate"] == null) { embed.AddField("Release Date", "To Be Announced", true); } else { embed.AddField("Release Date", token["startDate"] + " - " + (token["endDate"] != null ? "???" : token["endDate"]), true); } if (!string.IsNullOrEmpty(token["ageRatingGuide"].Value <string>())) { embed.AddField("Audiance Warning", token["ageRatingGuide"].Value <string>(), true); } if (!string.IsNullOrEmpty(token["averageRating"].Value <string>())) { embed.AddField("Kitsu User Rating", token["averageRating"].Value <string>(), true); } await ReplyAsync(embed : embed.Build()); }
public static async Task <JToken> SearchMediaAsync(JapaneseMedia media, string query, bool onlyExactMatch = false) { // Authentification is required to see NSFW content if (StaticObjects.KitsuAuth != null) { if (StaticObjects.KitsuAccessToken == null) { var answer = await StaticObjects.HttpClient.SendAsync(StaticObjects.KitsuAuth); var authJson = JsonConvert.DeserializeObject <JObject>(await answer.Content.ReadAsStringAsync()); StaticObjects.KitsuAccessToken = authJson["access_token"].Value <string>(); StaticObjects.KitsuRefreshDate = DateTime.Now.AddSeconds(authJson["expires_in"].Value <int>()); StaticObjects.KitsuRefreshToken = authJson["refresh_token"].Value <string>(); } else if (DateTime.Now > StaticObjects.KitsuRefreshDate) // An access token last 30 days so we probably don't have to care about this but we do, just in case { var request = new HttpRequestMessage(HttpMethod.Post, "https://kitsu.io/api/oauth/token") { Content = new FormUrlEncodedContent(new Dictionary <string, string> { { "grant_type", "refresh_token" }, { "refresh_token", StaticObjects.KitsuRefreshToken } }) }; var answer = await StaticObjects.HttpClient.SendAsync(request); var authJson = JsonConvert.DeserializeObject <JObject>(await answer.Content.ReadAsStringAsync()); StaticObjects.KitsuAccessToken = authJson["access_token"].Value <string>(); StaticObjects.KitsuRefreshDate = DateTime.Now.AddSeconds(authJson["expires_in"].Value <int>()); StaticObjects.KitsuRefreshToken = authJson["refresh_token"].Value <string>(); } } StaticObjects.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", StaticObjects.KitsuAccessToken); // TODO: Check if this can cause an issue with threads // For anime we need to contact the anime endpoint, manga and light novels are on the manga endpoint // The limit=5 is because we only take the 5 firsts results to not end up with things that are totally unrelated var json = JsonConvert.DeserializeObject <JObject>(await StaticObjects.HttpClient.GetStringAsync("https://kitsu.io/api/edge/" + (media == JapaneseMedia.ANIME ? "anime" : "manga") + "?page[limit]=5&filter[text]=" + HttpUtility.UrlEncode(query))); StaticObjects.HttpClient.DefaultRequestHeaders.Authorization = null; var data = json["data"].Value <JArray>(); // Filter data depending of wanted media JToken[] allData; if (media == JapaneseMedia.MANGA) { allData = data.Where(x => x["attributes"]["subtype"].Value <string>() != "novel").ToArray(); } else if (media == JapaneseMedia.LIGHT_NOVEL) { allData = data.Where(x => x["attributes"]["subtype"].Value <string>() == "novel").ToArray(); } else { allData = data.ToArray(); } if (allData.Length == 0) { throw new CommandFailed("Nothing was found with this name."); } string cleanName = Utils.CleanWord(query); // Cleaned word for comparaisons // If we can find an exact match, we go with that string upperName = query.ToUpper(); foreach (var elem in allData) { if (elem["attributes"]["canonicalTitle"].Value <string>().ToUpper() == upperName || elem["attributes"]["titles"]["en"] != null && elem["attributes"]["titles"]["en"].Value <string>().ToUpper() == upperName || elem["attributes"]["titles"]["en_jp"] != null && elem["attributes"]["titles"]["en_jp"].Value <string>().ToUpper() == upperName || elem["attributes"]["titles"]["en_us"] != null && elem["attributes"]["titles"]["en_us"].Value <string>().ToUpper() == upperName) { return(elem); } } if (onlyExactMatch) // Used by subscriptions (because we want to be 100% sure to not match the wrong anime) { throw new CommandFailed("Nothing was found with this name"); } // Else we try to find something that somehow match foreach (var elem in allData) { if (Utils.CleanWord(elem["attributes"]["canonicalTitle"].Value <string>()).Contains(cleanName) || elem["attributes"]["titles"]["en"] != null && Utils.CleanWord(elem["attributes"]["titles"]["en"].Value <string>() ?? "").Contains(cleanName) || elem["attributes"]["titles"]["en_jp"] != null && Utils.CleanWord(elem["attributes"]["titles"]["en_jp"].Value <string>() ?? "").Contains(cleanName) || elem["attributes"]["titles"]["en_us"] != null && Utils.CleanWord(elem["attributes"]["titles"]["en_us"].Value <string>() ?? "").Contains(cleanName)) { // We would rather find the episodes and not some OVA/ONA // We don't filter them before so we can fallback on them if we find nothing else if (media == JapaneseMedia.ANIME && elem["attributes"]["subtype"].Value <string>() != "TV") { continue; } return(elem); } } // Otherwise, we just fall back on the first result available return(allData[0]); }