.crypto will now show CoinMarketCap rank. Small refactor

This commit is contained in:
Kwoth
2022-01-31 11:50:49 +01:00
parent 3e0bbd8ada
commit 2d90ecaa51
5 changed files with 131 additions and 83 deletions

View File

@@ -12,32 +12,3 @@ public abstract class NadekoTypeReader<T> : TypeReader
IServiceProvider services) IServiceProvider services)
=> await ReadAsync(ctx, input); => await ReadAsync(ctx, input);
} }
public static class TypeReaderResult
{
public static TypeReaderResult<T> FromError<T>(CommandError error, string reason)
=> Discord.Commands.TypeReaderResult.FromError(error, reason);
public static TypeReaderResult<T> FromSuccess<T>(in T value)
=> Discord.Commands.TypeReaderResult.FromSuccess(value);
}
public readonly struct TypeReaderResult<T>
{
public bool IsSuccess
=> _result.IsSuccess;
public IReadOnlyCollection<TypeReaderValue> Values
=> _result.Values;
private readonly Discord.Commands.TypeReaderResult _result;
private TypeReaderResult(in Discord.Commands.TypeReaderResult result)
=> _result = result;
public static implicit operator TypeReaderResult<T>(in Discord.Commands.TypeReaderResult result)
=> new(result);
public static implicit operator Discord.Commands.TypeReaderResult(in TypeReaderResult<T> wrapper)
=> wrapper._result;
}

View File

@@ -0,0 +1,30 @@
namespace NadekoBot.Common.TypeReaders;
public readonly struct TypeReaderResult<T>
{
public bool IsSuccess
=> _result.IsSuccess;
public IReadOnlyCollection<TypeReaderValue> Values
=> _result.Values;
private readonly Discord.Commands.TypeReaderResult _result;
private TypeReaderResult(in Discord.Commands.TypeReaderResult result)
=> _result = result;
public static implicit operator TypeReaderResult<T>(in Discord.Commands.TypeReaderResult result)
=> new(result);
public static implicit operator Discord.Commands.TypeReaderResult(in TypeReaderResult<T> wrapper)
=> wrapper._result;
}
public static class TypeReaderResult
{
public static TypeReaderResult<T> FromError<T>(CommandError error, string reason)
=> Discord.Commands.TypeReaderResult.FromError(error, reason);
public static TypeReaderResult<T> FromSuccess<T>(in T value)
=> Discord.Commands.TypeReaderResult.FromSuccess(value);
}

View File

@@ -33,27 +33,29 @@ public partial class Searches
return; return;
} }
var sevenDay = decimal.TryParse(crypto.Quote.Usd.Percent_Change_7d, out var sd) var usd = crypto.Quote["USD"];
? sd.ToString("F2")
: crypto.Quote.Usd.Percent_Change_7d;
var lastDay = decimal.TryParse(crypto.Quote.Usd.Percent_Change_24h, out var ld) var sevenDay = usd.PercentChange7d.ToString("F2", Culture);
? ld.ToString("F2") var lastDay = usd.PercentChange24h.ToString("F2", Culture);
: crypto.Quote.Usd.Percent_Change_24h; 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() await ctx.Channel.EmbedAsync(_eb.Create()
.WithOkColor() .WithOkColor()
.WithAuthor($"#{crypto.CmcRank}")
.WithTitle($"{crypto.Name} ({crypto.Symbol})") .WithTitle($"{crypto.Name} ({crypto.Symbol})")
.WithUrl($"https://coinmarketcap.com/currencies/{crypto.Slug}/") .WithUrl($"https://coinmarketcap.com/currencies/{crypto.Slug}/")
.WithThumbnailUrl( .WithThumbnailUrl(
$"https://s3.coinmarketcap.com/static/img/coins/128x128/{crypto.Id}.png") $"https://s3.coinmarketcap.com/static/img/coins/128x128/{crypto.Id}.png")
.AddField(GetText(strs.market_cap), .AddField(GetText(strs.market_cap),
$"${crypto.Quote.Usd.Market_Cap:n0}", $"${marketCap}",
true)
.AddField(GetText(strs.price), $"${crypto.Quote.Usd.Price}", true)
.AddField(GetText(strs.volume_24h),
$"${crypto.Quote.Usd.Volume_24h:n0}",
true) true)
.AddField(GetText(strs.price), $"${price}", true)
.AddField(GetText(strs.volume_24h), $"${volume}", true)
.AddField(GetText(strs.change_7d_24h), $"{sevenDay}% / {lastDay}%", true) .AddField(GetText(strs.change_7d_24h), $"{sevenDay}% / {lastDay}%", true)
.WithImageUrl( .WithImageUrl(
$"https://s3.coinmarketcap.com/generated/sparklines/web/7d/usd/{crypto.Id}.png")); $"https://s3.coinmarketcap.com/generated/sparklines/web/7d/usd/{crypto.Id}.png"));

