mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-11 01:38:27 -04:00
.crypto will now show CoinMarketCap rank. Small refactor
This commit is contained in:
@@ -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;
|
|
||||||
}
|
|
30
src/NadekoBot/Common/TypeReaders/TypeReaderResult.cs
Normal file
30
src/NadekoBot/Common/TypeReaders/TypeReaderResult.cs
Normal 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);
|
||||||
|
}
|
@@ -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"));
|
||||||
|
@@ -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)
|
return (crypto, 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);
|
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<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)
|
||||||
{
|
{
|
||||||
|
@@ -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; }
|
||||||
}
|
}
|
Reference in New Issue
Block a user