using System; using NBitcoin; using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using BTCPayServer.Services; using BTCPayServer.Services.Rates; using Microsoft.Extensions.Hosting; using BTCPayServer.Logging; using System.Runtime.CompilerServices; using System.IO; using System.Text; namespace BTCPayServer.HostedServices { public class RatesHostedService : BaseAsyncService { private SettingsRepository _SettingsRepository; private CoinAverageSettings _coinAverageSettings; RateProviderFactory _RateProviderFactory; public RatesHostedService(SettingsRepository repo, RateProviderFactory rateProviderFactory, CoinAverageSettings coinAverageSettings) { this._SettingsRepository = repo; _coinAverageSettings = coinAverageSettings; _RateProviderFactory = rateProviderFactory; } internal override Task[] InitializeTasks() { return new Task[] { CreateLoopTask(RefreshCoinAverageSupportedExchanges), CreateLoopTask(RefreshCoinAverageSettings), CreateLoopTask(RefreshRates) }; } async Task RefreshRates() { using (var timeout = CancellationTokenSource.CreateLinkedTokenSource(Cancellation)) { timeout.CancelAfter(TimeSpan.FromSeconds(20.0)); try { await Task.WhenAll(_RateProviderFactory.Providers .Select(p => (Fetcher: p.Value as BackgroundFetcherRateProvider, ExchangeName: p.Key)).Where(p => p.Fetcher != null) .Select(p => p.Fetcher.UpdateIfNecessary(timeout.Token).ContinueWith(t => { if (t.Result.Exception != null) { Logs.PayServer.LogWarning($"Error while contacting {p.ExchangeName}: {t.Result.Exception.Message}"); } }, TaskScheduler.Default)) .ToArray()).WithCancellation(timeout.Token); } catch (OperationCanceledException) when (timeout.IsCancellationRequested) { } } await Task.Delay(TimeSpan.FromSeconds(30), Cancellation); } async Task RefreshCoinAverageSupportedExchanges() { var exchanges = new CoinAverageExchanges(); foreach (var item in (await new CoinAverageRateProvider() { Authenticator = _coinAverageSettings }.GetExchangeTickersAsync()) .Exchanges .Select(c => new CoinAverageExchange(c.Name, c.DisplayName, $"https://apiv2.bitcoinaverage.com/exchanges/{c.Name}"))) { exchanges.Add(item); } _coinAverageSettings.AvailableExchanges = exchanges; await Task.Delay(TimeSpan.FromHours(5), Cancellation); } async Task RefreshCoinAverageSettings() { var rates = (await _SettingsRepository.GetSettingAsync()) ?? new RatesSetting(); _RateProviderFactory.CacheSpan = TimeSpan.FromMinutes(rates.CacheInMinutes); if (!string.IsNullOrWhiteSpace(rates.PrivateKey) && !string.IsNullOrWhiteSpace(rates.PublicKey)) { _coinAverageSettings.KeyPair = (rates.PublicKey, rates.PrivateKey); } else { _coinAverageSettings.KeyPair = null; } await _SettingsRepository.WaitSettingsChanged(Cancellation); } } }