Using declarations and other code reformats

This commit is contained in:
Kwoth
2021-12-26 03:22:45 +01:00
parent d18f9429c6
commit b85ba177cd
64 changed files with 2349 additions and 2736 deletions

View File

@@ -47,64 +47,62 @@ public partial class Searches
var fullQueryLink = "https://myanimelist.net/profile/" + name;
var config = Configuration.Default.WithDefaultLoader();
using (var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink).ConfigureAwait(false))
{
var imageElem = document.QuerySelector("body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-left > div.user-profile > div.user-image > img");
var imageUrl = ((IHtmlImageElement)imageElem)?.Source ?? "http://icecream.me/uploads/870b03f36b59cc16ebfe314ef2dde781.png";
using var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink).ConfigureAwait(false);
var imageElem = document.QuerySelector("body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-left > div.user-profile > div.user-image > img");
var imageUrl = ((IHtmlImageElement)imageElem)?.Source ?? "http://icecream.me/uploads/870b03f36b59cc16ebfe314ef2dde781.png";
var stats = document.QuerySelectorAll("body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-right > div#statistics > div.user-statistics-stats > div.stats > div.clearfix > ul.stats-status > li > span").Select(x => x.InnerHtml).ToList();
var stats = document.QuerySelectorAll("body > div#myanimelist > div.wrapper > div#contentWrapper > div#content > div.content-container > div.container-right > div#statistics > div.user-statistics-stats > div.stats > div.clearfix > ul.stats-status > li > span").Select(x => x.InnerHtml).ToList();
var favorites = document.QuerySelectorAll("div.user-favorites > div.di-tc");
var favorites = document.QuerySelectorAll("div.user-favorites > div.di-tc");
var favAnime = GetText(strs.anime_no_fav);
if (favorites.Length > 0 && favorites[0].QuerySelector("p") is null)
favAnime = string.Join("\n", favorites[0].QuerySelectorAll("ul > li > div.di-tc.va-t > a")
.Shuffle()
.Take(3)
.Select(x =>
{
var elem = (IHtmlAnchorElement)x;
return $"[{elem.InnerHtml}]({elem.Href})";
}));
var favAnime = GetText(strs.anime_no_fav);
if (favorites.Length > 0 && favorites[0].QuerySelector("p") is null)
favAnime = string.Join("\n", favorites[0].QuerySelectorAll("ul > li > div.di-tc.va-t > a")
.Shuffle()
.Take(3)
.Select(x =>
{
var elem = (IHtmlAnchorElement)x;
return $"[{elem.InnerHtml}]({elem.Href})";
}));
var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li.clearfix")
.Select(x => Tuple.Create(x.Children[0].InnerHtml, x.Children[1].InnerHtml))
.ToList();
var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li.clearfix")
.Select(x => Tuple.Create(x.Children[0].InnerHtml, x.Children[1].InnerHtml))
.ToList();
var daysAndMean = document.QuerySelectorAll("div.anime:nth-child(1) > div:nth-child(2) > div")
.Select(x => x.TextContent.Split(':').Select(y => y.Trim()).ToArray())
.ToArray();
var daysAndMean = document.QuerySelectorAll("div.anime:nth-child(1) > div:nth-child(2) > div")
.Select(x => x.TextContent.Split(':').Select(y => y.Trim()).ToArray())
.ToArray();
var embed = _eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.mal_profile(name)))
.AddField("💚 " + GetText(strs.watching), stats[0], true)
.AddField("💙 " + GetText(strs.completed), stats[1], true);
if (info.Count < 3)
embed.AddField("💛 " + GetText(strs.on_hold), stats[2], true);
embed
.AddField("💔 " + GetText(strs.dropped), stats[3], true)
.AddField("⚪ " + GetText(strs.plan_to_watch), stats[4], true)
.AddField("🕐 " + daysAndMean[0][0], daysAndMean[0][1], true)
.AddField("📊 " + daysAndMean[1][0], daysAndMean[1][1], true)
.AddField(MalInfoToEmoji(info[0].Item1) + " " + info[0].Item1, info[0].Item2.TrimTo(20), true)
.AddField(MalInfoToEmoji(info[1].Item1) + " " + info[1].Item1, info[1].Item2.TrimTo(20), true);
if (info.Count > 2)
embed.AddField(MalInfoToEmoji(info[2].Item1) + " " + info[2].Item1, info[2].Item2.TrimTo(20), true);
var embed = _eb.Create()
.WithOkColor()
.WithTitle(GetText(strs.mal_profile(name)))
.AddField("💚 " + GetText(strs.watching), stats[0], true)
.AddField("💙 " + GetText(strs.completed), stats[1], true);
if (info.Count < 3)
embed.AddField("💛 " + GetText(strs.on_hold), stats[2], true);
embed
.AddField("💔 " + GetText(strs.dropped), stats[3], true)
.AddField("⚪ " + GetText(strs.plan_to_watch), stats[4], true)
.AddField("🕐 " + daysAndMean[0][0], daysAndMean[0][1], true)
.AddField("📊 " + daysAndMean[1][0], daysAndMean[1][1], true)
.AddField(MalInfoToEmoji(info[0].Item1) + " " + info[0].Item1, info[0].Item2.TrimTo(20), true)
.AddField(MalInfoToEmoji(info[1].Item1) + " " + info[1].Item1, info[1].Item2.TrimTo(20), true);
if (info.Count > 2)
embed.AddField(MalInfoToEmoji(info[2].Item1) + " " + info[2].Item1, info[2].Item2.TrimTo(20), true);
embed
.WithDescription($@"
embed
.WithDescription($@"
** https://myanimelist.net/animelist/{ name } **
**{GetText(strs.top_3_fav_anime)}**
{favAnime}"
)
.WithUrl(fullQueryLink)
.WithImageUrl(imageUrl);
)
.WithUrl(fullQueryLink)
.WithImageUrl(imageUrl);
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
private static string MalInfoToEmoji(string info)

View File

@@ -53,34 +53,32 @@ public class PicartoProvider : Provider
if (logins.Count == 0)
return new();
using (var http = _httpClientFactory.CreateClient())
using var http = _httpClientFactory.CreateClient();
var toReturn = new List<StreamData>();
foreach (var login in logins)
{
var toReturn = new List<StreamData>();
foreach (var login in logins)
try
{
try
{
http.DefaultRequestHeaders.Accept.Add(new("application/json"));
// get id based on the username
var res = await http.GetAsync($"https://api.picarto.tv/v1/channel/name/{login}");
http.DefaultRequestHeaders.Accept.Add(new("application/json"));
// get id based on the username
var res = await http.GetAsync($"https://api.picarto.tv/v1/channel/name/{login}");
if (!res.IsSuccessStatusCode)
continue;
if (!res.IsSuccessStatusCode)
continue;
var userData = JsonConvert.DeserializeObject<PicartoChannelResponse>(await res.Content.ReadAsStringAsync())!;
var userData = JsonConvert.DeserializeObject<PicartoChannelResponse>(await res.Content.ReadAsStringAsync())!;
toReturn.Add(ToStreamData(userData));
_failingStreams.TryRemove(login, out _);
}
catch (Exception ex)
{
Log.Warning(ex, $"Something went wrong retreiving {Platform} stream data for {login}: {ex.Message}");
_failingStreams.TryAdd(login, DateTime.UtcNow);
}
toReturn.Add(ToStreamData(userData));
_failingStreams.TryRemove(login, out _);
}
catch (Exception ex)
{
Log.Warning(ex, $"Something went wrong retreiving {Platform} stream data for {login}: {ex.Message}");
_failingStreams.TryAdd(login, DateTime.UtcNow);
}
return toReturn;
}
return toReturn;
}
private StreamData ToStreamData(PicartoChannelResponse stream)

View File

