]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add project to update Patrons.yml from a csv file containing Patreon webhooks, add...
authorDrSmugleaf <DrSmugleaf@users.noreply.github.com>
Thu, 12 Oct 2023 07:33:30 +0000 (00:33 -0700)
committerGitHub <noreply@github.com>
Thu, 12 Oct 2023 07:33:30 +0000 (00:33 -0700)
13 files changed:
Content.PatreonParser/Attributes.cs [new file with mode: 0644]
Content.PatreonParser/Content.PatreonParser.csproj [new file with mode: 0644]
Content.PatreonParser/CurrentlyEntitledTiers.cs [new file with mode: 0644]
Content.PatreonParser/Data.cs [new file with mode: 0644]
Content.PatreonParser/Included.cs [new file with mode: 0644]
Content.PatreonParser/Patron.cs [new file with mode: 0644]
Content.PatreonParser/Program.cs [new file with mode: 0644]
Content.PatreonParser/Relationships.cs [new file with mode: 0644]
Content.PatreonParser/Root.cs [new file with mode: 0644]
Content.PatreonParser/Row.cs [new file with mode: 0644]
Content.PatreonParser/TierData.cs [new file with mode: 0644]
Resources/Credits/Patrons.yml
SpaceStation14.sln

diff --git a/Content.PatreonParser/Attributes.cs b/Content.PatreonParser/Attributes.cs
new file mode 100644 (file)
index 0000000..d3ebeb5
--- /dev/null
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace Content.PatreonParser;
+
+public sealed class Attributes
+{
+    [JsonPropertyName("full_name")]
+    public string FullName = default!;
+
+    [JsonPropertyName("pledge_relationship_start")]
+    public DateTime? PledgeRelationshipStart;
+
+    [JsonPropertyName("title")]
+    public string Title = default!;
+}
diff --git a/Content.PatreonParser/Content.PatreonParser.csproj b/Content.PatreonParser/Content.PatreonParser.csproj
new file mode 100644 (file)
index 0000000..53b06b2
--- /dev/null
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <OutputType>Exe</OutputType>
+        <TargetFramework>net7.0</TargetFramework>
+        <ImplicitUsings>enable</ImplicitUsings>
+        <Nullable>enable</Nullable>
+    </PropertyGroup>
+
+    <ItemGroup>
+      <PackageReference Include="CsvHelper" Version="30.0.1" />
+    </ItemGroup>
+
+</Project>
diff --git a/Content.PatreonParser/CurrentlyEntitledTiers.cs b/Content.PatreonParser/CurrentlyEntitledTiers.cs
new file mode 100644 (file)
index 0000000..fd1747e
--- /dev/null
@@ -0,0 +1,9 @@
+using System.Text.Json.Serialization;
+
+namespace Content.PatreonParser;
+
+public sealed class CurrentlyEntitledTiers
+{
+    [JsonPropertyName("data")]
+    public List<TierData> Data = default!;
+}
diff --git a/Content.PatreonParser/Data.cs b/Content.PatreonParser/Data.cs
new file mode 100644 (file)
index 0000000..cdc7d79
--- /dev/null
@@ -0,0 +1,18 @@
+using System.Text.Json.Serialization;
+
+namespace Content.PatreonParser;
+
+public sealed class Data
+{
+    [JsonPropertyName("id")]
+    public string Id = default!;
+
+    [JsonPropertyName("type")]
+    public string Type = default!;
+
+    [JsonPropertyName("attributes")]
+    public Attributes Attributes = default!;
+
+    [JsonPropertyName("relationships")]
+    public Relationships Relationships = default!;
+}
diff --git a/Content.PatreonParser/Included.cs b/Content.PatreonParser/Included.cs
new file mode 100644 (file)
index 0000000..ec33635
--- /dev/null
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace Content.PatreonParser;
+
+public sealed class Included
+{
+    [JsonPropertyName("id")]
+    public int Id;
+
+    [JsonPropertyName("type")]
+    public string Type = default!;
+
+    [JsonPropertyName("attributes")]
+    public Attributes Attributes = default!;
+}
diff --git a/Content.PatreonParser/Patron.cs b/Content.PatreonParser/Patron.cs
new file mode 100644 (file)
index 0000000..d9943a6
--- /dev/null
@@ -0,0 +1,3 @@
+namespace Content.PatreonParser;
+
+public readonly record struct Patron(string FullName, string TierName, DateTime Start);
diff --git a/Content.PatreonParser/Program.cs b/Content.PatreonParser/Program.cs
new file mode 100644 (file)
index 0000000..60a3200
--- /dev/null
@@ -0,0 +1,125 @@
+using System.Globalization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Content.PatreonParser;
+using CsvHelper;
+using CsvHelper.Configuration;
+using static System.Environment;
+
+var repository = new DirectoryInfo(Directory.GetCurrentDirectory()).Parent!.Parent!.Parent!.Parent!;
+var patronsPath = Path.Combine(repository.FullName, "Resources/Credits/Patrons.yml");
+if (!File.Exists(patronsPath))
+{
+    Console.WriteLine($"File {patronsPath} not found.");
+    return;
+}
+
+Console.WriteLine($"Updating {patronsPath}");
+Console.WriteLine("Is this correct? [Y/N]");
+var response = Console.ReadLine()?.ToUpper();
+if (response != "Y")
+{
+    Console.WriteLine("Exiting");
+    return;
+}
+
+var delimiter = ",";
+var hasHeaderRecord = false;
+var mode = CsvMode.RFC4180;
+var escape = '\'';
+Console.WriteLine($"""
+Delimiter: {delimiter}
+HasHeaderRecord: {hasHeaderRecord}
+Mode: {mode}
+Escape Character: {escape}
+""");
+
+Console.WriteLine("Enter the full path to the .csv file containing the Patreon webhook data:");
+var filePath = Console.ReadLine();
+if (filePath == null)
+{
+    Console.Write("No path given.");
+    return;
+}
+
+var file = File.OpenRead(filePath);
+var csvConfig = new CsvConfiguration(CultureInfo.InvariantCulture)
+{
+    Delimiter = delimiter,
+    HasHeaderRecord = hasHeaderRecord,
+    Mode = mode,
+    Escape = escape,
+};
+
+using var reader = new CsvReader(new StreamReader(file), csvConfig);
+
+// This does not handle tier name changes, but we haven't had any yet
+var patrons = new Dictionary<Guid, Patron>();
+var jsonOptions = new JsonSerializerOptions
+{
+    IncludeFields = true,
+    NumberHandling = JsonNumberHandling.AllowReadingFromString
+};
+
+// This assumes that the rows are already sorted by id
+foreach (var record in reader.GetRecords<Row>())
+{
+    if (record.Trigger == "members:create")
+        continue;
+
+    var content = JsonSerializer.Deserialize<Root>(record.ContentJson, jsonOptions)!;
+
+    var id = Guid.Parse(content.Data.Id);
+    patrons.Remove(id);
+
+    var tiers = content.Data.Relationships.CurrentlyEntitledTiers.Data;
+    if (tiers.Count == 0)
+        continue;
+    else if (tiers.Count > 1)
+        throw new ArgumentException("Found more than one tier");
+
+    var tier = tiers[0];
+    var tierName = content.Included.SingleOrDefault(i => i.Id == tier.Id && i.Type == tier.Type)?.Attributes.Title;
+    if (tierName == null)
+        continue;
+
+    if (record.Trigger == "members:delete")
+        continue;
+
+    var fullName = content.Data.Attributes.FullName.Trim();
+    var pledgeStart = content.Data.Attributes.PledgeRelationshipStart;
+
+    switch (record.Trigger)
+    {
+        case "members:create":
+            break;
+        case "members:delete":
+            break;
+        case "members:update":
+            patrons.Add(id, new Patron(fullName, tierName, pledgeStart!.Value));
+            break;
+        case "members:pledge:create":
+            if (pledgeStart == null)
+                continue;
+
+            patrons.Add(id, new Patron(fullName, tierName, pledgeStart.Value));
+            break;
+        case "members:pledge:delete":
+            // Deleted pledge but still not expired, expired is handled earlier
+            patrons.Add(id, new Patron(fullName, tierName, pledgeStart!.Value));
+            break;
+        case "members:pledge:update":
+            patrons.Add(id, new Patron(fullName, tierName, pledgeStart!.Value));
+            break;
+    }
+}
+
+var patronList = patrons.Values.ToList();
+patronList.Sort((a, b) => a.Start.CompareTo(b.Start));
+var yaml = patronList.Select(p => $"""
+- Name: "{p.FullName.Replace("\"", "\\\"")}"
+  Tier: {p.TierName}
+""");
+var output = string.Join(NewLine, yaml) + NewLine;
+File.WriteAllText(patronsPath, output);
+Console.WriteLine($"Updated {patronsPath} with {patronList.Count} patrons.");
diff --git a/Content.PatreonParser/Relationships.cs b/Content.PatreonParser/Relationships.cs
new file mode 100644 (file)
index 0000000..f919f81
--- /dev/null
@@ -0,0 +1,9 @@
+using System.Text.Json.Serialization;
+
+namespace Content.PatreonParser;
+
+public sealed class Relationships
+{
+    [JsonPropertyName("currently_entitled_tiers")]
+    public CurrentlyEntitledTiers CurrentlyEntitledTiers = default!;
+}
diff --git a/Content.PatreonParser/Root.cs b/Content.PatreonParser/Root.cs
new file mode 100644 (file)
index 0000000..2a772a1
--- /dev/null
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace Content.PatreonParser;
+
+public sealed class Root
+{
+    [JsonPropertyName("data")]
+    public Data Data = default!;
+
+    [JsonPropertyName("included")]
+    public List<Included> Included = default!;
+}
diff --git a/Content.PatreonParser/Row.cs b/Content.PatreonParser/Row.cs
new file mode 100644 (file)
index 0000000..f4d2504
--- /dev/null
@@ -0,0 +1,19 @@
+using CsvHelper.Configuration.Attributes;
+
+namespace Content.PatreonParser;
+
+// These need to be properties or CSVHelper will not find them
+public sealed class Row
+{
+    [Name("Id"), Index(0)]
+    public int Id { get; set; }
+
+    [Name("Trigger"), Index(1)]
+    public string Trigger { get; set; } = default!;
+
+    [Name("Time"), Index(2)]
+    public DateTime Time { get; set; }
+
+    [Name("Content"), Index(3)]
+    public string ContentJson { get; set; } = default!;
+}
diff --git a/Content.PatreonParser/TierData.cs b/Content.PatreonParser/TierData.cs
new file mode 100644 (file)
index 0000000..a840b21
--- /dev/null
@@ -0,0 +1,12 @@
+using System.Text.Json.Serialization;
+
+namespace Content.PatreonParser;
+
+public sealed class TierData
+{
+    [JsonPropertyName("id")]
+    public int Id;
+
+    [JsonPropertyName("type")]
+    public string Type = default!;
+}
index acb9eb229a550154373e98d18a0dc81945d2544d..427a8f62f0933b33a21bb746b4c8f60d0b5129d3 100644 (file)
@@ -58,6 +58,8 @@
   Tier: Revolutionary
 - Name: "Mikhail"
   Tier: Revolutionary
