diff --git a/Controllers/ChannelsController.cs b/Controllers/ChannelsController.cs new file mode 100644 index 0000000..8f99e2e --- /dev/null +++ b/Controllers/ChannelsController.cs @@ -0,0 +1,37 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using vassago.Models; + +namespace vassago.Controllers; + +public class ChannelsController : Controller +{ + private readonly ILogger _logger; + private readonly ChattingContext _db; + + public ChannelsController(ILogger logger, ChattingContext db) + { + _logger = logger; + _db = db; + } + + public async Task Index(string searchString) + { + return _db.Channels != null ? + View(_db.Channels.Include(u => u.ParentChannel).ToList().OrderBy(c => c.LineageSummary)) : + Problem("Entity set '_db.Channels' is null."); + } + public async Task Details(Guid id) + { + return _db.Channels != null ? + View(await _db.Channels.Include(u => u.ParentChannel).FirstAsync(u => u.Id == id)) : + Problem("Entity set '_db.Channels' is null."); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Error() + { + return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); + } +} \ No newline at end of file diff --git a/Migrations/20231203193139_channeltype.Designer.cs b/Migrations/20231203193139_channeltype.Designer.cs new file mode 100644 index 0000000..fe7ab7b --- /dev/null +++ b/Migrations/20231203193139_channeltype.Designer.cs @@ -0,0 +1,349 @@ +// +using System; +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("20231203193139_channeltype")] + partial class channeltype + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("vassago.Models.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("ExternalId") + .HasColumnType("text"); + + b.Property("FeaturePermissionId") + .HasColumnType("uuid"); + + 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("FeaturePermissionId"); + + 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("FeaturePermissionId") + .HasColumnType("uuid"); + + b.Property("ParentChannelId") + .HasColumnType("uuid"); + + b.Property("PermissionsId") + .HasColumnType("integer"); + + b.Property("Protocol") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("FeaturePermissionId"); + + b.HasIndex("ParentChannelId"); + + b.HasIndex("PermissionsId"); + + b.ToTable("Channels"); + }); + + modelBuilder.Entity("vassago.Models.ChannelPermissions", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + 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("ReactionsPossible") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("ChannelPermissions"); + }); + + modelBuilder.Entity("vassago.Models.FeaturePermission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Inheritable") + .HasColumnType("boolean"); + + b.Property("InternalName") + .HasColumnType("text"); + + b.Property("InternalTag") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("FeaturePermissions"); + }); + + 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.HasKey("Id"); + + b.HasIndex("AuthorId"); + + b.HasIndex("ChannelId"); + + b.ToTable("Messages"); + }); + + modelBuilder.Entity("vassago.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("FeaturePermissionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("FeaturePermissionId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("vassago.Models.Account", b => + { + b.HasOne("vassago.Models.FeaturePermission", null) + .WithMany("RestrictedToAccounts") + .HasForeignKey("FeaturePermissionId"); + + b.HasOne("vassago.Models.User", "IsUser") + .WithMany("Accounts") + .HasForeignKey("IsUserId"); + + b.HasOne("vassago.Models.Channel", "SeenInChannel") + .WithMany("Users") + .HasForeignKey("SeenInChannelId"); + + b.Navigation("IsUser"); + + b.Navigation("SeenInChannel"); + }); + + modelBuilder.Entity("vassago.Models.Attachment", b => + { + b.HasOne("vassago.Models.Message", "Message") + .WithMany("Attachments") + .HasForeignKey("MessageId"); + + b.Navigation("Message"); + }); + + modelBuilder.Entity("vassago.Models.Channel", b => + { + b.HasOne("vassago.Models.FeaturePermission", null) + .WithMany("RestrictedToChannels") + .HasForeignKey("FeaturePermissionId"); + + b.HasOne("vassago.Models.Channel", "ParentChannel") + .WithMany("SubChannels") + .HasForeignKey("ParentChannelId"); + + b.HasOne("vassago.Models.ChannelPermissions", "Permissions") + .WithMany() + .HasForeignKey("PermissionsId"); + + b.Navigation("ParentChannel"); + + b.Navigation("Permissions"); + }); + + 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"); + + b.Navigation("Author"); + + b.Navigation("Channel"); + }); + + modelBuilder.Entity("vassago.Models.User", b => + { + b.HasOne("vassago.Models.FeaturePermission", null) + .WithMany("RestrictedToUsers") + .HasForeignKey("FeaturePermissionId"); + }); + + modelBuilder.Entity("vassago.Models.Channel", b => + { + b.Navigation("Messages"); + + b.Navigation("SubChannels"); + + b.Navigation("Users"); + }); + + modelBuilder.Entity("vassago.Models.FeaturePermission", b => + { + b.Navigation("RestrictedToAccounts"); + + b.Navigation("RestrictedToChannels"); + + b.Navigation("RestrictedToUsers"); + }); + + 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/20231203193139_channeltype.cs b/Migrations/20231203193139_channeltype.cs new file mode 100644 index 0000000..e9ede1d --- /dev/null +++ b/Migrations/20231203193139_channeltype.cs @@ -0,0 +1,40 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace vassago.Migrations +{ + /// + public partial class channeltype : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsDM", + table: "Channels"); + + migrationBuilder.AddColumn( + name: "ChannelType", + table: "Channels", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ChannelType", + table: "Channels"); + + migrationBuilder.AddColumn( + name: "IsDM", + table: "Channels", + type: "boolean", + nullable: false, + defaultValue: false); + } + } +} diff --git a/Migrations/ChattingContextModelSnapshot.cs b/Migrations/ChattingContextModelSnapshot.cs index 026e761..b4f89a1 100644 --- a/Migrations/ChattingContextModelSnapshot.cs +++ b/Migrations/ChattingContextModelSnapshot.cs @@ -106,6 +106,9 @@ namespace vassago.Migrations .ValueGeneratedOnAdd() .HasColumnType("uuid"); + b.Property("ChannelType") + .HasColumnType("integer"); + b.Property("DisplayName") .HasColumnType("text"); @@ -115,9 +118,6 @@ namespace vassago.Migrations b.Property("FeaturePermissionId") .HasColumnType("uuid"); - b.Property("IsDM") - .HasColumnType("boolean"); - b.Property("ParentChannelId") .HasColumnType("uuid"); diff --git a/Models/Channel.cs b/Models/Channel.cs index 1b391f6..1ab2734 100644 --- a/Models/Channel.cs +++ b/Models/Channel.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Reflection; using System.Threading.Tasks; -using Discord; +using static vassago.Models.Enumerations; public class Channel { @@ -13,13 +13,13 @@ public class Channel public Guid Id { get; set; } public string ExternalId { get; set; } public string DisplayName { get; set; } - public bool IsDM { get; set; } public ChannelPermissions Permissions { get; set; } public List SubChannels { get; set; } public Channel ParentChannel { get; set; } public string Protocol { get; set; } public List Messages { get; set; } public List Users { get; set; } + public ChannelType ChannelType {get; set;} //public Dictionary EmoteOverrides{get;set;} [NonSerialized] @@ -62,4 +62,18 @@ public class Channel return settings; } } + public string LineageSummary + { + get + { + if(this.ParentChannel != null) + { + return this.ParentChannel.LineageSummary + '/' + this.DisplayName; + } + else + { + return this.Protocol; + } + } + } } \ No newline at end of file diff --git a/Models/Enums.cs b/Models/Enums.cs index f532a3d..40487e4 100644 --- a/Models/Enums.cs +++ b/Models/Enums.cs @@ -27,6 +27,18 @@ public static class Enumerations Unrestricted } + public enum ChannelType + { + [Description("Normal")] + Normal, + [Description("DM")] + DM, + [Description("protocol psuedo-channel")] + Protocol, + [Description("organizational psuedo-channel")] + OU + } + public static string GetDescription(this T enumerationValue) where T : struct { diff --git a/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs b/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs index b8556df..2622bf6 100644 --- a/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs +++ b/ProtocolInterfaces/DiscordInterface/DiscordInterface.cs @@ -253,7 +253,7 @@ public class DiscordInterface c.DisplayName = channel.Name; c.ExternalId = channel.Id.ToString(); - c.IsDM = channel is IPrivateChannel; + c.ChannelType = (channel is IPrivateChannel) ? vassago.Models.Enumerations.ChannelType.DM : vassago.Models.Enumerations.ChannelType.Normal; c.Messages = c.Messages ?? new List(); c.Protocol = PROTOCOL; if (channel is IGuildChannel) @@ -273,6 +273,13 @@ public class DiscordInterface c.SubChannels = c.SubChannels ?? new List(); c.SendMessage = (t) => { return channel.SendMessageAsync(t); }; c.SendFile = (f, t) => { return channel.SendFileAsync(f, t); }; + + switch(c.ChannelType) + { + case vassago.Models.Enumerations.ChannelType.DM: + c.DisplayName = "DM: " + (channel as IPrivateChannel).Recipients?.FirstOrDefault(u => u.Id != client.CurrentUser.Id).Username; + break; + } return c; } internal Channel UpsertChannel(IGuild channel) @@ -286,7 +293,7 @@ public class DiscordInterface c.DisplayName = channel.Name; c.ExternalId = channel.Id.ToString(); - c.IsDM = false; + c.ChannelType = vassago.Models.Enumerations.ChannelType.Normal; c.Messages = c.Messages ?? new List(); c.Protocol = protocolAsChannel.Protocol; c.ParentChannel = protocolAsChannel; diff --git a/ProtocolInterfaces/TwitchInterface/TwitchInterface.cs b/ProtocolInterfaces/TwitchInterface/TwitchInterface.cs index cc6cc37..815391d 100644 --- a/ProtocolInterfaces/TwitchInterface/TwitchInterface.cs +++ b/ProtocolInterfaces/TwitchInterface/TwitchInterface.cs @@ -113,7 +113,7 @@ public class TwitchInterface return; } var m = UpsertMessage(e.WhisperMessage); - m.Channel.IsDM = true; + m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM; m.MentionsMe = Regex.IsMatch(e.WhisperMessage.Message?.ToLower(), $"\\b@{e.WhisperMessage.BotUsername.ToLower()}\\b"); _db.SaveChanges(); @@ -192,7 +192,7 @@ public class TwitchInterface } c.DisplayName = channelName; c.ExternalId = channelName; - c.IsDM = false; + c.ChannelType = vassago.Models.Enumerations.ChannelType.Normal; c.Messages = c.Messages ?? new List(); c.Protocol = PROTOCOL; c.ParentChannel = protocolAsChannel; @@ -211,7 +211,7 @@ public class TwitchInterface } c.DisplayName = $"Whisper: {whisperWith}"; c.ExternalId = $"w_{whisperWith}"; - c.IsDM = true; + c.ChannelType = vassago.Models.Enumerations.ChannelType.DM; c.Messages = c.Messages ?? new List(); c.Protocol = PROTOCOL; c.ParentChannel = protocolAsChannel; @@ -267,7 +267,7 @@ public class TwitchInterface m.Content = whisperMessage.Message; m.ExternalId = whisperMessage.MessageId; m.Channel = UpsertDMChannel(whisperMessage.Username); - m.Channel.IsDM = true; + m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM; m.Author = UpsertAccount(whisperMessage.Username, m.Channel.Id); m.Author.SeenInChannel = m.Channel; diff --git a/Views/Channels/Index.cshtml b/Views/Channels/Index.cshtml new file mode 100644 index 0000000..282eb5f --- /dev/null +++ b/Views/Channels/Index.cshtml @@ -0,0 +1,42 @@ +@model IEnumerable +@{ + ViewData["Title"] = "Channels"; +} + + + + + + + + + + + + @foreach (var item in Model) { + + + + + + + + } + +
+ protocol + type + display name + + Lineage +
+
 
+
+
 
+
+ @Html.DisplayFor(modelItem => item.DisplayName) + + @item.LineageSummary + + Details +
diff --git a/wwwroot/css/site.css b/wwwroot/css/site.css index b159833..902e6a7 100644 --- a/wwwroot/css/site.css +++ b/wwwroot/css/site.css @@ -20,15 +20,15 @@ html { body { margin-bottom: 60px; } -.account .protocol-icon{ +.protocol-icon{ display:inline-block; width: 32px; height: 32px; background-size: 32px; } -.account.discord .protocol-icon{ +.discord .protocol-icon{ background-image: url("../imgs/discord_logo1600.png"); } -.account.twitch .protocol-icon{ +.twitch .protocol-icon{ background-image: url("../imgs/twitch.png"); } \ No newline at end of file