@@ -53,63 +53,61 @@ public class TwitchProvider : Provider
if (logins.Count == 0)
return new();
using (var http = _httpClientFactory.CreateClient())
using var http = _httpClientFactory.CreateClient();
http.DefaultRequestHeaders.Add("Client-Id", "67w6z9i09xv2uoojdm9l0wsyph4hxo6");
http.DefaultRequestHeaders.Add("Accept", "application/vnd.twitchtv.v5+json");
var toReturn = new List<StreamData>();
foreach (var login in logins)
{
http.DefaultRequestHeaders.Add("Client-Id", "67w6z9i09xv2uoojdm9l0wsyph4hxo6");
http.DefaultRequestHeaders.Add("Accept", "application/vnd.twitchtv.v5+json");
var toReturn = new List<StreamData>();
foreach (var login in logins)
try
{
try
// get id based on the username
var idsStr = await http.GetStringAsync($"https://api.twitch.tv/kraken/users?login={login}");
var userData = JsonConvert.DeserializeObject<TwitchUsersResponseV5>(idsStr);
var user = userData?.Users.FirstOrDefault();
// if user can't be found, skip, it means there is no such user
if (user is null)
continue;
// get stream data
var str = await http.GetStringAsync($"https://api.twitch.tv/kraken/streams/{user.Id}");
var resObj =
JsonConvert.DeserializeAnonymousType(str, new {Stream = new TwitchResponseV5.Stream()});
// if stream is null, user is not streaming
if (resObj?.Stream is null)
{
// get id based on the username
var idsStr = await http.GetStringAsync($"https://api.twitch.tv/kraken/users?login={login}");
var userData = JsonConvert.DeserializeObject<TwitchUsersResponseV5>(idsStr);
var user = userData?.Users.FirstOrDefault();
// if user can't be found, skip, it means there is no such user
if (user is null)
continue;
// get stream data
var str = await http.GetStringAsync($"https://api.twitch.tv/kraken/streams/{user.Id}");
var resObj =
JsonConvert.DeserializeAnonymousType(str, new {Stream = new TwitchResponseV5.Stream()});
// if stream is null, user is not streaming
if (resObj?.Stream is null)
{
// if user is not streaming, get his offline banner
var chStr = await http.GetStringAsync($"https://api.twitch.tv/kraken/channels/{user.Id}");
var ch = JsonConvert.DeserializeObject<TwitchResponseV5.Channel>(chStr)!;
// if user is not streaming, get his offline banner
var chStr = await http.GetStringAsync($"https://api.twitch.tv/kraken/channels/{user.Id}");
var ch = JsonConvert.DeserializeObject<TwitchResponseV5.Channel>(chStr)!;
toReturn.Add(new()
{
StreamType = FollowedStream.FType.Twitch,
Name = ch.DisplayName,
UniqueName = ch.Name,
Title = ch.Status,
IsLive = false,
AvatarUrl = ch.Logo,
StreamUrl = $"https://twitch.tv/{ch.Name}",
Preview = ch.VideoBanner // set video banner as the preview,
});
continue; // move on
}
toReturn.Add(new()
{
StreamType = FollowedStream.FType.Twitch,
Name = ch.DisplayName,
UniqueName = ch.Name,
Title = ch.Status,
IsLive = false,
AvatarUrl = ch.Logo,
StreamUrl = $"https://twitch.tv/{ch.Name}",
Preview = ch.VideoBanner // set video banner as the preview,
});
continue; // move on
}
toReturn.Add(ToStreamData(resObj.Stream));
_failingStreams.TryRemove(login, out _);
}
catch (Exception ex)
{
Log.Warning($"Something went wrong retreiving {Platform} stream data for {login}: {ex.Message}");
_failingStreams.TryAdd(login, DateTime.UtcNow);
}
toReturn.Add(ToStreamData(resObj.Stream));
_failingStreams.TryRemove(login, out _);
}
catch (Exception ex)
{
Log.Warning($"Something went wrong retreiving {Platform} stream data for {login}: {ex.Message}");
_failingStreams.TryAdd(login, DateTime.UtcNow);
}
return toReturn;
}
return toReturn;
}
private StreamData ToStreamData(TwitchResponseV5.Stream stream)

View File

@@ -39,29 +39,27 @@ public partial class Searches
if (--page < 0)
return;
using (var http = _httpFactory.CreateClient("memelist"))
{
var res = await http.GetAsync("https://api.memegen.link/templates/")
.ConfigureAwait(false);
using var http = _httpFactory.CreateClient("memelist");
var res = await http.GetAsync("https://api.memegen.link/templates/")
.ConfigureAwait(false);
var rawJson = await res.Content.ReadAsStringAsync();
var rawJson = await res.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<List<MemegenTemplate>>(rawJson);
var data = JsonConvert.DeserializeObject<List<MemegenTemplate>>(rawJson);
await ctx.SendPaginatedConfirmAsync(page, curPage =>
await ctx.SendPaginatedConfirmAsync(page, curPage =>
{
var templates = string.Empty;
foreach (var template in data.Skip(curPage * 15).Take(15))
{
var templates = string.Empty;
foreach (var template in data.Skip(curPage * 15).Take(15))
{
templates += $"**{template.Name}:**\n key: `{template.Id}`\n";
}
var embed = _eb.Create()
.WithOkColor()
.WithDescription(templates);
templates += $"**{template.Name}:**\n key: `{template.Id}`\n";
}
var embed = _eb.Create()
.WithOkColor()
.WithDescription(templates);
return embed;
}, data.Count, 15).ConfigureAwait(false);
}
return embed;
}, data.Count, 15).ConfigureAwait(false);
}
[NadekoCommand, Aliases]

View File

