using Scalar.AspNetCore; using Microsoft.AspNetCore.OpenApi; using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using System.DirectoryServices.Protocols; SignalIntegration.Instance = new SignalIntegration(AppConfiguration.Instance.SignalAPIEndpointUri, int.Parse(AppConfiguration.Instance.SignalAPIEndpointPort), AppConfiguration.Instance.HostPhoneNumber); var builder = WebApplication.CreateBuilder(args); Console.WriteLine("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 dci = DataContext.Instance; var needsSaving = false; foreach (var memberId in memberList) { Console.WriteLine("found member: " + memberId); 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 }; dci.Users.Add(newUser); needsSaving = true; } } if (needsSaving) { await dci.SaveChangesAsync(); } await dci.DisposeAsync(); }; //userCheckTimer.Start(); Console.WriteLine("Setting up user intro timer"); 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(); Console.WriteLine("Setting up pick of the day timer"); 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(); var connection = new LdapConnection(AppConfiguration.Instance.LDAPConfig.LDAPserver) { Credential = new( AppConfiguration.Instance.LDAPConfig.Username, AppConfiguration.Instance.LDAPConfig.Password ) }; var attributesToQuery = new string[] { "objectGUID", "sAMAccountName", "displayName", "mail", "whenCreated" }; SearchResponse SearchInAD( string ldapServer, int ldapPort, string domainForAD, string username, string password, string targetOU, string query, SearchScope scope, params string[] attributeList ) { // on Windows the authentication type is Negotiate, so there is no need to prepend // AD user login with domain. On other platforms at the moment only // Basic authentication is supported var authType = AuthType.Basic; //var connection = new LdapConnection(ldapServer) var connection = new LdapConnection( new LdapDirectoryIdentifier(ldapServer, ldapPort) ) { AuthType = authType, Credential = new(username, password) }; // the default one is v2 (at least in that version), and it is unknown if v3 // is actually needed, but at least Synology LDAP works only with v3, // and since our Exchange doesn't complain, let it be v3 connection.SessionOptions.ProtocolVersion = 3; // this is for connecting via LDAPS (636 port). It should be working, // according to https://github.com/dotnet/runtime/issues/43890, // but it doesn't (at least with Synology DSM LDAP), although perhaps // for a different reason //connection.SessionOptions.SecureSocketLayer = true; connection.Bind(); var request = new SearchRequest(targetOU, query, scope, attributeList); return (SearchResponse)connection.SendRequest(request); } var searchResults = SearchInAD( AppConfiguration.Instance.LDAPConfig.LDAPserver, AppConfiguration.Instance.LDAPConfig.Port, AppConfiguration.Instance.LDAPConfig.Username, AppConfiguration.Instance.LDAPConfig.Password, AppConfiguration.Instance.LDAPConfig.LDAPQueryBase, new StringBuilder("(&") .Append("(objectCategory=person)") .Append("(objectClass=user)") .Append($"(memberOf={_configurationAD.Crew})") .Append("(!(userAccountControl:1.2.840.113556.1.4.803:=2))") .Append(")") .ToString(), SearchScope.Subtree, attributesToQuery ); // Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddOpenApi(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); } else { app.MapOpenApi(); app.MapScalarApiReference(); } app.UseRouting(); app.UseAuthorization(); app.MapStaticAssets(); app.MapRazorPages() .WithStaticAssets(); app.Run();