using System.Net.Http.Json; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; using CliWrap; using CliWrap.Buffered; using LeagueAPI.Models.Challenges; using LeagueAPI.Models.ChampSelect; using LeagueAPI.Models.DDragon; using LeagueAPI.Models.DDragon.Champions; using LeagueAPI.Utils; using static System.Runtime.InteropServices.JavaScript.JSType; namespace LeagueAPI; public class APIClient : IDisposable { private const string ALL_RANDOM_ALL_CHAMPIONS = "101301"; private const string DDRAGON_BASE_URL = "https://ddragon.leagueoflegends.com"; private const int MAYHEM_QUEUE_ID = 2400; private bool _isDisposed; private readonly LcuHttpClient _lcuHttpClient; private readonly HttpClient _dDragonHttpClient; private string? _latestVersion; private readonly Dictionary _championResponseCache = []; public APIClient() { _lcuHttpClient = new(new LcuHttpClientHandler()); _dDragonHttpClient = new HttpClient() { BaseAddress = new Uri(DDRAGON_BASE_URL) }; } public async Task> GetAllChallengesAsync() { return await _lcuHttpClient.GetContentAsync>("/lol-challenges/v1/challenges/local-player") ?? []; } public async Task GetAllRandomAllChampionsCompletedChampionsAsync() { Dictionary challenges = await GetAllChallengesAsync(); if (!challenges.TryGetValue(ALL_RANDOM_ALL_CHAMPIONS, out LolChallengesUIChallenge? allRandomAllChampions)) { return []; } return allRandomAllChampions.CompletedIds ?? []; } public async Task GetSelectableChampionIdsAsync() { ChampSelectSession? session = await _lcuHttpClient.GetContentAsync("/lol-champ-select/v1/session"); return GetSelectableChampionIds(session); } public int[] GetSelectableChampionIds(ChampSelectSession? session) { if (session is null || !session.BenchEnabled) { return []; } IEnumerable benchChampions = session.BenchChampions.Select(b => b.ChampionId); IEnumerable teamChampions = session.MyTeam.Select(c => c.ChampionId); return [.. benchChampions, .. teamChampions]; } private record struct LobbyChangeGame([property: JsonPropertyName("queueId")] int QueueId); public async Task CreateMayhemLobbyAsync() { await _lcuHttpClient.PostAsJsonAsync("/lol-lobby/v2/lobby", new LobbyChangeGame(MAYHEM_QUEUE_ID)); } public async Task StartMatchmakingQueueAsync() { await _lcuHttpClient.PostAsJsonAsync("/lol-lobby/v2/lobby/matchmaking/search", ""); } public async Task MatchmakingAcceptAsync() { await _lcuHttpClient.PostAsJsonAsync("/lol-matchmaking/v1/ready-check/accept", ""); } #region DDragon private async Task DDragonGetAsync(string path) { HttpResponseMessage response = await _dDragonHttpClient.GetAsync(path); if (!response.IsSuccessStatusCode) { Command cmd = Cli.Wrap("curl") .WithArguments($"{DDRAGON_BASE_URL}{path}"); BufferedCommandResult result = await cmd.ExecuteBufferedAsync(); return result.IsSuccess ? result.StandardOutput : throw new Exception($"Failed to fetch from Datadragon: {path}"); } return await response.Content.ReadAsStringAsync(); } private async Task DDragonGetAsync(string path) { string json = await DDragonGetAsync(path); return JsonSerializer.Deserialize(json); } private async Task GetPatch(string patch = "latest") { if (patch is "latest") { return _latestVersion ??= (await DDragonGetAsync("/api/versions.json") ?? []).FirstOrDefault() ?? throw new Exception($"Failed to fetch version: {patch}"); } else { return patch; } } public async Task GetAllChampionsAsync(string patch = "latest") { patch = await GetPatch(patch); if (!_championResponseCache.TryGetValue(patch, out ChampionResponse? championResponse) || championResponse is null || championResponse.Data is null) { string apiUrl = $"/cdn/{patch}/data/en_US/champion.json"; championResponse = await DDragonGetAsync(apiUrl); if (championResponse is not null) { _championResponseCache[patch] = championResponse; } } if (championResponse is null || championResponse.Data is null) { return []; } return [.. championResponse.Data.Values]; } public async Task GetChampionByIdAsync(int id) { ChampionData[] championData = await GetAllChampionsAsync(); if (championData is not { Length: > 0 }) { return null; } return championData.FirstOrDefault(c => c.Id == id); } #endregion #region IDisposable protected virtual void Dispose(bool disposing) { if (!_isDisposed) { if (disposing) { _lcuHttpClient?.Dispose(); _dDragonHttpClient?.Dispose(); } _championResponseCache?.Clear(); _isDisposed = true; } } public void Dispose() { // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method Dispose(disposing: true); GC.SuppressFinalize(this); } #endregion }