@@ -23,101 +23,97 @@ public partial class Searches
if (string.IsNullOrWhiteSpace(user))
return;
using (var http = _httpFactory.CreateClient())
using var http = _httpFactory.CreateClient();
var modeNumber = string.IsNullOrWhiteSpace(mode)
? 0
: ResolveGameMode(mode);
try
{
var modeNumber = string.IsNullOrWhiteSpace(mode)
? 0
: ResolveGameMode(mode);
try
if (string.IsNullOrWhiteSpace(_creds.OsuApiKey))
{
if (string.IsNullOrWhiteSpace(_creds.OsuApiKey))
{
await ReplyErrorLocalizedAsync(strs.osu_api_key).ConfigureAwait(false);
return;
}
var smode = ResolveGameMode(modeNumber);
var userReq = $"https://osu.ppy.sh/api/get_user?k={_creds.OsuApiKey}&u={user}&m={modeNumber}";
var userResString = await http.GetStringAsync(userReq)
.ConfigureAwait(false);
var objs = JsonConvert.DeserializeObject<List<OsuUserData>>(userResString);
if (objs.Count == 0)
{
await ReplyErrorLocalizedAsync(strs.osu_user_not_found).ConfigureAwait(false);
return;
}
var obj = objs[0];
var userId = obj.UserId;
await ctx.Channel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle($"osu! {smode} profile for {user}")
.WithThumbnailUrl($"https://a.ppy.sh/{userId}")
.WithDescription($"https://osu.ppy.sh/u/{userId}")
.AddField("Official Rank", $"#{obj.PpRank}", true)
.AddField("Country Rank", $"#{obj.PpCountryRank} :flag_{obj.Country.ToLower()}:", true)
.AddField("Total PP", Math.Round(obj.PpRaw, 2), true)
.AddField("Accuracy", Math.Round(obj.Accuracy, 2) + "%", true)
.AddField("Playcount", obj.Playcount, true)
.AddField("Level", Math.Round(obj.Level), true)
);
await ReplyErrorLocalizedAsync(strs.osu_api_key).ConfigureAwait(false);
return;
}
catch (ArgumentOutOfRangeException)
var smode = ResolveGameMode(modeNumber);
var userReq = $"https://osu.ppy.sh/api/get_user?k={_creds.OsuApiKey}&u={user}&m={modeNumber}";
var userResString = await http.GetStringAsync(userReq)
.ConfigureAwait(false);
var objs = JsonConvert.DeserializeObject<List<OsuUserData>>(userResString);
if (objs.Count == 0)
{
await ReplyErrorLocalizedAsync(strs.osu_user_not_found).ConfigureAwait(false);
return;
}
catch (Exception ex)
{
await ReplyErrorLocalizedAsync(strs.osu_failed).ConfigureAwait(false);
Log.Warning(ex, "Osu command failed");
}
var obj = objs[0];
var userId = obj.UserId;
await ctx.Channel.EmbedAsync(_eb.Create()
.WithOkColor()
.WithTitle($"osu! {smode} profile for {user}")
.WithThumbnailUrl($"https://a.ppy.sh/{userId}")
.WithDescription($"https://osu.ppy.sh/u/{userId}")
.AddField("Official Rank", $"#{obj.PpRank}", true)
.AddField("Country Rank", $"#{obj.PpCountryRank} :flag_{obj.Country.ToLower()}:", true)
.AddField("Total PP", Math.Round(obj.PpRaw, 2), true)
.AddField("Accuracy", Math.Round(obj.Accuracy, 2) + "%", true)
.AddField("Playcount", obj.Playcount, true)
.AddField("Level", Math.Round(obj.Level), true)
);
}
catch (ArgumentOutOfRangeException)
{
await ReplyErrorLocalizedAsync(strs.osu_user_not_found).ConfigureAwait(false);
}
catch (Exception ex)
{
await ReplyErrorLocalizedAsync(strs.osu_failed).ConfigureAwait(false);
Log.Warning(ex, "Osu command failed");
}
}
[NadekoCommand, Aliases]
public async Task Gatari(string user, [Leftover] string mode = null)
{
using (var http = _httpFactory.CreateClient())
using var http = _httpFactory.CreateClient();
var modeNumber = string.IsNullOrWhiteSpace(mode)
? 0
: ResolveGameMode(mode);
var modeStr = ResolveGameMode(modeNumber);
var resString = await http
.GetStringAsync($"https://api.gatari.pw/user/stats?u={user}&mode={modeNumber}")
.ConfigureAwait(false);
var statsResponse = JsonConvert.DeserializeObject<GatariUserStatsResponse>(resString);
if (statsResponse.Code != 200 || statsResponse.Stats.Id == 0)
{
var modeNumber = string.IsNullOrWhiteSpace(mode)
? 0
: ResolveGameMode(mode);
var modeStr = ResolveGameMode(modeNumber);
var resString = await http
.GetStringAsync($"https://api.gatari.pw/user/stats?u={user}&mode={modeNumber}")
.ConfigureAwait(false);
var statsResponse = JsonConvert.DeserializeObject<GatariUserStatsResponse>(resString);
if (statsResponse.Code != 200 || statsResponse.Stats.Id == 0)
{
await ReplyErrorLocalizedAsync(strs.osu_user_not_found).ConfigureAwait(false);
return;
}
var usrResString = await http.GetStringAsync($"https://api.gatari.pw/users/get?u={user}")
.ConfigureAwait(false);
var userData = JsonConvert.DeserializeObject<GatariUserResponse>(usrResString).Users[0];
var userStats = statsResponse.Stats;
var embed = _eb.Create()
.WithOkColor()
.WithTitle($"osu!Gatari {modeStr} profile for {user}")
.WithThumbnailUrl($"https://a.gatari.pw/{userStats.Id}")
.WithDescription($"https://osu.gatari.pw/u/{userStats.Id}")
.AddField("Official Rank", $"#{userStats.Rank}", true)
.AddField("Country Rank", $"#{userStats.CountryRank} :flag_{userData.Country.ToLower()}:", true)
.AddField("Total PP", userStats.Pp, true)
.AddField("Accuracy", $"{Math.Round(userStats.AvgAccuracy, 2)}%", true)
.AddField("Playcount", userStats.Playcount, true)
.AddField("Level", userStats.Level, true);
await ctx.Channel.EmbedAsync(embed);
await ReplyErrorLocalizedAsync(strs.osu_user_not_found).ConfigureAwait(false);
return;
}
var usrResString = await http.GetStringAsync($"https://api.gatari.pw/users/get?u={user}")
.ConfigureAwait(false);
var userData = JsonConvert.DeserializeObject<GatariUserResponse>(usrResString).Users[0];
var userStats = statsResponse.Stats;
var embed = _eb.Create()
.WithOkColor()
.WithTitle($"osu!Gatari {modeStr} profile for {user}")
.WithThumbnailUrl($"https://a.gatari.pw/{userStats.Id}")
.WithDescription($"https://osu.gatari.pw/u/{userStats.Id}")
.AddField("Official Rank", $"#{userStats.Rank}", true)
.AddField("Country Rank", $"#{userStats.CountryRank} :flag_{userData.Country.ToLower()}:", true)
.AddField("Total PP", userStats.Pp, true)
.AddField("Accuracy", $"{Math.Round(userStats.AvgAccuracy, 2)}%", true)
.AddField("Playcount", userStats.Playcount, true)
.AddField("Level", userStats.Level, true);
await ctx.Channel.EmbedAsync(embed);
}
[NadekoCommand, Aliases]
@@ -135,62 +131,60 @@ public partial class Searches
return;
}
using (var http = _httpFactory.CreateClient())
using var http = _httpFactory.CreateClient();
var m = 0;
if (!string.IsNullOrWhiteSpace(mode))
{
var m = 0;
if (!string.IsNullOrWhiteSpace(mode))
{
m = ResolveGameMode(mode);
}
m = ResolveGameMode(mode);
}
var reqString = $"https://osu.ppy.sh/api/get_user_best" +
$"?k={_creds.OsuApiKey}" +
$"&u={Uri.EscapeDataString(user)}" +
$"&type=string" +
$"&limit=5" +
$"&m={m}";
var reqString = $"https://osu.ppy.sh/api/get_user_best" +
$"?k={_creds.OsuApiKey}" +
$"&u={Uri.EscapeDataString(user)}" +
$"&type=string" +
$"&limit=5" +
$"&m={m}";
var resString = await http.GetStringAsync(reqString).ConfigureAwait(false);
var obj = JsonConvert.DeserializeObject<List<OsuUserBests>>(resString);
var resString = await http.GetStringAsync(reqString).ConfigureAwait(false);
var obj = JsonConvert.DeserializeObject<List<OsuUserBests>>(resString);
var mapTasks = obj.Select(async item =>
{
var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps" +
$"?k={_creds.OsuApiKey}" +
$"&b={item.BeatmapId}";
var mapTasks = obj.Select(async item =>
{
var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps" +
$"?k={_creds.OsuApiKey}" +
$"&b={item.BeatmapId}";
var mapResString = await http.GetStringAsync(mapReqString).ConfigureAwait(false);
var map = JsonConvert.DeserializeObject<List<OsuMapData>>(mapResString).FirstOrDefault();
if (map is null)
return default;
var pp = Math.Round(item.Pp, 2);
var acc = CalculateAcc(item, m);
var mods = ResolveMods(item.EnabledMods);
var mapResString = await http.GetStringAsync(mapReqString).ConfigureAwait(false);
var map = JsonConvert.DeserializeObject<List<OsuMapData>>(mapResString).FirstOrDefault();
if (map is null)
return default;
var pp = Math.Round(item.Pp, 2);
var acc = CalculateAcc(item, m);
var mods = ResolveMods(item.EnabledMods);
var title = $"{map.Artist}-{map.Title} ({map.Version})";
var desc = $@"[/b/{item.BeatmapId}](https://osu.ppy.sh/b/{item.BeatmapId})
var title = $"{map.Artist}-{map.Title} ({map.Version})";
var desc = $@"[/b/{item.BeatmapId}](https://osu.ppy.sh/b/{item.BeatmapId})
{pp + "pp",-7} | {acc + "%",-7}
";
if (mods != "+")
{
desc += Format.Bold(mods);
}
return (title, desc);
});
var eb = _eb.Create()
.WithOkColor()
.WithTitle($"Top 5 plays for {user}");
var mapData = await Task.WhenAll(mapTasks);
foreach (var (title, desc) in mapData.Where(x => x != default))
if (mods != "+")
{
eb.AddField(title, desc, false);
desc += Format.Bold(mods);
}
await ctx.Channel.EmbedAsync(eb).ConfigureAwait(false);
return (title, desc);
});
var eb = _eb.Create()
.WithOkColor()
.WithTitle($"Top 5 plays for {user}");
var mapData = await Task.WhenAll(mapTasks);
foreach (var (title, desc) in mapData.Where(x => x != default))
{
eb.AddField(title, desc, false);
}
await ctx.Channel.EmbedAsync(eb).ConfigureAwait(false);
}
//https://osu.ppy.sh/wiki/Accuracy

View File