+- Name: "Ramiro Agis"
+  Tier: Revolutionary
 - Name: "osborn"
   Tier: Syndicate Agent
 - Name: "Uinseann"
   Tier: Syndicate Agent
 - Name: "Odin The Wanderer"
   Tier: Revolutionary
+- Name: "tokie"
+  Tier: Nuclear Operative
 - Name: "Wallace Megas"
   Tier: Revolutionary
 - Name: "Vandell"
   Tier: Revolutionary
 - Name: "eric156"
   Tier: Revolutionary
+- Name: "SHANE ALAN ZINDA"
+  Tier: Nuclear Operative
 - Name: "Glenn Olsen"
   Tier: Syndicate Agent
 - Name: "Constellations"
index 2dc4c95508dd3a48586d8f379c16bd947e42fa23..a94daa316dd47ff85ef27470bdf797b42fffb978 100644 (file)
@@ -127,6 +127,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Shared.CompNetworkGe
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Serialization.Generator", "RobustToolbox\Robust.Serialization.Generator\Robust.Serialization.Generator.csproj", "{6FBF108E-5CB5-47DE-8D7E-B496ABA9E3E2}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Content.PatreonParser", "Content.PatreonParser\Content.PatreonParser.csproj", "{D97D8258-D915-4D1D-B1E3-1A8D00CF9EB5}"
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Any CPU = Debug|Any CPU
@@ -430,6 +432,14 @@ Global
                {6FBF108E-5CB5-47DE-8D7E-B496ABA9E3E2}.DebugOpt|Any CPU.Build.0 = Debug|Any CPU
                {6FBF108E-5CB5-47DE-8D7E-B496ABA9E3E2}.Tools|Any CPU.ActiveCfg = Debug|Any CPU
                {6FBF108E-5CB5-47DE-8D7E-B496ABA9E3E2}.Tools|Any CPU.Build.0 = Debug|Any CPU
+               {D97D8258-D915-4D1D-B1E3-1A8D00CF9EB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {D97D8258-D915-4D1D-B1E3-1A8D00CF9EB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {D97D8258-D915-4D1D-B1E3-1A8D00CF9EB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {D97D8258-D915-4D1D-B1E3-1A8D00CF9EB5}.Release|Any CPU.Build.0 = Release|Any CPU
+               {D97D8258-D915-4D1D-B1E3-1A8D00CF9EB5}.DebugOpt|Any CPU.ActiveCfg = Debug|Any CPU
+               {D97D8258-D915-4D1D-B1E3-1A8D00CF9EB5}.DebugOpt|Any CPU.Build.0 = Debug|Any CPU
+               {D97D8258-D915-4D1D-B1E3-1A8D00CF9EB5}.Tools|Any CPU.ActiveCfg = Debug|Any CPU
+               {D97D8258-D915-4D1D-B1E3-1A8D00CF9EB5}.Tools|Any CPU.Build.0 = Debug|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE