diff --git a/Containerfile b/Containerfile deleted file mode 100644 index 4c50966..0000000 --- a/Containerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM mcr.microsoft.com/dotnet/runtime:9.0 -COPY ./song_of_the_day/bin/Release/net9.0/ /app -WORKDIR /app -CMD ["song_of_the_day"] diff --git a/nuget.config b/nuget.config index 1550c4a..65e7055 100644 --- a/nuget.config +++ b/nuget.config @@ -4,8 +4,8 @@ - + diff --git a/song_of_the_day.Tests/UnitTest1.cs b/song_of_the_day.Tests/UnitTest1.cs index 95b7ebe..34e18a2 100644 --- a/song_of_the_day.Tests/UnitTest1.cs +++ b/song_of_the_day.Tests/UnitTest1.cs @@ -1,6 +1,6 @@ namespace song_of_the_day.Tests; -using song_of_the_day; +using song_of_the_day; public class UnitTest1 { diff --git a/song_of_the_day/Config/AppConfiguration.cs b/song_of_the_day/Config/AppConfiguration.cs new file mode 100644 index 0000000..d3f7099 --- /dev/null +++ b/song_of_the_day/Config/AppConfiguration.cs @@ -0,0 +1,75 @@ + +public class AppConfiguration +{ + public static AppConfiguration Instance = new AppConfiguration(); + + private AppConfiguration() + { + this.SignalAPIEndpointUri = Environment.GetEnvironmentVariable("SIGNAL_API_URI") ?? "http://192.168.1.108"; + this.SignalAPIEndpointPort = Environment.GetEnvironmentVariable("SIGNAL_API_PORT") ?? "8719"; + this.HostPhoneNumber = Environment.GetEnvironmentVariable("HOST_PHONE") ?? "+4367762751895"; + this.DatabaseUri = Environment.GetEnvironmentVariable("DB_URI") ?? "192.168.1.108"; + this.DatabasePort = Environment.GetEnvironmentVariable("DB_PORT") ?? "5477"; + this.DatabaseName = Environment.GetEnvironmentVariable("DB_NAME") ?? "sotd"; + this.DatabaseUser = Environment.GetEnvironmentVariable("DB_USER") ?? "sotd"; + this.DatabasePW = Environment.GetEnvironmentVariable("DB_PASS") ?? "SotdP0stgresP4ss"; + this.SignalGroupId = Environment.GetEnvironmentVariable("SIGNAL_GROUP_ID") ?? "group.Wmk1UTVQTnh0Sjd6a0xiOGhnTnMzZlNkc2p2Q3c0SXJiQkU2eDlNU0hyTT0="; + this.WebUIBaseURL = Environment.GetEnvironmentVariable("WEB_BASE_URL") ?? "https://sotd.disi.dev/"; + this.UseBotTag = bool.Parse(Environment.GetEnvironmentVariable("USE_BOT_TAG") ?? "true"); + } + + public string SignalAPIEndpointUri + { + get; private set; + } + + public string SignalAPIEndpointPort + { + get; private set; + } + + public string DatabaseUri + { + get; private set; + } + + public string DatabasePort + { + get; private set; + } + + public string DatabaseName + { + get; private set; + } + + public string DatabaseUser + { + get; private set; + } + + public string DatabasePW + { + get; private set; + } + + public string SignalGroupId + { + get; private set; + } + + public string HostPhoneNumber + { + get; private set; + } + + public string WebUIBaseURL + { + get; private set; + } + + public bool UseBotTag + { + get; private set; + } +} \ No newline at end of file diff --git a/song_of_the_day/Data/DataContext.cs b/song_of_the_day/Data/DataContext.cs index 00fbd39..b4d54eb 100644 --- a/song_of_the_day/Data/DataContext.cs +++ b/song_of_the_day/Data/DataContext.cs @@ -1,13 +1,20 @@ using Microsoft.EntityFrameworkCore; +using sotd.Pages; public class DataContext : DbContext { - public static DataContext Instance; + public static DataContext Instance + { + get { return new DataContext(); } + } - public DbSet Users { get; set; } - public DbSet Songs { get; set; } - public DbSet SongSuggestions { get; set; } + public DbSet? Users { get; set; } + public DbSet? Songs { get; set; } + public DbSet? SongSuggestions { get; set; } + public DbSet? SuggestionHelpers { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - => optionsBuilder.UseNpgsql(@"Host=192.168.1.108:5477;Username=sotd;Password=SotdP0stgresP4ss;Database=sotd"); + => optionsBuilder.UseNpgsql($"Host={AppConfiguration.Instance.DatabaseUri}:{AppConfiguration.Instance.DatabasePort};" + + $"Username={AppConfiguration.Instance.DatabaseUser};Password={AppConfiguration.Instance.DatabasePW};" + + $"Database={AppConfiguration.Instance.DatabaseName}"); } \ No newline at end of file diff --git a/song_of_the_day/Data/Migrations/20250413192634_AdditionalFields.Designer.cs b/song_of_the_day/Data/Migrations/20250413192634_AdditionalFields.Designer.cs new file mode 100644 index 0000000..c81e0cb --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250413192634_AdditionalFields.Designer.cs @@ -0,0 +1,127 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250413192634_AdditionalFields")] + partial class AdditionalFields + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Song", b => + { + b.Property("SongId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("SongId")); + + b.Property("Artist") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Url") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("SongId"); + + b.ToTable("Songs"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("SongId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SongId"); + + b.HasIndex("UserId"); + + b.ToTable("SongSuggestions"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserId")); + + b.Property("IsIntroduced") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("SignalId") + .IsRequired() + .HasColumnType("text"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("UserId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.HasOne("Song", "Song") + .WithMany() + .HasForeignKey("SongId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Song"); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/song_of_the_day/Data/Migrations/20250413192634_AdditionalFields.cs b/song_of_the_day/Data/Migrations/20250413192634_AdditionalFields.cs new file mode 100644 index 0000000..d0cfcfc --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250413192634_AdditionalFields.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + /// + public partial class AdditionalFields : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "IsIntroduced", + table: "Users", + type: "boolean", + nullable: false, + defaultValue: false); + + migrationBuilder.AddColumn( + name: "UserName", + table: "Users", + type: "text", + nullable: false, + defaultValue: ""); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsIntroduced", + table: "Users"); + + migrationBuilder.DropColumn( + name: "UserName", + table: "Users"); + } + } +} diff --git a/song_of_the_day/Data/Migrations/20250414161136_Update user properties.Designer.cs b/song_of_the_day/Data/Migrations/20250414161136_Update user properties.Designer.cs new file mode 100644 index 0000000..8b22cab --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250414161136_Update user properties.Designer.cs @@ -0,0 +1,127 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250414161136_Update user properties")] + partial class Updateuserproperties + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Song", b => + { + b.Property("SongId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("SongId")); + + b.Property("Artist") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Url") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("SongId"); + + b.ToTable("Songs"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("SongId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SongId"); + + b.HasIndex("UserId"); + + b.ToTable("SongSuggestions"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserId")); + + b.Property("IsIntroduced") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NickName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SignalMemberId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("UserId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.HasOne("Song", "Song") + .WithMany() + .HasForeignKey("SongId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Song"); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/song_of_the_day/Data/Migrations/20250414161136_Update user properties.cs b/song_of_the_day/Data/Migrations/20250414161136_Update user properties.cs new file mode 100644 index 0000000..2584c2f --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250414161136_Update user properties.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + /// + public partial class Updateuserproperties : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "UserName", + table: "Users", + newName: "SignalMemberId"); + + migrationBuilder.RenameColumn( + name: "SignalId", + table: "Users", + newName: "NickName"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "SignalMemberId", + table: "Users", + newName: "UserName"); + + migrationBuilder.RenameColumn( + name: "NickName", + table: "Users", + newName: "SignalId"); + } + } +} diff --git a/song_of_the_day/Data/Migrations/20250414181019_Add SuggestionHelper entity.Designer.cs b/song_of_the_day/Data/Migrations/20250414181019_Add SuggestionHelper entity.Designer.cs new file mode 100644 index 0000000..2891c67 --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250414181019_Add SuggestionHelper entity.Designer.cs @@ -0,0 +1,127 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250414181019_Add SuggestionHelper entity")] + partial class AddSuggestionHelperentity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Song", b => + { + b.Property("SongId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("SongId")); + + b.Property("Artist") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Url") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("SongId"); + + b.ToTable("Songs"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("SongId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SongId"); + + b.HasIndex("UserId"); + + b.ToTable("SongSuggestions"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserId")); + + b.Property("IsIntroduced") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NickName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SignalMemberId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("UserId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.HasOne("Song", "Song") + .WithMany() + .HasForeignKey("SongId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Song"); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/song_of_the_day/Data/Migrations/20250414181019_Add SuggestionHelper entity.cs b/song_of_the_day/Data/Migrations/20250414181019_Add SuggestionHelper entity.cs new file mode 100644 index 0000000..5a012d3 --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250414181019_Add SuggestionHelper entity.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + /// + public partial class AddSuggestionHelperentity : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/song_of_the_day/Data/Migrations/20250414181911_Now really add SuggestionHelper entity.Designer.cs b/song_of_the_day/Data/Migrations/20250414181911_Now really add SuggestionHelper entity.Designer.cs new file mode 100644 index 0000000..3f5560d --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250414181911_Now really add SuggestionHelper entity.Designer.cs @@ -0,0 +1,148 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250414181911_Now really add SuggestionHelper entity")] + partial class NowreallyaddSuggestionHelperentity + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Song", b => + { + b.Property("SongId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("SongId")); + + b.Property("Artist") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Url") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("SongId"); + + b.ToTable("Songs"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Date") + .HasColumnType("timestamp with time zone"); + + b.Property("SongId") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SongId"); + + b.HasIndex("UserId"); + + b.ToTable("SongSuggestions"); + }); + + modelBuilder.Entity("SuggestionHelper", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("SuggestionHelpers"); + }); + + modelBuilder.Entity("User", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserId")); + + b.Property("IsIntroduced") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NickName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SignalMemberId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("UserId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("SongSuggestion", b => + { + b.HasOne("Song", "Song") + .WithMany() + .HasForeignKey("SongId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Song"); + + b.Navigation("User"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/song_of_the_day/Data/Migrations/20250414181911_Now really add SuggestionHelper entity.cs b/song_of_the_day/Data/Migrations/20250414181911_Now really add SuggestionHelper entity.cs new file mode 100644 index 0000000..c6e2b89 --- /dev/null +++ b/song_of_the_day/Data/Migrations/20250414181911_Now really add SuggestionHelper entity.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace song_of_the_day.DataMigrations +{ + /// + public partial class NowreallyaddSuggestionHelperentity : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SuggestionHelpers", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Title = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SuggestionHelpers", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SuggestionHelpers"); + } + } +} diff --git a/song_of_the_day/Data/Migrations/DataContextModelSnapshot.cs b/song_of_the_day/Data/Migrations/DataContextModelSnapshot.cs index 4839f16..c61ba65 100644 --- a/song_of_the_day/Data/Migrations/DataContextModelSnapshot.cs +++ b/song_of_the_day/Data/Migrations/DataContextModelSnapshot.cs @@ -72,6 +72,27 @@ namespace song_of_the_day.DataMigrations b.ToTable("SongSuggestions"); }); + modelBuilder.Entity("SuggestionHelper", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("SuggestionHelpers"); + }); + modelBuilder.Entity("User", b => { b.Property("UserId") @@ -80,11 +101,18 @@ namespace song_of_the_day.DataMigrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("UserId")); + b.Property("IsIntroduced") + .HasColumnType("boolean"); + b.Property("Name") .IsRequired() .HasColumnType("text"); - b.Property("SignalId") + b.Property("NickName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SignalMemberId") .IsRequired() .HasColumnType("text"); diff --git a/song_of_the_day/Data/Song.cs b/song_of_the_day/Data/Song.cs index 592042d..55aeb9d 100644 --- a/song_of_the_day/Data/Song.cs +++ b/song_of_the_day/Data/Song.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore; public class Song { public int SongId { get; set; } - public string Name { get; set; } - public string Artist { get; set; } - public string Url { get; set; } + public string? Name { get; set; } + public string? Artist { get; set; } + public string? Url { get; set; } } \ No newline at end of file diff --git a/song_of_the_day/Data/SongSuggestion.cs b/song_of_the_day/Data/SongSuggestion.cs index e46882c..68fb4d3 100644 --- a/song_of_the_day/Data/SongSuggestion.cs +++ b/song_of_the_day/Data/SongSuggestion.cs @@ -2,9 +2,9 @@ using Microsoft.EntityFrameworkCore; public class SongSuggestion { - - public int Id { get; set;} - public User User { get; set; } - public Song Song { get; set; } + + public int Id { get; set; } + public User? User { get; set; } + public Song? Song { get; set; } public DateTime Date { get; set; } } \ No newline at end of file diff --git a/song_of_the_day/Data/SuggestionHelper.cs b/song_of_the_day/Data/SuggestionHelper.cs new file mode 100644 index 0000000..57d993f --- /dev/null +++ b/song_of_the_day/Data/SuggestionHelper.cs @@ -0,0 +1,9 @@ +using Microsoft.EntityFrameworkCore; + +public class SuggestionHelper +{ + + public int Id { get; set; } + public string? Title { get; set; } + public string? Description { get; set; } +} \ No newline at end of file diff --git a/song_of_the_day/Data/User.cs b/song_of_the_day/Data/User.cs index d782ff0..cf01c7d 100644 --- a/song_of_the_day/Data/User.cs +++ b/song_of_the_day/Data/User.cs @@ -3,6 +3,8 @@ using Microsoft.EntityFrameworkCore; public class User { public int UserId { get; set; } - public string SignalId { get; set; } - public string Name { get; set; } + public string? SignalMemberId { get; set; } + public string? Name { get; set; } + public string? NickName { get; set; } + public bool IsIntroduced { get; set; } } \ No newline at end of file diff --git a/song_of_the_day/Dockerfile b/song_of_the_day/Dockerfile new file mode 100644 index 0000000..343c85a --- /dev/null +++ b/song_of_the_day/Dockerfile @@ -0,0 +1,15 @@ +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build +WORKDIR /App + +# Copy everything +COPY . ./ +# Restore as distinct layers +RUN dotnet restore +# Build and publish a release +RUN dotnet publish -o out + +# Build runtime image +FROM mcr.microsoft.com/dotnet/aspnet:9.0 +WORKDIR /App +COPY --from=build /App/out . +ENTRYPOINT ["dotnet", "song_of_the_day.dll"] diff --git a/song_of_the_day/MessengerIntegration/SignalIntegration.cs b/song_of_the_day/MessengerIntegration/SignalIntegration.cs index b7feaf3..f129242 100644 --- a/song_of_the_day/MessengerIntegration/SignalIntegration.cs +++ b/song_of_the_day/MessengerIntegration/SignalIntegration.cs @@ -1,12 +1,17 @@ +using System.Collections; +using System.ComponentModel; +using song_of_the_day; + public class SignalIntegration { - public static SignalIntegration Instance; + public static SignalIntegration? Instance; public SignalIntegration(string uri, int port, string phoneNumber) { - var http = new HttpClient() { - BaseAddress = new Uri("http://" + uri + ":" + port) + var http = new HttpClient() + { + BaseAddress = new Uri(uri + ":" + port) }; apiClient = new song_of_the_day.swaggerClient(http); apiClient.BaseUrl = ""; @@ -17,17 +22,103 @@ public class SignalIntegration private string phoneNumber; - public async Task ListGroups() + public async Task ListGroupsAsync() { - Console.WriteLine("listing groups"); - try { + try + { ICollection groupEntries = await apiClient.GroupsAllAsync(this.phoneNumber); - Console.WriteLine($"{groupEntries.Count} groups"); + foreach (var group in groupEntries) + { + Console.WriteLine($"{group.Name} {group.Id}"); + } } - catch (Exception ex) { - Console.WriteLine("Exception: " + ex.Message); + catch (Exception ex) + { + Console.WriteLine("Exception (ListGroupsAsync): " + ex.Message); + } + } + + public async Task SendMessageToGroupAsync(string message) + { + try + { + SendMessageV2 data = new SendMessageV2(); + data.Recipients = new List(); + data.Recipients.Add(AppConfiguration.Instance.SignalGroupId); + data.Message = message; + data.Text_mode = SendMessageV2Text_mode.Styled; + data.Number = AppConfiguration.Instance.HostPhoneNumber; + var response = await apiClient.Send2Async(data); + } + catch (Exception ex) + { + Console.WriteLine("Exception (SendMessageToGroupAsync): " + ex.Message); + } + } + + public async Task SendMessageToUserAsync(string message, string userId) + { + try + { + SendMessageV2 data = new SendMessageV2(); + data.Recipients = new List(); + data.Recipients.Add(userId); + data.Message = (AppConfiguration.Instance.UseBotTag ? "**[Proggy]**\n" : "") + message; + data.Text_mode = SendMessageV2Text_mode.Styled; + data.Number = AppConfiguration.Instance.HostPhoneNumber; + var response = await apiClient.Send2Async(data); + } + catch (Exception ex) + { + Console.WriteLine("Exception (SendMessageToUserAsync): " + ex.Message); + } + } + + public async Task IntroduceUserAsync(User user) + { + await this.SendMessageToUserAsync("Hi, my name is Proggy and I am your friendly neighborhood *Song of the Day* bot!", user.SignalMemberId); + await this.SendMessageToUserAsync("You are receiving this message because you have been invited to a *Song of the Day* community group.", user.SignalMemberId); + await this.SendMessageToUserAsync("In that community group I will pick a person at random each day at 8 AM and encourage them to share a song with the rest of the community.", user.SignalMemberId); + if (AppConfiguration.Instance.UseBotTag) + { + await this.SendMessageToUserAsync("You can always see which messages are sent by me rather than the community host by the **[Proggy]** tag at the beginning of the message", user.SignalMemberId); + } + await this.SendMessageToUserAsync($"Not right now, but eventually you will be able to see more details about your community at {AppConfiguration.Instance.WebUIBaseURL}.", user.SignalMemberId); + await this.SendMessageToUserAsync($"""You can navigate to {AppConfiguration.Instance.WebUIBaseURL + (AppConfiguration.Instance.WebUIBaseURL.EndsWith("/") ? "" : "/")}User/{user.UserId} to set your preferred display name for me to use.""", user.SignalMemberId); + await this.SendMessageToUserAsync($"Now have fun and enjoy being a part of this community!", user.SignalMemberId); + } + + public async Task> GetMemberListAsync() + { + try + { + var response = await apiClient.Groups2Async(AppConfiguration.Instance.HostPhoneNumber, AppConfiguration.Instance.SignalGroupId); + return response.Members; + } + catch (Exception ex) + { + Console.WriteLine("Exception (GetMemberListAsync): " + ex.Message); + } + return new List(); + } + + public async Task GetContactAsync(string memberId) + { + try + { + var allIdentities = await apiClient.ContactsAllAsync(AppConfiguration.Instance.HostPhoneNumber); + var identityCandidates = allIdentities.Where(u => u.Number == memberId || u.Uuid == memberId); + var identity = identityCandidates.SingleOrDefault(); + if (identity == null) + { + throw new Exception($"Could not determine identity for memberId '{memberId}'!"); + } + return identity; + } + catch (Exception ex) + { + Console.WriteLine("Exception (GetContactAsync): " + ex.Message); + return new ListContactsResponse(); } - - Console.WriteLine("listing groups done"); } } \ No newline at end of file diff --git a/song_of_the_day/Pages/Index.cshtml b/song_of_the_day/Pages/Index.cshtml index 03f7f0f..b38ff36 100644 --- a/song_of_the_day/Pages/Index.cshtml +++ b/song_of_the_day/Pages/Index.cshtml @@ -1,7 +1,7 @@ @page @model IndexModel @{ - ViewData["Title"] = "Home page"; + ViewData["Title"] = "Song of the Day"; }
diff --git a/song_of_the_day/Pages/Privacy.cshtml b/song_of_the_day/Pages/Privacy.cshtml deleted file mode 100644 index 5c16860..0000000 --- a/song_of_the_day/Pages/Privacy.cshtml +++ /dev/null @@ -1,8 +0,0 @@ -@page -@model PrivacyModel -@{ - ViewData["Title"] = "Privacy Policy"; -} -

@ViewData["Title"]

- -

Use this page to detail your site's privacy policy.

diff --git a/song_of_the_day/Pages/Privacy.cshtml.cs b/song_of_the_day/Pages/Privacy.cshtml.cs deleted file mode 100644 index ae6f36e..0000000 --- a/song_of_the_day/Pages/Privacy.cshtml.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace sotd.Pages; - -public class PrivacyModel : PageModel -{ - private readonly ILogger _logger; - - public PrivacyModel(ILogger logger) - { - _logger = logger; - } - - public void OnGet() - { - } -} - diff --git a/song_of_the_day/Pages/Shared/_Layout.cshtml b/song_of_the_day/Pages/Shared/_Layout.cshtml index 7ed56f3..90e1c82 100644 --- a/song_of_the_day/Pages/Shared/_Layout.cshtml +++ b/song_of_the_day/Pages/Shared/_Layout.cshtml @@ -13,7 +13,7 @@