@@ -41,11 +41,9 @@ public partial class Searches
try
{
using (var http = _httpFactory.CreateClient())
{
var res = await http.GetStringAsync($"{_poeURL}{usr}").ConfigureAwait(false);
characters = JsonConvert.DeserializeObject<List<Account>>(res);
}
using var http = _httpFactory.CreateClient();
var res = await http.GetStringAsync($"{_poeURL}{usr}").ConfigureAwait(false);
characters = JsonConvert.DeserializeObject<List<Account>>(res);
}
catch
{
@@ -102,11 +100,9 @@ public partial class Searches
try
{
using (var http = _httpFactory.CreateClient())
{
var res = await http.GetStringAsync("http://api.pathofexile.com/leagues?type=main&compact=1").ConfigureAwait(false);
leagues = JsonConvert.DeserializeObject<List<Leagues>>(res);
}
using var http = _httpFactory.CreateClient();
var res = await http.GetStringAsync("http://api.pathofexile.com/leagues?type=main&compact=1").ConfigureAwait(false);
leagues = JsonConvert.DeserializeObject<List<Leagues>>(res);
}
catch
{
@@ -159,48 +155,46 @@ public partial class Searches
try
{
var res = $"{_ponURL}{leagueName}";
using (var http = _httpFactory.CreateClient())
using var http = _httpFactory.CreateClient();
var obj = JObject.Parse(await http.GetStringAsync(res).ConfigureAwait(false));
var chaosEquivalent = 0.0F;
var conversionEquivalent = 0.0F;
// poe.ninja API does not include a "chaosEquivalent" property for Chaos Orbs.
if (cleanCurrency == "Chaos Orb")
{
var obj = JObject.Parse(await http.GetStringAsync(res).ConfigureAwait(false));
var chaosEquivalent = 0.0F;
var conversionEquivalent = 0.0F;
// poe.ninja API does not include a "chaosEquivalent" property for Chaos Orbs.
if (cleanCurrency == "Chaos Orb")
{
chaosEquivalent = 1.0F;
}
else
{
var currencyInput = obj["lines"].Values<JObject>()
.Where(i => i["currencyTypeName"].Value<string>() == cleanCurrency)
.FirstOrDefault();
chaosEquivalent = float.Parse(currencyInput["chaosEquivalent"].ToString(), System.Globalization.CultureInfo.InvariantCulture);
}
if (cleanConvert == "Chaos Orb")
{
conversionEquivalent = 1.0F;
}
else
{
var currencyOutput = obj["lines"].Values<JObject>()
.Where(i => i["currencyTypeName"].Value<string>() == cleanConvert)
.FirstOrDefault();
conversionEquivalent = float.Parse(currencyOutput["chaosEquivalent"].ToString(), System.Globalization.CultureInfo.InvariantCulture);
}
var embed = _eb.Create()
.WithAuthor($"{leagueName} Currency Exchange",
"https://web.poecdn.com/image/favicon/ogimage.png",
"http://poe.ninja")
.AddField("Currency Type", cleanCurrency, true)
.AddField($"{cleanConvert} Equivalent", chaosEquivalent / conversionEquivalent, true)
.WithOkColor();
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
chaosEquivalent = 1.0F;
}
else
{
var currencyInput = obj["lines"].Values<JObject>()
.Where(i => i["currencyTypeName"].Value<string>() == cleanCurrency)
.FirstOrDefault();
chaosEquivalent = float.Parse(currencyInput["chaosEquivalent"].ToString(), System.Globalization.CultureInfo.InvariantCulture);
}
if (cleanConvert == "Chaos Orb")
{
conversionEquivalent = 1.0F;
}
else
{
var currencyOutput = obj["lines"].Values<JObject>()
.Where(i => i["currencyTypeName"].Value<string>() == cleanConvert)
.FirstOrDefault();
conversionEquivalent = float.Parse(currencyOutput["chaosEquivalent"].ToString(), System.Globalization.CultureInfo.InvariantCulture);
}
var embed = _eb.Create()
.WithAuthor($"{leagueName} Currency Exchange",
"https://web.poecdn.com/image/favicon/ogimage.png",
"http://poe.ninja")
.AddField("Currency Type", cleanCurrency, true)
.AddField($"{cleanConvert} Equivalent", chaosEquivalent / conversionEquivalent, true)
.WithOkColor();
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
catch
{

View File

@@ -39,15 +39,13 @@ public partial class Searches : NadekoModule<SearchesService>
var av = usr.RealAvatarUrl();
if (av is null)
return;
await using (var picStream = await _service.GetRipPictureAsync(usr.Nickname ?? usr.Username, av).ConfigureAwait(false))
{
await ctx.Channel.SendFileAsync(
picStream,
"rip.png",
$"Rip {Format.Bold(usr.ToString())} \n\t- " +
Format.Italics(ctx.User.ToString()))
.ConfigureAwait(false);
}
await using var picStream = await _service.GetRipPictureAsync(usr.Nickname ?? usr.Username, av).ConfigureAwait(false);
await ctx.Channel.SendFileAsync(
picStream,
"rip.png",
$"Rip {Format.Bold(usr.ToString())} \n\t- " +
Format.Italics(ctx.User.ToString()))
.ConfigureAwait(false);
}
[NadekoCommand, Aliases]
@@ -224,30 +222,28 @@ public partial class Searches : NadekoModule<SearchesService>
var fullQueryLink = $"http://imgur.com/search?q={ query }";
var config = Configuration.Default.WithDefaultLoader();
using (var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink).ConfigureAwait(false))
{
var elems = document.QuerySelectorAll("a.image-list-link").ToList();
using var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink).ConfigureAwait(false);
var elems = document.QuerySelectorAll("a.image-list-link").ToList();
if (!elems.Any())
return;
if (!elems.Any())
return;
var img = elems.ElementAtOrDefault(new NadekoRandom().Next(0, elems.Count))?.Children?.FirstOrDefault() as IHtmlImageElement;
var img = elems.ElementAtOrDefault(new NadekoRandom().Next(0, elems.Count))?.Children?.FirstOrDefault() as IHtmlImageElement;
if (img?.Source is null)
return;
if (img?.Source is null)
return;
var source = img.Source.Replace("b.", ".", StringComparison.InvariantCulture);
var source = img.Source.Replace("b.", ".", StringComparison.InvariantCulture);
var embed = _eb.Create()
.WithOkColor()
.WithAuthor(GetText(strs.image_search_for) + " " + oterms.TrimTo(50),
"http://s.imgur.com/images/logo-1200-630.jpg?",
fullQueryLink)
.WithDescription(source)
.WithImageUrl(source)
.WithTitle(ctx.User.ToString());
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
var embed = _eb.Create()
.WithOkColor()
.WithAuthor(GetText(strs.image_search_for) + " " + oterms.TrimTo(50),
"http://s.imgur.com/images/logo-1200-630.jpg?",
fullQueryLink)
.WithDescription(source)
.WithImageUrl(source)
.WithTitle(ctx.User.ToString());
await ctx.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
}
@@ -280,28 +276,24 @@ public partial class Searches : NadekoModule<SearchesService>
{
try
{
using (var _http = _httpFactory.CreateClient())
using (var req = new HttpRequestMessage(HttpMethod.Post, "https://goolnk.com/api/v1/shorten"))
using var _http = _httpFactory.CreateClient();
using var req = new HttpRequestMessage(HttpMethod.Post, "https://goolnk.com/api/v1/shorten");
var formData = new MultipartFormDataContent
{
var formData = new MultipartFormDataContent
{
{ new StringContent(query), "url" }
};
req.Content = formData;
{ new StringContent(query), "url" }
};
req.Content = formData;
using (var res = await _http.SendAsync(req).ConfigureAwait(false))
{
var content = await res.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<ShortenData>(content);
using var res = await _http.SendAsync(req).ConfigureAwait(false);
var content = await res.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject<ShortenData>(content);
if (!string.IsNullOrWhiteSpace(data?.ResultUrl))
cachedShortenedLinks.TryAdd(query, data.ResultUrl);
else
return;
if (!string.IsNullOrWhiteSpace(data?.ResultUrl))
cachedShortenedLinks.TryAdd(query, data.ResultUrl);
else
return;
shortLink = data.ResultUrl;
}
}
shortLink = data.ResultUrl;
}
catch (Exception ex)
{
@@ -477,78 +469,74 @@ public partial class Searches : NadekoModule<SearchesService>
if (!await ValidateQuery(word).ConfigureAwait(false))
return;
using (var _http = _httpFactory.CreateClient())
using var _http = _httpFactory.CreateClient();
string res;
try
{
string res;
try
res = await _cache.GetOrCreateAsync($"define_{word}", e =>
{
res = await _cache.GetOrCreateAsync($"define_{word}", e =>
{
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
return _http.GetStringAsync("https://api.pearson.com/v2/dictionaries/entries?headword=" + WebUtility.UrlEncode(word));
}).ConfigureAwait(false);
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(12);
return _http.GetStringAsync("https://api.pearson.com/v2/dictionaries/entries?headword=" + WebUtility.UrlEncode(word));
}).ConfigureAwait(false);
var data = JsonConvert.DeserializeObject<DefineModel>(res);
var data = JsonConvert.DeserializeObject<DefineModel>(res);
var datas = data.Results
.Where(x => x.Senses is not null && x.Senses.Count > 0 && x.Senses[0].Definition is not null)
.Select(x => (Sense: x.Senses[0], x.PartOfSpeech));
var datas = data.Results
.Where(x => x.Senses is not null && x.Senses.Count > 0 && x.Senses[0].Definition is not null)
.Select(x => (Sense: x.Senses[0], x.PartOfSpeech));
if (!datas.Any())
{
Log.Warning("Definition not found: {Word}", word);
await ReplyErrorLocalizedAsync(strs.define_unknown).ConfigureAwait(false);
}
var col = datas.Select(data => (
Definition: data.Sense.Definition is string
? data.Sense.Definition.ToString()
: ((JArray)JToken.Parse(data.Sense.Definition.ToString())).First.ToString(),
Example: data.Sense.Examples is null || data.Sense.Examples.Count == 0
? string.Empty
: data.Sense.Examples[0].Text,
Word: word,
WordType: string.IsNullOrWhiteSpace(data.PartOfSpeech) ? "-" : data.PartOfSpeech
)).ToList();
Log.Information($"Sending {col.Count} definition for: {word}");
await ctx.SendPaginatedConfirmAsync(0, page =>
{
var data = col.Skip(page).First();
var embed = _eb.Create()
.WithDescription(ctx.User.Mention)
.AddField(GetText(strs.word), data.Word, true)
.AddField(GetText(strs._class), data.WordType, true)
.AddField(GetText(strs.definition), data.Definition)
.WithOkColor();
if (!string.IsNullOrWhiteSpace(data.Example))
embed.AddField(GetText(strs.example), data.Example);
return embed;
}, col.Count, 1);
}
catch (Exception ex)
if (!datas.Any())
{
Log.Error(ex, "Error retrieving definition data for: {Word}", word);
Log.Warning("Definition not found: {Word}", word);
await ReplyErrorLocalizedAsync(strs.define_unknown).ConfigureAwait(false);
}
var col = datas.Select(data => (
Definition: data.Sense.Definition is string
? data.Sense.Definition.ToString()
: ((JArray)JToken.Parse(data.Sense.Definition.ToString())).First.ToString(),
Example: data.Sense.Examples is null || data.Sense.Examples.Count == 0
? string.Empty
: data.Sense.Examples[0].Text,
Word: word,
WordType: string.IsNullOrWhiteSpace(data.PartOfSpeech) ? "-" : data.PartOfSpeech
)).ToList();
Log.Information($"Sending {col.Count} definition for: {word}");
await ctx.SendPaginatedConfirmAsync(0, page =>
{
var data = col.Skip(page).First();
var embed = _eb.Create()
.WithDescription(ctx.User.Mention)
.AddField(GetText(strs.word), data.Word, true)
.AddField(GetText(strs._class), data.WordType, true)
.AddField(GetText(strs.definition), data.Definition)
.WithOkColor();
if (!string.IsNullOrWhiteSpace(data.Example))
embed.AddField(GetText(strs.example), data.Example);
return embed;
}, col.Count, 1);
}
catch (Exception ex)
{
Log.Error(ex, "Error retrieving definition data for: {Word}", word);
}
}
[NadekoCommand, Aliases]
public async Task Catfact()
{
using (var http = _httpFactory.CreateClient())
{
var response = await http.GetStringAsync("https://catfact.ninja/fact").ConfigureAwait(false);
if (response is null)
return;
using var http = _httpFactory.CreateClient();
var response = await http.GetStringAsync("https://catfact.ninja/fact").ConfigureAwait(false);
if (response is null)
return;
var fact = JObject.Parse(response)["fact"].ToString();
await SendConfirmAsync("🐈" + GetText(strs.catfact), fact).ConfigureAwait(false);
}
var fact = JObject.Parse(response)["fact"].ToString();
await SendConfirmAsync("🐈" + GetText(strs.catfact), fact).ConfigureAwait(false);
}
//done in 3.0
@@ -585,15 +573,13 @@ public partial class Searches : NadekoModule<SearchesService>
if (!await ValidateQuery(query).ConfigureAwait(false))
return;
using (var http = _httpFactory.CreateClient())
{
var result = await http.GetStringAsync("https://en.wikipedia.org//w/api.php?action=query&format=json&prop=info&redirects=1&formatversion=2&inprop=url&titles=" + Uri.EscapeDataString(query)).ConfigureAwait(false);
var data = JsonConvert.DeserializeObject<WikipediaApiModel>(result);
if (data.Query.Pages[0].Missing || string.IsNullOrWhiteSpace(data.Query.Pages[0].FullUrl))
await ReplyErrorLocalizedAsync(strs.wiki_page_not_found).ConfigureAwait(false);
else
await ctx.Channel.SendMessageAsync(data.Query.Pages[0].FullUrl).ConfigureAwait(false);
}
using var http = _httpFactory.CreateClient();
var result = await http.GetStringAsync("https://en.wikipedia.org//w/api.php?action=query&format=json&prop=info&redirects=1&formatversion=2&inprop=url&titles=" + Uri.EscapeDataString(query)).ConfigureAwait(false);
var data = JsonConvert.DeserializeObject<WikipediaApiModel>(result);
if (data.Query.Pages[0].Missing || string.IsNullOrWhiteSpace(data.Query.Pages[0].FullUrl))
await ReplyErrorLocalizedAsync(strs.wiki_page_not_found).ConfigureAwait(false);
else
await ctx.Channel.SendMessageAsync(data.Query.Pages[0].FullUrl).ConfigureAwait(false);
}
[NadekoCommand, Aliases]
@@ -605,24 +591,20 @@ public partial class Searches : NadekoModule<SearchesService>
var colorObjects = colors.Take(10)
.ToArray();
using (var img = new Image<Rgba32>(colorObjects.Length * 50, 50))
using var img = new Image<Rgba32>(colorObjects.Length * 50, 50);
for (var i = 0; i < colorObjects.Length; i++)
{
for (var i = 0; i < colorObjects.Length; i++)
{
var x = i * 50;
img.Mutate(m => m.FillPolygon(colorObjects[i], new PointF[] {
new(x, 0),
new(x + 50, 0),
new(x + 50, 50),
new(x, 50)
}));
}
await using (var ms = img.ToStream())
{
await ctx.Channel.SendFileAsync(ms, $"colors.png").ConfigureAwait(false);
}
var x = i * 50;
img.Mutate(m => m.FillPolygon(colorObjects[i], new PointF[] {
new(x, 0),
new(x + 50, 0),
new(x + 50, 50),
new(x, 50)
}));
}
await using var ms = img.ToStream();
await ctx.Channel.SendFileAsync(ms, $"colors.png").ConfigureAwait(false);
}
[NadekoCommand, Aliases]
@@ -655,35 +637,33 @@ public partial class Searches : NadekoModule<SearchesService>
return;
}
await ctx.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = _httpFactory.CreateClient())
using var http = _httpFactory.CreateClient();
http.DefaultRequestHeaders.Clear();
try
{
http.DefaultRequestHeaders.Clear();
try
{
var res = await http.GetStringAsync($"https://{Uri.EscapeDataString(target)}.fandom.com/api.php" +
$"?action=query" +
$"&format=json" +
$"&list=search" +
$"&srsearch={Uri.EscapeDataString(query)}" +
$"&srlimit=1").ConfigureAwait(false);
var items = JObject.Parse(res);
var title = items["query"]?["search"]?.FirstOrDefault()?["title"]?.ToString();
var res = await http.GetStringAsync($"https://{Uri.EscapeDataString(target)}.fandom.com/api.php" +
$"?action=query" +
$"&format=json" +
$"&list=search" +
$"&srsearch={Uri.EscapeDataString(query)}" +
$"&srlimit=1").ConfigureAwait(false);
var items = JObject.Parse(res);
var title = items["query"]?["search"]?.FirstOrDefault()?["title"]?.ToString();
if (string.IsNullOrWhiteSpace(title))
{
await ReplyErrorLocalizedAsync(strs.wikia_error).ConfigureAwait(false);
return;
}
var url = Uri.EscapeDataString($"https://{target}.fandom.com/wiki/{title}");
var response = $@"`{GetText(strs.title)}` {title.SanitizeMentions()}
`{GetText(strs.url)}:` {url}";
await ctx.Channel.SendMessageAsync(response).ConfigureAwait(false);
}
catch
if (string.IsNullOrWhiteSpace(title))
{
await ReplyErrorLocalizedAsync(strs.wikia_error).ConfigureAwait(false);
return;
}
var url = Uri.EscapeDataString($"https://{target}.fandom.com/wiki/{title}");
var response = $@"`{GetText(strs.title)}` {title.SanitizeMentions()}
`{GetText(strs.url)}:` {url}";
await ctx.Channel.SendMessageAsync(response).ConfigureAwait(false);
}
catch
{
await ReplyErrorLocalizedAsync(strs.wikia_error).ConfigureAwait(false);
}
}
@@ -694,13 +674,11 @@ public partial class Searches : NadekoModule<SearchesService>
var obj = new BibleVerses();
try
{
using (var http = _httpFactory.CreateClient())
{
var res = await http
.GetStringAsync("https://bible-api.com/" + book + " " + chapterAndVerse).ConfigureAwait(false);
using var http = _httpFactory.CreateClient();
var res = await http
.GetStringAsync("https://bible-api.com/" + book + " " + chapterAndVerse).ConfigureAwait(false);
obj = JsonConvert.DeserializeObject<BibleVerses>(res);
}
obj = JsonConvert.DeserializeObject<BibleVerses>(res);
}
catch
{

View File

@@ -61,58 +61,56 @@ public class AnimeSearchService : INService
if (!ok)
{
var config = Configuration.Default.WithDefaultLoader();
using (var document = await BrowsingContext.New(config).OpenAsync(link).ConfigureAwait(false))
using var document = await BrowsingContext.New(config).OpenAsync(link).ConfigureAwait(false);
var imageElem = document.QuerySelector("div.seriesimg > img");
if (imageElem is null)
return null;
var imageUrl = ((IHtmlImageElement)imageElem).Source;
var descElem = document.QuerySelector("div#editdescription > p");
var desc = descElem.InnerHtml;
var genres = document.QuerySelector("div#seriesgenre").Children
.Select(x => x as IHtmlAnchorElement)
.Where(x => x != null)
.Select(x => $"[{x.InnerHtml}]({x.Href})")
.ToArray();
var authors = document
.QuerySelector("div#showauthors")
.Children
.Select(x => x as IHtmlAnchorElement)
.Where(x => x != null)
.Select(x => $"[{x.InnerHtml}]({x.Href})")
.ToArray();
var score = ((IHtmlSpanElement)document
.QuerySelector("h5.seriesother > span.uvotes"))
.InnerHtml;
var status = document
.QuerySelector("div#editstatus")
.InnerHtml;
var title = document
.QuerySelector("div.w-blog-content > div.seriestitlenu")
.InnerHtml;
var obj = new NovelResult()
{
var imageElem = document.QuerySelector("div.seriesimg > img");
if (imageElem is null)
return null;
var imageUrl = ((IHtmlImageElement)imageElem).Source;
Description = desc,
Authors = authors,
Genres = genres,
ImageUrl = imageUrl,
Link = link,
Score = score,
Status = status,
Title = title,
};
var descElem = document.QuerySelector("div#editdescription > p");
var desc = descElem.InnerHtml;
await _cache.SetNovelDataAsync(link,
JsonConvert.SerializeObject(obj)).ConfigureAwait(false);
var genres = document.QuerySelector("div#seriesgenre").Children
.Select(x => x as IHtmlAnchorElement)
.Where(x => x != null)
.Select(x => $"[{x.InnerHtml}]({x.Href})")
.ToArray();
var authors = document
.QuerySelector("div#showauthors")
.Children
.Select(x => x as IHtmlAnchorElement)
.Where(x => x != null)
.Select(x => $"[{x.InnerHtml}]({x.Href})")
.ToArray();
var score = ((IHtmlSpanElement)document
.QuerySelector("h5.seriesother > span.uvotes"))
.InnerHtml;
var status = document
.QuerySelector("div#editstatus")
.InnerHtml;
var title = document
.QuerySelector("div.w-blog-content > div.seriestitlenu")
.InnerHtml;
var obj = new NovelResult()
{
Description = desc,
Authors = authors,
Genres = genres,
ImageUrl = imageUrl,
Link = link,
Score = score,
Status = status,
Title = title,
};
await _cache.SetNovelDataAsync(link,
JsonConvert.SerializeObject(obj)).ConfigureAwait(false);
return obj;
}
return obj;
}
return JsonConvert.DeserializeObject<NovelResult>(data);

