From 2d90ecaa51f1b3f9896538f0dd3153b4622b1f3f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 31 Jan 2022 11:50:49 +0100 Subject: [PATCH] .crypto will now show CoinMarketCap rank. Small refactor --- .../Common/TypeReaders/NadekoTypeReader.cs | 29 ------- .../Common/TypeReaders/TypeReaderResult.cs | 30 +++++++ .../Modules/Searches/Crypto/CryptoCommands.cs | 24 ++--- .../Modules/Searches/Crypto/CryptoService.cs | 44 +++++----- .../Modules/Searches/_Common/CryptoData.cs | 87 ++++++++++++++----- 5 files changed, 131 insertions(+), 83 deletions(-) create mode 100644 src/NadekoBot/Common/TypeReaders/TypeReaderResult.cs diff --git a/src/NadekoBot/Common/TypeReaders/NadekoTypeReader.cs b/src/NadekoBot/Common/TypeReaders/NadekoTypeReader.cs index 1b24cb4b9..ea7b5a115 100644 --- a/src/NadekoBot/Common/TypeReaders/NadekoTypeReader.cs +++ b/src/NadekoBot/Common/TypeReaders/NadekoTypeReader.cs @@ -11,33 +11,4 @@ public abstract class NadekoTypeReader : TypeReader string input, IServiceProvider services) => await ReadAsync(ctx, input); -} - -public static class TypeReaderResult -{ - public static TypeReaderResult FromError(CommandError error, string reason) - => Discord.Commands.TypeReaderResult.FromError(error, reason); - - public static TypeReaderResult FromSuccess(in T value) - => Discord.Commands.TypeReaderResult.FromSuccess(value); -} - -public readonly struct TypeReaderResult -{ - public bool IsSuccess - => _result.IsSuccess; - - public IReadOnlyCollection Values - => _result.Values; - - private readonly Discord.Commands.TypeReaderResult _result; - - private TypeReaderResult(in Discord.Commands.TypeReaderResult result) - => _result = result; - - public static implicit operator TypeReaderResult(in Discord.Commands.TypeReaderResult result) - => new(result); - - public static implicit operator Discord.Commands.TypeReaderResult(in TypeReaderResult wrapper) - => wrapper._result; } \ No newline at end of file diff --git a/src/NadekoBot/Common/TypeReaders/TypeReaderResult.cs b/src/NadekoBot/Common/TypeReaders/TypeReaderResult.cs new file mode 100644 index 000000000..15f96158e --- /dev/null +++ b/src/NadekoBot/Common/TypeReaders/TypeReaderResult.cs @@ -0,0 +1,30 @@ +namespace NadekoBot.Common.TypeReaders; + +public readonly struct TypeReaderResult +{ + public bool IsSuccess + => _result.IsSuccess; + + public IReadOnlyCollection Values + => _result.Values; + + private readonly Discord.Commands.TypeReaderResult _result; + + private TypeReaderResult(in Discord.Commands.TypeReaderResult result) + => _result = result; + + public static implicit operator TypeReaderResult(in Discord.Commands.TypeReaderResult result) + => new(result); + + public static implicit operator Discord.Commands.TypeReaderResult(in TypeReaderResult wrapper) + => wrapper._result; +} + +public static class TypeReaderResult +{ + public static TypeReaderResult FromError(CommandError error, string reason) + => Discord.Commands.TypeReaderResult.FromError(error, reason); + + public static TypeReaderResult FromSuccess(in T value) + => Discord.Commands.TypeReaderResult.FromSuccess(value); +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Crypto/CryptoCommands.cs b/src/NadekoBot/Modules/Searches/Crypto/CryptoCommands.cs index ba1a63040..603584fab 100644 --- a/src/NadekoBot/Modules/Searches/Crypto/CryptoCommands.cs +++ b/src/NadekoBot/Modules/Searches/Crypto/CryptoCommands.cs @@ -33,27 +33,29 @@ public partial class Searches return; } - var sevenDay = decimal.TryParse(crypto.Quote.Usd.Percent_Change_7d, out var sd) - ? sd.ToString("F2") - : crypto.Quote.Usd.Percent_Change_7d; + var usd = crypto.Quote["USD"]; - var lastDay = decimal.TryParse(crypto.Quote.Usd.Percent_Change_24h, out var ld) - ? ld.ToString("F2") - : crypto.Quote.Usd.Percent_Change_24h; + var sevenDay = usd.PercentChange7d.ToString("F2", Culture); + var lastDay = usd.PercentChange24h.ToString("F2", Culture); + var price = usd.Price < 0.01 + ? usd.Price.ToString(Culture) + : usd.Price.ToString("F2", Culture); + var volume = usd.Volume24h.ToString("n0", Culture); + var marketCap = usd.MarketCap.ToString("n0", Culture); + await ctx.Channel.EmbedAsync(_eb.Create() .WithOkColor() + .WithAuthor($"#{crypto.CmcRank}") .WithTitle($"{crypto.Name} ({crypto.Symbol})") .WithUrl($"https://coinmarketcap.com/currencies/{crypto.Slug}/") .WithThumbnailUrl( $"https://s3.coinmarketcap.com/static/img/coins/128x128/{crypto.Id}.png") .AddField(GetText(strs.market_cap), - $"${crypto.Quote.Usd.Market_Cap:n0}", - true) - .AddField(GetText(strs.price), $"${crypto.Quote.Usd.Price}", true) - .AddField(GetText(strs.volume_24h), - $"${crypto.Quote.Usd.Volume_24h:n0}", + $"${marketCap}", true) + .AddField(GetText(strs.price), $"${price}", true) + .AddField(GetText(strs.volume_24h), $"${volume}", true) .AddField(GetText(strs.change_7d_24h), $"{sevenDay}% / {lastDay}%", true) .WithImageUrl( $"https://s3.coinmarketcap.com/generated/sparklines/web/7d/usd/{crypto.Id}.png")); diff --git a/src/NadekoBot/Modules/Searches/Crypto/CryptoService.cs b/src/NadekoBot/Modules/Searches/Crypto/CryptoService.cs index bc8e53b9d..cf8b97999 100644 --- a/src/NadekoBot/Modules/Searches/Crypto/CryptoService.cs +++ b/src/NadekoBot/Modules/Searches/Crypto/CryptoService.cs @@ -1,6 +1,8 @@ #nullable disable using NadekoBot.Modules.Searches.Common; -using Newtonsoft.Json; +using System.Net.Http.Json; +using JsonSerializer = System.Text.Json.JsonSerializer; +using StringExtensions = NadekoBot.Extensions.StringExtensions; namespace NadekoBot.Modules.Searches.Services; @@ -19,38 +21,36 @@ public class CryptoService : INService _creds = creds; } - public async Task<(CryptoResponseData Data, CryptoResponseData Nearest)> GetCryptoData(string name) + public async Task<(CmcResponseData Data, CmcResponseData Nearest)> GetCryptoData(string name) { - if (string.IsNullOrWhiteSpace(name)) return (null, null); + if (string.IsNullOrWhiteSpace(name)) + return (null, null); name = name.ToUpperInvariant(); - var cryptos = await CryptoData(); + var cryptos = await GetCryptoDataInternal(); if (cryptos is null) return (null, null); var crypto = cryptos?.FirstOrDefault(x - => x.Id.ToUpperInvariant() == name + => x.Slug.ToUpperInvariant() == name || x.Name.ToUpperInvariant() == name || x.Symbol.ToUpperInvariant() == name); - (CryptoResponseData Elem, int Distance)? nearest = null; - if (crypto is null) - { - nearest = cryptos.Select(x => (x, Distance: x.Name.ToUpperInvariant().LevenshteinDistance(name))) - .OrderBy(x => x.Distance) - .Where(x => x.Distance <= 2) - .FirstOrDefault(); + if (crypto is not null) + return (crypto, null); - crypto = nearest?.Elem; - } - if (nearest is not null) return (null, crypto); + var nearest = cryptos + .Select(elem => (Elem: elem, + Distance: StringExtensions.LevenshteinDistance(elem.Name.ToUpperInvariant(), name))) + .OrderBy(x => x.Distance) + .FirstOrDefault(x => x.Distance <= 2); - return (crypto, null); + return (null, nearest.Elem); } - public async Task> CryptoData() + public async Task> GetCryptoDataInternal() { await _getCryptoLock.WaitAsync(); try @@ -61,16 +61,14 @@ public class CryptoService : INService try { using var http = _httpFactory.CreateClient(); - var strData = await http.GetStringAsync( + var strData = await http.GetFromJsonAsync( "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?" + $"CMC_PRO_API_KEY={_creds.CoinmarketcapApiKey}" + "&start=1" + "&limit=5000" + "&convert=USD"); - - JsonConvert.DeserializeObject(strData); // just to see if its' valid - - return strData; + + return JsonSerializer.Serialize(strData); } catch (Exception ex) { @@ -81,7 +79,7 @@ public class CryptoService : INService "", TimeSpan.FromHours(2)); - return JsonConvert.DeserializeObject(fullStrData)?.Data ?? new(); + return JsonSerializer.Deserialize(fullStrData)?.Data ?? new(); } catch (Exception ex) { diff --git a/src/NadekoBot/Modules/Searches/_Common/CryptoData.cs b/src/NadekoBot/Modules/Searches/_Common/CryptoData.cs index e62674784..a7f1770dd 100644 --- a/src/NadekoBot/Modules/Searches/_Common/CryptoData.cs +++ b/src/NadekoBot/Modules/Searches/_Common/CryptoData.cs @@ -1,37 +1,84 @@ #nullable disable -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace NadekoBot.Modules.Searches.Common; public class CryptoResponse { - public List Data { get; set; } + public List Data { get; set; } } -public class CryptoResponseData +public class CmcQuote { - public string Id { get; set; } + [JsonPropertyName("price")] + public double Price { get; set; } + + [JsonPropertyName("volume_24h")] + public double Volume24h { get; set; } + + [JsonPropertyName("volume_change_24h")] + public double VolumeChange24h { get; set; } + + [JsonPropertyName("percent_change_1h")] + public double PercentChange1h { get; set; } + + [JsonPropertyName("percent_change_24h")] + public double PercentChange24h { get; set; } + + [JsonPropertyName("percent_change_7d")] + public double PercentChange7d { get; set; } + + [JsonPropertyName("market_cap")] + public double MarketCap { get; set; } + + [JsonPropertyName("market_cap_dominance")] + public double MarketCapDominance { get; set; } + + [JsonPropertyName("fully_diluted_market_cap")] + public double FullyDilutedMarketCap { get; set; } + + [JsonPropertyName("last_updated")] + public DateTime LastUpdated { get; set; } +} + +public class CmcResponseData +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("name")] public string Name { get; set; } + + [JsonPropertyName("symbol")] public string Symbol { get; set; } + + [JsonPropertyName("slug")] public string Slug { get; set; } - [JsonProperty("cmc_rank")] - public int Rank { get; set; } + [JsonPropertyName("cmc_rank")] + public int CmcRank { get; set; } - public CurrencyQuotes Quote { get; set; } -} + [JsonPropertyName("num_market_pairs")] + public int NumMarketPairs { get; set; } -public class CurrencyQuotes -{ - public Quote Usd { get; set; } -} + [JsonPropertyName("circulating_supply")] + public double? CirculatingSupply { get; set; } -public class Quote -{ - public double Price { get; set; } - public double Market_Cap { get; set; } - public string Percent_Change_1h { get; set; } - public string Percent_Change_24h { get; set; } - public string Percent_Change_7d { get; set; } - public double? Volume_24h { get; set; } + [JsonPropertyName("total_supply")] + public double? TotalSupply { get; set; } + + [JsonPropertyName("max_supply")] + public double? MaxSupply { get; set; } + + [JsonPropertyName("last_updated")] + public DateTime LastUpdated { get; set; } + + [JsonPropertyName("date_added")] + public DateTime DateAdded { get; set; } + + [JsonPropertyName("tags")] + public List Tags { get; set; } + + [JsonPropertyName("quote")] + public Dictionary Quote { get; set; } } \ No newline at end of file