using System.Text.RegularExpressions; using CurseForge.APIClient.Models.Mods; using Modrinth; using Octokit; using ApiClient = CurseForge.APIClient.ApiClient; namespace ModpackUpdater.Manager; public class ModpackFactory { private readonly GitHubClient github = new(new ProductHeaderValue("MinecraftModpackUpdater")) { Credentials = new Credentials("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"), }; private readonly ApiClient curseForge = new("$2a$10$pE4dD09gmr7IcOe8hjWhleWWjXopJcDNpq1P9FlrDMCBw05pCyAXa", "pilzinsel64@gmx.de") { RequestTimeout = new TimeSpan(0, 0, 30), }; private readonly ModrinthClient modrinth = new(new ModrinthClientConfig { ModrinthToken = "mrp_zUlDSET5actMUdTU3FK242TXgvlWgaErSSEFuegNG7thLgkC50IiK2NCGOzW", UserAgent = "Pilz.ModpackUpater", }); ~ModpackFactory() { curseForge.Dispose(); modrinth.Dispose(); } public async Task ResolveSourceUrl(InstallAction action, Version? targetVersion = null, string? overwriteVersion = null) { switch (action.SourceType) { case SourceType.GitHub: { var repo = await github.Repository.Get(action.SourceOwner, action.SourceName); var release = await github.Repository.Release.Get(repo.Id, action.SourceTag); var assets = await github.Repository.Release.GetAllAssets(repo.Id, release.Id); if (assets.LastOrDefault(a => Regex.IsMatch(a.Name, action.SourceRegex)) is ReleaseAsset asset) return asset.BrowserDownloadUrl; break; } case SourceType.GitLab: throw new NotSupportedException("To be implemented soon."); case SourceType.CurseForge: { var res = await curseForge.GetModFileDownloadUrlAsync(Convert.ToInt32(action.SourceName), Convert.ToInt32(action.SourceTag)); if (!string.IsNullOrWhiteSpace(res.Data)) return res.Data; break; } case SourceType.Modrinth: { var res = await modrinth.VersionFile.GetVersionByHashAsync(action.SourceTag); var file = res.Files.Length == 1 ? res.Files[0] : res.Files.FirstOrDefault(n => Regex.IsMatch(n.FileName, action.SourceRegex)); if (file != null) return file.Url; break; } } return action.GetSourceUrl(targetVersion, overwriteVersion: overwriteVersion); } public async Task FindUpdates(InstallAction action, string? gameVersion, ModLoader modLoader) { switch (action.SourceType) { case SourceType.GitHub: try { var repo = await github.Repository.Get(action.SourceOwner, action.SourceName); var releases = await github.Repository.Release.GetAll(repo.Id); return releases.Select(r => new ModVersionInfo(string.IsNullOrWhiteSpace(r.Name) ? r.TagName : r.Name, r.TagName)).ToArray(); } catch { // Ignore } break; case SourceType.GitLab: throw new NotSupportedException("To be implemented soon."); case SourceType.CurseForge: { var res = await curseForge.GetModFilesAsync(Convert.ToInt32(action.SourceName), gameVersion: gameVersion, modLoaderType: GetModLoaderForCurseForge(modLoader)); if (res.Data != null) return res.Data.Select(n => new ModVersionInfo(n.DisplayName, n.Id.ToString())).ToArray(); break; } case SourceType.Modrinth: { var res = await modrinth.Version.GetProjectVersionListAsync(action.SourceName, gameVersions: GetGameVersionForModrinth(gameVersion), loaders: GetModLoaderForModrinth(modLoader)); return res.Select(v => new ModVersionInfo(v.VersionNumber, v.Id)).ToArray(); } } return null; } private ModLoaderType? GetModLoaderForCurseForge(ModLoader modLoader) { return modLoader switch { ModLoader.Any => null, ModLoader.Forge => ModLoaderType.Forge, ModLoader.NeoForge => ModLoaderType.NeoForge, ModLoader.Fabric => ModLoaderType.Fabric, ModLoader.LiteLoader => ModLoaderType.LiteLoader, ModLoader.Cauldron => ModLoaderType.Cauldron, ModLoader.Quilt => ModLoaderType.Quilt, _ => null, }; } private string[] GetModLoaderForModrinth(ModLoader modLoader) { return modLoader switch { ModLoader.Any => [], ModLoader.Forge => ["forge"], ModLoader.NeoForge => ["neoforge"], ModLoader.Fabric => ["fabric"], ModLoader.LiteLoader => ["liteloader"], ModLoader.Cauldron => ["cauldron"], ModLoader.Quilt => ["quilt"], _ => [], }; } private string[] GetGameVersionForModrinth(string? gameVersion) { if (string.IsNullOrWhiteSpace(gameVersion) || gameVersion == "*") return []; return [gameVersion]; } }