diff --git a/.gitignore b/.gitignore index 0fa1ef5e..5169035d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ #Manually added files +patreon_rewards.json command_errors*.txt src/NadekoBot/Command Errors*.txt diff --git a/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs b/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs index f325c402..1e7f5d22 100644 --- a/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs +++ b/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs @@ -1,94 +1,93 @@ -//using Discord; -//using Discord.Commands; -//using NadekoBot.Attributes; -//using NadekoBot.Extensions; -//using NadekoBot.Services.Utility; -//using System; -//using System.Linq; -//using System.Threading.Tasks; -////todo Rewrite -//namespace NadekoBot.Modules.Utility -//{ -// public partial class Utility -// { -// [Group] -// public class UnitConverterCommands : NadekoSubmodule -// { -// private readonly ConverterService _service; +using Discord; +using Discord.Commands; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Services.Utility; +using System; +using System.Linq; +using System.Threading.Tasks; +namespace NadekoBot.Modules.Utility +{ + public partial class Utility + { + [Group] + public class UnitConverterCommands : NadekoSubmodule + { + private readonly ConverterService _service; -// public UnitConverterCommands(ConverterService service) -// { -// _service = service; -// } + public UnitConverterCommands(ConverterService service) + { + _service = service; + } -// [NadekoCommand, Usage, Description, Aliases] -// public async Task ConvertList() -// { -// var res = _service.Units.GroupBy(x => x.UnitType) -// .Aggregate(new EmbedBuilder().WithTitle(GetText("convertlist")) -// .WithColor(NadekoBot.OkColor), -// (embed, g) => embed.AddField(efb => -// efb.WithName(g.Key.ToTitleCase()) -// .WithValue(String.Join(", ", g.Select(x => x.Triggers.FirstOrDefault()) -// .OrderBy(x => x))))); -// await Context.Channel.EmbedAsync(res); -// } -// [NadekoCommand, Usage, Description, Aliases] -// public async Task Convert(string origin, string target, decimal value) -// { -// var originUnit = _service.Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(origin.ToLowerInvariant())); -// var targetUnit = _service.Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant())); -// if (originUnit == null || targetUnit == null) -// { -// await ReplyErrorLocalized("convert_not_found", Format.Bold(origin), Format.Bold(target)).ConfigureAwait(false); -// return; -// } -// if (originUnit.UnitType != targetUnit.UnitType) -// { -// await ReplyErrorLocalized("convert_type_error", Format.Bold(originUnit.Triggers.First()), Format.Bold(targetUnit.Triggers.First())).ConfigureAwait(false); -// return; -// } -// decimal res; -// if (originUnit.Triggers == targetUnit.Triggers) res = value; -// else if (originUnit.UnitType == "temperature") -// { -// //don't really care too much about efficiency, so just convert to Kelvin, then to target -// switch (originUnit.Triggers.First().ToUpperInvariant()) -// { -// case "C": -// res = value + 273.15m; //celcius! -// break; -// case "F": -// res = (value + 459.67m) * (5m / 9m); -// break; -// default: -// res = value; -// break; -// } -// //from Kelvin to target -// switch (targetUnit.Triggers.First().ToUpperInvariant()) -// { -// case "C": -// res = res - 273.15m; //celcius! -// break; -// case "F": -// res = res * (9m / 5m) - 459.67m; -// break; -// } -// } -// else -// { -// if (originUnit.UnitType == "currency") -// { -// res = (value * targetUnit.Modifier) / originUnit.Modifier; -// } -// else -// res = (value * originUnit.Modifier) / targetUnit.Modifier; -// } -// res = Math.Round(res, 4); + [NadekoCommand, Usage, Description, Aliases] + public async Task ConvertList() + { + var res = _service.Units.GroupBy(x => x.UnitType) + .Aggregate(new EmbedBuilder().WithTitle(GetText("convertlist")) + .WithColor(NadekoBot.OkColor), + (embed, g) => embed.AddField(efb => + efb.WithName(g.Key.ToTitleCase()) + .WithValue(String.Join(", ", g.Select(x => x.Triggers.FirstOrDefault()) + .OrderBy(x => x))))); + await Context.Channel.EmbedAsync(res); + } + [NadekoCommand, Usage, Description, Aliases] + public async Task Convert(string origin, string target, decimal value) + { + var originUnit = _service.Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(origin.ToLowerInvariant())); + var targetUnit = _service.Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant())); + if (originUnit == null || targetUnit == null) + { + await ReplyErrorLocalized("convert_not_found", Format.Bold(origin), Format.Bold(target)).ConfigureAwait(false); + return; + } + if (originUnit.UnitType != targetUnit.UnitType) + { + await ReplyErrorLocalized("convert_type_error", Format.Bold(originUnit.Triggers.First()), Format.Bold(targetUnit.Triggers.First())).ConfigureAwait(false); + return; + } + decimal res; + if (originUnit.Triggers == targetUnit.Triggers) res = value; + else if (originUnit.UnitType == "temperature") + { + //don't really care too much about efficiency, so just convert to Kelvin, then to target + switch (originUnit.Triggers.First().ToUpperInvariant()) + { + case "C": + res = value + 273.15m; //celcius! + break; + case "F": + res = (value + 459.67m) * (5m / 9m); + break; + default: + res = value; + break; + } + //from Kelvin to target + switch (targetUnit.Triggers.First().ToUpperInvariant()) + { + case "C": + res = res - 273.15m; //celcius! + break; + case "F": + res = res * (9m / 5m) - 459.67m; + break; + } + } + else + { + if (originUnit.UnitType == "currency") + { + res = (value * targetUnit.Modifier) / originUnit.Modifier; + } + else + res = (value * originUnit.Modifier) / targetUnit.Modifier; + } + res = Math.Round(res, 4); -// await Context.Channel.SendConfirmAsync(GetText("convert", value, (originUnit.Triggers.First()).SnPl(value.IsInteger() ? (int)value : 2), res, (targetUnit.Triggers.First() + "s").SnPl(res.IsInteger() ? (int)res : 2))); -// } -// } -// } -//} \ No newline at end of file + await Context.Channel.SendConfirmAsync(GetText("convert", value, (originUnit.Triggers.First()).SnPl(value.IsInteger() ? (int)value : 2), res, (targetUnit.Triggers.First() + "s").SnPl(res.IsInteger() ? (int)res : 2))); + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 9025a0a3..2ee3d1b3 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -290,7 +290,10 @@ namespace NadekoBot Task SetClientReady() { - clientReady.TrySetResult(true); + var _ = Task.Run(() => + { + clientReady.TrySetResult(true); + }); return Task.CompletedTask; } diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index 0cc722bc..18ad4911 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -90,5 +90,6 @@ + diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 8cce6936..64507106 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -126,7 +126,6 @@ namespace NadekoBot.Services.Impl return Task.CompletedTask; }; - //todo carbonitex update if (sc != null) { _carbonitexTimer = new Timer(async (state) => diff --git a/src/NadekoBot/Services/Utility/ConverterService.cs b/src/NadekoBot/Services/Utility/ConverterService.cs index b7388335..1edb470a 100644 --- a/src/NadekoBot/Services/Utility/ConverterService.cs +++ b/src/NadekoBot/Services/Utility/ConverterService.cs @@ -1,4 +1,6 @@ -using NadekoBot.Services.Database.Models; +using Discord.WebSocket; +using NadekoBot.Services; +using NadekoBot.Services.Database.Models; using Newtonsoft.Json; using NLog; using System; @@ -11,27 +13,29 @@ using System.Threading.Tasks; namespace NadekoBot.Services.Utility { - //todo periodically load from the database, update only on shard 0 public class ConverterService { - public List Units { get; set; } = new List(); + public List Units { get; } = new List(); private readonly Logger _log; - private Timer _timer; + private readonly Timer _currencyUpdater; + private readonly Timer _currencyLoader; private readonly TimeSpan _updateInterval = new TimeSpan(12, 0, 0); private readonly DbService _db; - public ConverterService(DbService db) + public ConverterService(DiscordSocketClient client, DbService db) { _log = LogManager.GetCurrentClassLogger(); _db = db; try { - var data = JsonConvert.DeserializeObject>(File.ReadAllText("data/units.json")).Select(u => new ConvertUnit() - { - Modifier = u.Modifier, - UnitType = u.UnitType, - InternalTrigger = string.Join("|", u.Triggers) - }).ToArray(); + var data = JsonConvert.DeserializeObject>( + File.ReadAllText("data/units.json")) + .Select(u => new ConvertUnit() + { + Modifier = u.Modifier, + UnitType = u.UnitType, + InternalTrigger = string.Join("|", u.Triggers) + }).ToArray(); using (var uow = _db.UnitOfWork) { @@ -48,10 +52,10 @@ namespace NadekoBot.Services.Utility _log.Warn("Could not load units: " + ex.Message); } - _timer = new Timer(async (obj) => await UpdateCurrency(), null, _updateInterval, _updateInterval); + _currencyUpdater = new Timer(async (shouldLoad) => await UpdateCurrency((bool)shouldLoad), client.ShardId == 0, _updateInterval, _updateInterval); } - public static async Task UpdateCurrencyRates() + private async Task GetCurrencyRates() { using (var http = new HttpClient()) { @@ -60,38 +64,48 @@ namespace NadekoBot.Services.Utility } } - public async Task UpdateCurrency() + private async Task UpdateCurrency(bool shouldLoad) { try { - var currencyRates = await UpdateCurrencyRates(); var unitTypeString = "currency"; - var range = currencyRates.ConversionRates.Select(u => new ConvertUnit() + if (shouldLoad) { - InternalTrigger = u.Key, - Modifier = u.Value, - UnitType = unitTypeString - }).ToArray(); - var baseType = new ConvertUnit() - { - Triggers = new[] { currencyRates.Base }, - Modifier = decimal.One, - UnitType = unitTypeString - }; - var toRemove = Units.Where(u => u.UnitType == unitTypeString); + var currencyRates = await GetCurrencyRates(); + var baseType = new ConvertUnit() + { + Triggers = new[] { currencyRates.Base }, + Modifier = decimal.One, + UnitType = unitTypeString + }; + var range = currencyRates.ConversionRates.Select(u => new ConvertUnit() + { + InternalTrigger = u.Key, + Modifier = u.Value, + UnitType = unitTypeString + }).ToArray(); + var toRemove = Units.Where(u => u.UnitType == unitTypeString); - using (var uow = _db.UnitOfWork) - { - uow.ConverterUnits.RemoveRange(toRemove.ToArray()); - uow.ConverterUnits.Add(baseType); - uow.ConverterUnits.AddRange(range); + using (var uow = _db.UnitOfWork) + { + uow.ConverterUnits.RemoveRange(toRemove.ToArray()); + uow.ConverterUnits.Add(baseType); + uow.ConverterUnits.AddRange(range); - await uow.CompleteAsync().ConfigureAwait(false); + await uow.CompleteAsync().ConfigureAwait(false); + } + Units.RemoveAll(u => u.UnitType == unitTypeString); + Units.Add(baseType); + Units.AddRange(range); + } + else + { + using (var uow = _db.UnitOfWork) + { + Units.RemoveAll(u => u.UnitType == unitTypeString); + Units.AddRange(uow.ConverterUnits.GetAll().ToArray()); + } } - Units.RemoveAll(u => u.UnitType == unitTypeString); - Units.Add(baseType); - Units.AddRange(range); - _log.Info("Updated Currency"); } catch { diff --git a/src/NadekoBot/Services/Utility/PatreonRewardsService.cs b/src/NadekoBot/Services/Utility/PatreonRewardsService.cs index 0f7105b7..f3811c6d 100644 --- a/src/NadekoBot/Services/Utility/PatreonRewardsService.cs +++ b/src/NadekoBot/Services/Utility/PatreonRewardsService.cs @@ -1,12 +1,15 @@ -using NadekoBot.Services.Database.Models; +using Discord.WebSocket; +using NadekoBot.Services.Database.Models; using NadekoBot.Services.Utility.Patreon; using Newtonsoft.Json; using NLog; using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; using System.Net.Http; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -24,12 +27,13 @@ namespace NadekoBot.Services.Utility private readonly SemaphoreSlim claimLockJustInCase = new SemaphoreSlim(1, 1); private readonly Logger _log; - public readonly TimeSpan Interval = TimeSpan.FromMinutes(15); - private IBotCredentials _creds; + public readonly TimeSpan Interval = TimeSpan.FromMinutes(3); + private readonly IBotCredentials _creds; private readonly DbService _db; private readonly CurrencyService _currency; - public PatreonRewardsService(IBotCredentials creds, DbService db, CurrencyService currency) + public PatreonRewardsService(IBotCredentials creds, DbService db, CurrencyService currency, + DiscordSocketClient client) { _creds = creds; _db = db; @@ -37,58 +41,63 @@ namespace NadekoBot.Services.Utility if (string.IsNullOrWhiteSpace(creds.PatreonAccessToken)) return; _log = LogManager.GetCurrentClassLogger(); - Updater = new Timer(async (_) => await LoadPledges(), null, TimeSpan.Zero, Interval); + Updater = new Timer(async (load) => await RefreshPledges((bool)load), client.ShardId == 0, TimeSpan.Zero, Interval); } - public async Task LoadPledges() + public async Task RefreshPledges(bool shouldLoad) { - LastUpdate = DateTime.UtcNow; - await getPledgesLocker.WaitAsync(1000).ConfigureAwait(false); - try + if (shouldLoad) { - var rewards = new List(); - var users = new List(); - using (var http = new HttpClient()) + LastUpdate = DateTime.UtcNow; + await getPledgesLocker.WaitAsync().ConfigureAwait(false); + try { - http.DefaultRequestHeaders.Clear(); - http.DefaultRequestHeaders.Add("Authorization", "Bearer " + _creds.PatreonAccessToken); - var data = new PatreonData() + var rewards = new List(); + var users = new List(); + using (var http = new HttpClient()) { - Links = new PatreonDataLinks() + http.DefaultRequestHeaders.Clear(); + http.DefaultRequestHeaders.Add("Authorization", "Bearer " + _creds.PatreonAccessToken); + var data = new PatreonData() { - next = "https://api.patreon.com/oauth2/api/campaigns/334038/pledges" - } - }; - do + Links = new PatreonDataLinks() + { + next = "https://api.patreon.com/oauth2/api/campaigns/334038/pledges" + } + }; + do + { + var res = await http.GetStringAsync(data.Links.next) + .ConfigureAwait(false); + data = JsonConvert.DeserializeObject(res); + var pledgers = data.Data.Where(x => x["type"].ToString() == "pledge"); + rewards.AddRange(pledgers.Select(x => JsonConvert.DeserializeObject(x.ToString())) + .Where(x => x.attributes.declined_since == null)); + users.AddRange(data.Included + .Where(x => x["type"].ToString() == "user") + .Select(x => JsonConvert.DeserializeObject(x.ToString()))); + } while (!string.IsNullOrWhiteSpace(data.Links.next)); + } + Pledges = rewards.Join(users, (r) => r.relationships?.patron?.data?.id, (u) => u.id, (x, y) => new PatreonUserAndReward() { - var res = await http.GetStringAsync(data.Links.next) - .ConfigureAwait(false); - data = JsonConvert.DeserializeObject(res); - var pledgers = data.Data.Where(x => x["type"].ToString() == "pledge"); - rewards.AddRange(pledgers.Select(x => JsonConvert.DeserializeObject(x.ToString())) - .Where(x => x.attributes.declined_since == null)); - users.AddRange(data.Included - .Where(x => x["type"].ToString() == "user") - .Select(x => JsonConvert.DeserializeObject(x.ToString()))); - } while (!string.IsNullOrWhiteSpace(data.Links.next)); + User = y, + Reward = x, + }).ToImmutableArray(); + File.WriteAllText("./patreon_rewards.json", JsonConvert.SerializeObject(Pledges)); } - Pledges = rewards.Join(users, (r) => r.relationships?.patron?.data?.id, (u) => u.id, (x, y) => new PatreonUserAndReward() + catch (Exception ex) { - User = y, - Reward = x, - }).ToImmutableArray(); - } - catch (Exception ex) - { - _log.Warn(ex); - } - finally - { - var _ = Task.Run(async () => + _log.Warn(ex); + } + finally { - await Task.Delay(TimeSpan.FromMinutes(5)).ConfigureAwait(false); getPledgesLocker.Release(); - }); + } + } + else + { + Pledges = JsonConvert.DeserializeObject(File.ReadAllText("./patreon_rewards.json")) + .ToImmutableArray(); } }