feat: initial working version of service refs NOISSUE
This commit is contained in:
parent
2bf3258081
commit
d04b453e6f
@ -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"]
|
|
@ -4,8 +4,8 @@
|
|||||||
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||||
<clear />
|
<clear />
|
||||||
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
|
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
|
||||||
<add key="gitea-projects" value="https://git.disi.dev/api/packages/Projects/nuget/index.json" />
|
<!--add key="gitea-projects" value="https://git.disi.dev/api/packages/Projects/nuget/index.json" />
|
||||||
<add key="gitea-homelab" value="https://git.disi.dev/api/packages/Homelab/nuget/index.json" />
|
<add key="gitea-homelab" value="https://git.disi.dev/api/packages/Homelab/nuget/index.json" />
|
||||||
<add key="gitea-artifacts" value="https://git.disi.dev/api/packages/Artifacts/nuget/index.json" />
|
<add key="gitea-artifacts" value="https://git.disi.dev/api/packages/Artifacts/nuget/index.json" /-->
|
||||||
</packageSources>
|
</packageSources>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
75
song_of_the_day/Config/AppConfiguration.cs
Normal file
75
song_of_the_day/Config/AppConfiguration.cs
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,20 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using sotd.Pages;
|
||||||
|
|
||||||
public class DataContext : DbContext
|
public class DataContext : DbContext
|
||||||
{
|
{
|
||||||
public static DataContext Instance;
|
public static DataContext Instance
|
||||||
|
{
|
||||||
|
get { return new DataContext(); }
|
||||||
|
}
|
||||||
|
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User>? Users { get; set; }
|
||||||
public DbSet<Song> Songs { get; set; }
|
public DbSet<Song>? Songs { get; set; }
|
||||||
public DbSet<SongSuggestion> SongSuggestions { get; set; }
|
public DbSet<SongSuggestion>? SongSuggestions { get; set; }
|
||||||
|
public DbSet<SuggestionHelper>? SuggestionHelpers { get; set; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
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}");
|
||||||
}
|
}
|
127
song_of_the_day/Data/Migrations/20250413192634_AdditionalFields.Designer.cs
generated
Normal file
127
song_of_the_day/Data/Migrations/20250413192634_AdditionalFields.Designer.cs
generated
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<int>("SongId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
|
||||||
|
|
||||||
|
b.Property<string>("Artist")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("SongId");
|
||||||
|
|
||||||
|
b.ToTable("Songs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SongSuggestion", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("Date")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("SongId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SongId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("SongSuggestions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
|
||||||
|
|
||||||
|
b.Property<bool>("IsIntroduced")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("SignalId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace song_of_the_day.DataMigrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AdditionalFields : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<bool>(
|
||||||
|
name: "IsIntroduced",
|
||||||
|
table: "Users",
|
||||||
|
type: "boolean",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: false);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "UserName",
|
||||||
|
table: "Users",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "IsIntroduced",
|
||||||
|
table: "Users");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "UserName",
|
||||||
|
table: "Users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
127
song_of_the_day/Data/Migrations/20250414161136_Update user properties.Designer.cs
generated
Normal file
127
song_of_the_day/Data/Migrations/20250414161136_Update user properties.Designer.cs
generated
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<int>("SongId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
|
||||||
|
|
||||||
|
b.Property<string>("Artist")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("SongId");
|
||||||
|
|
||||||
|
b.ToTable("Songs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SongSuggestion", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("Date")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("SongId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SongId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("SongSuggestions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
|
||||||
|
|
||||||
|
b.Property<bool>("IsIntroduced")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("NickName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace song_of_the_day.DataMigrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class Updateuserproperties : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameColumn(
|
||||||
|
name: "UserName",
|
||||||
|
table: "Users",
|
||||||
|
newName: "SignalMemberId");
|
||||||
|
|
||||||
|
migrationBuilder.RenameColumn(
|
||||||
|
name: "SignalId",
|
||||||
|
table: "Users",
|
||||||
|
newName: "NickName");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.RenameColumn(
|
||||||
|
name: "SignalMemberId",
|
||||||
|
table: "Users",
|
||||||
|
newName: "UserName");
|
||||||
|
|
||||||
|
migrationBuilder.RenameColumn(
|
||||||
|
name: "NickName",
|
||||||
|
table: "Users",
|
||||||
|
newName: "SignalId");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
127
song_of_the_day/Data/Migrations/20250414181019_Add SuggestionHelper entity.Designer.cs
generated
Normal file
127
song_of_the_day/Data/Migrations/20250414181019_Add SuggestionHelper entity.Designer.cs
generated
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<int>("SongId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
|
||||||
|
|
||||||
|
b.Property<string>("Artist")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("SongId");
|
||||||
|
|
||||||
|
b.ToTable("Songs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SongSuggestion", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("Date")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("SongId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SongId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("SongSuggestions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
|
||||||
|
|
||||||
|
b.Property<bool>("IsIntroduced")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("NickName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace song_of_the_day.DataMigrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSuggestionHelperentity : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
148
song_of_the_day/Data/Migrations/20250414181911_Now really add SuggestionHelper entity.Designer.cs
generated
Normal file
148
song_of_the_day/Data/Migrations/20250414181911_Now really add SuggestionHelper entity.Designer.cs
generated
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
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<int>("SongId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("SongId"));
|
||||||
|
|
||||||
|
b.Property<string>("Artist")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Url")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("SongId");
|
||||||
|
|
||||||
|
b.ToTable("Songs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SongSuggestion", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("Date")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<int>("SongId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SongId");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("SongSuggestions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SuggestionHelper", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SuggestionHelpers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
|
||||||
|
|
||||||
|
b.Property<bool>("IsIntroduced")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("NickName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace song_of_the_day.DataMigrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class NowreallyaddSuggestionHelperentity : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "SuggestionHelpers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
Title = table.Column<string>(type: "text", nullable: false),
|
||||||
|
Description = table.Column<string>(type: "text", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_SuggestionHelpers", x => x.Id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "SuggestionHelpers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -72,6 +72,27 @@ namespace song_of_the_day.DataMigrations
|
|||||||
b.ToTable("SongSuggestions");
|
b.ToTable("SongSuggestions");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("SuggestionHelper", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("SuggestionHelpers");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("User", b =>
|
modelBuilder.Entity("User", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("UserId")
|
b.Property<int>("UserId")
|
||||||
@ -80,11 +101,18 @@ namespace song_of_the_day.DataMigrations
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("UserId"));
|
||||||
|
|
||||||
|
b.Property<bool>("IsIntroduced")
|
||||||
|
.HasColumnType("boolean");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
b.Property<string>("SignalId")
|
b.Property<string>("NickName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("SignalMemberId")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
public class Song
|
public class Song
|
||||||
{
|
{
|
||||||
public int SongId { get; set; }
|
public int SongId { get; set; }
|
||||||
public string Name { get; set; }
|
public string? Name { get; set; }
|
||||||
public string Artist { get; set; }
|
public string? Artist { get; set; }
|
||||||
public string Url { get; set; }
|
public string? Url { get; set; }
|
||||||
}
|
}
|
@ -3,8 +3,8 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
public class SongSuggestion
|
public class SongSuggestion
|
||||||
{
|
{
|
||||||
|
|
||||||
public int Id { get; set;}
|
public int Id { get; set; }
|
||||||
public User User { get; set; }
|
public User? User { get; set; }
|
||||||
public Song Song { get; set; }
|
public Song? Song { get; set; }
|
||||||
public DateTime Date { get; set; }
|
public DateTime Date { get; set; }
|
||||||
}
|
}
|
9
song_of_the_day/Data/SuggestionHelper.cs
Normal file
9
song_of_the_day/Data/SuggestionHelper.cs
Normal file
@ -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; }
|
||||||
|
}
|
@ -3,6 +3,8 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
public class User
|
public class User
|
||||||
{
|
{
|
||||||
public int UserId { get; set; }
|
public int UserId { get; set; }
|
||||||
public string SignalId { get; set; }
|
public string? SignalMemberId { get; set; }
|
||||||
public string Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
public string? NickName { get; set; }
|
||||||
|
public bool IsIntroduced { get; set; }
|
||||||
}
|
}
|
15
song_of_the_day/Dockerfile
Normal file
15
song_of_the_day/Dockerfile
Normal file
@ -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"]
|
@ -1,12 +1,17 @@
|
|||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using song_of_the_day;
|
||||||
|
|
||||||
public class SignalIntegration
|
public class SignalIntegration
|
||||||
{
|
{
|
||||||
public static SignalIntegration Instance;
|
public static SignalIntegration? Instance;
|
||||||
|
|
||||||
public SignalIntegration(string uri, int port, string phoneNumber)
|
public SignalIntegration(string uri, int port, string phoneNumber)
|
||||||
{
|
{
|
||||||
var http = new HttpClient() {
|
var http = new HttpClient()
|
||||||
BaseAddress = new Uri("http://" + uri + ":" + port)
|
{
|
||||||
|
BaseAddress = new Uri(uri + ":" + port)
|
||||||
};
|
};
|
||||||
apiClient = new song_of_the_day.swaggerClient(http);
|
apiClient = new song_of_the_day.swaggerClient(http);
|
||||||
apiClient.BaseUrl = "";
|
apiClient.BaseUrl = "";
|
||||||
@ -17,17 +22,103 @@ public class SignalIntegration
|
|||||||
|
|
||||||
private string phoneNumber;
|
private string phoneNumber;
|
||||||
|
|
||||||
public async Task ListGroups()
|
public async Task ListGroupsAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Console.WriteLine("listing groups");
|
|
||||||
try {
|
|
||||||
ICollection<song_of_the_day.GroupEntry> groupEntries = await apiClient.GroupsAllAsync(this.phoneNumber);
|
ICollection<song_of_the_day.GroupEntry> 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 (ListGroupsAsync): " + ex.Message);
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
|
||||||
Console.WriteLine("Exception: " + ex.Message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("listing groups done");
|
public async Task SendMessageToGroupAsync(string message)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SendMessageV2 data = new SendMessageV2();
|
||||||
|
data.Recipients = new List<string>();
|
||||||
|
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<string>();
|
||||||
|
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<ICollection<string>> 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<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ListContactsResponse> 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
@page
|
@page
|
||||||
@model IndexModel
|
@model IndexModel
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Home page";
|
ViewData["Title"] = "Song of the Day";
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
@page
|
|
||||||
@model PrivacyModel
|
|
||||||
@{
|
|
||||||
ViewData["Title"] = "Privacy Policy";
|
|
||||||
}
|
|
||||||
<h1>@ViewData["Title"]</h1>
|
|
||||||
|
|
||||||
<p>Use this page to detail your site's privacy policy.</p>
|
|
@ -1,19 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
|
||||||
|
|
||||||
namespace sotd.Pages;
|
|
||||||
|
|
||||||
public class PrivacyModel : PageModel
|
|
||||||
{
|
|
||||||
private readonly ILogger<PrivacyModel> _logger;
|
|
||||||
|
|
||||||
public PrivacyModel(ILogger<PrivacyModel> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnGet()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
|||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a class="navbar-brand" asp-area="" asp-page="/Index">sotd</a>
|
<a class="navbar-brand" asp-area="" asp-page="/Index">Song of the Day</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
|
||||||
aria-expanded="false" aria-label="Toggle navigation">
|
aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
|
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
|
<a class="nav-link text-dark" asp-area="" asp-page="/SuggestionHelpers">Suggestion Helpers</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -39,7 +39,7 @@
|
|||||||
|
|
||||||
<footer class="border-top footer text-muted">
|
<footer class="border-top footer text-muted">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
© 2025 - sotd - <a asp-area="" asp-page="/Privacy">Privacy</a>
|
© 2025 - Song of the Day
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
32
song_of_the_day/Pages/SuggestionHelpers.cshtml
Normal file
32
song_of_the_day/Pages/SuggestionHelpers.cshtml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
@page
|
||||||
|
@model SuggestionHelpersModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "Suggestion Helpers";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="text-left">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
@foreach (var helper in @Model.SuggestionHelpers)
|
||||||
|
{
|
||||||
|
var title = helper.Title; var description = helper.Description;
|
||||||
|
<tr>
|
||||||
|
<td>@title</td>
|
||||||
|
<td>@description</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</table>
|
||||||
|
<hr />
|
||||||
|
<form method="post">
|
||||||
|
<label asp-for="NewSuggestionTitle">Title</label>
|
||||||
|
<input asp-for="NewSuggestionTitle" />
|
||||||
|
<br />
|
||||||
|
<label asp-for="NewSuggestionDescription">Description</label>
|
||||||
|
<input asp-for="NewSuggestionDescription" />
|
||||||
|
<br />
|
||||||
|
<input type="submit" title="Submit" />
|
||||||
|
</form>
|
||||||
|
</div>
|
52
song_of_the_day/Pages/SuggestionHelpers.cshtml.cs
Normal file
52
song_of_the_day/Pages/SuggestionHelpers.cshtml.cs
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
|
namespace sotd.Pages;
|
||||||
|
|
||||||
|
public class SuggestionHelpersModel : PageModel
|
||||||
|
{
|
||||||
|
private readonly ILogger<SuggestionHelpersModel> _logger;
|
||||||
|
|
||||||
|
public SuggestionHelpersModel(ILogger<SuggestionHelpersModel> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
this.NewSuggestionDescription = "";
|
||||||
|
this.NewSuggestionTitle = "";
|
||||||
|
this.SuggestionHelpers = new List<SuggestionHelper>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[BindProperty]
|
||||||
|
public ICollection<SuggestionHelper> SuggestionHelpers { get; set; }
|
||||||
|
|
||||||
|
[BindProperty]
|
||||||
|
public string NewSuggestionTitle { get; set; }
|
||||||
|
|
||||||
|
[BindProperty]
|
||||||
|
public string NewSuggestionDescription { get; set; }
|
||||||
|
|
||||||
|
public void OnGet()
|
||||||
|
{
|
||||||
|
using (var dci = DataContext.Instance)
|
||||||
|
{
|
||||||
|
this.SuggestionHelpers = dci.SuggestionHelpers.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPost()
|
||||||
|
{
|
||||||
|
using (var dci = DataContext.Instance)
|
||||||
|
{
|
||||||
|
var newHelper = new SuggestionHelper()
|
||||||
|
{
|
||||||
|
Title = this.NewSuggestionTitle,
|
||||||
|
Description = this.NewSuggestionDescription
|
||||||
|
};
|
||||||
|
dci.SuggestionHelpers.Add(newHelper);
|
||||||
|
dci.SaveChanges();
|
||||||
|
this.SuggestionHelpers = dci.SuggestionHelpers.ToList();
|
||||||
|
}
|
||||||
|
this.NewSuggestionDescription = "";
|
||||||
|
this.NewSuggestionTitle = "";
|
||||||
|
}
|
||||||
|
}
|
17
song_of_the_day/Pages/User.cshtml
Normal file
17
song_of_the_day/Pages/User.cshtml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
@page "{userIndex}"
|
||||||
|
@model UserModel
|
||||||
|
@{
|
||||||
|
ViewData["Title"] = "User #" + @Model.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="text-left">
|
||||||
|
<form method="post">
|
||||||
|
<label asp-for="UserNickName">Preferred Name</label>
|
||||||
|
<input asp-for="UserNickName" />
|
||||||
|
<br />
|
||||||
|
<label asp-for="UserName">Contact Name</label>
|
||||||
|
<input asp-for="UserName" disabled />
|
||||||
|
<br />
|
||||||
|
<input type="submit" title="Submit" />
|
||||||
|
</form>
|
||||||
|
</div>
|
45
song_of_the_day/Pages/User.cshtml.cs
Normal file
45
song_of_the_day/Pages/User.cshtml.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||||
|
|
||||||
|
namespace sotd.Pages;
|
||||||
|
|
||||||
|
public class UserModel : PageModel
|
||||||
|
{
|
||||||
|
private readonly ILogger<UserModel> _logger;
|
||||||
|
|
||||||
|
public UserModel(ILogger<UserModel> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
this.UserNickName = "";
|
||||||
|
this.UserName = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public int userId { get; set; }
|
||||||
|
|
||||||
|
public string UserName { get; set; }
|
||||||
|
|
||||||
|
[BindProperty]
|
||||||
|
public string UserNickName { get; set; }
|
||||||
|
|
||||||
|
public void OnGet(int userIndex)
|
||||||
|
{
|
||||||
|
using (var dci = DataContext.Instance)
|
||||||
|
{
|
||||||
|
var user = dci.Users.Find(userIndex);
|
||||||
|
this.UserName = user.Name;
|
||||||
|
this.UserNickName = user.NickName;
|
||||||
|
this.userId = userIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPost(int userIndex)
|
||||||
|
{
|
||||||
|
using (var dci = DataContext.Instance)
|
||||||
|
{
|
||||||
|
var user = dci.Users.Find(userIndex);
|
||||||
|
user.NickName = this.UserNickName;
|
||||||
|
dci.SaveChanges();
|
||||||
|
this.UserName = user.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,83 @@
|
|||||||
|
|
||||||
using Scalar.AspNetCore;
|
using Scalar.AspNetCore;
|
||||||
using Microsoft.AspNetCore.OpenApi;
|
using Microsoft.AspNetCore.OpenApi;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
DataContext.Instance = new DataContext();
|
SignalIntegration.Instance = new SignalIntegration(AppConfiguration.Instance.SignalAPIEndpointUri,
|
||||||
var groupId = "group.Wmk1UTVQTnh0Sjd6a0xiOGhnTnMzZlNkc2p2Q3c0SXJiQkU2eDlNU0hyTT0=";
|
int.Parse(AppConfiguration.Instance.SignalAPIEndpointPort),
|
||||||
SignalIntegration.Instance = new SignalIntegration("192.168.1.108", 8719, "+4367762751895");
|
AppConfiguration.Instance.HostPhoneNumber);
|
||||||
await SignalIntegration.Instance.ListGroups();
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
var userCheckTimer = new CronTimer("*/1 * * * *", "Europe/Vienna", includingSeconds: false);
|
||||||
|
userCheckTimer.OnOccurence += async (s, ea) =>
|
||||||
|
{
|
||||||
|
var memberList = await SignalIntegration.Instance.GetMemberListAsync();
|
||||||
|
var dci = DataContext.Instance;
|
||||||
|
var needsSaving = false;
|
||||||
|
foreach (var memberId in memberList)
|
||||||
|
{
|
||||||
|
var foundUser = dci.Users.Where(u => u.SignalMemberId == memberId).SingleOrDefault();
|
||||||
|
if (foundUser == null)
|
||||||
|
{
|
||||||
|
var newUserContact = await SignalIntegration.Instance.GetContactAsync(memberId);
|
||||||
|
Console.WriteLine("New user:");
|
||||||
|
Console.WriteLine($" Name: {newUserContact.Name}");
|
||||||
|
Console.WriteLine($" MemberId: {memberId}");
|
||||||
|
User newUser = new User()
|
||||||
|
{
|
||||||
|
Name = newUserContact.Name,
|
||||||
|
SignalMemberId = memberId,
|
||||||
|
NickName = string.Empty,
|
||||||
|
IsIntroduced = false
|
||||||
|
};
|
||||||
|
needsSaving = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsSaving)
|
||||||
|
{
|
||||||
|
await dci.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
await dci.DisposeAsync();
|
||||||
|
};
|
||||||
|
userCheckTimer.Start();
|
||||||
|
|
||||||
|
var userIntroTimer = new CronTimer("*/1 * * * *", "Europe/Vienna", includingSeconds: false);
|
||||||
|
userIntroTimer.OnOccurence += async (s, ea) =>
|
||||||
|
{
|
||||||
|
var dci = DataContext.Instance;
|
||||||
|
var introUsers = dci.Users.Where(u => !u.IsIntroduced);
|
||||||
|
bool needsSaving = false;
|
||||||
|
foreach (var user in introUsers)
|
||||||
|
{
|
||||||
|
await SignalIntegration.Instance.IntroduceUserAsync(user);
|
||||||
|
user.IsIntroduced = true;
|
||||||
|
needsSaving = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsSaving)
|
||||||
|
{
|
||||||
|
await dci.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
await dci.DisposeAsync();
|
||||||
|
};
|
||||||
|
userIntroTimer.Start();
|
||||||
|
|
||||||
|
var pickOfTheDayTimer = new CronTimer("0 8 * * *", "Europe/Vienna", includingSeconds: false);
|
||||||
|
pickOfTheDayTimer.OnOccurence += async (s, ea) =>
|
||||||
|
{
|
||||||
|
var dci = DataContext.Instance;
|
||||||
|
var luckyUser = await dci.Users.ElementAtAsync((new Random()).Next(await dci.Users.CountAsync()));
|
||||||
|
var userName = string.IsNullOrEmpty(luckyUser.NickName) ? luckyUser.Name : luckyUser.NickName;
|
||||||
|
SignalIntegration.Instance.SendMessageToGroupAsync($"Today's chosen person to share a song is: **{userName}**");
|
||||||
|
SignalIntegration.Instance.SendMessageToUserAsync($"Congratulations, you have been chosen to share a song today!", luckyUser.SignalMemberId);
|
||||||
|
var suggestion = await dci.SuggestionHelpers.ElementAtAsync((new Random()).Next(await dci.SuggestionHelpers.CountAsync()));
|
||||||
|
SignalIntegration.Instance.SendMessageToUserAsync($"Today's (optional) suggestion helper to help you pick a song is:\n\n**{suggestion.Title}**\n\n*{suggestion.Description}*", luckyUser.SignalMemberId);
|
||||||
|
SignalIntegration.Instance.SendMessageToUserAsync($"For now please just share your suggestion with the group - in the future I might ask you to share directly with me or via the website to help me keep track of past suggestions!", luckyUser.SignalMemberId);
|
||||||
|
};
|
||||||
|
pickOfTheDayTimer.Start();
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
builder.Services.AddOpenApi();
|
builder.Services.AddOpenApi();
|
||||||
@ -35,6 +104,3 @@ app.MapRazorPages()
|
|||||||
.WithStaticAssets();
|
.WithStaticAssets();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
//Console.WriteLine("Size: " + DataContext.Instance.Songs.Count());
|
|
||||||
//await SignalIntegration.Instance.ListGroups();
|
|
@ -16,6 +16,7 @@
|
|||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||||
<PackageReference Include="NSwag.ApiDescription.Client" Version="13.0.5" />
|
<PackageReference Include="NSwag.ApiDescription.Client" Version="13.0.5" />
|
||||||
<PackageReference Include="Scalar.AspNetCore" Version="2.1.*" />
|
<PackageReference Include="Scalar.AspNetCore" Version="2.1.*" />
|
||||||
|
<PackageReference Include="CronTimer" Version="2.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<OpenApiReference Include="swagger.json" SourceUrl="https://bbernhard.github.io/signal-cli-rest-api/src/docs/swagger.json" />
|
<OpenApiReference Include="swagger.json" SourceUrl="https://bbernhard.github.io/signal-cli-rest-api/src/docs/swagger.json" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user