From 13980b454f022e96700a24d3c8ec831ea133cd37 Mon Sep 17 00:00:00 2001 From: Kieran Date: Thu, 6 Jul 2023 22:01:09 +0100 Subject: [PATCH] Content warning --- NostrStreamer/ApiModel/PatchEvent.cs | 3 + NostrStreamer/Controllers/NostrController.cs | 2 +- .../Configuration/UserConfiguration.cs | 3 + NostrStreamer/Database/User.cs | 5 + .../20230706204906_ContentWarning.Designer.cs | 116 ++++++++++++++++++ .../20230706204906_ContentWarning.cs | 28 +++++ .../StreamerContextModelSnapshot.cs | 3 + NostrStreamer/Services/StreamManager.cs | 11 +- 8 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 NostrStreamer/Migrations/20230706204906_ContentWarning.Designer.cs create mode 100644 NostrStreamer/Migrations/20230706204906_ContentWarning.cs diff --git a/NostrStreamer/ApiModel/PatchEvent.cs b/NostrStreamer/ApiModel/PatchEvent.cs index 5556c5b..7401376 100644 --- a/NostrStreamer/ApiModel/PatchEvent.cs +++ b/NostrStreamer/ApiModel/PatchEvent.cs @@ -15,4 +15,7 @@ public class PatchEvent [JsonProperty("tags")] public string[] Tags { get; init; } = Array.Empty(); + + [JsonProperty("content_warning")] + public string? ContentWarning { get; init; } } diff --git a/NostrStreamer/Controllers/NostrController.cs b/NostrStreamer/Controllers/NostrController.cs index d78b77d..0485dd6 100644 --- a/NostrStreamer/Controllers/NostrController.cs +++ b/NostrStreamer/Controllers/NostrController.cs @@ -71,7 +71,7 @@ public class NostrController : Controller var pubkey = GetPubKey(); if (string.IsNullOrEmpty(pubkey)) return Unauthorized(); - await _streamManager.PatchEvent(pubkey, req.Title, req.Summary, req.Image, req.Tags); + await _streamManager.PatchEvent(pubkey, req.Title, req.Summary, req.Image, req.Tags, req.ContentWarning); return Accepted(); } diff --git a/NostrStreamer/Database/Configuration/UserConfiguration.cs b/NostrStreamer/Database/Configuration/UserConfiguration.cs index 1962dc5..6c548ea 100644 --- a/NostrStreamer/Database/Configuration/UserConfiguration.cs +++ b/NostrStreamer/Database/Configuration/UserConfiguration.cs @@ -17,5 +17,8 @@ public class UserConfiguration : IEntityTypeConfiguration builder.Property(a => a.Version) .IsRowVersion(); + + builder.Property(a => a.Tags); + builder.Property(a => a.ContentWarning); } } diff --git a/NostrStreamer/Database/User.cs b/NostrStreamer/Database/User.cs index 1eb4280..f01f995 100644 --- a/NostrStreamer/Database/User.cs +++ b/NostrStreamer/Database/User.cs @@ -38,6 +38,11 @@ public class User /// Comma seperated tags /// public string? Tags { get; set; } + + /// + /// Any content warning tag (NIP-36) + /// + public string? ContentWarning { get; set; } /// /// Concurrency token diff --git a/NostrStreamer/Migrations/20230706204906_ContentWarning.Designer.cs b/NostrStreamer/Migrations/20230706204906_ContentWarning.Designer.cs new file mode 100644 index 0000000..08def6e --- /dev/null +++ b/NostrStreamer/Migrations/20230706204906_ContentWarning.Designer.cs @@ -0,0 +1,116 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NostrStreamer.Database; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace NostrStreamer.Migrations +{ + [DbContext(typeof(StreamerContext))] + [Migration("20230706204906_ContentWarning")] + partial class ContentWarning + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("NostrStreamer.Database.Payment", b => + { + b.Property("PaymentHash") + .HasColumnType("text"); + + b.Property("Amount") + .HasColumnType("numeric(20,0)"); + + b.Property("Created") + .HasColumnType("timestamp with time zone"); + + b.Property("Invoice") + .IsRequired() + .HasColumnType("text"); + + b.Property("IsPaid") + .HasColumnType("boolean"); + + b.Property("PubKey") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("PaymentHash"); + + b.HasIndex("PubKey"); + + b.ToTable("Payments"); + }); + + modelBuilder.Entity("NostrStreamer.Database.User", b => + { + b.Property("PubKey") + .HasColumnType("text"); + + b.Property("Balance") + .HasColumnType("bigint"); + + b.Property("ContentWarning") + .HasColumnType("text"); + + b.Property("Event") + .HasColumnType("text"); + + b.Property("Image") + .HasColumnType("text"); + + b.Property("StreamKey") + .IsRequired() + .HasColumnType("text"); + + b.Property("Summary") + .HasColumnType("text"); + + b.Property("Tags") + .HasColumnType("text"); + + b.Property("Title") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("PubKey"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("NostrStreamer.Database.Payment", b => + { + b.HasOne("NostrStreamer.Database.User", "User") + .WithMany("Payments") + .HasForeignKey("PubKey") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("NostrStreamer.Database.User", b => + { + b.Navigation("Payments"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/NostrStreamer/Migrations/20230706204906_ContentWarning.cs b/NostrStreamer/Migrations/20230706204906_ContentWarning.cs new file mode 100644 index 0000000..a7a7481 --- /dev/null +++ b/NostrStreamer/Migrations/20230706204906_ContentWarning.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace NostrStreamer.Migrations +{ + /// + public partial class ContentWarning : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ContentWarning", + table: "Users", + type: "text", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ContentWarning", + table: "Users"); + } + } +} diff --git a/NostrStreamer/Migrations/StreamerContextModelSnapshot.cs b/NostrStreamer/Migrations/StreamerContextModelSnapshot.cs index f8eb75d..00c186f 100644 --- a/NostrStreamer/Migrations/StreamerContextModelSnapshot.cs +++ b/NostrStreamer/Migrations/StreamerContextModelSnapshot.cs @@ -59,6 +59,9 @@ namespace NostrStreamer.Migrations b.Property("Balance") .HasColumnType("bigint"); + b.Property("ContentWarning") + .HasColumnType("text"); + b.Property("Event") .HasColumnType("text"); diff --git a/NostrStreamer/Services/StreamManager.cs b/NostrStreamer/Services/StreamManager.cs index 49ea84f..ef2c534 100644 --- a/NostrStreamer/Services/StreamManager.cs +++ b/NostrStreamer/Services/StreamManager.cs @@ -82,7 +82,7 @@ public class StreamManager } } - public async Task PatchEvent(string pubkey, string? title, string? summary, string? image, string[]? tags) + public async Task PatchEvent(string pubkey, string? title, string? summary, string? image, string[]? tags, string? contentWarning) { var user = await _db.Users.SingleOrDefaultAsync(a => a.PubKey == pubkey); if (user == default) throw new Exception("User not found"); @@ -91,6 +91,7 @@ public class StreamManager user.Summary = summary; user.Image = image; user.Tags = tags != null ? string.Join(",", tags) : null; + user.ContentWarning = contentWarning; var ev = CreateStreamEvent(user); user.Event = JsonConvert.SerializeObject(ev, NostrSerializer.Settings); @@ -151,7 +152,8 @@ public class StreamManager new("streaming", GetStreamUrl(user)), new("image", user.Image ?? ""), new("status", status), - new("p", user.PubKey, "", "host") + new("p", user.PubKey, "", "host"), + new("relays", _config.Relays), }; if (status == "live") @@ -162,6 +164,11 @@ public class StreamManager new("current_participants", (viewers.HasValue ? viewers.ToString() : null) ?? existingEvent?.Tags?.FindFirstTagValue("current_participants") ?? "0")); + + if (!string.IsNullOrEmpty(user.ContentWarning)) + { + tags.Add(new("content-warning", user.ContentWarning)); + } } foreach (var tag in !string.IsNullOrEmpty(user.Tags) ?