fix: some cleanup and fixing runtime bugs, refs NOISSUE
This commit is contained in:
parent
dbd83ebb6a
commit
0d2ec3712e
@ -9,7 +9,7 @@ RUN apt update && apt install libldap-2.5-0 -y
|
||||
# Restore as distinct layers
|
||||
RUN dotnet restore ./song_of_the_day/song_of_the_day.csproj
|
||||
# Build and publish a release
|
||||
RUN dotnet publish ./song_of_the_day/song_of_the_day.csproj -o out
|
||||
RUN dotnet publish ./song_of_the_day/song_of_the_day.csproj -o out /p:EnvironmentName=Production
|
||||
|
||||
# Build runtime image
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0
|
||||
|
2
Makefile
2
Makefile
@ -2,7 +2,7 @@
|
||||
|
||||
.PHONY: issetup
|
||||
issetup:
|
||||
@[ -f .git/hooks/commit-msg ] || [ $SKIP_MAKE_SETUP_CHECK = "true" ] || (echo "You must run 'make setup' first to initialize the repo!" && exit 1)
|
||||
@[ -f .git/hooks/commit-msg ] || [ ${SKIP_MAKE_SETUP_CHECK} = "true" ] || (echo "You must run 'make setup' first to initialize the repo!" && exit 1)
|
||||
|
||||
.PHONY: setup
|
||||
setup:
|
||||
|
@ -2,3 +2,5 @@
|
||||
|
||||
# CS8981: The type name only contains lower-cased ascii characters. Such names may become reserved for the language.
|
||||
dotnet_diagnostic.CS8981.severity = none
|
||||
dotnet_diagnostic.CS8602.severity = none
|
||||
dotnet_diagnostic.CS8604.severity = none
|
||||
|
46
song_of_the_day/.vscode/launch.json
vendored
Normal file
46
song_of_the_day/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "C#: SongOfTheDay Debug",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "publish Debug",
|
||||
"program": "${workspaceFolder}/bin/Debug/net9.0/publish/song_of_the_day.dll",
|
||||
"cwd": "${workspaceFolder}/bin/Debug/net9.0/publish/",
|
||||
"args": [
|
||||
"/p:EnvironmentName=Development"
|
||||
],
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"Logging__LogLevel__Microsoft": "Information"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "C#: SongOfTheDay Production",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "publish Release",
|
||||
"program": "${workspaceFolder}/bin/Release/net9.0/publish/song_of_the_day.dll",
|
||||
"cwd": "${workspaceFolder}/bin/Release/net9.0/publish/",
|
||||
"args": [
|
||||
"/p:EnvironmentName=Production"
|
||||
],
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Production",
|
||||
"Logging__LogLevel__Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
47
song_of_the_day/.vscode/tasks.json
vendored
Normal file
47
song_of_the_day/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "publish Debug",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
"publish",
|
||||
// Ask dotnet build to generate full paths for file names.
|
||||
"/property:GenerateFullPaths=true",
|
||||
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
"/consoleloggerparameters:NoSummary",
|
||||
"/p:EnvironmentName=Development",
|
||||
"-c",
|
||||
"Debug"
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish Release",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
"publish",
|
||||
// Ask dotnet build to generate full paths for file names.
|
||||
"/property:GenerateFullPaths=true",
|
||||
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
"/consoleloggerparameters:NoSummary",
|
||||
"/p:EnvironmentName=Production",
|
||||
"-c",
|
||||
"Release"
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,15 +1,16 @@
|
||||
public class LdapAuthenticationService : IAuthenticationService
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private LdapIntegration _ldapIntegration;
|
||||
|
||||
public LdapAuthenticationService(IConfiguration configuration)
|
||||
public LdapAuthenticationService(IConfiguration configuration, LdapIntegration ldapIntegration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_ldapIntegration = ldapIntegration;
|
||||
}
|
||||
|
||||
public bool Authenticate(string username, string password)
|
||||
{
|
||||
var ldapInstance = LdapIntegration.Instance;
|
||||
return ldapInstance == null ? false : ldapInstance.TestLogin(username, password);
|
||||
return _ldapIntegration == null ? false : _ldapIntegration.TestLogin(username, password);
|
||||
}
|
||||
}
|
@ -2,11 +2,13 @@ public class PhoneClaimCodeProviderService
|
||||
{
|
||||
private Dictionary<string, string> _phoneClaimCodes;
|
||||
private Dictionary<string, string> _phoneClaimNumbers;
|
||||
private SignalIntegration _signalIntegration;
|
||||
|
||||
public PhoneClaimCodeProviderService()
|
||||
public PhoneClaimCodeProviderService(SignalIntegration signalIntegration)
|
||||
{
|
||||
_phoneClaimCodes = new Dictionary<string, string>();
|
||||
_phoneClaimNumbers = new Dictionary<string, string>();
|
||||
_signalIntegration = signalIntegration;
|
||||
}
|
||||
|
||||
private static Random random = new Random();
|
||||
@ -32,7 +34,10 @@ public class PhoneClaimCodeProviderService
|
||||
_phoneClaimNumbers[username] = phoneNumber;
|
||||
}
|
||||
|
||||
await SignalIntegration.Instance.SendMessageToUserAsync("Your phone number validation code is: " + generatedCode, phoneNumber);
|
||||
if (_signalIntegration != null)
|
||||
{
|
||||
await _signalIntegration.SendMessageToUserAsync("Your phone number validation code is: " + generatedCode, phoneNumber);
|
||||
}
|
||||
}
|
||||
|
||||
public string ValidateClaimCodeForUser(string code, string username)
|
||||
|
@ -6,8 +6,6 @@ using System.Linq;
|
||||
|
||||
public class LdapIntegration
|
||||
{
|
||||
public static LdapIntegration? Instance;
|
||||
|
||||
private readonly string[] attributesToQuery = new string[]
|
||||
{
|
||||
"uid",
|
||||
@ -16,12 +14,13 @@ public class LdapIntegration
|
||||
"mail"
|
||||
};
|
||||
|
||||
public LdapIntegration(string uri, int port, string adminBind, string adminPass)
|
||||
public LdapIntegration(ILogger<LdapIntegration> logger)
|
||||
{
|
||||
this.Uri = uri;
|
||||
this.Port = port;
|
||||
this.AdminBind = adminBind;
|
||||
this.AdminPass = adminPass;
|
||||
this.Uri = AppConfiguration.Instance.LDAPConfig.LDAPserver;
|
||||
this.Port = AppConfiguration.Instance.LDAPConfig.Port;
|
||||
this.AdminBind = AppConfiguration.Instance.LDAPConfig.Username;
|
||||
this.AdminPass = AppConfiguration.Instance.LDAPConfig.Password;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private string Uri { get; set; }
|
||||
@ -32,6 +31,8 @@ public class LdapIntegration
|
||||
|
||||
private string AdminPass { get; set; }
|
||||
|
||||
private ILogger<LdapIntegration> logger { get; set; }
|
||||
|
||||
public bool TestLogin(string username, string password)
|
||||
{
|
||||
try
|
||||
|
@ -1,29 +1,28 @@
|
||||
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using song_of_the_day;
|
||||
|
||||
public class SignalIntegration
|
||||
{
|
||||
public static SignalIntegration? Instance;
|
||||
private readonly ILogger<SignalIntegration> logger;
|
||||
|
||||
private readonly ILogger logger;
|
||||
|
||||
public SignalIntegration(string uri, int port, string phoneNumber)
|
||||
public SignalIntegration(ILogger<SignalIntegration> logger)
|
||||
{
|
||||
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Information));
|
||||
this.logger = factory.CreateLogger("SignalIntegration");
|
||||
|
||||
var uri = AppConfiguration.Instance.SignalAPIEndpointUri;
|
||||
var port = int.Parse(AppConfiguration.Instance.SignalAPIEndpointPort);
|
||||
var phoneNumber = AppConfiguration.Instance.HostPhoneNumber;
|
||||
this.logger = logger;
|
||||
|
||||
var http = new HttpClient()
|
||||
{
|
||||
BaseAddress = new Uri(uri + ":" + port)
|
||||
};
|
||||
apiClient = new song_of_the_day.swaggerClient(http);
|
||||
apiClient = new swaggerClient(http);
|
||||
apiClient.BaseUrl = "";
|
||||
this.phoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
private song_of_the_day.swaggerClient apiClient;
|
||||
private swaggerClient apiClient;
|
||||
|
||||
private string phoneNumber;
|
||||
|
||||
@ -83,6 +82,11 @@ public class SignalIntegration
|
||||
|
||||
public async Task IntroduceUserAsync(User user)
|
||||
{
|
||||
if (user == null || user.SignalMemberId == null)
|
||||
{
|
||||
logger.LogWarning("Attempt to introduce unknown user was aborted.");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
|
@ -3,8 +3,8 @@
|
||||
{
|
||||
using (var dci = DataContext.Instance)
|
||||
{
|
||||
var user = dci.Users.Where(u => u.LdapUserName == User.Identity.Name);
|
||||
return user.Any();
|
||||
var user = dci.Users?.Where(u => u.LdapUserName == User.Identity.Name);
|
||||
return user == null ? false : user.Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@
|
||||
<script type="importmap"></script>
|
||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/sotd.styles.css" asp-append-version="true" />
|
||||
<link rel="stylesheet" href="~/song_of_the_day.styles.css" asp-append-version="true" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
@ -37,13 +37,15 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/SuggestionHelpers">Suggestion Helpers</a>
|
||||
</li>
|
||||
@if (this.User.Identity.IsAuthenticated && !DoesUserHaveClaimedPhoneNumber())
|
||||
@if (this.User != null && this.User.Identity != null &&
|
||||
this.User.Identity.IsAuthenticated && !DoesUserHaveClaimedPhoneNumber())
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/UnclaimedPhoneNumbers">Unclaimed Phone Numbers</a>
|
||||
</li>
|
||||
}
|
||||
@if (this.User.Identity.IsAuthenticated && DoesUserHaveClaimedPhoneNumber())
|
||||
@if (this.User != null && this.User.Identity != null &&
|
||||
this.User.Identity.IsAuthenticated && DoesUserHaveClaimedPhoneNumber())
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-dark" asp-area="" asp-page="/SubmitSongs">Submit Songs</a>
|
||||
|
@ -1,7 +1,7 @@
|
||||
@using Microsoft.AspNetCore.Authentication
|
||||
|
||||
<div class="loginform">
|
||||
@if (!this.User.Identity.IsAuthenticated)
|
||||
@if (this.User == null || this.User.Identity == null || !this.User.Identity.IsAuthenticated)
|
||||
{
|
||||
<form method="post" action="Auth/Login">
|
||||
<div>
|
||||
|
@ -17,20 +17,26 @@ public class SubmitSongsModel : PageModel
|
||||
{
|
||||
_logger = logger;
|
||||
this.songResolver = songResolver;
|
||||
_submitUrl = string.Empty;
|
||||
SongData = new Song();
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
public bool IsValidUrl { get; set; } = true;
|
||||
|
||||
[BindProperty]
|
||||
public string SubmitUrl {
|
||||
get {
|
||||
public string SubmitUrl
|
||||
{
|
||||
get
|
||||
{
|
||||
return _submitUrl;
|
||||
}
|
||||
set {
|
||||
set
|
||||
{
|
||||
_submitUrl = value.ToString();
|
||||
Uri? newValue = default;
|
||||
try {
|
||||
try
|
||||
{
|
||||
newValue = new Uri(_submitUrl);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
@ -41,7 +47,7 @@ public class SubmitSongsModel : PageModel
|
||||
|
||||
IsValidUrl = true;
|
||||
|
||||
if(this.songResolver.CanValidate(newValue))
|
||||
if (this.songResolver.CanValidate(newValue))
|
||||
{
|
||||
this.SongData = this.songResolver.ResolveSongAsync(newValue).Result;
|
||||
}
|
||||
@ -49,9 +55,13 @@ public class SubmitSongsModel : PageModel
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
public bool CanSubmit { get {
|
||||
return !string.IsNullOrEmpty(SongData?.Artist) && ! string.IsNullOrEmpty(SongData?.Name);
|
||||
} }
|
||||
public bool CanSubmit
|
||||
{
|
||||
get
|
||||
{
|
||||
return !string.IsNullOrEmpty(SongData?.Artist) && !string.IsNullOrEmpty(SongData?.Name);
|
||||
}
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
public Song SongData { get; set; }
|
||||
@ -66,6 +76,6 @@ public class SubmitSongsModel : PageModel
|
||||
{
|
||||
var songUrl = Request.Query["SubmitUrl"];
|
||||
this.SubmitUrl = songUrl.ToString();
|
||||
return Partial("_SongPartial", SongData);;
|
||||
return Partial("_SongPartial", SongData); ;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class SuggestionHelpersModel : PageModel
|
||||
{
|
||||
using (var dci = DataContext.Instance)
|
||||
{
|
||||
this.SuggestionHelpers = dci.SuggestionHelpers.ToList();
|
||||
this.SuggestionHelpers = dci.SuggestionHelpers == null ? new List<SuggestionHelper>() : dci.SuggestionHelpers.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,9 +42,9 @@ public class SuggestionHelpersModel : PageModel
|
||||
Title = this.NewSuggestionTitle,
|
||||
Description = this.NewSuggestionDescription
|
||||
};
|
||||
dci.SuggestionHelpers.Add(newHelper);
|
||||
dci.SuggestionHelpers?.Add(newHelper);
|
||||
dci.SaveChanges();
|
||||
this.SuggestionHelpers = dci.SuggestionHelpers.ToList();
|
||||
this.SuggestionHelpers = dci.SuggestionHelpers == null ? new List<SuggestionHelper>() : dci.SuggestionHelpers.ToList();
|
||||
}
|
||||
this.NewSuggestionDescription = "";
|
||||
this.NewSuggestionTitle = "";
|
||||
|
@ -1,6 +1,7 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.VisualBasic;
|
||||
using SpotifyAPI.Web;
|
||||
|
||||
namespace sotd.Pages;
|
||||
|
||||
@ -11,6 +12,7 @@ public class UnclaimedPhoneNumbersModel : PageModel
|
||||
public UnclaimedPhoneNumbersModel(ILogger<UserModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
UnclaimedUsers = new List<User>();
|
||||
}
|
||||
|
||||
public int userId { get; set; }
|
||||
@ -22,7 +24,7 @@ public class UnclaimedPhoneNumbersModel : PageModel
|
||||
{
|
||||
using (var dci = DataContext.Instance)
|
||||
{
|
||||
this.UnclaimedUsers = dci.Users.Where(u => string.IsNullOrEmpty(u.LdapUserName)).ToList();
|
||||
this.UnclaimedUsers = dci.Users == null ? new List<User>(): dci.Users.Where(u => string.IsNullOrEmpty(u.LdapUserName)).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,9 +25,9 @@ public class UserModel : PageModel
|
||||
{
|
||||
using (var dci = DataContext.Instance)
|
||||
{
|
||||
var user = dci.Users.Find(userIndex);
|
||||
this.UserName = user.Name;
|
||||
this.UserNickName = user.NickName;
|
||||
var user = dci.Users?.Find(userIndex);
|
||||
this.UserName = user == null ? string.Empty : (user.Name ?? string.Empty);
|
||||
this.UserNickName = user == null ? string.Empty : (user.NickName ?? string.Empty);
|
||||
this.userId = userIndex;
|
||||
}
|
||||
}
|
||||
@ -36,10 +36,13 @@ public class UserModel : PageModel
|
||||
{
|
||||
using (var dci = DataContext.Instance)
|
||||
{
|
||||
var user = dci.Users.Find(userIndex);
|
||||
user.NickName = this.UserNickName;
|
||||
dci.SaveChanges();
|
||||
this.UserName = user.Name;
|
||||
var user = dci.Users?.Find(userIndex);
|
||||
if (user != null)
|
||||
{
|
||||
user.NickName = this.UserNickName;
|
||||
dci.SaveChanges();
|
||||
this.UserName = user.Name ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,37 @@
|
||||
|
||||
using Scalar.AspNetCore;
|
||||
using Microsoft.AspNetCore.OpenApi;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using System.DirectoryServices.Protocols;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
SignalIntegration.Instance = new SignalIntegration(AppConfiguration.Instance.SignalAPIEndpointUri,
|
||||
int.Parse(AppConfiguration.Instance.SignalAPIEndpointPort),
|
||||
AppConfiguration.Instance.HostPhoneNumber);
|
||||
|
||||
LdapIntegration.Instance = new LdapIntegration(AppConfiguration.Instance.LDAPConfig.LDAPserver,
|
||||
AppConfiguration.Instance.LDAPConfig.Port,
|
||||
AppConfiguration.Instance.LDAPConfig.Username,
|
||||
AppConfiguration.Instance.LDAPConfig.Password);
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Information));
|
||||
var logger = factory.CreateLogger("SongResolver");
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddOpenApi();
|
||||
builder.Services.AddLogging((ILoggingBuilder b) => b.ClearProviders().AddConsole());
|
||||
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddCookie(options =>
|
||||
{
|
||||
options.LoginPath = "/Auth/Login";
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<LdapAuthenticationService>();
|
||||
builder.Services.AddSingleton<PhoneClaimCodeProviderService>();
|
||||
builder.Services.AddSingleton<SignalIntegration>();
|
||||
builder.Services.AddSingleton<LdapIntegration>();
|
||||
builder.Services.AddSingleton<SongResolver>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
var logger = app.Logger;
|
||||
|
||||
logger.LogTrace("Setting up user check timer");
|
||||
var userCheckTimer = new CronTimer("*/1 * * * *", "Europe/Vienna", includingSeconds: false);
|
||||
userCheckTimer.OnOccurence += async (s, ea) =>
|
||||
{
|
||||
var memberList = await SignalIntegration.Instance.GetMemberListAsync();
|
||||
var signalIntegration = app.Services.GetService<SignalIntegration>();
|
||||
var memberList = await signalIntegration.GetMemberListAsync();
|
||||
var dci = DataContext.Instance;
|
||||
var needsSaving = false;
|
||||
foreach (var memberId in memberList)
|
||||
@ -33,7 +39,7 @@ userCheckTimer.OnOccurence += async (s, ea) =>
|
||||
var foundUser = dci.Users?.Where(u => u.SignalMemberId == memberId).SingleOrDefault();
|
||||
if (foundUser == null)
|
||||
{
|
||||
var newUserContact = await SignalIntegration.Instance.GetContactAsync(memberId);
|
||||
var newUserContact = await signalIntegration.GetContactAsync(memberId);
|
||||
logger.LogDebug("New user:");
|
||||
logger.LogDebug($" Name: {newUserContact.Name}");
|
||||
logger.LogDebug($" MemberId: {memberId}");
|
||||
@ -74,7 +80,8 @@ userIntroTimer.OnOccurence += async (s, ea) =>
|
||||
bool needsSaving = false;
|
||||
foreach (var user in introUsers)
|
||||
{
|
||||
await SignalIntegration.Instance.IntroduceUserAsync(user);
|
||||
var signalIntegration = app.Services.GetService<SignalIntegration>();
|
||||
await signalIntegration.IntroduceUserAsync(user);
|
||||
user.IsIntroduced = true;
|
||||
needsSaving = true;
|
||||
}
|
||||
@ -137,12 +144,14 @@ pickOfTheDayTimer.OnOccurence += async (s, ea) =>
|
||||
if (luckyUser.SignalMemberId is string signalId)
|
||||
{
|
||||
await dci.SongSuggestions.AddAsync(newSongSuggestion);
|
||||
luckyUser.WasChosenForSuggestionThisRound = true;
|
||||
await dci.SaveChangesAsync();
|
||||
await SignalIntegration.Instance.SendMessageToGroupAsync($"Today's chosen person to share a song is: **{userName}**");
|
||||
await SignalIntegration.Instance.SendMessageToGroupAsync($"Today's (optional) suggestion helper to help you pick a song is:\n\n**{suggestion.Title}**\n\n*{suggestion.Description}*");
|
||||
await SignalIntegration.Instance.SendMessageToUserAsync($"Congratulations, you have been chosen to share a song today!", signalId);
|
||||
await SignalIntegration.Instance.SendMessageToUserAsync($"Today's (optional) suggestion helper to help you pick a song is:\n\n**{suggestion.Title}**\n\n*{suggestion.Description}*", signalId);
|
||||
await 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);
|
||||
var signalIntegration = app.Services.GetService<SignalIntegration>();
|
||||
await signalIntegration.SendMessageToGroupAsync($"Today's chosen person to share a song is: **{userName}**");
|
||||
await signalIntegration.SendMessageToGroupAsync($"Today's (optional) suggestion helper to help you pick a song is:\n\n**{suggestion.Title}**\n\n*{suggestion.Description}*");
|
||||
await signalIntegration.SendMessageToUserAsync($"Congratulations, you have been chosen to share a song today!", signalId);
|
||||
await signalIntegration.SendMessageToUserAsync($"Today's (optional) suggestion helper to help you pick a song is:\n\n**{suggestion.Title}**\n\n*{suggestion.Description}*", signalId);
|
||||
await signalIntegration.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);
|
||||
}
|
||||
await dci.DisposeAsync();
|
||||
};
|
||||
@ -151,10 +160,11 @@ var startUserAssociationProcess = async (User userToAssociate) =>
|
||||
{
|
||||
if (userToAssociate.SignalMemberId is string signalId)
|
||||
{
|
||||
await SignalIntegration.Instance.SendMessageToUserAsync($"Hi, I see you are not associated with any website user yet.", signalId);
|
||||
await SignalIntegration.Instance.SendMessageToUserAsync($"If you haven't yet, please navigate to https://users.disi.dev to create a new account.", signalId);
|
||||
await SignalIntegration.Instance.SendMessageToUserAsync($"Once you have done so, go to https://sotd.disi.dev, login, navigate to \"Unclaimed Phone Numbers\" and click on the \"Claim\" button to start the claim process.", signalId);
|
||||
await SignalIntegration.Instance.SendMessageToUserAsync($"With a future update you will be required to submit songs via your user account - at that point you will be skipped during the selection process if you have not yet claimed your phone number!", signalId);
|
||||
var signalIntegration = app.Services.GetService<SignalIntegration>();
|
||||
await signalIntegration.SendMessageToUserAsync($"Hi, I see you are not associated with any website user yet.", signalId);
|
||||
await signalIntegration.SendMessageToUserAsync($"If you haven't yet, please navigate to https://users.disi.dev to create a new account.", signalId);
|
||||
await signalIntegration.SendMessageToUserAsync($"Once you have done so, go to https://sotd.disi.dev, login, navigate to \"Unclaimed Phone Numbers\" and click on the \"Claim\" button to start the claim process.", signalId);
|
||||
await signalIntegration.SendMessageToUserAsync($"With a future update you will be required to submit songs via your user account - at that point you will be skipped during the selection process if you have not yet claimed your phone number!", signalId);
|
||||
}
|
||||
};
|
||||
|
||||
@ -187,30 +197,20 @@ ldapAssociationTimer.OnOccurence += async (s, ea) =>
|
||||
await dci.DisposeAsync();
|
||||
};
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddOpenApi();
|
||||
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddCookie(options =>
|
||||
{
|
||||
options.LoginPath = "/Auth/Login";
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<LdapAuthenticationService>();
|
||||
builder.Services.AddSingleton<PhoneClaimCodeProviderService>();
|
||||
builder.Services.AddSingleton<SongResolver>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// only start interaction timers in production builds
|
||||
// for local/development testing we want those disabled
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
logger.LogTrace("Starting timer for scheduled processes.");
|
||||
userCheckTimer.Start();
|
||||
userIntroTimer.Start();
|
||||
pickOfTheDayTimer.Start();
|
||||
ldapAssociationTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogTrace("This is a debug build - scheduled processes are disabled.");
|
||||
}
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (!app.Environment.IsDevelopment())
|
||||
|
@ -4,17 +4,17 @@ public class SongResolver
|
||||
{
|
||||
private readonly IEnumerable<ISongValidator> _songValidators;
|
||||
|
||||
private readonly ILogger logger;
|
||||
private readonly ILogger<SongResolver> logger;
|
||||
|
||||
public SongResolver()
|
||||
public SongResolver(ILogger<SongResolver> logger)
|
||||
{
|
||||
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Information));
|
||||
this.logger = factory.CreateLogger("SongResolver");
|
||||
|
||||
this.logger = logger;
|
||||
|
||||
this._songValidators = new List<ISongValidator>();
|
||||
|
||||
foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
|
||||
.Where(mytype => mytype.GetInterfaces().Contains(typeof(ISongValidator)) && !(mytype.Name.EndsWith("Base")))) {
|
||||
.Where(mytype => mytype.GetInterfaces().Contains(typeof(ISongValidator)) && !(mytype.Name.EndsWith("Base"))))
|
||||
{
|
||||
if (Activator.CreateInstance(mytype) is ISongValidator validator)
|
||||
{
|
||||
logger.LogDebug("Registering song validator: {ValidatorType}", mytype.Name);
|
||||
@ -32,23 +32,25 @@ public class SongResolver
|
||||
if (!await validator.CanExtractSongMetadataAsync(songUri))
|
||||
{
|
||||
this.logger.LogWarning("Cannot extract metadata for song URI: {SongUri}", songUri);
|
||||
return new Song {
|
||||
Artist = "Unknown Artist",
|
||||
Name = "Unknown Title",
|
||||
Url = songUri.ToString(),
|
||||
Provider = SongProvider.PlainHttp,
|
||||
};
|
||||
}
|
||||
return await validator.ValidateAsync(songUri);
|
||||
}
|
||||
}
|
||||
|
||||
return new Song {
|
||||
return new Song
|
||||
{
|
||||
Artist = "Unknown Artist",
|
||||
Name = "Unknown Title",
|
||||
Url = songUri.ToString(),
|
||||
Provider = SongProvider.PlainHttp,
|
||||
};
|
||||
}
|
||||
return await validator.ValidateAsync(songUri);
|
||||
}
|
||||
}
|
||||
|
||||
return new Song
|
||||
{
|
||||
Artist = "Unknown Artist",
|
||||
Name = "Unknown Title",
|
||||
Url = songUri.ToString(),
|
||||
Provider = SongProvider.PlainHttp,
|
||||
};
|
||||
}
|
||||
|
||||
public bool CanValidate(Uri songUri)
|
||||
|
@ -14,9 +14,9 @@ public class SpotifyValidator : UriBasedSongValidatorBase
|
||||
spotifyApiClient = new SpotifyApiClient();
|
||||
}
|
||||
|
||||
public override async Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
||||
public override Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
||||
{
|
||||
return this.CanValidateUri(songUri);
|
||||
return Task.FromResult(this.CanValidateUri(songUri));
|
||||
}
|
||||
|
||||
public override async Task<Song> ValidateAsync(Uri songUri)
|
||||
@ -25,7 +25,7 @@ public class SpotifyValidator : UriBasedSongValidatorBase
|
||||
var trackIdMatch = regexp.Match(songUri.ToString()).Groups[2].Value;
|
||||
|
||||
var track = await spotifyApiClient.GetTrackByIdAsync(trackIdMatch);
|
||||
|
||||
|
||||
var song = new Song
|
||||
{
|
||||
Name = track.Name,
|
||||
|
@ -5,9 +5,9 @@ public class YoutubeMusicValidator : UriBasedSongValidatorBase
|
||||
{
|
||||
public override string UriValidatorRegex => @"^(https?://)?(music\.youtube\.com/watch\?v=|youtu\.be/)([a-zA-Z0-9_-]{11})";
|
||||
|
||||
public override async Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
||||
public override Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
||||
{
|
||||
return this.CanValidateUri(songUri);
|
||||
return Task.FromResult(this.CanValidateUri(songUri));
|
||||
}
|
||||
|
||||
public override async Task<Song> ValidateAsync(Uri songUri)
|
||||
@ -15,12 +15,12 @@ public class YoutubeMusicValidator : UriBasedSongValidatorBase
|
||||
var title = string.Empty;
|
||||
var artist = string.Empty;
|
||||
|
||||
using(HttpClient httpClient = new HttpClient())
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
var response = await httpClient.GetAsync(songUri);
|
||||
var config = Configuration.Default.WithDefaultLoader();
|
||||
var context = BrowsingContext.New(config);
|
||||
using(var document = await context.OpenAsync(async req => req.Content(await response.Content.ReadAsStringAsync())))
|
||||
using (var document = await context.OpenAsync(async req => req.Content(await response.Content.ReadAsStringAsync())))
|
||||
{
|
||||
// document.getElementsByTagName("ytmusic-player-queue-item")[0].getElementsByClassName("song-title")[0].innerHTML
|
||||
title = document.QuerySelector(".ytmusic-player-queue-item")?.QuerySelector(".song-title")?.InnerHtml;
|
||||
@ -28,7 +28,9 @@ public class YoutubeMusicValidator : UriBasedSongValidatorBase
|
||||
artist = document.QuerySelector(".ytmusic-player-queue-item")?.QuerySelector(".byline")?.InnerHtml;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
var song = new Song
|
||||
{
|
||||
Name = title,
|
||||
@ -37,6 +39,8 @@ public class YoutubeMusicValidator : UriBasedSongValidatorBase
|
||||
Provider = SongProvider.YouTube,
|
||||
SpotifyId = this.LookupSpotifyId(title, artist)
|
||||
};
|
||||
#pragma warning restore CS8604 // Possible null reference argument.
|
||||
#pragma warning restore CS8604 // Possible null reference argument.
|
||||
|
||||
return song;
|
||||
}
|
||||
|
@ -8,14 +8,16 @@ public class YoutubeValidator : UriBasedSongValidatorBase
|
||||
|
||||
public override async Task<bool> CanExtractSongMetadataAsync(Uri songUri)
|
||||
{
|
||||
using(HttpClient httpClient = new HttpClient())
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
var response = await httpClient.GetAsync(songUri);
|
||||
var config = Configuration.Default.WithDefaultLoader();
|
||||
var context = BrowsingContext.New(config);
|
||||
using(var document = await context.OpenAsync(async req => req.Content(await response.Content.ReadAsStringAsync())))
|
||||
using (var document = await context.OpenAsync(async req => req.Content(await response.Content.ReadAsStringAsync())))
|
||||
{
|
||||
#pragma warning disable CS8602 // Dereference of a possibly null reference.
|
||||
var documentContents = (document.ChildNodes[1] as HtmlElement).InnerHtml;
|
||||
#pragma warning restore CS8602 // Dereference of a possibly null reference.
|
||||
var titleElement = document.QuerySelectorAll(".yt-video-attribute-view-model__title")[0];
|
||||
var artistParentElement = document.QuerySelectorAll(".yt-video-attribute-view-model__secondary-subtitle")[0];
|
||||
|
||||
@ -29,18 +31,20 @@ public class YoutubeValidator : UriBasedSongValidatorBase
|
||||
var title = string.Empty;
|
||||
var artist = string.Empty;
|
||||
|
||||
using(HttpClient httpClient = new HttpClient())
|
||||
using (HttpClient httpClient = new HttpClient())
|
||||
{
|
||||
var response = await httpClient.GetAsync(songUri);
|
||||
var config = Configuration.Default.WithDefaultLoader();
|
||||
var context = BrowsingContext.New(config);
|
||||
using(var document = await context.OpenAsync(async req => req.Content(await response.Content.ReadAsStringAsync())))
|
||||
using (var document = await context.OpenAsync(async req => req.Content(await response.Content.ReadAsStringAsync())))
|
||||
{
|
||||
title = document.QuerySelectorAll(".yt-video-attribute-view-model__title")[0]?.InnerHtml;
|
||||
artist = document.QuerySelectorAll(".yt-video-attribute-view-model__secondary-subtitle")[0]?.Children[0]?.InnerHtml;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
#pragma warning disable CS8604 // Possible null reference argument.
|
||||
var song = new Song
|
||||
{
|
||||
Name = title,
|
||||
@ -49,6 +53,8 @@ public class YoutubeValidator : UriBasedSongValidatorBase
|
||||
Provider = SongProvider.YouTube,
|
||||
SpotifyId = this.LookupSpotifyId(title, artist)
|
||||
};
|
||||
#pragma warning restore CS8604 // Possible null reference argument.
|
||||
#pragma warning restore CS8604 // Possible null reference argument.
|
||||
|
||||
return song;
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ public class SpotifyApiClient
|
||||
{
|
||||
var config = SpotifyClientConfig.CreateDefault()
|
||||
.WithAuthenticator(new ClientCredentialsAuthenticator(
|
||||
AppConfiguration.Instance.SpotifyClientId,
|
||||
AppConfiguration.Instance.SpotifyClientId,
|
||||
AppConfiguration.Instance.SpotifyClientSecret));
|
||||
|
||||
_spotifyClient = new SpotifyClient(config);
|
||||
|
@ -2,8 +2,11 @@
|
||||
"DetailedErrors": true,
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
"Default": "Trace",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"SongResolver": "Trace",
|
||||
"SignalIntegration": "Trace",
|
||||
"SongOfTheDay": "Trace"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
"Default": "Trace",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"SongResolver": "Information",
|
||||
"SignalIntegration": "Information",
|
||||
"SongOfTheDay": "Trace"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
|
Loading…
x
Reference in New Issue
Block a user