View File

@@ -156,15 +156,13 @@ public class FeedsService : INService
public List<FeedSub> GetFeeds(ulong guildId)
{
using (var uow = _db.GetDbContext())
{
return uow.GuildConfigsForId(guildId,
set => set.Include(x => x.FeedSubs)
.ThenInclude(x => x.GuildConfig))
.FeedSubs
.OrderBy(x => x.Id)
.ToList();
}
using var uow = _db.GetDbContext();
return uow.GuildConfigsForId(guildId,
set => set.Include(x => x.FeedSubs)
.ThenInclude(x => x.GuildConfig))
.FeedSubs
.OrderBy(x => x.Id)
.ToList();
}
public bool AddFeed(ulong guildId, ulong channelId, string rssFeed)
@@ -177,32 +175,30 @@ public class FeedsService : INService
Url = rssFeed.Trim(),
};
using (var uow = _db.GetDbContext())
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId,
set => set.Include(x => x.FeedSubs)
.ThenInclude(x => x.GuildConfig));
if (gc.FeedSubs.Any(x => x.Url.ToLower() == fs.Url.ToLower()))
{
var gc = uow.GuildConfigsForId(guildId,
set => set.Include(x => x.FeedSubs)
.ThenInclude(x => x.GuildConfig));
return false;
}
else if (gc.FeedSubs.Count >= 10)
{
return false;
}
if (gc.FeedSubs.Any(x => x.Url.ToLower() == fs.Url.ToLower()))
gc.FeedSubs.Add(fs);
uow.SaveChanges();
//adding all, in case bot wasn't on this guild when it started
foreach (var feed in gc.FeedSubs)
{
_subs.AddOrUpdate(feed.Url.ToLower(), new HashSet<FeedSub>() {feed}, (k, old) =>
{
return false;
}
else if (gc.FeedSubs.Count >= 10)
{
return false;
}
gc.FeedSubs.Add(fs);
uow.SaveChanges();
//adding all, in case bot wasn't on this guild when it started
foreach (var feed in gc.FeedSubs)
{
_subs.AddOrUpdate(feed.Url.ToLower(), new HashSet<FeedSub>() {feed}, (k, old) =>
{
old.Add(feed);
return old;
});
}
old.Add(feed);
return old;
});
}
return true;
@@ -213,24 +209,22 @@ public class FeedsService : INService
if (index < 0)
return false;
using (var uow = _db.GetDbContext())
{
var items = uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs))
.FeedSubs
.OrderBy(x => x.Id)
.ToList();
using var uow = _db.GetDbContext();
var items = uow.GuildConfigsForId(guildId, set => set.Include(x => x.FeedSubs))
.FeedSubs
.OrderBy(x => x.Id)
.ToList();
if (items.Count <= index)
return false;
var toRemove = items[index];
_subs.AddOrUpdate(toRemove.Url.ToLower(), new HashSet<FeedSub>(), (key, old) =>
{
old.Remove(toRemove);
return old;
});
uow.Remove(toRemove);
uow.SaveChanges();
}
if (items.Count <= index)
return false;
var toRemove = items[index];
_subs.AddOrUpdate(toRemove.Url.ToLower(), new HashSet<FeedSub>(), (key, old) =>
{
old.Remove(toRemove);
return old;
});
uow.Remove(toRemove);
uow.SaveChanges();
return true;
}

