#nullable disable using NadekoBot.Modules.Searches.Common; using Newtonsoft.Json; namespace NadekoBot.Modules.Searches.Services; public class CryptoService : INService { private readonly IDataCache _cache; private readonly IHttpClientFactory _httpFactory; private readonly IBotCredentials _creds; private readonly SemaphoreSlim getCryptoLock = new(1, 1); public CryptoService(IDataCache cache, IHttpClientFactory httpFactory, IBotCredentials creds) { _cache = cache; _httpFactory = httpFactory; _creds = creds; } public async Task<(CryptoResponseData Data, CryptoResponseData Nearest)> GetCryptoData(string name) { if (string.IsNullOrWhiteSpace(name)) return (null, null); name = name.ToUpperInvariant(); var cryptos = await CryptoData(); if (cryptos is null) return (null, null); var crypto = cryptos?.FirstOrDefault(x => x.Id.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(); crypto = nearest?.Elem; } if (nearest is not null) return (null, crypto); return (crypto, null); } public async Task> CryptoData() { await getCryptoLock.WaitAsync(); try { var fullStrData = await _cache.GetOrAddCachedDataAsync("nadeko:crypto_data", async _ => { try { using var http = _httpFactory.CreateClient(); var strData = await http.GetStringAsync( "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; } catch (Exception ex) { Log.Error(ex, "Error getting crypto data: {Message}", ex.Message); return default; } }, "", TimeSpan.FromHours(1)); return JsonConvert.DeserializeObject(fullStrData).Data; } catch (Exception ex) { Log.Error(ex, "Error retreiving crypto data: {Message}", ex.Message); return default; } finally { getCryptoLock.Release(); } } }