View File

@@ -1,6 +1,8 @@
#nullable disable #nullable disable
using NadekoBot.Modules.Searches.Common; 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; namespace NadekoBot.Modules.Searches.Services;
@@ -19,38 +21,36 @@ public class CryptoService : INService
_creds = creds; _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(); name = name.ToUpperInvariant();
var cryptos = await CryptoData(); var cryptos = await GetCryptoDataInternal();
if (cryptos is null) if (cryptos is null)
return (null, null); return (null, null);
var crypto = cryptos?.FirstOrDefault(x var crypto = cryptos?.FirstOrDefault(x
=> x.Id.ToUpperInvariant() == name => x.Slug.ToUpperInvariant() == name
|| x.Name.ToUpperInvariant() == name || x.Name.ToUpperInvariant() == name
|| x.Symbol.ToUpperInvariant() == name); || x.Symbol.ToUpperInvariant() == name);
(CryptoResponseData Elem, int Distance)? nearest = null; if (crypto is not 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();
crypto = nearest?.Elem;
}
if (nearest is not null) return (null, crypto);
return (crypto, null); return (crypto, null);
var nearest = cryptos
.Select(elem => (Elem: elem,
Distance: StringExtensions.LevenshteinDistance(elem.Name.ToUpperInvariant(), name)))
.OrderBy(x => x.Distance)
.FirstOrDefault(x => x.Distance <= 2);
return (null, nearest.Elem);
} }
public async Task<List<CryptoResponseData>> CryptoData() public async Task<List<CmcResponseData>> GetCryptoDataInternal()
{ {
await _getCryptoLock.WaitAsync(); await _getCryptoLock.WaitAsync();
try try
@@ -61,16 +61,14 @@ public class CryptoService : INService
try try
{ {
using var http = _httpFactory.CreateClient(); using var http = _httpFactory.CreateClient();
var strData = await http.GetStringAsync( var strData = await http.GetFromJsonAsync<CryptoResponse>(
"https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?" "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?"
+ $"CMC_PRO_API_KEY={_creds.CoinmarketcapApiKey}" + $"CMC_PRO_API_KEY={_creds.CoinmarketcapApiKey}"
+ "&start=1" + "&start=1"
+ "&limit=5000" + "&limit=5000"
+ "&convert=USD"); + "&convert=USD");
JsonConvert.DeserializeObject<CryptoResponse>(strData); // just to see if its' valid return JsonSerializer.Serialize(strData);
return strData;
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -81,7 +79,7 @@ public class CryptoService : INService
"", "",
TimeSpan.FromHours(2)); TimeSpan.FromHours(2));
return JsonConvert.DeserializeObject<CryptoResponse>(fullStrData)?.Data ?? new(); return JsonSerializer.Deserialize<CryptoResponse>(fullStrData)?.Data ?? new();
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,37 +1,84 @@
#nullable disable #nullable disable
using Newtonsoft.Json; using System.Text.Json.Serialization;
namespace NadekoBot.Modules.Searches.Common; namespace NadekoBot.Modules.Searches.Common;
public class CryptoResponse public class CryptoResponse
{ {
public List<CryptoResponseData> Data { get; set; } public List<CmcResponseData> 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; } public string Name { get; set; }
[JsonPropertyName("symbol")]
public string Symbol { get; set; } public string Symbol { get; set; }
[JsonPropertyName("slug")]
public string Slug { get; set; } public string Slug { get; set; }
[JsonProperty("cmc_rank")] [JsonPropertyName("cmc_rank")]
public int Rank { get; set; } public int CmcRank { get; set; }
public CurrencyQuotes Quote { get; set; } [JsonPropertyName("num_market_pairs")]
} public int NumMarketPairs { get; set; }
public class CurrencyQuotes [JsonPropertyName("circulating_supply")]
{ public double? CirculatingSupply { get; set; }
public Quote Usd { get; set; }
}
public class Quote [JsonPropertyName("total_supply")]
{ public double? TotalSupply { get; set; }
public double Price { get; set; }
public double Market_Cap { get; set; } [JsonPropertyName("max_supply")]
public string Percent_Change_1h { get; set; } public double? MaxSupply { get; set; }
public string Percent_Change_24h { get; set; }
public string Percent_Change_7d { get; set; } [JsonPropertyName("last_updated")]
public double? Volume_24h { get; set; } public DateTime LastUpdated { get; set; }
[JsonPropertyName("date_added")]
public DateTime DateAdded { get; set; }
[JsonPropertyName("tags")]
public List<string> Tags { get; set; }
[JsonPropertyName("quote")]
public Dictionary<string, CmcQuote> Quote { get; set; }
} }