From 544dc95db15ec1b08140e8e19a2ef9f07c7ef554 Mon Sep 17 00:00:00 2001 From: adam Date: Sat, 12 Jul 2025 10:42:00 -0400 Subject: [PATCH] webhooks fire --- Behavior/Webhook.cs | 103 ++-- ConsoleService.cs | 1 + .../20250709204232_WebhookObjects.Designer.cs | 467 ++++++++++++++++++ Migrations/20250709204232_WebhookObjects.cs | 51 ++ Migrations/ChattingContextModelSnapshot.cs | 43 ++ Models/ChattingContext.cs | 3 +- Models/Webhook.cs | 2 +- Rememberer.cs | 39 +- WebInterface/Controllers/HomeController.cs | 98 ++-- .../Controllers/WebhooksController.cs | 74 +++ WebInterface/Views/Configuration/Index.cshtml | 1 + WebInterface/Views/UACs/Details.cshtml | 14 +- WebInterface/Views/Webhooks/Details.cshtml | 100 ++++ 13 files changed, 884 insertions(+), 112 deletions(-) create mode 100644 Migrations/20250709204232_WebhookObjects.Designer.cs create mode 100644 Migrations/20250709204232_WebhookObjects.cs create mode 100644 WebInterface/Controllers/WebhooksController.cs create mode 100644 WebInterface/Views/Webhooks/Details.cshtml diff --git a/Behavior/Webhook.cs b/Behavior/Webhook.cs index 1bd0413..570d11d 100644 --- a/Behavior/Webhook.cs +++ b/Behavior/Webhook.cs @@ -21,46 +21,13 @@ public class Webhook : Behavior public override string Description => "call a webhook"; - private static List configuredWebhooks = new List(); + private static List configuredWebhooks = new List(); private ConcurrentDictionary authedCache = new ConcurrentDictionary(); private HttpClient hc = new HttpClient(); - public static void SetupWebhooks(IEnumerable confSection) + public static void SetupWebhooks() { - //configuredWebhooks = confSection.Get>(); - if (confSection != null) foreach (var confLine in confSection) - { - var conf = JsonConvert.DeserializeObject(confLine); - var confName = $"Webhook: {conf.Trigger}"; - var changed = false; - var myUAC = rememberer.SearchUAC(uac => uac.OwnerId == conf.uacID); - if (myUAC == null) - { - myUAC = new() - { - OwnerId = conf.uacID, - DisplayName = confName, - Description = conf.Description - }; - changed = true; - rememberer.RememberUAC(myUAC); - } - else - { - if (myUAC.DisplayName != confName) - { - myUAC.DisplayName = confName; - changed = true; - } - if (myUAC.Description != conf.Description) - { - myUAC.Description = conf.Description; - changed = true; - } - } - if (changed) - rememberer.RememberUAC(myUAC); - } + configuredWebhooks = rememberer.Webhooks(); } public override bool ShouldAct(Message message, List matchedUACs) @@ -70,9 +37,11 @@ public class Webhook : Behavior return false; } + Console.WriteLine($"{configuredWebhooks.Count()} configured webhooks."); foreach (var wh in configuredWebhooks) { var triggerTarget = wh.Trigger; + Console.WriteLine(triggerTarget); foreach (var uacMatch in matchedUACs) { foreach (var substitution in uacMatch.CommandAlterations) @@ -80,12 +49,12 @@ public class Webhook : Behavior triggerTarget = new Regex(substitution.Key).Replace(triggerTarget, substitution.Value); } } - if (Regex.IsMatch(message.TranslatedContent, $"\\b{triggerTarget}\\b", RegexOptions.IgnoreCase)) + Console.WriteLine($"translated, {triggerTarget}"); + if (Regex.IsMatch(message.TranslatedContent, $"{triggerTarget}\\b", RegexOptions.IgnoreCase)) { var webhookableMessageContent = message.Content.Substring(message.Content.IndexOf(triggerTarget) + triggerTarget.Length + 1); Console.WriteLine($"webhookable content: {webhookableMessageContent}"); - var uacConf = rememberer.SearchUAC(uac => uac.OwnerId == wh.uacID); - if (uacConf.Users.Contains(message.Author.IsUser) || uacConf.Channels.Contains(message.Channel) || uacConf.AccountInChannels.Contains(message.Author)) + if (wh.Uac.Users.Contains(message.Author.IsUser) || wh.Uac.Channels.Contains(message.Channel) || wh.Uac.AccountInChannels.Contains(message.Author)) { Console.WriteLine("webhook UAC passed, preparing WebhookActionOrder"); authedCache.TryAdd(message.Id, new WebhookActionOrder() @@ -97,6 +66,10 @@ public class Webhook : Behavior return true; } } + else + { + Console.WriteLine($"{message.TranslatedContent} didn't match {triggerTarget}"); + } } return false; } @@ -109,27 +82,40 @@ public class Webhook : Behavior Console.Error.WriteLine($"{message.Id} was supposed to act, but authedCache doesn't have it! it has {authedCache?.Count()} other stuff, though."); return false; } + else + { + Console.WriteLine("acquired actionorder"); + } var msg = translate(actionOrder, message); var req = new HttpRequestMessage(new HttpMethod(actionOrder.Conf.Method.ToString()), actionOrder.Conf.Uri); var theContentHeader = actionOrder.Conf.Headers?.FirstOrDefault(h => h?.ToLower().StartsWith("content-type:") ?? false); - var contentHeaderVal = theContentHeader?.Split(':')?[1]?.ToLower(); + Console.WriteLine($"found content header: {theContentHeader}"); + var contentHeaderVal = theContentHeader?.Split(':')?[1]?.ToLower().Trim(); + Console.WriteLine($"contentHeaderrVal: {contentHeaderVal}"); if (contentHeaderVal != null) { - switch (contentHeaderVal) + if(contentHeaderVal =="multipart/form-data") { - //json content is constructed some other weird way. - case "multipart/form-data": - req.Content = new System.Net.Http.MultipartFormDataContent(msg); - break; - default: - req.Content = new System.Net.Http.StringContent(msg); - break; + Console.WriteLine($"wish my errors weren't vanishing. 2"); + req.Content = new System.Net.Http.MultipartFormDataContent(msg); } - req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentHeaderVal); + Console.WriteLine($"wish my errors weren't vanishing. 3"); + try + { + req.Content = new System.Net.Http.StringContent(msg); + req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentHeaderVal); + } + catch (Exception e){ + Console.Error.WriteLine("sorry I have to swallow this exception because something else is somewhere and I have no idea where."); + Console.Error.WriteLine(JsonConvert.SerializeObject(e)); + } + Console.WriteLine($"wish my errors weren't vanishing. 4"); } - if (req.Content == null) + if(req.Content == null) { + Console.WriteLine($"wish my errors weren't vanishing. 5"); req.Content = new System.Net.Http.StringContent(msg); + Console.WriteLine($"wish my errors weren't vanishing. 6"); } Console.WriteLine($"survived translating string content. request content: {req.Content}"); if (actionOrder.Conf.Headers?.ToList().Count > 0) @@ -143,7 +129,7 @@ public class Webhook : Behavior else { Console.WriteLine($"adding header; {header}"); - req.Headers.Add(header.Split(':')[0], header.Split(':')[1]); + req.Headers.Add(header.Split(':')[0], header.Split(':')[0]); Console.WriteLine("survived."); } } @@ -153,7 +139,7 @@ public class Webhook : Behavior Console.WriteLine("no headers to add."); } Console.WriteLine("about to Send."); - var response = hc.Send(req); + var response = await hc.SendAsync(req); Console.WriteLine($"{response.StatusCode} - {response.ReasonPhrase}"); if (!response.IsSuccessStatusCode) { @@ -179,19 +165,8 @@ public class Webhook : Behavior } } -public class WebhookConf -{ - public Guid uacID { get; set; } - public string Trigger { get; set; } - public Uri Uri { get; set; } - //public HttpMethod Method { get; set; } - public Enumerations.HttpVerb Method { get; set; } - public List Headers { get; set; } - public string Content { get; set; } - public string Description { get; set; } -} public class WebhookActionOrder { - public WebhookConf Conf { get; set; } + public vassago.Models.Webhook Conf { get; set; } public string webhookContent { get; set; } } diff --git a/ConsoleService.cs b/ConsoleService.cs index 21f88df..cf57f3c 100644 --- a/ConsoleService.cs +++ b/ConsoleService.cs @@ -73,6 +73,7 @@ namespace vassago } Conversion.Converter.Load(confEntity.ExchangePairsLocation); Telefranz.Configure(confEntity.KafkaName, confEntity.KafkaBootstrap); + vassago.Behavior.Webhook.SetupWebhooks(); } } } diff --git a/Migrations/20250709204232_WebhookObjects.Designer.cs b/Migrations/20250709204232_WebhookObjects.Designer.cs new file mode 100644 index 0000000..c5133b0 --- /dev/null +++ b/Migrations/20250709204232_WebhookObjects.Designer.cs @@ -0,0 +1,467 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using vassago.Models; + +#nullable disable + +namespace vassago.Migrations +{ + [DbContext(typeof(ChattingContext))] + [Migration("20250709204232_WebhookObjects")] + partial class WebhookObjects + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "hstore"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AccountUAC", b => + { + b.Property("AccountInChannelsId") + .HasColumnType("uuid"); + + b.Property("UACsId") + .HasColumnType("uuid"); + + b.HasKey("AccountInChannelsId", "UACsId"); + + b.HasIndex("UACsId"); + + b.ToTable("AccountUAC"); + }); + + modelBuilder.Entity("ChannelUAC", b => + { + b.Property("ChannelsId") + .HasColumnType("uuid"); + + b.Property("UACsId") + .HasColumnType("uuid"); + + b.HasKey("ChannelsId", "UACsId"); + + b.HasIndex("UACsId"); + + b.ToTable("ChannelUAC"); + }); + + modelBuilder.Entity("UACUser", b => + { + b.Property("UACsId") + .HasColumnType("uuid"); + + b.Property("UsersId") + .HasColumnType("uuid"); + + b.HasKey("UACsId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("UACUser"); + }); + + modelBuilder.Entity("vassago.Models.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("ExternalId") + .HasColumnType("text"); + + b.Property("IsBot") + .HasColumnType("boolean"); + + b.Property("IsUserId") + .HasColumnType("uuid"); + + b.Property("Protocol") + .HasColumnType("text"); + + b.Property("SeenInChannelId") + .HasColumnType("uuid"); + + b.Property("Username") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("IsUserId"); + + b.HasIndex("SeenInChannelId"); + + b.ToTable("Accounts"); + }); + + modelBuilder.Entity("vassago.Models.Attachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Content") + .HasColumnType("bytea"); + + b.Property("ContentType") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("ExternalId") + .HasColumnType("numeric(20,0)"); + + b.Property("Filename") + .HasColumnType("text"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("Size") + .HasColumnType("integer"); + + b.Property("Source") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("MessageId"); + + b.ToTable("Attachments"); + }); + + modelBuilder.Entity("vassago.Models.Channel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChannelType") + .HasColumnType("integer"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("ExternalId") + .HasColumnType("text"); + + b.Property("LewdnessFilterLevel") + .HasColumnType("integer"); + + b.Property("LinksAllowed") + .HasColumnType("boolean"); + + b.Property("MaxAttachmentBytes") + .HasColumnType("numeric(20,0)"); + + b.Property("MaxTextChars") + .HasColumnType("bigint"); + + b.Property("MeannessFilterLevel") + .HasColumnType("integer"); + + b.Property("ParentChannelId") + .HasColumnType("uuid"); + + b.Property("Protocol") + .HasColumnType("text"); + + b.Property("ReactionsPossible") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("ParentChannelId"); + + b.ToTable("Channels"); + }); + + modelBuilder.Entity("vassago.Models.Configuration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property>("DiscordTokens") + .HasColumnType("text[]"); + + b.Property("ExchangePairsLocation") + .HasColumnType("text"); + + b.Property("KafkaBootstrap") + .HasColumnType("text"); + + b.Property("KafkaName") + .HasColumnType("text"); + + b.Property("SetupDiscordSlashCommands") + .HasColumnType("boolean"); + + b.Property>("TwitchConfigs") + .HasColumnType("text[]"); + + b.Property("reportedApiUrl") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Configurations"); + }); + + modelBuilder.Entity("vassago.Models.Message", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ActedOn") + .HasColumnType("boolean"); + + b.Property("AuthorId") + .HasColumnType("uuid"); + + b.Property("ChannelId") + .HasColumnType("uuid"); + + b.Property("Content") + .HasColumnType("text"); + + b.Property("ExternalId") + .HasColumnType("text"); + + b.Property("MentionsMe") + .HasColumnType("boolean"); + + b.Property("Protocol") + .HasColumnType("text"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.Property("TranslatedContent") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.HasIndex("ChannelId"); + + b.ToTable("Messages"); + }); + + modelBuilder.Entity("vassago.Models.UAC", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property>("CommandAlterations") + .HasColumnType("hstore"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("OwnerId") + .HasColumnType("uuid"); + + b.Property>("Translations") + .HasColumnType("hstore"); + + b.HasKey("Id"); + + b.ToTable("UACs"); + }); + + modelBuilder.Entity("vassago.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("vassago.Models.Webhook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Content") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property>("Headers") + .HasColumnType("text[]"); + + b.Property("Method") + .HasColumnType("integer"); + + b.Property("Trigger") + .HasColumnType("text"); + + b.Property("UacId") + .HasColumnType("uuid"); + + b.Property("Uri") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UacId"); + + b.ToTable("Webhooks"); + }); + + modelBuilder.Entity("AccountUAC", b => + { + b.HasOne("vassago.Models.Account", null) + .WithMany() + .HasForeignKey("AccountInChannelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("vassago.Models.UAC", null) + .WithMany() + .HasForeignKey("UACsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ChannelUAC", b => + { + b.HasOne("vassago.Models.Channel", null) + .WithMany() + .HasForeignKey("ChannelsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("vassago.Models.UAC", null) + .WithMany() + .HasForeignKey("UACsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("UACUser", b => + { + b.HasOne("vassago.Models.UAC", null) + .WithMany() + .HasForeignKey("UACsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("vassago.Models.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("vassago.Models.Account", b => + { + b.HasOne("vassago.Models.User", "IsUser") + .WithMany("Accounts") + .HasForeignKey("IsUserId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("vassago.Models.Channel", "SeenInChannel") + .WithMany("Users") + .HasForeignKey("SeenInChannelId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("IsUser"); + + b.Navigation("SeenInChannel"); + }); + + modelBuilder.Entity("vassago.Models.Attachment", b => + { + b.HasOne("vassago.Models.Message", "Message") + .WithMany("Attachments") + .HasForeignKey("MessageId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Message"); + }); + + modelBuilder.Entity("vassago.Models.Channel", b => + { + b.HasOne("vassago.Models.Channel", "ParentChannel") + .WithMany("SubChannels") + .HasForeignKey("ParentChannelId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("ParentChannel"); + }); + + modelBuilder.Entity("vassago.Models.Message", b => + { + b.HasOne("vassago.Models.Account", "Author") + .WithMany() + .HasForeignKey("AuthorId"); + + b.HasOne("vassago.Models.Channel", "Channel") + .WithMany("Messages") + .HasForeignKey("ChannelId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Author"); + + b.Navigation("Channel"); + }); + + modelBuilder.Entity("vassago.Models.Webhook", b => + { + b.HasOne("vassago.Models.UAC", "Uac") + .WithMany() + .HasForeignKey("UacId"); + + b.Navigation("Uac"); + }); + + modelBuilder.Entity("vassago.Models.Channel", b => + { + b.Navigation("Messages"); + + b.Navigation("SubChannels"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("vassago.Models.Message", b => + { + b.Navigation("Attachments"); + }); + + modelBuilder.Entity("vassago.Models.User", b => + { + b.Navigation("Accounts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Migrations/20250709204232_WebhookObjects.cs b/Migrations/20250709204232_WebhookObjects.cs new file mode 100644 index 0000000..99a8328 --- /dev/null +++ b/Migrations/20250709204232_WebhookObjects.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace vassago.Migrations +{ + /// + public partial class WebhookObjects : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Webhooks", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UacId = table.Column(type: "uuid", nullable: true), + Trigger = table.Column(type: "text", nullable: true), + Uri = table.Column(type: "text", nullable: true), + Method = table.Column(type: "integer", nullable: false), + Headers = table.Column>(type: "text[]", nullable: true), + Content = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Webhooks", x => x.Id); + table.ForeignKey( + name: "FK_Webhooks_UACs_UacId", + column: x => x.UacId, + principalTable: "UACs", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Webhooks_UacId", + table: "Webhooks", + column: "UacId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Webhooks"); + } + } +} diff --git a/Migrations/ChattingContextModelSnapshot.cs b/Migrations/ChattingContextModelSnapshot.cs index cb68600..d6e99d8 100644 --- a/Migrations/ChattingContextModelSnapshot.cs +++ b/Migrations/ChattingContextModelSnapshot.cs @@ -299,6 +299,40 @@ namespace vassago.Migrations b.ToTable("Users"); }); + modelBuilder.Entity("vassago.Models.Webhook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Content") + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property>("Headers") + .HasColumnType("text[]"); + + b.Property("Method") + .HasColumnType("integer"); + + b.Property("Trigger") + .HasColumnType("text"); + + b.Property("UacId") + .HasColumnType("uuid"); + + b.Property("Uri") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UacId"); + + b.ToTable("Webhooks"); + }); + modelBuilder.Entity("AccountUAC", b => { b.HasOne("vassago.Models.Account", null) @@ -397,6 +431,15 @@ namespace vassago.Migrations b.Navigation("Channel"); }); + modelBuilder.Entity("vassago.Models.Webhook", b => + { + b.HasOne("vassago.Models.UAC", "Uac") + .WithMany() + .HasForeignKey("UacId"); + + b.Navigation("Uac"); + }); + modelBuilder.Entity("vassago.Models.Channel", b => { b.Navigation("Messages"); diff --git a/Models/ChattingContext.cs b/Models/ChattingContext.cs index 26919f4..0c09a25 100644 --- a/Models/ChattingContext.cs +++ b/Models/ChattingContext.cs @@ -12,12 +12,13 @@ public class ChattingContext : DbContext public DbSet Accounts { get; set; } public DbSet Users { get; set; } public DbSet Configurations {get; set;} + public DbSet Webhooks {get; set;} public ChattingContext(DbContextOptions options) : base(options) { } public ChattingContext() : base() { } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseNpgsql(Shared.DBConnectionString); - //.EnableSensitiveDataLogging(true); //"sensitive" is one thing. writing "did something" every time you think a thought is a different thing. + //.EnableSensitiveDataLogging(true); //"sensitive" is one thing. writing "did something" every time you think a thought is a different, stupid thing. } } diff --git a/Models/Webhook.cs b/Models/Webhook.cs index 5865213..43eb317 100644 --- a/Models/Webhook.cs +++ b/Models/Webhook.cs @@ -10,7 +10,7 @@ public class Webhook [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid Id { get; set; } public UAC Uac {get;set;} - public string Trigger { get; set; } + public string Trigger { get; set; } public Uri Uri { get; set; } public Enumerations.HttpVerb Method { get; set; } public List Headers { get; set; } diff --git a/Rememberer.cs b/Rememberer.cs index 0a1154a..6ca1694 100644 --- a/Rememberer.cs +++ b/Rememberer.cs @@ -262,7 +262,7 @@ public class Rememberer if (channelCacheDirty) Task.Run(() => cacheChannels()).Wait(); var ch = channels.Find(c => c.Id == Id); - if(accounts) + if (accounts) ch.Users = SearchAccounts(a => a.SeenInChannel == ch); if (messages) ch.Messages = SearchMessages(m => m.ChannelId == ch.Id); @@ -361,4 +361,41 @@ public class Rememberer db.SaveChanges(); dbAccessSemaphore.Release(); } + public Webhook Webhook(Guid id) + { + Webhook toReturn; + dbAccessSemaphore.Wait(); + toReturn = db.Webhooks.Include(wh => wh.Uac).FirstOrDefault(wh => wh.Id == id); + dbAccessSemaphore.Release(); + return toReturn; + } + public List Webhooks() + { + List toReturn; + dbAccessSemaphore.Wait(); + toReturn = db.Webhooks.Include(wh => wh.Uac).ToList(); + dbAccessSemaphore.Release(); + return toReturn; + } + public void RememberWebhook(Webhook wh) + { + dbAccessSemaphore.Wait(); + db.Update(wh); + db.SaveChanges(); + dbAccessSemaphore.Release(); + } + public void ForgetWebhook(Guid id) + { + dbAccessSemaphore.Wait(); + db.Remove(db.Webhooks.Find(id)); + db.SaveChanges(); + dbAccessSemaphore.Release(); + } + public void ForgetWebhook(Webhook wh) + { + dbAccessSemaphore.Wait(); + db.Remove(wh); + db.SaveChanges(); + dbAccessSemaphore.Release(); + } } diff --git a/WebInterface/Controllers/HomeController.cs b/WebInterface/Controllers/HomeController.cs index 19395f1..6a33b3e 100644 --- a/WebInterface/Controllers/HomeController.cs +++ b/WebInterface/Controllers/HomeController.cs @@ -23,20 +23,40 @@ public class HomeController : Controller { var allAccounts = r.AccountsOverview(); var allChannels = r.ChannelsOverview(); + var allWebhooks = r.Webhooks(); Console.WriteLine($"accounts: {allAccounts?.Count ?? 0}, channels: {allChannels?.Count ?? 0}"); var sb = new StringBuilder(); sb.Append('['); sb.Append($"{{\"text\": \"Configuration\"}},"); + var first = true; + //webhooks + sb.Append("{text: \"Webhooks\", expanded:true, nodes:["); + if(allWebhooks.Any()) + { + foreach(var wh in allWebhooks) + { + var displayedName = wh.Trigger; + if (string.IsNullOrWhiteSpace(displayedName)) + { + displayedName = $"[unnamed - {wh.Id}]"; + } + sb.Append($"{{\"text\": \"{displayedName}\"}},"); + } + } + sb.Append($"{{\"text\": \"add new\"}}"); + + sb.Append("]},"); + //UACs var allUACs = r.UACsOverview(); - var first = true; - if(allUACs.Any()) + first = true; + if (allUACs.Any()) { sb.Append("{text: \"uacs\", expanded:true, nodes: ["); - first=true; - foreach(var uac in allUACs) + first = true; + foreach (var uac in allUACs) { if (first) { @@ -47,11 +67,11 @@ public class HomeController : Controller sb.Append(','); } var displayedName = uac.DisplayName; - if(string.IsNullOrWhiteSpace(displayedName)) + if (string.IsNullOrWhiteSpace(displayedName)) { displayedName = $"[unnamed - {uac.Id}]"; } - sb.Append($"{{\"text\": \"{displayedName}\"}}"); + sb.Append($"{{\"text\": \"{displayedName}\"}}"); } sb.Append("]}"); } @@ -62,13 +82,13 @@ public class HomeController : Controller //users var users = r.UsersOverview(); - if(users.Any()) + if (users.Any()) { sb.Append(",{text: \"users\", expanded:true, nodes: ["); - first=true; + first = true; //refresh list; we'll be knocking them out again in serializeUser allAccounts = r.AccountsOverview(); - foreach(var user in users) + foreach (var user in users) { if (first) { @@ -151,7 +171,7 @@ public class HomeController : Controller { allChannels.Remove(currentChannel); //"but adam", you say, "there's an href attribute, why make a link?" because that makes the entire bar a link, and trying to expand the node will probably click the link - sb.Append($"{{\"text\": \"{currentChannel.DisplayName}\""); + sb.Append($"{{\"text\": \"{currentChannel.DisplayName}\""); sb.Append(", expanded:true "); var theseAccounts = allAccounts.Where(a => a.SeenInChannel?.Id == currentChannel.Id).ToList(); allAccounts.RemoveAll(a => a.SeenInChannel?.Id == currentChannel.Id); @@ -159,54 +179,54 @@ public class HomeController : Controller if (currentChannel.SubChannels != null || theseAccounts != null) { sb.Append(", \"nodes\": ["); - if (currentChannel.SubChannels != null) - { - foreach (var subChannel in currentChannel.SubChannels) + if (currentChannel.SubChannels != null) { - if (first) + foreach (var subChannel in currentChannel.SubChannels) { - first = false; + if (first) + { + first = false; + } + else + { + sb.Append(','); + } + serializeChannel(ref sb, ref allChannels, ref allAccounts, subChannel); } - else + if (theseAccounts != null && !first) //"first" here tells us that we have at least one subchannel { sb.Append(','); } - serializeChannel(ref sb, ref allChannels, ref allAccounts, subChannel); } - if (theseAccounts != null && !first) //"first" here tells us that we have at least one subchannel + if (theseAccounts != null) { - sb.Append(','); - } - } - if (theseAccounts != null) - { - first = true; - sb.Append($"{{\"text\": \"(accounts: {theseAccounts.Count()})\", \"expanded\":true, nodes:["); - foreach (var account in theseAccounts) - { - if (first) + first = true; + sb.Append($"{{\"text\": \"(accounts: {theseAccounts.Count()})\", \"expanded\":true, nodes:["); + foreach (var account in theseAccounts) { - first = false; + if (first) + { + first = false; + } + else + { + sb.Append(','); + } + serializeAccount(ref sb, account); } - else - { - sb.Append(','); - } - serializeAccount(ref sb, account); + sb.Append("]}"); } - sb.Append("]}"); - } sb.Append(']'); } sb.Append('}'); } private void serializeAccount(ref StringBuilder sb, Account currentAccount) { - sb.Append($"{{\"text\": \"{currentAccount.DisplayName}\"}}"); + sb.Append($"{{\"text\": \"{currentAccount.DisplayName}\"}}"); } private void serializeUser(ref StringBuilder sb, ref List allAccounts, User currentUser) { - sb.Append($"{{\"text\": \""); + sb.Append($"{{\"text\": \""); sb.Append(currentUser.DisplayName); sb.Append("\", "); var ownedAccounts = allAccounts.Where(a => a.IsUser == currentUser); @@ -217,7 +237,7 @@ public class HomeController : Controller var first = true; foreach (var acc in ownedAccounts) { - if(!first) + if (!first) sb.Append(','); serializeAccount(ref sb, acc); first = false; diff --git a/WebInterface/Controllers/WebhooksController.cs b/WebInterface/Controllers/WebhooksController.cs new file mode 100644 index 0000000..55dde1c --- /dev/null +++ b/WebInterface/Controllers/WebhooksController.cs @@ -0,0 +1,74 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using vassago.Models; +using vassago.WebInterface.Models; + +namespace vassago.WebInterface.Controllers; + +public class WebhooksController() : Controller +{ + Rememberer r = Rememberer.Instance; + + public async Task Details(Guid id) + { + var webhook = r.Webhook(id); + // Console.WriteLine(JsonConvert.SerializeObject(webhook)); + return webhook != null ? + View(webhook) : + Problem($"webhook {id} is null."); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } + [HttpPost] + public IActionResult Submit(Webhook incoming) + { + var wh = r.Webhook(incoming.Id) ?? new Webhook(); + wh.Trigger = incoming.Trigger; + wh.Uri = incoming.Uri; + wh.Method = incoming.Method; + wh.Headers = incoming.Headers; + wh.Content = incoming.Content; + wh.Description = incoming.Description; + wh.Uac.DisplayName = "webhook: " + wh.Trigger; + r.RememberWebhook(wh); + return RedirectToAction("Details", "Webhooks", new {Id = wh.Id}); + } + public IActionResult Delete(Guid id) + { + r.ForgetWebhook(id); + return RedirectToAction("Home", "Index"); + } + public IActionResult New() + { + var wh = new Webhook(); + wh.Uac = new UAC(); + wh.Content = "this webhook needs to be set up"; + wh.Description = "new webhook"; + r.RememberWebhook(wh); + + return RedirectToAction("Details", "Webhooks", new {Id = wh.Id}); + } + [HttpPost] + public IActionResult RemoveHeader(Guid Id, int index) + { + var wh = r.Webhook(Id); + wh.Headers.RemoveAt(index); + r.RememberWebhook(wh); + return RedirectToAction("Details", "Webhooks", new {Id = wh.Id}); + } + [HttpPost] + public IActionResult AddHeader(Guid id) + { + var wh = r.Webhook(id); + wh.Headers ??= []; + wh.Headers.Add(":"); + r.RememberWebhook(wh); + return RedirectToAction("Details", "Webhooks", new {Id = wh.Id}); + } +} diff --git a/WebInterface/Views/Configuration/Index.cshtml b/WebInterface/Views/Configuration/Index.cshtml index 5addafc..eea4099 100644 --- a/WebInterface/Views/Configuration/Index.cshtml +++ b/WebInterface/Views/Configuration/Index.cshtml @@ -6,6 +6,7 @@ home/configuration
+ diff --git a/WebInterface/Views/UACs/Details.cshtml b/WebInterface/Views/UACs/Details.cshtml index bb6e61c..2ce9521 100644 --- a/WebInterface/Views/UACs/Details.cshtml +++ b/WebInterface/Views/UACs/Details.cshtml @@ -37,14 +37,16 @@
Translations (@Model.Translations?.Count) - next would be iterating over a dictionary. All reference on the internet implies this should work. I'm sick of trying to figure out why it doesn't. razor says to me, so i say to you: go fuck yourself; edit the db manually. - @Html.Raw(""); - @Html.Raw(""); - @Html.Raw(""); - @Html.Raw(""); - @Html.Raw(""); + @for(var i = 0; i < (Model.Translations?.Count() ?? 0); i++) + { + + + + + + }
diff --git a/WebInterface/Views/Webhooks/Details.cshtml b/WebInterface/Views/Webhooks/Details.cshtml new file mode 100644 index 0000000..c14cb50 --- /dev/null +++ b/WebInterface/Views/Webhooks/Details.cshtml @@ -0,0 +1,100 @@ +@model vassago.Models.Webhook +@using Newtonsoft.Json; +@using vassago.Behavior; +@using vassago.Models; +@using vassago.TwitchInterface; +@using System.Text; + +home/Webhooks/@Model.Id + + + + + + + + + + + + + + + + + + + + + @for(var i = 0; i < Model.Headers?.Count(); i++) + { + + + + } + + + + + + + + + + + + + + + + + + + +
Trigger
Uri
Content
Headers
+ + +
+ +
Description
Method + +
UACuac
+ + +
+ +
+ + + Are you sure? + + + +
+
+ +
+ +
+ Are you sure? + + + + +
+
+ +@section scripts{ + +}