View File

@@ -85,55 +85,49 @@ public class SearchesService : INService
public async Task<byte[]> GetRipPictureFactory((string text, Uri avatarUrl) arg)
{
var (text, avatarUrl) = arg;
using (var bg = Image.Load<Rgba32>(_imgs.Rip.ToArray()))
using var bg = Image.Load<Rgba32>(_imgs.Rip.ToArray());
var (succ, data) = (false, (byte[])null); //await _cache.TryGetImageDataAsync(avatarUrl);
if (!succ)
{
var (succ, data) = (false, (byte[])null); //await _cache.TryGetImageDataAsync(avatarUrl);
if (!succ)
using var http = _httpFactory.CreateClient();
data = await http.GetByteArrayAsync(avatarUrl);
using (var avatarImg = Image.Load<Rgba32>(data))
{
using (var http = _httpFactory.CreateClient())
{
data = await http.GetByteArrayAsync(avatarUrl);
using (var avatarImg = Image.Load<Rgba32>(data))
{
avatarImg.Mutate(x => x
.Resize(85, 85)
.ApplyRoundedCorners(42));
data = avatarImg.ToStream().ToArray();
DrawAvatar(bg, avatarImg);
}
await _cache.SetImageDataAsync(avatarUrl, data);
}
avatarImg.Mutate(x => x
.Resize(85, 85)
.ApplyRoundedCorners(42));
data = avatarImg.ToStream().ToArray();
DrawAvatar(bg, avatarImg);
}
else
{
using (var avatarImg = Image.Load<Rgba32>(data))
{
DrawAvatar(bg, avatarImg);
}
}
bg.Mutate(x => x.DrawText(
new()
{
TextOptions = new TextOptions
{
HorizontalAlignment = HorizontalAlignment.Center,
WrapTextWidth = 190,
}.WithFallbackFonts(_fonts.FallBackFonts)
},
text,
_fonts.RipFont,
SixLabors.ImageSharp.Color.Black,
new(25, 225)));
//flowa
using (var flowers = Image.Load(_imgs.RipOverlay.ToArray()))
{
bg.Mutate(x => x.DrawImage(flowers, new(0, 0), new GraphicsOptions()));
}
return bg.ToStream().ToArray();
await _cache.SetImageDataAsync(avatarUrl, data);
}
else
{
using var avatarImg = Image.Load<Rgba32>(data);
DrawAvatar(bg, avatarImg);
}
bg.Mutate(x => x.DrawText(
new()
{
TextOptions = new TextOptions
{
HorizontalAlignment = HorizontalAlignment.Center,
WrapTextWidth = 190,
}.WithFallbackFonts(_fonts.FallBackFonts)
},
text,
_fonts.RipFont,
SixLabors.ImageSharp.Color.Black,
new(25, 225)));
//flowa
using (var flowers = Image.Load(_imgs.RipOverlay.ToArray()))
{
bg.Mutate(x => x.DrawImage(flowers, new(0, 0), new GraphicsOptions()));
}
return bg.ToStream().ToArray();
}
public Task<WeatherData> GetWeatherDataAsync(string query)
@@ -148,25 +142,23 @@ public class SearchesService : INService
private async Task<WeatherData> GetWeatherDataFactory(string query)
{
using (var http = _httpFactory.CreateClient())
using var http = _httpFactory.CreateClient();
try
{
try
{
var data = await http.GetStringAsync($"http://api.openweathermap.org/data/2.5/weather?" +
$"q={query}&" +
$"appid=42cd627dd60debf25a5739e50a217d74&" +
$"units=metric").ConfigureAwait(false);
var data = await http.GetStringAsync($"http://api.openweathermap.org/data/2.5/weather?" +
$"q={query}&" +
$"appid=42cd627dd60debf25a5739e50a217d74&" +
$"units=metric").ConfigureAwait(false);
if (data is null)
return null;
return JsonConvert.DeserializeObject<WeatherData>(data);
}
catch (Exception ex)
{
Log.Warning(ex.Message);
if (data is null)
return null;
}
return JsonConvert.DeserializeObject<WeatherData>(data);
}
catch (Exception ex)
{
Log.Warning(ex.Message);
return null;
}
}
@@ -195,49 +187,42 @@ public class SearchesService : INService
try
{
using (var _http = _httpFactory.CreateClient())
using var _http = _httpFactory.CreateClient();
var res = await _cache.GetOrAddCachedDataAsync($"geo_{query}", _ =>
{
var res = await _cache.GetOrAddCachedDataAsync($"geo_{query}", _ =>
{
var url = "https://eu1.locationiq.com/v1/search.php?" +
(string.IsNullOrWhiteSpace(_creds.LocationIqApiKey) ? "key=" : $"key={_creds.LocationIqApiKey}&") +
$"q={Uri.EscapeDataString(query)}&" +
$"format=json";
var url = "https://eu1.locationiq.com/v1/search.php?" +
(string.IsNullOrWhiteSpace(_creds.LocationIqApiKey) ? "key=" : $"key={_creds.LocationIqApiKey}&") +
$"q={Uri.EscapeDataString(query)}&" +
$"format=json";
var res = _http.GetStringAsync(url);
return res;
}, "", TimeSpan.FromHours(1));
var res = _http.GetStringAsync(url);
return res;
}, "", TimeSpan.FromHours(1));
var responses = JsonConvert.DeserializeObject<LocationIqResponse[]>(res);
if (responses is null || responses.Length == 0)
{
Log.Warning("Geocode lookup failed for: {Query}", query);
return (default, TimeErrors.NotFound);
}
var geoData = responses[0];
using (var req = new HttpRequestMessage(HttpMethod.Get, "http://api.timezonedb.com/v2.1/get-time-zone?" +
$"key={_creds.TimezoneDbApiKey}&format=json&" +
"by=position&" +
$"lat={geoData.Lat}&lng={geoData.Lon}"))
{
using (var geoRes = await _http.SendAsync(req))
{
var resString = await geoRes.Content.ReadAsStringAsync();
var timeObj = JsonConvert.DeserializeObject<TimeZoneResult>(resString);
var time = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc).AddSeconds(timeObj.Timestamp);
return ((
Address: responses[0].DisplayName,
Time: time,
TimeZoneName: timeObj.TimezoneName
), default);
}
}
var responses = JsonConvert.DeserializeObject<LocationIqResponse[]>(res);
if (responses is null || responses.Length == 0)
{
Log.Warning("Geocode lookup failed for: {Query}", query);
return (default, TimeErrors.NotFound);
}
var geoData = responses[0];
using var req = new HttpRequestMessage(HttpMethod.Get, "http://api.timezonedb.com/v2.1/get-time-zone?" +
$"key={_creds.TimezoneDbApiKey}&format=json&" +
"by=position&" +
$"lat={geoData.Lat}&lng={geoData.Lon}");
using var geoRes = await _http.SendAsync(req);
var resString = await geoRes.Content.ReadAsStringAsync();
var timeObj = JsonConvert.DeserializeObject<TimeZoneResult>(resString);
var time = new DateTime(1970, 1, 1, 0, 0, 0, System.DateTimeKind.Utc).AddSeconds(timeObj.Timestamp);
return ((
Address: responses[0].DisplayName,
Time: time,
TimeZoneName: timeObj.TimezoneName
), default);
}
catch (Exception ex)
{
@@ -310,21 +295,17 @@ public class SearchesService : INService
public async Task<(string Setup, string Punchline)> GetRandomJoke()
{
using (var http = _httpFactory.CreateClient())
{
var res = await http.GetStringAsync("https://official-joke-api.appspot.com/random_joke");
var resObj = JsonConvert.DeserializeAnonymousType(res, new {setup = "", punchline = ""});
return (resObj.setup, resObj.punchline);
}
using var http = _httpFactory.CreateClient();
var res = await http.GetStringAsync("https://official-joke-api.appspot.com/random_joke");
var resObj = JsonConvert.DeserializeAnonymousType(res, new {setup = "", punchline = ""});
return (resObj.setup, resObj.punchline);
}
public async Task<string> GetChuckNorrisJoke()
{
using (var http = _httpFactory.CreateClient())
{
var response = await http.GetStringAsync(new Uri("http://api.icndb.com/jokes/random/")).ConfigureAwait(false);
return JObject.Parse(response)["value"]["joke"].ToString() + " 😆";
}
using var http = _httpFactory.CreateClient();
var response = await http.GetStringAsync(new Uri("http://api.icndb.com/jokes/random/")).ConfigureAwait(false);
return JObject.Parse(response)["value"]["joke"].ToString() + " 😆";
}
public async Task<MtgData> GetMtgCardAsync(string search)
@@ -367,30 +348,28 @@ public class SearchesService : INService
};
}
using (var http = _httpFactory.CreateClient())
using var http = _httpFactory.CreateClient();
http.DefaultRequestHeaders.Clear();
var response = await http.GetStringAsync($"https://api.magicthegathering.io/v1/cards?name={Uri.EscapeDataString(search)}")
.ConfigureAwait(false);
var responseObject = JsonConvert.DeserializeObject<MtgResponse>(response);
if (responseObject is null)
return new MtgData[0];
var cards = responseObject.Cards.Take(5).ToArray();
if (cards.Length == 0)
return new MtgData[0];
var tasks = new List<Task<MtgData>>(cards.Length);
for (var i = 0; i < cards.Length; i++)
{
http.DefaultRequestHeaders.Clear();
var response = await http.GetStringAsync($"https://api.magicthegathering.io/v1/cards?name={Uri.EscapeDataString(search)}")
.ConfigureAwait(false);
var card = cards[i];
var responseObject = JsonConvert.DeserializeObject<MtgResponse>(response);
if (responseObject is null)
return new MtgData[0];
var cards = responseObject.Cards.Take(5).ToArray();
if (cards.Length == 0)
return new MtgData[0];
var tasks = new List<Task<MtgData>>(cards.Length);
for (var i = 0; i < cards.Length; i++)
{
var card = cards[i];
tasks.Add(GetMtgDataAsync(card));
}
return await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Add(GetMtgDataAsync(card));
}
return await Task.WhenAll(tasks).ConfigureAwait(false);
}
public Task<HearthstoneCardData> GetHearthstoneCardDataAsync(string name)
@@ -404,38 +383,36 @@ public class SearchesService : INService
private async Task<HearthstoneCardData> HearthstoneCardDataFactory(string name)
{
using (var http = _httpFactory.CreateClient())
using var http = _httpFactory.CreateClient();
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("x-rapidapi-key", _creds.RapidApiKey);
try
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("x-rapidapi-key", _creds.RapidApiKey);
try
{
var response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.rapidapi.com/" +
$"cards/search/{Uri.EscapeDataString(name)}").ConfigureAwait(false);
var objs = JsonConvert.DeserializeObject<HearthstoneCardData[]>(response);
if (objs is null || objs.Length == 0)
return null;
var data = objs.FirstOrDefault(x => x.Collectible)
?? objs.FirstOrDefault(x => !string.IsNullOrEmpty(x.PlayerClass))
?? objs.FirstOrDefault();
if (data is null)
return null;
if (!string.IsNullOrWhiteSpace(data.Img))
{
data.Img = await _google.ShortenUrl(data.Img).ConfigureAwait(false);
}
if (!string.IsNullOrWhiteSpace(data.Text))
{
var converter = new Html2Markdown.Converter();
data.Text = converter.Convert(data.Text);
}
return data;
}
catch (Exception ex)
{
Log.Error(ex.Message);
var response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.rapidapi.com/" +
$"cards/search/{Uri.EscapeDataString(name)}").ConfigureAwait(false);
var objs = JsonConvert.DeserializeObject<HearthstoneCardData[]>(response);
if (objs is null || objs.Length == 0)
return null;
var data = objs.FirstOrDefault(x => x.Collectible)
?? objs.FirstOrDefault(x => !string.IsNullOrEmpty(x.PlayerClass))
?? objs.FirstOrDefault();
if (data is null)
return null;
if (!string.IsNullOrWhiteSpace(data.Img))
{
data.Img = await _google.ShortenUrl(data.Img).ConfigureAwait(false);
}
if (!string.IsNullOrWhiteSpace(data.Text))
{
var converter = new Html2Markdown.Converter();
data.Text = converter.Convert(data.Text);
}
return data;
}
catch (Exception ex)
{
Log.Error(ex.Message);
return null;
}
}
@@ -450,16 +427,14 @@ public class SearchesService : INService
private async Task<OmdbMovie> GetMovieDataFactory(string name)
{
using (var http = _httpFactory.CreateClient())
{
var res = await http.GetStringAsync(string.Format("https://omdbapi.nadeko.bot/?t={0}&y=&plot=full&r=json",
name.Trim().Replace(' ', '+'))).ConfigureAwait(false);
var movie = JsonConvert.DeserializeObject<OmdbMovie>(res);
if (movie?.Title is null)
return null;
movie.Poster = await _google.ShortenUrl(movie.Poster).ConfigureAwait(false);
return movie;
}
using var http = _httpFactory.CreateClient();
var res = await http.GetStringAsync(string.Format("https://omdbapi.nadeko.bot/?t={0}&y=&plot=full&r=json",
name.Trim().Replace(' ', '+'))).ConfigureAwait(false);
var movie = JsonConvert.DeserializeObject<OmdbMovie>(res);
if (movie?.Title is null)
return null;
movie.Poster = await _google.ShortenUrl(movie.Poster).ConfigureAwait(false);
return movie;
}
public async Task<int> GetSteamAppIdByName(string query)
@@ -486,20 +461,18 @@ public class SearchesService : INService
var gamesMap = await _cache.GetOrAddCachedDataAsync(STEAM_GAME_IDS_KEY, async _ =>
{
using (var http = _httpFactory.CreateClient())
{
// https://api.steampowered.com/ISteamApps/GetAppList/v2/
var gamesStr = await http.GetStringAsync("https://api.steampowered.com/ISteamApps/GetAppList/v2/").ConfigureAwait(false);
var apps = JsonConvert.DeserializeAnonymousType(gamesStr, new { applist = new { apps = new List<SteamGameId>() } }).applist.apps;
using var http = _httpFactory.CreateClient();
// https://api.steampowered.com/ISteamApps/GetAppList/v2/
var gamesStr = await http.GetStringAsync("https://api.steampowered.com/ISteamApps/GetAppList/v2/").ConfigureAwait(false);
var apps = JsonConvert.DeserializeAnonymousType(gamesStr, new { applist = new { apps = new List<SteamGameId>() } }).applist.apps;
return apps
.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
.GroupBy(x => x.Name)
.ToDictionary(x => x.Key, x => x.First().AppId);
//await db.HashSetAsync("steam_game_ids", apps.Select(app => new HashEntry(app.Name.Trim().ToLowerInvariant(), app.AppId)).ToArray()).ConfigureAwait(false);
//await db.StringSetAsync("steam_game_ids", gamesStr, TimeSpan.FromHours(24));
//await db.KeyExpireAsync("steam_game_ids", TimeSpan.FromHours(24), CommandFlags.FireAndForget).ConfigureAwait(false);
}
return apps
.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)
.GroupBy(x => x.Name)
.ToDictionary(x => x.Key, x => x.First().AppId);
//await db.HashSetAsync("steam_game_ids", apps.Select(app => new HashEntry(app.Name.Trim().ToLowerInvariant(), app.AppId)).ToArray()).ConfigureAwait(false);
//await db.StringSetAsync("steam_game_ids", gamesStr, TimeSpan.FromHours(24));
//await db.KeyExpireAsync("steam_game_ids", TimeSpan.FromHours(24), CommandFlags.FireAndForget).ConfigureAwait(false);
}, default(string), TimeSpan.FromHours(24));
if (gamesMap is null)

