#:project ../src/MeWeb.Core/MeWeb.Core.csproj using System.Text.Json; using System.Text.Json.Serialization; using MeWeb.Core.Knowledge; var json = new KnowledgeJsonContext(new JsonSerializerOptions(JsonSerializerDefaults.Web) { WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }); var root = FindRoot(); var reviewed = Path.Combine(root, "data", "observed.json"); if (args.Length < 2 || args[0] is not ("show" or "accept")) { Console.Error.WriteLine(""" usage: dotnet run tools/observed.cs -- show dotnet run tools/observed.cs -- accept AS/ ... is the server's count of shared findings (/var/lib/meweb/observed.json on the box). show prints every provider line and which notes are ready (at least 5 networks, 4 in 5 agreeing); accept copies the ready notes of the named lines into data/observed.json, which every release embeds. """); return 2; } var candidates = Load(args[1]); if (args[0] == "show") { foreach (var line in candidates) { Console.WriteLine($"{line.Key,-16} {line.Name} - {line.Networks} network(s), {line.From} to {line.To}"); foreach (var (label, fact) in new[] { ("ipv4", line.Ipv4), ("ipv6", line.Ipv6), ("port25", line.Port25) }) if (fact is not null) Console.WriteLine($" {label,-7} {fact.Value,-8} {fact.Agree} of {fact.Of}{(fact.Ready ? " ready" : "")}"); } Console.WriteLine($"{candidates.Count} provider line(s), {candidates.Count(c => Ready(c) is not null)} with a ready note"); return 0; } var wanted = args.Skip(2).ToHashSet(StringComparer.OrdinalIgnoreCase); var current = File.Exists(reviewed) ? Load(reviewed) : []; foreach (var key in wanted) { var line = candidates.FirstOrDefault(c => string.Equals(c.Key, key, StringComparison.OrdinalIgnoreCase)); var ready = line is null ? null : Ready(line); if (ready is null) { Console.Error.WriteLine($"{key}: no ready note in {args[1]}"); return 1; } current.RemoveAll(c => c.Key == ready.Key); current.Add(ready); Console.WriteLine($"{ready.Key}: accepted ({string.Join(", ", new[] { ready.Ipv4, ready.Ipv6, ready.Port25 }.Where(f => f is not null).Select(f => $"{f.Value} {f.Agree}/{f.Of}"))})"); } File.WriteAllText(reviewed, JsonSerializer.Serialize(current.OrderBy(c => c.Asn).ThenBy(c => c.Line, StringComparer.Ordinal).ToList(), json.ListObservedNetwork) + "\n"); return 0; List Load(string path) { using var stream = File.OpenRead(path); return JsonSerializer.Deserialize(stream, json.ListObservedNetwork) ?? []; } static ObservedNetwork Ready(ObservedNetwork line) { var copy = new ObservedNetwork { Asn = line.Asn, Line = line.Line, Name = line.Name, Networks = line.Networks, From = line.From, To = line.To, Ipv4 = line.Ipv4 is { Ready: true } ? line.Ipv4 : null, Ipv6 = line.Ipv6 is { Ready: true } ? line.Ipv6 : null, Port25 = line.Port25 is { Ready: true } ? line.Port25 : null, }; return copy.Ipv4 is null && copy.Ipv6 is null && copy.Port25 is null ? null : copy; } static string FindRoot() { var dir = new DirectoryInfo(Directory.GetCurrentDirectory()); while (dir is not null && !File.Exists(Path.Combine(dir.FullName, "MeWeb.slnx"))) dir = dir.Parent; return dir?.FullName ?? throw new InvalidOperationException("run from inside the repository (MeWeb.slnx not found)"); }