CVR Manager 1.0.0
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using CVRManager.Models;
|
||||
|
||||
namespace CVRManager.Services;
|
||||
|
||||
public record CvrmgMod(
|
||||
int Id, string Name, string[] Aliases, string Category, string ModVersion, string GameVersion, string LoaderVersion,
|
||||
string Type, string Author, string Description, string DownloadLink, string SourceLink, string Hash, int ApprovalStatus, DateTime Updated)
|
||||
{
|
||||
public string StatusText => ApprovalStatus switch { 1 => "approved", 2 => "outdated", 3 => "obsolete", _ => "unknown" };
|
||||
public string ModType => Type.Equals("Plugin", StringComparison.OrdinalIgnoreCase) ? "Plugins" : "Mods";
|
||||
}
|
||||
|
||||
public class CvrmgService
|
||||
{
|
||||
const string ListUrl = "https://api.cvrmg.com/v1/mods";
|
||||
readonly HttpClient http = new();
|
||||
List<CvrmgMod>? cache;
|
||||
DateTime fetched;
|
||||
|
||||
public CvrmgService()
|
||||
{
|
||||
http.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("CVRManager", "1.0"));
|
||||
}
|
||||
|
||||
public static string Normalize(string s) => Regex.Replace(s ?? "", "[^a-z0-9]", "", RegexOptions.IgnoreCase).ToLowerInvariant();
|
||||
|
||||
public async Task<List<CvrmgMod>> GetAllAsync(CancellationToken ct, bool force = false)
|
||||
{
|
||||
if (!force && cache != null && DateTime.UtcNow - fetched < TimeSpan.FromMinutes(10)) return cache;
|
||||
var path = Path.Combine(Paths.Root, "sources", "cvrmg.json");
|
||||
string? json = null;
|
||||
if (!force && File.Exists(path) && DateTime.UtcNow - File.GetLastWriteTimeUtc(path) < TimeSpan.FromHours(1))
|
||||
json = await File.ReadAllTextAsync(path, ct);
|
||||
if (json == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var resp = await http.GetAsync(ListUrl, ct);
|
||||
if (!resp.IsSuccessStatusCode) throw new InvalidOperationException($"CVRMG returned {(int)resp.StatusCode}.");
|
||||
json = await resp.Content.ReadAsStringAsync(ct);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
await File.WriteAllTextAsync(path, json, ct);
|
||||
}
|
||||
catch (Exception e) when (e is not OperationCanceledException && File.Exists(path))
|
||||
{
|
||||
json = await File.ReadAllTextAsync(path, ct);
|
||||
}
|
||||
}
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var list = new List<CvrmgMod>();
|
||||
foreach (var m in doc.RootElement.EnumerateArray())
|
||||
{
|
||||
if (!m.TryGetProperty("versions", out var versions) || versions.ValueKind != JsonValueKind.Array) continue;
|
||||
JsonElement? best = null;
|
||||
var bestNo = int.MinValue;
|
||||
foreach (var v in versions.EnumerateArray())
|
||||
{
|
||||
var no = v.TryGetProperty("_version", out var n) && n.ValueKind == JsonValueKind.Number ? n.GetInt32() : 0;
|
||||
if (no >= bestNo) { best = v; bestNo = no; }
|
||||
}
|
||||
if (best is not { } ver) continue;
|
||||
|
||||
string S(JsonElement e, string k) => e.TryGetProperty(k, out var p) && p.ValueKind == JsonValueKind.String ? p.GetString() ?? "" : "";
|
||||
var aliases = m.TryGetProperty("aliases", out var al) && al.ValueKind == JsonValueKind.Array
|
||||
? al.EnumerateArray().Where(a => a.ValueKind == JsonValueKind.String).Select(a => a.GetString()!).ToArray()
|
||||
: Array.Empty<string>();
|
||||
DateTime.TryParse(S(ver, "updateDate"), null, System.Globalization.DateTimeStyles.AdjustToUniversal, out var updated);
|
||||
list.Add(new CvrmgMod(
|
||||
m.GetProperty("_id").GetInt32(), S(ver, "name"), aliases, S(m, "category"),
|
||||
S(ver, "modVersion"), S(ver, "gameVersion"), S(ver, "loaderVersion").TrimStart('v'), S(ver, "modType"),
|
||||
S(ver, "author"), S(ver, "description"), S(ver, "downloadLink"), S(ver, "sourceLink"), S(ver, "hash"),
|
||||
ver.TryGetProperty("approvalStatus", out var st) && st.ValueKind == JsonValueKind.Number ? st.GetInt32() : 0, updated));
|
||||
}
|
||||
cache = list;
|
||||
fetched = DateTime.UtcNow;
|
||||
return list;
|
||||
}
|
||||
|
||||
public static ReleaseInfo ToRelease(CvrmgMod m, string fileName) => new()
|
||||
{
|
||||
Tag = m.ModVersion,
|
||||
Published = m.Updated,
|
||||
GameVersion = m.GameVersion,
|
||||
LoaderVersion = m.LoaderVersion,
|
||||
Assets = { new AssetInfo { Name = fileName, Url = m.DownloadLink, Hash = m.Hash } },
|
||||
};
|
||||
|
||||
public static void ApplyMeta(ModDef def, CvrmgMod m)
|
||||
{
|
||||
def.CvrmgId = m.Id;
|
||||
if (def.Author.Length == 0) def.Author = m.Author;
|
||||
def.Description = m.Description;
|
||||
def.LatestVersion = m.ModVersion;
|
||||
def.Category = m.Category;
|
||||
def.Status = m.StatusText;
|
||||
if (def.SourceLink.Length == 0) def.SourceLink = m.SourceLink;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user