View File

@@ -130,24 +130,22 @@ public sealed class StreamNotificationService : INService
var deleteGroups = failingStreams.GroupBy(x => x.Type)
.ToDictionary(x => x.Key, x => x.Select(x => x.Name).ToList());
using (var uow = _db.GetDbContext())
using var uow = _db.GetDbContext();
foreach (var kvp in deleteGroups)
{
foreach (var kvp in deleteGroups)
{
Log.Information($"Deleting {kvp.Value.Count} {kvp.Key} streams because " +
$"they've been erroring for more than {errorLimit}: {string.Join(", ", kvp.Value)}");
Log.Information($"Deleting {kvp.Value.Count} {kvp.Key} streams because " +
$"they've been erroring for more than {errorLimit}: {string.Join(", ", kvp.Value)}");
var toDelete = uow.Set<FollowedStream>()
.AsQueryable()
.Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
.ToList();
var toDelete = uow.Set<FollowedStream>()
.AsQueryable()
.Where(x => x.Type == kvp.Key && kvp.Value.Contains(x.Username))
.ToList();
uow.RemoveRange(toDelete);
uow.SaveChanges();
uow.RemoveRange(toDelete);
uow.SaveChanges();
foreach(var loginToDelete in kvp.Value)
_streamTracker.UntrackStreamByKey(new(kvp.Key, loginToDelete));
}
foreach(var loginToDelete in kvp.Value)
_streamTracker.UntrackStreamByKey(new(kvp.Key, loginToDelete));
}
}
catch (Exception ex)
@@ -461,20 +459,18 @@ public sealed class StreamNotificationService : INService
public bool ToggleStreamOffline(ulong guildId)
{
bool newValue;
using (var uow = _db.GetDbContext())
{
var gc = uow.GuildConfigsForId(guildId, set => set);
newValue = gc.NotifyStreamOffline = !gc.NotifyStreamOffline;
uow.SaveChanges();
using var uow = _db.GetDbContext();
var gc = uow.GuildConfigsForId(guildId, set => set);
newValue = gc.NotifyStreamOffline = !gc.NotifyStreamOffline;
uow.SaveChanges();
if (newValue)
{
_offlineNotificationServers.Add(guildId);
}
else
{
_offlineNotificationServers.TryRemove(guildId);
}
if (newValue)
{
_offlineNotificationServers.Add(guildId);
}
else
{
_offlineNotificationServers.TryRemove(guildId);
}
return newValue;
@@ -510,35 +506,33 @@ public sealed class StreamNotificationService : INService
public bool SetStreamMessage(ulong guildId, int index, string message, out FollowedStream fs)
{
using (var uow = _db.GetDbContext())
using var uow = _db.GetDbContext();
var fss = uow.Set<FollowedStream>()
.AsQueryable()
.Where(x => x.GuildId == guildId)
.OrderBy(x => x.Id)
.ToList();
if (fss.Count <= index)
{
var fss = uow.Set<FollowedStream>()
.AsQueryable()
.Where(x => x.GuildId == guildId)
.OrderBy(x => x.Id)
.ToList();
if (fss.Count <= index)
{
fs = null;
return false;
}
fs = fss[index];
fs.Message = message;
lock (_shardLock)
{
var streams = GetLocalGuildStreams(fs.CreateKey(), guildId);
// message doesn't participate in equality checking
// removing and adding = update
streams.Remove(fs);
streams.Add(fs);
}
uow.SaveChanges();
fs = null;
return false;
}
fs = fss[index];
fs.Message = message;
lock (_shardLock)
{
var streams = GetLocalGuildStreams(fs.CreateKey(), guildId);
// message doesn't participate in equality checking
// removing and adding = update
streams.Remove(fs);
streams.Add(fs);
}
uow.SaveChanges();
return true;
}

View File

@@ -23,22 +23,20 @@ public partial class Searches
{
try
{
using (var http = _httpFactory.CreateClient())
{
var res = await http.GetStringAsync($"{_xkcdUrl}/info.0.json").ConfigureAwait(false);
var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = _eb.Create().WithOkColor()
.WithImageUrl(comic.ImageLink)
.WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{_xkcdUrl}/{comic.Num}")
.AddField(GetText(strs.comic_number), comic.Num.ToString(), true)
.AddField(GetText(strs.date), $"{comic.Month}/{comic.Year}", true);
var sent = await ctx.Channel.EmbedAsync(embed)
.ConfigureAwait(false);
using var http = _httpFactory.CreateClient();
var res = await http.GetStringAsync($"{_xkcdUrl}/info.0.json").ConfigureAwait(false);
var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = _eb.Create().WithOkColor()
.WithImageUrl(comic.ImageLink)
.WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{_xkcdUrl}/{comic.Num}")
.AddField(GetText(strs.comic_number), comic.Num.ToString(), true)
.AddField(GetText(strs.date), $"{comic.Month}/{comic.Year}", true);
var sent = await ctx.Channel.EmbedAsync(embed)
.ConfigureAwait(false);
await Task.Delay(10000).ConfigureAwait(false);
await Task.Delay(10000).ConfigureAwait(false);
await sent.ModifyAsync(m => m.Embed = embed.AddField("Alt", comic.Alt.ToString(), false).Build()).ConfigureAwait(false);
}
await sent.ModifyAsync(m => m.Embed = embed.AddField("Alt", comic.Alt.ToString(), false).Build()).ConfigureAwait(false);
}
catch (HttpRequestException)
{
@@ -57,25 +55,23 @@ public partial class Searches
return;
try
{
using (var http = _httpFactory.CreateClient())
{
var res = await http.GetStringAsync($"{_xkcdUrl}/{num}/info.0.json").ConfigureAwait(false);
using var http = _httpFactory.CreateClient();
var res = await http.GetStringAsync($"{_xkcdUrl}/{num}/info.0.json").ConfigureAwait(false);
var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = _eb.Create()
.WithOkColor()
.WithImageUrl(comic.ImageLink)
.WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{_xkcdUrl}/{num}")
.AddField(GetText(strs.comic_number), comic.Num.ToString(), true)
.AddField(GetText(strs.date), $"{comic.Month}/{comic.Year}", true);
var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = _eb.Create()
.WithOkColor()
.WithImageUrl(comic.ImageLink)
.WithAuthor(comic.Title, "https://xkcd.com/s/919f27.ico", $"{_xkcdUrl}/{num}")
.AddField(GetText(strs.comic_number), comic.Num.ToString(), true)
.AddField(GetText(strs.date), $"{comic.Month}/{comic.Year}", true);
var sent = await ctx.Channel.EmbedAsync(embed)
.ConfigureAwait(false);
var sent = await ctx.Channel.EmbedAsync(embed)
.ConfigureAwait(false);
await Task.Delay(10000).ConfigureAwait(false);
await Task.Delay(10000).ConfigureAwait(false);
await sent.ModifyAsync(m => m.Embed = embed.AddField("Alt", comic.Alt.ToString(), false).Build()).ConfigureAwait(false);
}
await sent.ModifyAsync(m => m.Embed = embed.AddField("Alt", comic.Alt.ToString(), false).Build()).ConfigureAwait(false);
}
catch (HttpRequestException)
{