#pragma warning disable CS8602
#pragma warning disable CS8603
#pragma warning disable CS8604
#pragma warning disable CS8618
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
private const string USER_NAME = "Gundwn";
private const int COLUMN_WIDTH = 30;
private const int FIAT_COLUMN_WIDTH = 18;
private const int CRYPTO_PRECISION = 8;
private const char USD_SYMBOL = '$';
private const char EURO_SYMBOL = '€';
private const string CURRENCY_URI = "https://duckdice.io/api/currencies";
private const string USER_SEARCH_URI = "https://duckdice.io/api/user/search/{0}";
private const string USER_STATS_URI = "https://duckdice.io/api/user/{0}/{1}";
private static Dictionary<string, DuckDiceCurrency> _currencies = new();
private static ConcurrentDictionary<string, DuckDiceUserStats> _wagered = new();
public static void Main()
var userStats = ProcessUser(USER_NAME);
Console.WriteLine(userStats);
public static string ProcessUser(string userName)
var userHash = GetUserHash(userName);
var stats = new ConcurrentDictionary<string, DuckDiceUserStats>();
Parallel.ForEach(_currencies.Keys, symbol =>
var currency = _currencies[symbol];
var wagered = GetUserWageredStat(userHash, currency);
stats.TryAdd(symbol, wagered);
var sortedKeys = stats.Values
.Where(w => w.HasWagered)
.OrderByDescending(w => w.WageredUsd)
.Select(w => w.Currency);
var rows = sortedKeys.Select(key => stats[key]).Select(w => w.ToString()).ToList();
var rowText = string.Join(Environment.NewLine, rows);
var lineLength = rows[0].Length;
var border = string.Empty.PadLeft(lineLength, '─');
var header = $"User: {userName} ({userHash})".PadLeft(lineLength, ' ');
var usdTotal = stats.Values.Sum(w => w.WageredUsd);
var euroTotal = stats.Values.Sum(w => w.WageredEuro);
var usdTotalText = ToFiatFormat(usdTotal, USD_SYMBOL);
var euroTotalText = ToFiatFormat(usdTotal, EURO_SYMBOL);
var totalSpacing = string.Empty.PadLeft(COLUMN_WIDTH + 3, ' ');
var totalRow = $"{totalSpacing}{usdTotalText} | {euroTotalText}";
var sb = new StringBuilder();
var message = e is ArgumentException ? e.Message : e.ToString();
Console.WriteLine(message);
private static string GetUserHash(string userName)
using var client = new HttpClient();
var requestUri = string.Format(USER_SEARCH_URI, userName);
var response = client.GetStringAsync(requestUri).Result;
var doc = JArray.Parse(response);
var user = doc.First(e => e.Value<string>("username").Equals(userName, StringComparison.InvariantCultureIgnoreCase));
return user.Value<string>("hash");
throw new ArgumentException($"Unable to find the user '{userName}'. Please check the spelling and try again.", e);
public static DuckDiceUserStats GetUserWageredStat(string userHash, DuckDiceCurrency currency)
var requestUri = string.Format(USER_STATS_URI, userHash, currency.Symbol);
using var client = new HttpClient();
var response = client.GetStringAsync(requestUri).Result;
var doc = JObject.Parse(response);
var wagered = double.Parse(doc["volume"].ToString());
var stats = new DuckDiceUserStats
private static string ToFiatFormat(double amount, char currencySymbol, int maxChars = FIAT_COLUMN_WIDTH)
var amountDisplayText = amount == 0 ? "-" : $"{currencySymbol}{amount:N2}";
return amountDisplayText.PadLeft(maxChars, ' ');
private static string ToCryptoFormat(double amount, string label, int precision = CRYPTO_PRECISION, int maxChars = COLUMN_WIDTH)
var amountDisplayText = amount == 0 ? "-" : amount.ToString($"N{precision}");
var labelFormat = label.PadLeft(4, ' ');
var formattedValue = $"{amountDisplayText} {labelFormat}";
return formattedValue.PadLeft(maxChars, ' ');
private static void InitCurrencies()
using var client = new HttpClient();
var currencyResponseText = client.GetStringAsync(CURRENCY_URI).Result;
var currencyResponse = JsonConvert.DeserializeObject<DuckDiceCurrency[]>(currencyResponseText);
_currencies = currencyResponse.ToDictionary(currency => currency.Symbol);
public class DuckDiceUserStats
public string UserHash { get; set; }
public DuckDiceCurrency CurrencyInfo { get; set; }
public double Wagered { get; set; }
public bool HasWagered => Wagered != 0;
public string Currency => CurrencyInfo.Symbol;
public double WageredUsd => Wagered * CurrencyInfo.PriceUsd;
public double WageredEuro => Wagered * CurrencyInfo.PriceEuro;
public override string ToString()
var amountFmt = ToCryptoFormat(Wagered, Currency);
var usdFormat = ToFiatFormat(WageredUsd, USD_SYMBOL);
var euroFormat = ToFiatFormat(WageredEuro, EURO_SYMBOL);
return $"{amountFmt} | {usdFormat} | {euroFormat}";
public class DuckDiceCurrency
public string Symbol { get; set; }
public string Name { get; set; }
public double PriceUsd { get; set; }
public double PriceEuro { get; set; }
public double PriceBtc { get; set; }
public override string ToString() => Symbol;