forked from adam/discord-bot-shtik
Compare commits
12 Commits
be36c3cb55
...
631347aed8
Author | SHA1 | Date | |
---|---|---|---|
631347aed8 | |||
e85a61607e | |||
2d50a75f2e | |||
be94366763 | |||
8b41696e00 | |||
91752f012a | |||
1d70074d71 | |||
5ff601a60c | |||
fc73df1d63 | |||
e4384a2ea0 | |||
401a3ecbc8 | |||
d7416b480b |
.gitignoreBehaver.cs
Behavior
ConsoleService.csConversion
Migrations
20230704160720_initial.Designer.cs20230704160720_initial.cs20231203193139_channeltype.Designer.cs20231203193139_channeltype.cs20240510202057_channelpermissions_partofchannel.Designer.cs20240510202057_channelpermissions_partofchannel.cs20250204004906_cascade.Designer.cs20250204004906_cascade.cs20250423002254_UAC.Designer.cs20250423002254_UAC.csChattingContextModelSnapshot.cs
Models
ProtocolInterfaces
Rememberer.csShared.csWebInterface
appsettings.jsonassets
devutils.shvassago.csprojwwwroot/js
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,7 @@
|
||||
appsettings.Development.json
|
||||
assets/exchangepairs.json
|
||||
fail*/
|
||||
.projectile
|
||||
|
||||
# ---> VisualStudio
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
@ -377,4 +378,4 @@ FodyWeavers.xsd
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
tmp/
|
||||
tmp/
|
||||
|
@ -65,8 +65,7 @@ public class Behaver
|
||||
|
||||
internal bool IsSelf(Guid AccountId)
|
||||
{
|
||||
var db = new ChattingContext();
|
||||
var acc = db.Accounts.Find(AccountId);
|
||||
var acc = Rememberer.SearchAccount(a => a.Id == AccountId);
|
||||
|
||||
return SelfAccounts.Any(acc => acc.Id == AccountId);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ using System.Collections.Generic;
|
||||
|
||||
public abstract class Behavior
|
||||
{
|
||||
//recommendation: set up your UACs in your constructor.
|
||||
public abstract Task<bool> ActOn(Message message);
|
||||
|
||||
public virtual bool ShouldAct(Message message)
|
||||
|
@ -10,7 +10,7 @@ public class RoomRead : Behavior
|
||||
{
|
||||
public override string Name => "Room Read";
|
||||
|
||||
public override string Trigger => "roomread";
|
||||
public override string Trigger => "!roomread";
|
||||
|
||||
public override async Task<bool> ActOn(Message message)
|
||||
{
|
||||
|
@ -11,6 +11,37 @@ public class TwitchSummon : Behavior
|
||||
|
||||
public override string Trigger => "!twitchsummon";
|
||||
|
||||
private static Guid uacID = new Guid("06ad2008-3d48-4ba6-8722-7eaea000ec70");
|
||||
private static UAC myUAC;
|
||||
|
||||
public TwitchSummon()
|
||||
{
|
||||
myUAC = Rememberer.SearchUAC(uac => uac.OwnerId == uacID);
|
||||
if(myUAC == null)
|
||||
{
|
||||
myUAC = new()
|
||||
{
|
||||
OwnerId = uacID,
|
||||
DisplayName = Name
|
||||
};
|
||||
Rememberer.RememberUAC(myUAC);
|
||||
}
|
||||
}
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
if (!base.ShouldAct(message))
|
||||
return false;
|
||||
var uacConf = Rememberer.SearchUAC(uac => uac.OwnerId == uacID);
|
||||
if (uacConf == null)
|
||||
{
|
||||
Console.Error.WriteLine("no UAC conf for TwitchSummon! Set one up!");
|
||||
}
|
||||
|
||||
Console.WriteLine($"uacConf: {uacConf} users: {uacConf?.Users?.Count()}. message author: {message?.Author}. has an IsUser: {message?.Author?.IsUser}.");
|
||||
Console.WriteLine($"and therefore: {uacConf.Users.Contains(message.Author.IsUser)}");
|
||||
return uacConf.Users.Contains(message.Author.IsUser);
|
||||
}
|
||||
|
||||
public override async Task<bool> ActOn(Message message)
|
||||
{
|
||||
var ti = ProtocolInterfaces.ProtocolList.twitchs.FirstOrDefault();
|
||||
|
@ -13,9 +13,13 @@ public class TwitchDismiss : Behavior
|
||||
|
||||
public override bool ShouldAct(Message message)
|
||||
{
|
||||
var ti = ProtocolInterfaces.ProtocolList.twitchs.FirstOrDefault();
|
||||
Console.WriteLine($"TwitchDismiss checking. menions me? {message.MentionsMe}");
|
||||
if(message.MentionsMe &&
|
||||
(Regex.IsMatch(message.Content.ToLower(), "\\bbegone\\b") || Regex.IsMatch(message.Content.ToLower(), "\\bfuck off\\b")))
|
||||
{
|
||||
var channelTarget = message.Content.Substring(message.Content.IndexOf(Trigger) + Trigger.Length + 1).Trim();
|
||||
ti.AttemptLeave(channelTarget);
|
||||
//TODO: PERMISSION! who can dismiss me? pretty simple list:
|
||||
//1) anyone in the channel with authority*
|
||||
//2) whoever summoned me
|
||||
|
@ -12,6 +12,7 @@ namespace vassago
|
||||
public ConsoleService(IConfiguration aspConfig)
|
||||
{
|
||||
Shared.DBConnectionString = aspConfig["DBConnectionString"];
|
||||
Shared.SetupSlashCommands = aspConfig["SetupSlashCommands"]?.ToLower() == "true";
|
||||
DiscordTokens = aspConfig.GetSection("DiscordTokens").Get<IEnumerable<string>>();
|
||||
TwitchConfigs = aspConfig.GetSection("TwitchConfigs").Get<IEnumerable<TwitchConfig>>();
|
||||
Conversion.Converter.Load(aspConfig["ExchangePairsLocation"]);
|
||||
|
@ -80,12 +80,12 @@ namespace vassago.Conversion
|
||||
|
||||
public static string Convert(decimal numericTerm, string sourceunit, string destinationUnit)
|
||||
{
|
||||
var normalizedSourceUnit = normalizeUnit(sourceunit);
|
||||
var normalizedSourceUnit = NormalizeUnit(sourceunit);
|
||||
if (string.IsNullOrWhiteSpace(normalizedSourceUnit))
|
||||
{
|
||||
return $"parse failure: what's {sourceunit}?";
|
||||
}
|
||||
var normalizedDestUnit = normalizeUnit(destinationUnit);
|
||||
var normalizedDestUnit = NormalizeUnit(destinationUnit);
|
||||
if (string.IsNullOrWhiteSpace(normalizedDestUnit))
|
||||
{
|
||||
return $"parse failure: what's {destinationUnit}?";
|
||||
@ -130,7 +130,7 @@ namespace vassago.Conversion
|
||||
}
|
||||
return "dimensional analysis failure - I know those units but can't find a path between them.";
|
||||
}
|
||||
private static string normalizeUnit(string unit)
|
||||
private static string NormalizeUnit(string unit)
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(unit))
|
||||
return null;
|
||||
@ -149,11 +149,11 @@ namespace vassago.Conversion
|
||||
}
|
||||
if (normalizedUnit.EndsWith("es"))
|
||||
{
|
||||
return normalizeUnit(normalizedUnit.Substring(0, normalizedUnit.Length - 2));
|
||||
return NormalizeUnit(normalizedUnit.Substring(0, normalizedUnit.Length - 2));
|
||||
}
|
||||
else if (normalizedUnit.EndsWith('s'))
|
||||
{
|
||||
return normalizeUnit(normalizedUnit.Substring(0, normalizedUnit.Length - 1));
|
||||
return NormalizeUnit(normalizedUnit.Substring(0, normalizedUnit.Length - 1));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -170,9 +170,9 @@ namespace vassago.Conversion
|
||||
{
|
||||
if (conv.Item1 == last && currentPath.Contains(conv.Item2) == false && conv.Item3 != null)
|
||||
{
|
||||
var test = exhaustiveBreadthFirst(dest, currentPath.Append(conv.Item2));
|
||||
if (test != null)
|
||||
return test;
|
||||
var test = exhaustiveBreadthFirst(dest, currentPath.Append(conv.Item2));
|
||||
if (test != null)
|
||||
return test;
|
||||
}
|
||||
if (conv.Item2 == last && currentPath.Contains(conv.Item1) == false && conv.Item4 != null)
|
||||
{
|
||||
|
2
Migrations/20230704160720_initial.Designer.cs
generated
2
Migrations/20230704160720_initial.Designer.cs
generated
@ -13,7 +13,7 @@ namespace vassago.Migrations
|
||||
{
|
||||
[DbContext(typeof(ChattingContext))]
|
||||
[Migration("20230704160720_initial")]
|
||||
partial class initial
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
|
@ -7,7 +7,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace vassago.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class initial : Migration
|
||||
public partial class Initial : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
|
@ -13,7 +13,7 @@ namespace vassago.Migrations
|
||||
{
|
||||
[DbContext(typeof(ChattingContext))]
|
||||
[Migration("20231203193139_channeltype")]
|
||||
partial class channeltype
|
||||
partial class ChannelType
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
|
@ -5,7 +5,7 @@
|
||||
namespace vassago.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class channeltype : Migration
|
||||
public partial class ChannelType : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
|
@ -13,7 +13,7 @@ namespace vassago.Migrations
|
||||
{
|
||||
[DbContext(typeof(ChattingContext))]
|
||||
[Migration("20240510202057_channelpermissions_partofchannel")]
|
||||
partial class channelpermissions_partofchannel
|
||||
partial class Channelpermissions_partofchannel
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
|
@ -7,7 +7,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
namespace vassago.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class channelpermissions_partofchannel : Migration
|
||||
public partial class Channelpermissions_partofchannel : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
|
2
Migrations/20250204004906_cascade.Designer.cs
generated
2
Migrations/20250204004906_cascade.Designer.cs
generated
@ -13,7 +13,7 @@ namespace vassago.Migrations
|
||||
{
|
||||
[DbContext(typeof(ChattingContext))]
|
||||
[Migration("20250204004906_cascade")]
|
||||
partial class cascade
|
||||
partial class Cascade
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
|
@ -5,7 +5,7 @@
|
||||
namespace vassago.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class cascade : Migration
|
||||
public partial class Cascade : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
@ -130,4 +130,4 @@ namespace vassago.Migrations
|
||||
principalColumn: "Id");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
378
Migrations/20250423002254_UAC.Designer.cs
generated
Normal file
378
Migrations/20250423002254_UAC.Designer.cs
generated
Normal file
@ -0,0 +1,378 @@
|
||||
// <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;
|
||||
using vassago.Models;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace vassago.Migrations
|
||||
{
|
||||
[DbContext(typeof(ChattingContext))]
|
||||
[Migration("20250423002254_UAC")]
|
||||
partial class UAC
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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("AccountUAC", b =>
|
||||
{
|
||||
b.Property<Guid>("AccountInChannelsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("UACsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("AccountInChannelsId", "UACsId");
|
||||
|
||||
b.HasIndex("UACsId");
|
||||
|
||||
b.ToTable("AccountUAC");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ChannelUAC", b =>
|
||||
{
|
||||
b.Property<Guid>("ChannelsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("UACsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("ChannelsId", "UACsId");
|
||||
|
||||
b.HasIndex("UACsId");
|
||||
|
||||
b.ToTable("ChannelUAC");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("UACUser", b =>
|
||||
{
|
||||
b.Property<Guid>("UACsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("UsersId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("UACsId", "UsersId");
|
||||
|
||||
b.HasIndex("UsersId");
|
||||
|
||||
b.ToTable("UACUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.Account", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("IsBot")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<Guid?>("IsUserId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Protocol")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("SeenInChannelId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("IsUserId");
|
||||
|
||||
b.HasIndex("SeenInChannelId");
|
||||
|
||||
b.ToTable("Accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.Attachment", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<byte[]>("Content")
|
||||
.HasColumnType("bytea");
|
||||
|
||||
b.Property<string>("ContentType")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<decimal?>("ExternalId")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<string>("Filename")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid?>("MessageId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("Size")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Source")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MessageId");
|
||||
|
||||
b.ToTable("Attachments");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.Channel", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<int>("ChannelType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<int?>("LewdnessFilterLevel")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<bool?>("LinksAllowed")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<decimal?>("MaxAttachmentBytes")
|
||||
.HasColumnType("numeric(20,0)");
|
||||
|
||||
b.Property<long?>("MaxTextChars")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<int?>("MeannessFilterLevel")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<Guid?>("ParentChannelId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Protocol")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool?>("ReactionsPossible")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ParentChannelId");
|
||||
|
||||
b.ToTable("Channels");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.Message", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<bool>("ActedOn")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<Guid?>("AuthorId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid?>("ChannelId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("Content")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<string>("ExternalId")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<bool>("MentionsMe")
|
||||
.HasColumnType("boolean");
|
||||
|
||||
b.Property<string>("Protocol")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<DateTimeOffset>("Timestamp")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AuthorId");
|
||||
|
||||
b.HasIndex("ChannelId");
|
||||
|
||||
b.ToTable("Messages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.UAC", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("OwnerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UACs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
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.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
|
||||
}
|
||||
}
|
||||
}
|
131
Migrations/20250423002254_UAC.cs
Normal file
131
Migrations/20250423002254_UAC.cs
Normal file
@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace vassago.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class UAC : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UACs",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
OwnerId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
DisplayName = table.Column<string>(type: "text", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UACs", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AccountUAC",
|
||||
columns: table => new
|
||||
{
|
||||
AccountInChannelsId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UACsId = table.Column<Guid>(type: "uuid", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AccountUAC", x => new { x.AccountInChannelsId, x.UACsId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AccountUAC_Accounts_AccountInChannelsId",
|
||||
column: x => x.AccountInChannelsId,
|
||||
principalTable: "Accounts",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AccountUAC_UACs_UACsId",
|
||||
column: x => x.UACsId,
|
||||
principalTable: "UACs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "ChannelUAC",
|
||||
columns: table => new
|
||||
{
|
||||
ChannelsId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UACsId = table.Column<Guid>(type: "uuid", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_ChannelUAC", x => new { x.ChannelsId, x.UACsId });
|
||||
table.ForeignKey(
|
||||
name: "FK_ChannelUAC_Channels_ChannelsId",
|
||||
column: x => x.ChannelsId,
|
||||
principalTable: "Channels",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_ChannelUAC_UACs_UACsId",
|
||||
column: x => x.UACsId,
|
||||
principalTable: "UACs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "UACUser",
|
||||
columns: table => new
|
||||
{
|
||||
UACsId = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
UsersId = table.Column<Guid>(type: "uuid", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_UACUser", x => new { x.UACsId, x.UsersId });
|
||||
table.ForeignKey(
|
||||
name: "FK_UACUser_UACs_UACsId",
|
||||
column: x => x.UACsId,
|
||||
principalTable: "UACs",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_UACUser_Users_UsersId",
|
||||
column: x => x.UsersId,
|
||||
principalTable: "Users",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AccountUAC_UACsId",
|
||||
table: "AccountUAC",
|
||||
column: "UACsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_ChannelUAC_UACsId",
|
||||
table: "ChannelUAC",
|
||||
column: "UACsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_UACUser_UsersId",
|
||||
table: "UACUser",
|
||||
column: "UsersId");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AccountUAC");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "ChannelUAC");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UACUser");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "UACs");
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,51 @@ namespace vassago.Migrations
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("AccountUAC", b =>
|
||||
{
|
||||
b.Property<Guid>("AccountInChannelsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("UACsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("AccountInChannelsId", "UACsId");
|
||||
|
||||
b.HasIndex("UACsId");
|
||||
|
||||
b.ToTable("AccountUAC");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ChannelUAC", b =>
|
||||
{
|
||||
b.Property<Guid>("ChannelsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("UACsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("ChannelsId", "UACsId");
|
||||
|
||||
b.HasIndex("UACsId");
|
||||
|
||||
b.ToTable("ChannelUAC");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("UACUser", b =>
|
||||
{
|
||||
b.Property<Guid>("UACsId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<Guid>("UsersId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("UACsId", "UsersId");
|
||||
|
||||
b.HasIndex("UsersId");
|
||||
|
||||
b.ToTable("UACUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.Account", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -180,6 +225,23 @@ namespace vassago.Migrations
|
||||
b.ToTable("Messages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.UAC", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.Property<string>("DisplayName")
|
||||
.HasColumnType("text");
|
||||
|
||||
b.Property<Guid>("OwnerId")
|
||||
.HasColumnType("uuid");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("UACs");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("vassago.Models.User", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
@ -191,6 +253,51 @@ namespace vassago.Migrations
|
||||
b.ToTable("Users");
|
||||
});
|
||||
|
||||
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")
|
||||
|
@ -5,7 +5,6 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Reflection;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class Account
|
||||
{
|
||||
@ -28,6 +27,7 @@ public class Account
|
||||
public bool IsBot { get; set; } //webhook counts
|
||||
public Channel SeenInChannel { get; set; }
|
||||
public string Protocol { get; set; }
|
||||
public List<UAC> UACs { get; set; }
|
||||
[JsonIgnore]
|
||||
public User IsUser {get; set;}
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ public class Channel
|
||||
public Enumerations.LewdnessFilterLevel? LewdnessFilterLevel { get; set; }
|
||||
public Enumerations.MeannessFilterLevel? MeannessFilterLevel { get; set; }
|
||||
|
||||
public List<UAC> UACs { get; set; }
|
||||
|
||||
[NonSerialized]
|
||||
public Func<string, string, Task> SendFile;
|
||||
|
||||
@ -111,4 +113,4 @@ public class DefinitePermissionSettings
|
||||
public bool ReactionsPossible { get; set; }
|
||||
public Enumerations.LewdnessFilterLevel LewdnessFilterLevel { get; set; }
|
||||
public Enumerations.MeannessFilterLevel MeannessFilterLevel { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ public class ChattingContext : DbContext
|
||||
{
|
||||
public DbSet<Attachment> Attachments { get; set; }
|
||||
public DbSet<Channel> Channels { get; set; }
|
||||
//public DbSet<Emoji> Emoji {get;set;}
|
||||
public DbSet<UAC> UACs { get; set; }
|
||||
public DbSet<Message> Messages { get; set; }
|
||||
public DbSet<Account> Accounts { get; set; }
|
||||
public DbSet<User> Users { get; set; }
|
||||
@ -19,4 +19,4 @@ public class ChattingContext : DbContext
|
||||
optionsBuilder.UseNpgsql(Shared.DBConnectionString);
|
||||
//.EnableSensitiveDataLogging(true); //"sensitive" is one thing. writing "did something" every time you think a thought is a different thing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ public class Message
|
||||
public Channel Channel { get; set; }
|
||||
|
||||
|
||||
|
||||
//TODO: these are nicities to make it OOP, but it couples them with their respective platform interfaces (and connections!)
|
||||
[NonSerialized]
|
||||
public Func<string, Task> Reply;
|
||||
|
||||
|
23
Models/UAC.cs
Normal file
23
Models/UAC.cs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace vassago.Models;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using vassago.Models;
|
||||
|
||||
public class UAC
|
||||
{
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
///<summary indulgence="haiku-like">
|
||||
///behaviors will have
|
||||
///a hardcoded ID thing
|
||||
///so they can find theirs.
|
||||
///</summary>
|
||||
public Guid OwnerId { get; set;}
|
||||
public string DisplayName { get; set; }
|
||||
public List<Account> AccountInChannels { get; set; }
|
||||
public List<Channel> Channels { get; set; }
|
||||
public List<User> Users { get; set; }
|
||||
}
|
@ -13,6 +13,8 @@ public class User
|
||||
[DeleteBehavior(DeleteBehavior.Cascade)]
|
||||
public List<Account> Accounts { get; set; }
|
||||
|
||||
public List<UAC> UACs { get; set; }
|
||||
|
||||
//if I ever get lots and lots of tags, or some automatic way to register a feature's arbitrary tags, then I can move this off.
|
||||
//public bool Tag_CanTwitchSummon { get; set; }
|
||||
|
||||
@ -30,8 +32,8 @@ public class User
|
||||
}
|
||||
else
|
||||
{
|
||||
return $"[accountless {Id}";
|
||||
return $"[accountless {Id}]";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,22 @@ using System.Reactive.Linq;
|
||||
|
||||
namespace vassago.ProtocolInterfaces.DiscordInterface;
|
||||
|
||||
//data received
|
||||
//translate data to internal type
|
||||
//store
|
||||
//ship off to behaver
|
||||
|
||||
public class DiscordInterface
|
||||
{
|
||||
internal const string PROTOCOL = "discord";
|
||||
internal static string PROTOCOL { get => "discord"; }
|
||||
internal DiscordSocketClient client;
|
||||
private bool eventsSignedUp = false;
|
||||
private static readonly SemaphoreSlim discordChannelSetup = new(1, 1);
|
||||
private Channel protocolAsChannel;
|
||||
|
||||
public async Task Init(string token)
|
||||
public async Task Init(string config)
|
||||
{
|
||||
var token = config;
|
||||
await SetupDiscordChannel();
|
||||
client = new DiscordSocketClient(new DiscordSocketConfig() { GatewayIntents = GatewayIntents.All });
|
||||
|
||||
@ -147,6 +153,7 @@ public class DiscordInterface
|
||||
}
|
||||
await Behaver.Instance.ActOn(m);
|
||||
m.ActedOn = true; // for its own ruposess it might act on it later, but either way, fuck it, we checked.
|
||||
// ...but we don't save?
|
||||
}
|
||||
|
||||
private Task UserJoined(SocketGuildUser arg)
|
||||
@ -312,7 +319,8 @@ public class DiscordInterface
|
||||
|
||||
c = Rememberer.RememberChannel(c);
|
||||
|
||||
var selfAccountInChannel = c.Users.FirstOrDefault(a => a.ExternalId == client.CurrentUser.Id.ToString());
|
||||
//Console.WriteLine($"no one knows how to make good tooling. c.users.first, which needs client currentuser id tostring. c: {c}, c.Users {c.Users}, client: {client}, client.CurrentUser: {client.CurrentUser}, client.currentUser.Id: {client.CurrentUser.Id}");
|
||||
var selfAccountInChannel = c.Users?.FirstOrDefault(a => a.ExternalId == client.CurrentUser.Id.ToString());
|
||||
if(selfAccountInChannel == null)
|
||||
{
|
||||
selfAccountInChannel = UpsertAccount(client.CurrentUser, c);
|
||||
@ -351,9 +359,9 @@ public class DiscordInterface
|
||||
{
|
||||
Console.WriteLine($"acc's user: {acc.IsUser?.Id}");
|
||||
}
|
||||
acc ??= new Account() {
|
||||
acc ??= new Account() {
|
||||
IsUser = Rememberer.SearchUser(u => u.Accounts.Any(a => a.ExternalId == discordUser.Id.ToString() && a.Protocol == PROTOCOL))
|
||||
?? new User()
|
||||
?? new User()
|
||||
};
|
||||
|
||||
acc.Username = discordUser.Username;
|
||||
@ -401,4 +409,4 @@ public class DiscordInterface
|
||||
return msg.AddReactionAsync(emote);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -17,23 +17,25 @@ namespace vassago.ProtocolInterfaces.DiscordInterface
|
||||
Id = "freedomunits",
|
||||
UpdatedAt = new DateTime(2023, 5, 21, 13, 3, 0),
|
||||
guild = 825293851110801428, //TODO: demagic this magic number
|
||||
register = register_FreedomUnits
|
||||
register = Register_FreedomUnits
|
||||
}
|
||||
};
|
||||
public static async Task Register(DiscordSocketClient client)
|
||||
{
|
||||
return;
|
||||
var commandsInContext = await client.GetGlobalApplicationCommandsAsync();
|
||||
await Register(client, commandsInContext, null);
|
||||
foreach (var guild in client.Guilds)
|
||||
if(Shared.SetupSlashCommands)
|
||||
{
|
||||
try
|
||||
var commandsInContext = await client.GetGlobalApplicationCommandsAsync();
|
||||
await Register(client, commandsInContext, null);
|
||||
foreach (var guild in client.Guilds)
|
||||
{
|
||||
await Register(client, await guild.GetApplicationCommandsAsync(), guild);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
Console.Error.WriteLine($"error registering slash commands for guild {guild.Name} (id {guild.Id}) - {ex.Message}");
|
||||
try
|
||||
{
|
||||
await Register(client, await guild.GetApplicationCommandsAsync(), guild);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
Console.Error.WriteLine($"error registering slash commands for guild {guild.Name} (id {guild.Id}) - {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,7 +68,7 @@ namespace vassago.ProtocolInterfaces.DiscordInterface
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task register_FreedomUnits(bool isNew, DiscordSocketClient client, SocketGuild guild)
|
||||
private static async Task Register_FreedomUnits(bool isNew, DiscordSocketClient client, SocketGuild guild)
|
||||
{
|
||||
var builtCommand = new SlashCommandBuilder()
|
||||
.WithName("freedomunits")
|
||||
|
@ -1,11 +1,11 @@
|
||||
using RestSharp;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text.RegularExpressions;
|
||||
using RestSharp;
|
||||
using TwitchLib.Api;
|
||||
using TwitchLib.Api.Helix.Models.Users.GetUsers;
|
||||
using TwitchLib.Client;
|
||||
using TwitchLib.Api;
|
||||
using TwitchLib.Client.Events;
|
||||
using TwitchLib.Client.Models;
|
||||
using TwitchLib.Client;
|
||||
using TwitchLib.Communication.Clients;
|
||||
using TwitchLib.Communication.Models;
|
||||
using vassago.Behavior;
|
||||
@ -13,27 +13,26 @@ using vassago.Models;
|
||||
|
||||
namespace vassago.TwitchInterface;
|
||||
|
||||
internal class unifiedTwitchMessage
|
||||
{
|
||||
public unifiedTwitchMessage(ChatMessage chatMessage){}
|
||||
}
|
||||
|
||||
public class TwitchInterface
|
||||
{
|
||||
internal const string PROTOCOL = "twitch";
|
||||
private bool eventsSignedUp = false;
|
||||
private ChattingContext _db;
|
||||
private static SemaphoreSlim twitchChannelSetup = new SemaphoreSlim(1, 1);
|
||||
private static SemaphoreSlim channelSetupSemaphpore = new SemaphoreSlim(1, 1);
|
||||
private Channel protocolAsChannel;
|
||||
private Account selfAccountInProtocol;
|
||||
TwitchClient client;
|
||||
TwitchAPI api;
|
||||
|
||||
public TwitchInterface()
|
||||
{
|
||||
_db = new ChattingContext();
|
||||
}
|
||||
private async Task SetupTwitchChannel()
|
||||
{
|
||||
await twitchChannelSetup.WaitAsync();
|
||||
await channelSetupSemaphpore.WaitAsync();
|
||||
|
||||
try
|
||||
{
|
||||
protocolAsChannel = _db.Channels.FirstOrDefault(c => c.ParentChannel == null && c.Protocol == PROTOCOL);
|
||||
protocolAsChannel = Rememberer.SearchChannel(c => c.ParentChannel == null && c.Protocol == PROTOCOL);
|
||||
if (protocolAsChannel == null)
|
||||
{
|
||||
protocolAsChannel = new Channel()
|
||||
@ -47,17 +46,23 @@ public class TwitchInterface
|
||||
ReactionsPossible = false,
|
||||
ExternalId = null,
|
||||
Protocol = PROTOCOL,
|
||||
SubChannels = new List<Channel>()
|
||||
SubChannels = []
|
||||
};
|
||||
protocolAsChannel.DisplayName = "twitch (itself)";
|
||||
protocolAsChannel.SendMessage = (t) => { throw new InvalidOperationException($"twitch itself cannot accept text"); };
|
||||
protocolAsChannel.SendFile = (f, t) => { throw new InvalidOperationException($"twitch itself cannot send file"); };
|
||||
_db.Channels.Add(protocolAsChannel);
|
||||
_db.SaveChanges();
|
||||
protocolAsChannel = Rememberer.RememberChannel(protocolAsChannel);
|
||||
Console.WriteLine($"protocol as channle added; {protocolAsChannel}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"twitch, channel with id {protocolAsChannel.Id}, already exists");
|
||||
}
|
||||
//protocolAsChan
|
||||
}
|
||||
finally
|
||||
{
|
||||
twitchChannelSetup.Release();
|
||||
channelSetupSemaphpore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,69 +86,46 @@ public class TwitchInterface
|
||||
client.OnWhisperReceived += Client_OnWhisperReceivedAsync;
|
||||
client.OnConnected += Client_OnConnected;
|
||||
|
||||
Console.WriteLine("twitch client 1 connecting...");
|
||||
client.Connect();
|
||||
Console.WriteLine("twitch client 1 connected");
|
||||
|
||||
// Console.WriteLine("twitch API client connecting...");
|
||||
// api = new TwitchAPI();
|
||||
// Console.WriteLine("can I just use the same creds as the other client?");
|
||||
// api.Settings.ClientId = tc.username;
|
||||
// api.Settings.AccessToken = tc.oauth;
|
||||
// try{
|
||||
// var neckbreads = await api.Helix.Moderation.GetModeratorsAsync("silvermeddlists");
|
||||
// Console.WriteLine($"{neckbreads?.Data?.Count()} shabby beards that need to be given up on");
|
||||
// }
|
||||
// catch(Exception e){
|
||||
// Console.Error.WriteLine(e);
|
||||
// }
|
||||
// Console.WriteLine("k.");
|
||||
}
|
||||
|
||||
private async void Client_OnWhisperReceivedAsync(object sender, OnWhisperReceivedArgs e)
|
||||
{
|
||||
//data received
|
||||
Console.WriteLine($"whisper#{e.WhisperMessage.Username}[{DateTime.Now}][{e.WhisperMessage.DisplayName} [id={e.WhisperMessage.Username}]][msg id: {e.WhisperMessage.MessageId}] {e.WhisperMessage.Message}");
|
||||
var old = _db.Messages.FirstOrDefault(m => m.ExternalId == e.WhisperMessage.MessageId && m.Protocol == PROTOCOL);
|
||||
if (old != null)
|
||||
{
|
||||
Console.WriteLine($"[whisperreceived]: {e.WhisperMessage.MessageId}? already seent it. Internal id: {old.Id}");
|
||||
return;
|
||||
}
|
||||
var m = UpsertMessage(e.WhisperMessage);
|
||||
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
||||
m.MentionsMe = Regex.IsMatch(e.WhisperMessage.Message?.ToLower(), $"\\b@{e.WhisperMessage.BotUsername.ToLower()}\\b");
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
//translate to internal, upsert
|
||||
var m = UpsertMessage(e.WhisperMessage);
|
||||
m.Reply = (t) => { return Task.Run(() => { client.SendWhisper(e.WhisperMessage.Username, t); }); };
|
||||
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
||||
//act on
|
||||
await Behaver.Instance.ActOn(m);
|
||||
await _db.SaveChangesAsync();
|
||||
m.ActedOn = true;
|
||||
//TODO: remember it again?
|
||||
}
|
||||
|
||||
private async void Client_OnMessageReceivedAsync(object sender, OnMessageReceivedArgs e)
|
||||
{
|
||||
//data eived
|
||||
Console.WriteLine($"#{e.ChatMessage.Channel}[{DateTime.Now}][{e.ChatMessage.DisplayName} [id={e.ChatMessage.Username}]][msg id: {e.ChatMessage.Id}] {e.ChatMessage.Message}");
|
||||
var old = _db.Messages.FirstOrDefault(m => m.ExternalId == e.ChatMessage.Id && m.Protocol == PROTOCOL);
|
||||
if (old != null)
|
||||
{
|
||||
Console.WriteLine($"[messagereceived]: {e.ChatMessage.Id}? already seent it");
|
||||
return;
|
||||
}
|
||||
Console.WriteLine($"[messagereceived]: {e.ChatMessage.Id}? new to me.");
|
||||
var m = UpsertMessage(e.ChatMessage);
|
||||
m.MentionsMe = Regex.IsMatch(e.ChatMessage.Message?.ToLower(), $"@{e.ChatMessage.BotUsername.ToLower()}\\b") ||
|
||||
e.ChatMessage.ChatReply?.ParentUserLogin == e.ChatMessage.BotUsername;
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
//translate to internal, upsert
|
||||
var m = UpsertMessage(e.ChatMessage);
|
||||
m.Reply = (t) => { return Task.Run(() => { client.SendReply(e.ChatMessage.Channel, e.ChatMessage.Id, t); }); };
|
||||
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.Normal;
|
||||
//act on
|
||||
await Behaver.Instance.ActOn(m);
|
||||
await _db.SaveChangesAsync();
|
||||
m.ActedOn = true;
|
||||
//TODO: remember again?
|
||||
}
|
||||
|
||||
private async void Client_OnConnected(object sender, OnConnectedArgs e)
|
||||
private void Client_OnConnected(object sender, OnConnectedArgs e)
|
||||
{
|
||||
Console.WriteLine($"twitch marking selfaccount as seeninchannel {protocolAsChannel.Id}");
|
||||
var selfAccount = UpsertAccount(e.BotUsername, protocolAsChannel.Id);
|
||||
Behaver.Instance.MarkSelf(selfAccount);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
selfAccountInProtocol = UpsertAccount(e.BotUsername, protocolAsChannel);
|
||||
selfAccountInProtocol.DisplayName = e.BotUsername;
|
||||
Behaver.Instance.MarkSelf(selfAccountInProtocol);
|
||||
|
||||
Console.WriteLine($"Connected to {e.AutoJoinChannel}");
|
||||
}
|
||||
@ -158,61 +140,96 @@ public class TwitchInterface
|
||||
Console.WriteLine($"{e.DateTime.ToString()}: {e.BotUsername} - {e.Data}");
|
||||
}
|
||||
|
||||
private Account UpsertAccount(string username, Guid inChannel)
|
||||
private Account UpsertAccount(string username, Channel inChannel)
|
||||
{
|
||||
var seenInChannel = _db.Channels.FirstOrDefault(c => c.Id == inChannel);
|
||||
var acc = _db.Accounts.FirstOrDefault(ui => ui.ExternalId == username && ui.SeenInChannel.Id == inChannel);
|
||||
if (acc == null)
|
||||
Console.WriteLine($"upserting twitch account. username: {username}. inChannel: {inChannel?.Id}");
|
||||
var acc = Rememberer.SearchAccount(ui => ui.ExternalId == username && ui.SeenInChannel.ExternalId == inChannel.ExternalId);
|
||||
Console.WriteLine($"upserting twitch account, retrieved {acc?.Id}.");
|
||||
if (acc != null)
|
||||
{
|
||||
acc = new Account();
|
||||
acc.SeenInChannel = seenInChannel;
|
||||
_db.Accounts.Add(acc);
|
||||
Console.WriteLine($"acc's usser: {acc.IsUser?.Id}");
|
||||
}
|
||||
acc ??= new Account() {
|
||||
IsUser = Rememberer.SearchUser(
|
||||
u => u.Accounts.Any(a => a.ExternalId == username && a.Protocol == PROTOCOL))
|
||||
?? new vassago.Models.User()
|
||||
};
|
||||
|
||||
acc.Username = username;
|
||||
acc.ExternalId = username;
|
||||
//acc.IsBot =
|
||||
//acc.IsBot = false? there is a way to tell, but you have to go back through the API
|
||||
acc.Protocol = PROTOCOL;
|
||||
acc.SeenInChannel = inChannel;
|
||||
|
||||
acc.IsUser = _db.Users.FirstOrDefault(u => u.Accounts.Any(a => a.ExternalId == acc.ExternalId && a.Protocol == acc.Protocol));
|
||||
if (acc.IsUser == null)
|
||||
Console.WriteLine($"we asked rememberer to search for acc's user. {acc.IsUser?.Id}");
|
||||
if (acc.IsUser != null)
|
||||
{
|
||||
acc.IsUser = new vassago.Models.User() { Accounts = new List<Account>() { acc } };
|
||||
_db.Users.Add(acc.IsUser);
|
||||
Console.WriteLine($"user has record of {acc.IsUser.Accounts?.Count ?? 0} accounts");
|
||||
}
|
||||
acc.IsUser ??= new vassago.Models.User() { Accounts = [acc] };
|
||||
if (inChannel.Users?.Count > 0)
|
||||
{
|
||||
Console.WriteLine($"channel has {inChannel.Users.Count} accounts");
|
||||
}
|
||||
Rememberer.RememberAccount(acc);
|
||||
inChannel.Users ??= [];
|
||||
if (!inChannel.Users.Contains(acc))
|
||||
{
|
||||
inChannel.Users.Add(acc);
|
||||
Rememberer.RememberChannel(inChannel);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
private Channel UpsertChannel(string channelName)
|
||||
{
|
||||
Channel c = _db.Channels.FirstOrDefault(ci => ci.ExternalId == channelName && ci.Protocol == PROTOCOL);
|
||||
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channelName
|
||||
&& ci.Protocol == PROTOCOL);
|
||||
if (c == null)
|
||||
{
|
||||
c = new Channel();
|
||||
_db.Channels.Add(c);
|
||||
Console.WriteLine($"couldn't find channel under protocol {PROTOCOL} with externalId {channelName}");
|
||||
c = new Channel()
|
||||
{
|
||||
Users = []
|
||||
};
|
||||
}
|
||||
|
||||
c.DisplayName = channelName;
|
||||
c.ExternalId = channelName;
|
||||
c.ChannelType = vassago.Models.Enumerations.ChannelType.Normal;
|
||||
c.Messages = c.Messages ?? new List<Message>();
|
||||
c.Messages ??= [];
|
||||
c.Protocol = PROTOCOL;
|
||||
c.ParentChannel = protocolAsChannel;
|
||||
c.SubChannels = c.SubChannels ?? new List<Channel>();
|
||||
c.SendMessage = (t) => { return Task.Run(() => { client.SendMessage(channelName, t); }); };
|
||||
c.SendFile = (f, t) => { throw new InvalidOperationException($"twitch cannot send files"); };
|
||||
c = Rememberer.RememberChannel(c);
|
||||
|
||||
var selfAccountInChannel = c.Users?.FirstOrDefault(a => a.ExternalId == selfAccountInProtocol.ExternalId);
|
||||
if(selfAccountInChannel == null)
|
||||
{
|
||||
selfAccountInChannel = UpsertAccount(selfAccountInProtocol.Username, c);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
private Channel UpsertDMChannel(string whisperWith)
|
||||
{
|
||||
Channel c = _db.Channels.FirstOrDefault(ci => ci.ExternalId == $"w_{whisperWith}" && ci.Protocol == PROTOCOL);
|
||||
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == $"w_{whisperWith}"
|
||||
&& ci.Protocol == PROTOCOL);
|
||||
if (c == null)
|
||||
{
|
||||
c = new Channel();
|
||||
_db.Channels.Add(c);
|
||||
Console.WriteLine($"couldn't find channel under protocol {PROTOCOL}, whisper with {whisperWith}");
|
||||
c = new Channel()
|
||||
{
|
||||
Users = []
|
||||
};
|
||||
}
|
||||
|
||||
c.DisplayName = $"Whisper: {whisperWith}";
|
||||
c.ExternalId = $"w_{whisperWith}";
|
||||
c.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
||||
c.Messages = c.Messages ?? new List<Message>();
|
||||
c.Messages ??= [];
|
||||
c.Protocol = PROTOCOL;
|
||||
c.ParentChannel = protocolAsChannel;
|
||||
c.SubChannels = c.SubChannels ?? new List<Channel>();
|
||||
@ -229,50 +246,58 @@ public class TwitchInterface
|
||||
});
|
||||
};
|
||||
c.SendFile = (f, t) => { throw new InvalidOperationException($"twitch cannot send files"); };
|
||||
return c;
|
||||
}
|
||||
c = Rememberer.RememberChannel(c);
|
||||
|
||||
private Message UpsertMessage(ChatMessage chatMessage)
|
||||
{
|
||||
var m = _db.Messages.FirstOrDefault(mi => mi.ExternalId == chatMessage.Id);
|
||||
if (m == null)
|
||||
var selfAccountInChannel = c.Users.FirstOrDefault(a => a.ExternalId == selfAccountInProtocol.ExternalId);
|
||||
if(selfAccountInChannel == null)
|
||||
{
|
||||
m = new Message();
|
||||
m.Protocol = PROTOCOL;
|
||||
_db.Messages.Add(m);
|
||||
m.Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
|
||||
selfAccountInChannel = UpsertAccount(selfAccountInChannel.Username, c);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
//n.b., I see you future adam. "we should unify these, they're redundant".
|
||||
//ah, but that's the trick, they aren't! twitchlib has a common base class, but
|
||||
//none of the features we care about are on it!
|
||||
private Message UpsertMessage(ChatMessage chatMessage)
|
||||
{
|
||||
var m = Rememberer.SearchMessage(mi => mi.ExternalId == chatMessage.Id && mi.Protocol == PROTOCOL)
|
||||
?? new()
|
||||
{
|
||||
Protocol = PROTOCOL,
|
||||
Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)
|
||||
};
|
||||
m.Content = chatMessage.Message;
|
||||
m.ExternalId = chatMessage.Id;
|
||||
m.Channel = UpsertChannel(chatMessage.Channel);
|
||||
m.Author = UpsertAccount(chatMessage.Username, m.Channel.Id);
|
||||
m.Author.SeenInChannel = m.Channel;
|
||||
|
||||
m.Author = UpsertAccount(chatMessage.Username, m.Channel);
|
||||
m.MentionsMe = Regex.IsMatch(m.Content?.ToLower(), $"@\\b{selfAccountInProtocol.Username.ToLower()}\\b");
|
||||
m.Reply = (t) => { return Task.Run(() => { client.SendReply(chatMessage.Channel, chatMessage.Id, t); }); };
|
||||
m.React = (e) => { throw new InvalidOperationException($"twitch cannot react"); };
|
||||
Rememberer.RememberMessage(m);
|
||||
return m;
|
||||
}
|
||||
//n.b., I see you future adam. "we should unify these, they're redundant".
|
||||
//ah, but that's the trick, they aren't! twitchlib has a common base class, but
|
||||
//none of the features we care about are on it!
|
||||
private Message UpsertMessage(WhisperMessage whisperMessage)
|
||||
{
|
||||
var m = _db.Messages.FirstOrDefault(mi => mi.ExternalId == whisperMessage.MessageId);
|
||||
if (m == null)
|
||||
{
|
||||
m = new Message();
|
||||
m.Protocol = PROTOCOL;
|
||||
_db.Messages.Add(m);
|
||||
m.Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
|
||||
}
|
||||
|
||||
//WhisperMessage.Id corresponds to chatMessage.Id. \*eye twitch*
|
||||
var m = Rememberer.SearchMessage(mi => mi.ExternalId == whisperMessage.MessageId && mi.Protocol == PROTOCOL)
|
||||
?? new()
|
||||
{
|
||||
Protocol = PROTOCOL,
|
||||
Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)
|
||||
};
|
||||
m.Content = whisperMessage.Message;
|
||||
m.ExternalId = whisperMessage.MessageId;
|
||||
m.Channel = UpsertDMChannel(whisperMessage.Username);
|
||||
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
||||
m.Author = UpsertAccount(whisperMessage.Username, m.Channel.Id);
|
||||
m.Author.SeenInChannel = m.Channel;
|
||||
|
||||
m.Author = UpsertAccount(whisperMessage.Username, m.Channel);
|
||||
m.MentionsMe = Regex.IsMatch(m.Content?.ToLower(), $"@\\b{selfAccountInProtocol.Username.ToLower()}\\b");
|
||||
m.Reply = (t) => { return Task.Run(() => { client.SendWhisper(whisperMessage.Username, t); }); };
|
||||
m.React = (e) => { throw new InvalidOperationException($"twitch cannot react"); };
|
||||
Rememberer.RememberMessage(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
@ -287,4 +312,4 @@ public class TwitchInterface
|
||||
client.SendMessage(channelTarget, "o7");
|
||||
client.LeaveChannel(channelTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
123
Rememberer.cs
123
Rememberer.cs
@ -6,105 +6,184 @@ using Microsoft.EntityFrameworkCore;
|
||||
|
||||
public static class Rememberer
|
||||
{
|
||||
private static readonly SemaphoreSlim dbAccessSemaphore = new(1, 1);
|
||||
private static readonly ChattingContext db = new();
|
||||
|
||||
public static Account SearchAccount(Expression<Func<Account, bool>> predicate)
|
||||
{
|
||||
return db.Accounts.Include(a => a.IsUser).FirstOrDefault(predicate);
|
||||
Account toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.Accounts?.Include(a => a.IsUser)?.FirstOrDefault(predicate);
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static List<Account> SearchAccounts(Expression<Func<Account, bool>> predicate)
|
||||
{
|
||||
return db.Accounts.Where(predicate).ToList();
|
||||
List<Account> toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.Accounts.Where(predicate).ToList();
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static Attachment SearchAttachment(Expression<Func<Attachment, bool>> predicate)
|
||||
{
|
||||
return db.Attachments.FirstOrDefault(predicate);
|
||||
Attachment toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.Attachments.FirstOrDefault(predicate);
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static Channel SearchChannel(Expression<Func<Channel, bool>> predicate)
|
||||
{
|
||||
return db.Channels.FirstOrDefault(predicate);
|
||||
Channel toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.Channels.FirstOrDefault(predicate);
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static Message SearchMessage(Expression<Func<Message, bool>> predicate)
|
||||
{
|
||||
return db.Messages.FirstOrDefault(predicate);
|
||||
Message toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.Messages.FirstOrDefault(predicate);
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static User SearchUser(Expression<Func<User, bool>> predicate)
|
||||
{
|
||||
return db.Users.Include(u => u.Accounts).FirstOrDefault(predicate);
|
||||
User toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.Users.Where(predicate).Include(u => u.Accounts).FirstOrDefault(predicate);
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static void RememberAccount(Account toRemember)
|
||||
{
|
||||
toRemember.IsUser ??= new User{ Accounts = [toRemember]};
|
||||
dbAccessSemaphore.Wait();
|
||||
toRemember.IsUser ??= new User { Accounts = [toRemember] };
|
||||
db.Update(toRemember.IsUser);
|
||||
db.SaveChanges();
|
||||
dbAccessSemaphore.Release();
|
||||
}
|
||||
public static void RememberAttachment(Attachment toRemember)
|
||||
{
|
||||
toRemember.Message ??= new Message() { Attachments = [toRemember]};
|
||||
dbAccessSemaphore.Wait();
|
||||
toRemember.Message ??= new Message() { Attachments = [toRemember] };
|
||||
db.Update(toRemember.Message);
|
||||
db.SaveChanges();
|
||||
dbAccessSemaphore.Release();
|
||||
}
|
||||
public static Channel RememberChannel(Channel toRemember)
|
||||
{
|
||||
dbAccessSemaphore.Wait();
|
||||
db.Update(toRemember);
|
||||
db.SaveChanges();
|
||||
dbAccessSemaphore.Release();
|
||||
return toRemember;
|
||||
}
|
||||
public static void RememberMessage(Message toRemember)
|
||||
{
|
||||
toRemember.Channel ??= new (){ Messages = [toRemember] };
|
||||
dbAccessSemaphore.Wait();
|
||||
toRemember.Channel ??= new() { Messages = [toRemember] };
|
||||
db.Update(toRemember.Channel);
|
||||
db.SaveChanges();
|
||||
dbAccessSemaphore.Release();
|
||||
}
|
||||
public static void RememberUser(User toRemember)
|
||||
{
|
||||
dbAccessSemaphore.Wait();
|
||||
db.Users.Update(toRemember);
|
||||
|
||||
db.SaveChanges();
|
||||
dbAccessSemaphore.Release();
|
||||
}
|
||||
public static void ForgetAccount(Account toForget)
|
||||
{
|
||||
var user = toForget.IsUser;
|
||||
var usersOnlyAccount = user.Accounts?.Count == 1;
|
||||
|
||||
if(usersOnlyAccount)
|
||||
|
||||
if (usersOnlyAccount)
|
||||
{
|
||||
Rememberer.ForgetUser(user);
|
||||
}
|
||||
else
|
||||
{
|
||||
dbAccessSemaphore.Wait();
|
||||
db.Accounts.Remove(toForget);
|
||||
db.SaveChanges();
|
||||
dbAccessSemaphore.Release();
|
||||
}
|
||||
}
|
||||
public static void ForgetChannel(Channel toForget)
|
||||
{
|
||||
dbAccessSemaphore.Wait();
|
||||
db.Channels.Remove(toForget);
|
||||
|
||||
db.SaveChanges();
|
||||
dbAccessSemaphore.Release();
|
||||
}
|
||||
public static void ForgetUser(User toForget)
|
||||
{
|
||||
dbAccessSemaphore.Wait();
|
||||
db.Users.Remove(toForget);
|
||||
|
||||
db.SaveChanges();
|
||||
dbAccessSemaphore.Release();
|
||||
}
|
||||
public static List<Account> AccountsOverview()
|
||||
{
|
||||
return [..db.Accounts];
|
||||
List<Account> toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = [.. db.Accounts];
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static List<Channel> ChannelsOverview()
|
||||
{
|
||||
return [..db.Channels.Include(u => u.SubChannels).Include(c => c.ParentChannel)];
|
||||
List<Channel> toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = [.. db.Channels.Include(u => u.SubChannels).Include(c => c.ParentChannel)];
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static Channel ChannelDetail(Guid Id)
|
||||
{
|
||||
return db.Channels.Find(Id);
|
||||
// .Include(u => u.SubChannels)
|
||||
// .Include(u => u.Users)
|
||||
// .Include(u => u.ParentChannel);
|
||||
Channel toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.Channels.Find(Id);
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
// .Include(u => u.SubChannels)
|
||||
// .Include(u => u.Users)
|
||||
// .Include(u => u.ParentChannel);
|
||||
}
|
||||
public static List<User> UsersOverview()
|
||||
{
|
||||
return db.Users.ToList();
|
||||
List<User> toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.Users.ToList();
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
}
|
||||
public static List<UAC> UACsOverview()
|
||||
{
|
||||
List<UAC> toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.UACs.Include(uac => uac.Users).Include(uac => uac.Channels).Include(uac => uac.AccountInChannels).ToList();
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static UAC SearchUAC(Expression<Func<UAC, bool>> predicate)
|
||||
{
|
||||
UAC toReturn;
|
||||
dbAccessSemaphore.Wait();
|
||||
toReturn = db.UACs.Include(uac => uac.Users).Include(uac => uac.Channels).Include(uac => uac.AccountInChannels)
|
||||
.FirstOrDefault(predicate);
|
||||
dbAccessSemaphore.Release();
|
||||
return toReturn;
|
||||
}
|
||||
public static void RememberUAC(UAC toRemember)
|
||||
{
|
||||
dbAccessSemaphore.Wait();
|
||||
db.Update(toRemember);
|
||||
db.SaveChanges();
|
||||
dbAccessSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
@ -10,4 +10,5 @@ public static class Shared
|
||||
public static Random r = new Random();
|
||||
public static string DBConnectionString { get; set; }
|
||||
public static HttpClient HttpClient { get; internal set; } = new HttpClient();
|
||||
public static bool SetupSlashCommands { get; set; }
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ namespace vassago.WebInterface.Controllers;
|
||||
|
||||
public class ChannelsController() : Controller
|
||||
{
|
||||
public async Task<IActionResult> Details(Guid id)
|
||||
public IActionResult Details(Guid id)
|
||||
{
|
||||
var allChannels = Rememberer.ChannelsOverview();
|
||||
if(allChannels == null)
|
||||
|
@ -25,10 +25,61 @@ public class HomeController : Controller
|
||||
Console.WriteLine($"accounts: {allAccounts?.Count ?? 0}, channels: {allChannels?.Count ?? 0}");
|
||||
var sb = new StringBuilder();
|
||||
sb.Append('[');
|
||||
sb.Append("{text: \"channels\", expanded:true, nodes: [");
|
||||
|
||||
//UACs
|
||||
var allUACs = Rememberer.UACsOverview();
|
||||
var first = true;
|
||||
if(allUACs.Any())
|
||||
{
|
||||
sb.Append("{text: \"uacs\", expanded:true, nodes: [");
|
||||
first=true;
|
||||
foreach(var uac in allUACs)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(',');
|
||||
}
|
||||
sb.Append($"{{\"text\": \"<a href=\\\"{Url.ActionLink(action: "Details", controller: "UACs", values: new {id = uac.Id})}\\\">{uac.DisplayName}</a>\"}}");
|
||||
}
|
||||
sb.Append("]}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("{text: \"uacs (0)\", }");
|
||||
}
|
||||
|
||||
//users
|
||||
var users = Rememberer.UsersOverview();
|
||||
if(users.Any())
|
||||
{
|
||||
sb.Append(",{text: \"users\", expanded:true, nodes: [");
|
||||
first=true;
|
||||
//refresh list; we'll be knocking them out again in serializeUser
|
||||
allAccounts = Rememberer.AccountsOverview();
|
||||
foreach(var user in users)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(',');
|
||||
}
|
||||
serializeUser(ref sb, ref allAccounts, user);
|
||||
}
|
||||
sb.Append("]}");
|
||||
}
|
||||
|
||||
//type error, e is not defined
|
||||
//channels
|
||||
sb.Append(",{text: \"channels\", expanded:true, nodes: [");
|
||||
var topLevelChannels = Rememberer.ChannelsOverview().Where(x => x.ParentChannel == null);
|
||||
first = true;
|
||||
foreach (var topLevelChannel in topLevelChannels)
|
||||
{
|
||||
if (first)
|
||||
@ -84,28 +135,8 @@ public class HomeController : Controller
|
||||
}
|
||||
sb.Append("]}");
|
||||
}
|
||||
var users = Rememberer.UsersOverview();// _db.Users.ToList();
|
||||
if(users.Any())
|
||||
{
|
||||
sb.Append(",{text: \"users\", expanded:true, nodes: [");
|
||||
first=true;
|
||||
//refresh list; we'll be knocking them out again in serializeUser
|
||||
allAccounts = Rememberer.AccountsOverview();
|
||||
foreach(var user in users)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(',');
|
||||
}
|
||||
serializeUser(ref sb, ref allAccounts, user);
|
||||
}
|
||||
sb.Append("]}");
|
||||
}
|
||||
sb.Append(']');
|
||||
|
||||
sb.Append("]");
|
||||
ViewData.Add("treeString", sb.ToString());
|
||||
return View("Index");
|
||||
}
|
||||
@ -121,7 +152,6 @@ public class HomeController : Controller
|
||||
if (currentChannel.SubChannels != null || theseAccounts != null)
|
||||
{
|
||||
sb.Append(", \"nodes\": [");
|
||||
}
|
||||
if (currentChannel.SubChannels != null)
|
||||
{
|
||||
foreach (var subChannel in currentChannel.SubChannels)
|
||||
@ -159,7 +189,9 @@ public class HomeController : Controller
|
||||
}
|
||||
sb.Append("]}");
|
||||
}
|
||||
sb.Append("]}");
|
||||
sb.Append(']');
|
||||
}
|
||||
sb.Append('}');
|
||||
}
|
||||
private void serializeAccount(ref StringBuilder sb, Account currentAccount)
|
||||
{
|
||||
@ -171,17 +203,21 @@ public class HomeController : Controller
|
||||
sb.Append(currentUser.DisplayName);
|
||||
sb.Append("</a>\", ");
|
||||
var ownedAccounts = allAccounts.Where(a => a.IsUser == currentUser);
|
||||
sb.Append("nodes: [");
|
||||
sb.Append($"{{\"text\": \"owned accounts:\", \"expanded\":true, \"nodes\": [");
|
||||
if (ownedAccounts != null)
|
||||
if (ownedAccounts?.Count() > 0)
|
||||
{
|
||||
sb.Append("nodes: [");
|
||||
sb.Append($"{{\"text\": \"owned accounts:\", \"expanded\":true, \"nodes\": [");
|
||||
var first = true;
|
||||
foreach (var acc in ownedAccounts)
|
||||
{
|
||||
if(!first)
|
||||
sb.Append(',');
|
||||
serializeAccount(ref sb, acc);
|
||||
sb.Append(',');
|
||||
first = false;
|
||||
}
|
||||
sb.Append("]}]");
|
||||
}
|
||||
sb.Append("]}]}");
|
||||
sb.Append("}");
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
|
25
WebInterface/Controllers/UACsController.cs
Normal file
25
WebInterface/Controllers/UACsController.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using vassago.Models;
|
||||
using vassago.WebInterface.Models;
|
||||
|
||||
namespace vassago.WebInterface.Controllers;
|
||||
|
||||
public class UACsController() : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View(Rememberer.UACsOverview());
|
||||
}
|
||||
public IActionResult Details(Guid id)
|
||||
{
|
||||
return View(Rememberer.SearchUAC(uac => uac.Id == id));
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
@ -36,4 +36,4 @@ public class UsersController(ChattingContext db) : Controller
|
||||
{
|
||||
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using vassago.Models;
|
||||
using vassago.ProtocolInterfaces.DiscordInterface;
|
||||
|
||||
namespace vassago.Controllers.api;
|
||||
|
||||
|
213
WebInterface/Controllers/api/UAC.cs
Normal file
213
WebInterface/Controllers/api/UAC.cs
Normal file
@ -0,0 +1,213 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using vassago.Models;
|
||||
using vassago.ProtocolInterfaces.DiscordInterface;
|
||||
|
||||
namespace vassago.Controllers.api;
|
||||
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class UACController: ControllerBase
|
||||
{
|
||||
private readonly ILogger<UACController> _logger;
|
||||
|
||||
public UACController(ILogger<UACController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
//microsoft: "you can't have multiple [FromBody]. The reason for this rule is some bullshti about storage buffers."
|
||||
//cool story, bro. nobody gives a fuck, look at the boilerplate you've necessitated.
|
||||
public class extraSpecialObjectReadGlorifiedTupleFor_LinkChannel
|
||||
{
|
||||
public Guid uac_guid;
|
||||
public Guid channel_guid;
|
||||
}
|
||||
[HttpPatch]
|
||||
[Route("LinkChannel")]
|
||||
[Produces("application/json")]
|
||||
public IActionResult LinkChannel([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkChannel req)
|
||||
{
|
||||
var uac_guid = req.uac_guid;
|
||||
var channel_guid = req.channel_guid;
|
||||
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
|
||||
if (uacFromDb == null)
|
||||
{
|
||||
var err =$"attempt to link channel for uac {uac_guid}, not found";
|
||||
_logger.LogError(err);
|
||||
return NotFound(err);
|
||||
}
|
||||
var channelFromDb = Rememberer.SearchChannel(c => c.Id == channel_guid);
|
||||
if (channelFromDb == null)
|
||||
{
|
||||
var err = $"attempt to link channel for channel {channel_guid}, not found";
|
||||
_logger.LogError(err);
|
||||
return NotFound(err);
|
||||
}
|
||||
|
||||
uacFromDb.Channels ??= [];
|
||||
if (uacFromDb.Channels.Contains(channelFromDb))
|
||||
{
|
||||
return BadRequest("channel already linked");
|
||||
}
|
||||
uacFromDb.Channels.Add(channelFromDb);
|
||||
Rememberer.RememberUAC(uacFromDb);
|
||||
return Ok(uacFromDb);
|
||||
}
|
||||
public class extraSpecialObjectReadGlorifiedTupleFor_LinkUser
|
||||
{
|
||||
public Guid uac_guid;
|
||||
public Guid user_guid;
|
||||
}
|
||||
[HttpPatch]
|
||||
[Route("LinkUser")]
|
||||
[Produces("application/json")]
|
||||
public IActionResult LinkUser([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkUser req)
|
||||
{
|
||||
var uac_guid = req.uac_guid;
|
||||
var user_guid = req.user_guid;
|
||||
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
|
||||
if (uacFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to link channal for uac {uac_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
var userFromDb = Rememberer.SearchUser(c => c.Id == user_guid);
|
||||
if (userFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to link user for user {user_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
uacFromDb.Users ??= [];
|
||||
if (uacFromDb.Users.Contains(userFromDb))
|
||||
{
|
||||
return BadRequest("user already linked");
|
||||
}
|
||||
uacFromDb.Users.Add(userFromDb);
|
||||
Rememberer.RememberUAC(uacFromDb);
|
||||
return Ok(uacFromDb);
|
||||
}
|
||||
public class extraSpecialObjectReadGlorifiedTupleFor_LinkAccount
|
||||
{
|
||||
public Guid uac_guid;
|
||||
public Guid account_guid;
|
||||
}
|
||||
[HttpPatch]
|
||||
[Route("LinkAccount")]
|
||||
[Produces("application/json")]
|
||||
public IActionResult LinkAccount([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkAccount req)
|
||||
{
|
||||
var uac_guid = req.uac_guid;
|
||||
var account_guid = req.account_guid;
|
||||
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
|
||||
if (uacFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to link channal for uac {uac_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
var accountFromDb = Rememberer.SearchAccount(c => c.Id == account_guid);
|
||||
if (accountFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to link account for user {account_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
uacFromDb.AccountInChannels ??= [];
|
||||
if (uacFromDb.AccountInChannels.Contains(accountFromDb))
|
||||
{
|
||||
return BadRequest("account already linked");
|
||||
}
|
||||
uacFromDb.AccountInChannels.Add(accountFromDb);
|
||||
Rememberer.RememberUAC(uacFromDb);
|
||||
return Ok(uacFromDb);
|
||||
}
|
||||
[HttpPatch]
|
||||
[Route("UnlinkUser")]
|
||||
[Produces("application/json")]
|
||||
public IActionResult UnlinkUser([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkUser req)
|
||||
{
|
||||
var uac_guid = req.uac_guid;
|
||||
var user_guid = req.user_guid;
|
||||
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
|
||||
if (uacFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to unlink uac for uac {uac_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
var userFromDb = Rememberer.SearchUser(c => c.Id == user_guid);
|
||||
if (userFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to unlink user for user {user_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
uacFromDb.Users ??= [];
|
||||
if (!uacFromDb.Users.Contains(userFromDb))
|
||||
{
|
||||
return BadRequest("user not linked");
|
||||
}
|
||||
uacFromDb.Users.Remove(userFromDb);
|
||||
Rememberer.RememberUAC(uacFromDb);
|
||||
return Ok(uacFromDb);
|
||||
}
|
||||
[HttpPatch]
|
||||
[Route("UnlinkAccount")]
|
||||
[Produces("application/json")]
|
||||
public IActionResult UnlinkAccount([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkAccount req)
|
||||
{
|
||||
var uac_guid = req.uac_guid;
|
||||
var account_guid = req.account_guid;
|
||||
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
|
||||
if (uacFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to unlink uac for uac {uac_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
var accountFromDb = Rememberer.SearchAccount(a => a.Id == account_guid);
|
||||
if (accountFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to unlink account for user {account_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
uacFromDb.AccountInChannels ??= [];
|
||||
if (!uacFromDb.AccountInChannels.Contains(accountFromDb))
|
||||
{
|
||||
return BadRequest("account not linked");
|
||||
}
|
||||
uacFromDb.AccountInChannels.Remove(accountFromDb);
|
||||
Rememberer.RememberUAC(uacFromDb);
|
||||
return Ok(uacFromDb);
|
||||
}
|
||||
[HttpPatch]
|
||||
[Route("UnlinkChannel")]
|
||||
[Produces("application/json")]
|
||||
public IActionResult UnlinkChannel([FromBody] extraSpecialObjectReadGlorifiedTupleFor_LinkChannel req)
|
||||
{
|
||||
var uac_guid = req.uac_guid;
|
||||
var channel_guid = req.channel_guid;
|
||||
var uacFromDb = Rememberer.SearchUAC(uac => uac.Id == uac_guid);
|
||||
if (uacFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to unlink channal for uac {uac_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
var channelFromDb = Rememberer.SearchChannel(c => c.Id == channel_guid);
|
||||
if (channelFromDb == null)
|
||||
{
|
||||
_logger.LogError($"attempt to unlink user for user {channel_guid}, not found");
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
uacFromDb.Users ??= [];
|
||||
if (!uacFromDb.Channels.Contains(channelFromDb))
|
||||
{
|
||||
return BadRequest("user not linked");
|
||||
}
|
||||
uacFromDb.Channels.Remove(channelFromDb);
|
||||
Rememberer.RememberUAC(uacFromDb);
|
||||
return Ok(uacFromDb);
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Channel type</th>
|
||||
<td>@(ThisChannel.ChannelType != null ? Enumerations.GetDescription(ThisChannel.ChannelType) : "?")</td>
|
||||
<td>@(Enumerations.GetDescription(ThisChannel.ChannelType))</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Lewdness Filter Level</th>
|
||||
|
@ -7,10 +7,11 @@
|
||||
<script type="text/javascript">
|
||||
function getTree() {
|
||||
var tree = @Html.Raw(ViewData["treeString"]);
|
||||
console.log(tree);
|
||||
console.log('tree');
|
||||
console.log('@ViewData["treeString"]');
|
||||
return tree;
|
||||
}
|
||||
|
||||
$('#tree').bstreeview({ data: getTree() });
|
||||
</script>
|
||||
}
|
||||
}
|
||||
|
214
WebInterface/Views/UACs/Details.cshtml
Normal file
214
WebInterface/Views/UACs/Details.cshtml
Normal file
@ -0,0 +1,214 @@
|
||||
@using System.ComponentModel
|
||||
@using Newtonsoft.Json
|
||||
@using System.Text;
|
||||
@model UAC
|
||||
|
||||
<a href="/">home</a>/
|
||||
@Html.Raw(ViewData["breadcrumbs"])
|
||||
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Display Name</th>
|
||||
<td>@Model.DisplayName</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Channels</th>
|
||||
<td>
|
||||
@Html.Raw("<div id=\"channelsTree\"></div>")
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Users</th>
|
||||
<td>
|
||||
@Html.Raw("<div id=\"usersTree\"></div>")
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">AccountInChannels</th>
|
||||
<td>
|
||||
@Html.Raw("<div id=\"accountsTree\"></div>")
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="add-modal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Insert GUID</h5>
|
||||
<button type="button" class="btn btn-close" data-dismiss="add-modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
<ul>
|
||||
<li>//TODO: search</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
<input id="addmodaltext" type="text" />
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="modalsubmit" type="button" class="btn btn-primary">Save changes</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="add-modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="remove-modal" class="modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Confirm</h5>
|
||||
<button type="button" class="btn-close" data-dismiss="remove-modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>
|
||||
are you sure you wnat to unlink
|
||||
<input id="removeModalText" enabled="false" type="text" />
|
||||
</p>
|
||||
<p>
|
||||
to be clear; this is going to "unlink", not like.. delete.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button id="modalsubmit" type="button" class="btn btn-danger">unlink</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="remove-modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@section Scripts{
|
||||
<script type="text/javascript">
|
||||
function addChannel(){
|
||||
let modalbutton = document.querySelector("#add-modal button#modalsubmit");
|
||||
modalbutton.onclick = addChannelSubmit;
|
||||
$("#add-modal").modal("show");
|
||||
}
|
||||
function addChannelSubmit(){
|
||||
let guid = document.querySelector("#add-modal #addmodaltext").value;
|
||||
linkUAC_Channel(guid, () => { window.location.reload(); });
|
||||
$("#add-modal").modal("hide");
|
||||
console.log(guid);
|
||||
}
|
||||
function addUser(){
|
||||
let modalbutton = document.querySelector("#add-modal button#modalsubmit");
|
||||
modalbutton.onclick = addUserSubmit;
|
||||
$("#add-modal").modal("show");
|
||||
}
|
||||
function addUserSubmit(){
|
||||
let guid = document.querySelector("#add-modal #addmodaltext").value;
|
||||
linkUAC_User(guid, () => {window.location.reload(); });
|
||||
$("#add-modal").modal("hide");
|
||||
console.log(guid);
|
||||
}
|
||||
function addAccount(){
|
||||
let modalbutton = document.querySelector("#add-modal button#modalsubmit");
|
||||
modalbutton.onclick = addAccountSubmit;
|
||||
$("#add-modal").modal("show");
|
||||
}
|
||||
function addAccountSubmit(){
|
||||
let guid = document.querySelector("#add-modal #addmodaltext").value;
|
||||
linkUAC_Account(guid, () => { window.location.reload(); });
|
||||
$("#add-modal").modal("hide");
|
||||
console.log(guid);
|
||||
}
|
||||
function removeUser(guid){
|
||||
document.querySelector("#remove-modal #removeModalText").value = guid;
|
||||
let modalbutton = document.querySelector("#remove-modal button#modalsubmit");
|
||||
modalbutton.onclick = removeUserSubmit;
|
||||
$("#remove-modal").modal("show");
|
||||
}
|
||||
function removeUserSubmit(){
|
||||
let guid = document.querySelector("#remove-modal #removeModalText").value;
|
||||
unlinkUAC_User(guid, () => { window.location.reload(); });
|
||||
$("#remove-modal").modal("hide");
|
||||
}
|
||||
function removeChannel(guid){
|
||||
document.querySelector("#remove-modal #removeModalText").value = guid;
|
||||
let modalbutton = document.querySelector("#remove-modal button#modalsubmit");
|
||||
modalbutton.onclick = removeChannelSubmit;
|
||||
$("#remove-modal").modal("show");
|
||||
}
|
||||
function removeChannelSubmit(){
|
||||
let guid = document.querySelector("#remove-modal #removeModalText").value;
|
||||
unlinkUAC_Channel(guid, () => { window.location.reload(); });
|
||||
$("#remove-modal").modal("hide");
|
||||
}
|
||||
function removeAccount(guid){
|
||||
document.querySelector("#remove-modal #removeModalText").value = guid;
|
||||
let modalbutton = document.querySelector("#remove-modal button#modalsubmit");
|
||||
modalbutton.onclick = removeAccountSubmit;
|
||||
$("#remove-modal").modal("show");
|
||||
}
|
||||
function removeAccountSubmit(){
|
||||
let guid = document.querySelector("#remove-modal #removeModalText").value;
|
||||
unlinkUAC_Account(guid , () => { window.location.reload(); });
|
||||
$("#remove-modal").modal("hide");
|
||||
}
|
||||
function channelsTree() {
|
||||
@{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("[{text: \"Channels\", \"expanded\":true, nodes: [");
|
||||
|
||||
sb.Append($"{{text: \"<button type=\\\"button\\\" class=\\\"btn btn-primary\\\" onclick=\\\"addChannel()\\\">add channel</button>\"}}");
|
||||
foreach (var acc in Model.Channels?.OrderBy(a => a.DisplayName))
|
||||
{
|
||||
sb.Append(',');
|
||||
sb.Append($"{{text: \"<a href=\\\"/Channels/Details/{acc.Id}\\\">{acc.DisplayName}</a> - <button type=\\\"button\\\" class=\\\"btn btn-danger\\\" onclick=\\\"removeChannel('{acc.Id}')\\\">remove</button>\"}}");
|
||||
}
|
||||
sb.Append("]}]");
|
||||
}
|
||||
var tree = @Html.Raw(sb.ToString());
|
||||
return tree;
|
||||
}
|
||||
function usersTree() {
|
||||
@{
|
||||
sb = new StringBuilder();
|
||||
sb.Append("[{text: \"Users\", \"expanded\":true, nodes: [");
|
||||
|
||||
sb.Append($"{{text: \"<button type=\\\"button\\\" class=\\\"btn btn-primary\\\" onclick=\\\"addUser()\\\">add user</button>\"}}");
|
||||
foreach (var acc in Model.Users?.OrderBy(a => a.DisplayName))
|
||||
{
|
||||
sb.Append(',');
|
||||
sb.Append($"{{text: \"<a href=\\\"/Users/Details/{acc.Id}\\\">{acc.DisplayName}</a> - <button type=\\\"button\\\" class=\\\"btn btn-danger\\\" onclick=\\\"removeUser('{acc.Id}')\\\">remove</button>\"}}");
|
||||
}
|
||||
sb.Append("]}]");
|
||||
}
|
||||
var tree = @Html.Raw(sb.ToString());
|
||||
return tree;
|
||||
}
|
||||
function accountsTree() {
|
||||
@{
|
||||
sb = new StringBuilder();
|
||||
sb.Append("[{text: \"Accounts\", \"expanded\":true, nodes: [");
|
||||
|
||||
sb.Append($"{{text: \"<button type=\\\"button\\\" class=\\\"btn btn-primary\\\" onclick=\\\"addAccount()\\\">add account</button>\"}}");
|
||||
foreach (var acc in Model.AccountInChannels?.OrderBy(a => a.DisplayName))
|
||||
{
|
||||
sb.Append(',');
|
||||
sb.Append($"{{text: \"<a href=\\\"/Accounts/Details/{acc.Id}\\\">{acc.DisplayName}</a> - <button type=\\\"button\\\" class=\\\"btn btn-danger\\\" onclick=\\\"removeAccount('{acc.Id}')\\\">remove</button>\"}}");
|
||||
}
|
||||
sb.Append("]}]");
|
||||
}
|
||||
var tree = @Html.Raw(sb.ToString());
|
||||
return tree;
|
||||
}
|
||||
$('#channelsTree').bstreeview({ data: channelsTree() });
|
||||
$('#usersTree').bstreeview({ data: usersTree() });
|
||||
$('#accountsTree').bstreeview({ data: accountsTree() });
|
||||
|
||||
var components = window.location.pathname.split('/');
|
||||
var uacId = components[3];
|
||||
|
||||
</script>
|
||||
}
|
@ -19,13 +19,7 @@
|
||||
<div id="accountsTree"></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Permission Tags</th>
|
||||
<td>
|
||||
<div id="tagsTree"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@section Scripts{
|
||||
@ -65,26 +59,7 @@
|
||||
return tree;
|
||||
}
|
||||
|
||||
function getTagsTree() {
|
||||
@{
|
||||
sb = new StringBuilder();
|
||||
sb.Append("[{text: \"permission tags\", \"expanded\":true, nodes: [");
|
||||
first = true;
|
||||
for (int i = 0; i < 1; i++)
|
||||
{
|
||||
if (!first)
|
||||
sb.Append(',');
|
||||
sb.Append($"{{text: \"<input type=\\\"checkbox\\\" > is goated (w/ sauce)</input>\"}}");
|
||||
first = false;
|
||||
}
|
||||
sb.Append("]}]");
|
||||
}
|
||||
console.log(@Html.Raw(sb.ToString()));
|
||||
var tree = @Html.Raw(sb.ToString());
|
||||
return tree;
|
||||
}
|
||||
$('#accountsTree').bstreeview({ data: getAccountsTree() });
|
||||
$('#tagsTree').bstreeview({ data: getTagsTree() });
|
||||
document.querySelectorAll("input[type=checkbox]").forEach(node => { node.onchange = () => { patchModel(jsonifyUser(), '/api/Users/') } });
|
||||
</script>
|
||||
}
|
||||
|
@ -13,5 +13,6 @@
|
||||
"TwitchConfigs": [
|
||||
],
|
||||
"exchangePairsLocation": "assets/exchangepairs.json",
|
||||
"DBConnectionString": "Host=azure.club;Database=db;Username=user;Password=password"
|
||||
"DBConnectionString": "Host=azure.club;Database=db;Username=user;Password=password",
|
||||
"SetupSlashCommands": false
|
||||
}
|
||||
|
@ -219,16 +219,16 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonical":"blue whale lengths",
|
||||
"canonical":"blue whale length",
|
||||
"aliases": [
|
||||
"bwl",
|
||||
"whales"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonical":"ångströms",
|
||||
"canonical":"ångström",
|
||||
"aliases": [
|
||||
"angstroms",
|
||||
"angstrom",
|
||||
"Å"
|
||||
]
|
||||
},
|
||||
@ -239,6 +239,12 @@
|
||||
"micrometres",
|
||||
"microns"
|
||||
]
|
||||
},
|
||||
{
|
||||
"canonical":"uncle jordan",
|
||||
"aliases":[
|
||||
"uj"
|
||||
]
|
||||
}
|
||||
],
|
||||
"linearPairs":[
|
||||
@ -267,6 +273,7 @@
|
||||
{"item1":"blue whale length", "item2": "m", "factor": 29.9},
|
||||
{"item1":"m", "item2": "ångström", "factor": 10000000000},
|
||||
{"item1":"smoot", "item2": "ft", "factor": 5.583333333333},
|
||||
{"item1":"uncle jordan", "item2": "cm", "factor":192.405},
|
||||
|
||||
{"item1":"floz", "item2":"mL", "factor":29.57344},
|
||||
{"item1":"L", "item2":"mL", "factor":1000},
|
||||
|
@ -20,7 +20,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.2" />
|
||||
<PackageReference Include="RestSharp" Version="110.2.0" />
|
||||
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.6.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.6.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.6.2" />
|
||||
|
@ -1,7 +1,5 @@
|
||||
// Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification
|
||||
// for details on configuring this project to bundle and minify static web assets.
|
||||
var apiUrl = '/api/';
|
||||
|
||||
// Write your JavaScript code.
|
||||
function Account(displayName, accountId, protocol){
|
||||
this.displayName = displayName;
|
||||
this.accountId = accountId;
|
||||
@ -10,23 +8,21 @@ function Account(displayName, accountId, protocol){
|
||||
//todo: figure out what the URL actually needs to be, rather than assuming you get a whole-ass server to yourself.
|
||||
//you selfish fuck... What are you, fox?
|
||||
//as it stands, you want something like /api/Channels/, trailing slash intentional
|
||||
function patchModel(model, apiUrl)
|
||||
function patchModel(model, deprecated_apiUrl)
|
||||
{
|
||||
//structure the model your (dang) self into a nice object
|
||||
console.log(model);
|
||||
//i know the page url.
|
||||
console.log(window.location.pathname);
|
||||
var components = window.location.pathname.split('/');
|
||||
if(components[2] !== "Details")
|
||||
{
|
||||
console.log("wtf are you doing? " + components[2] + " is something other than Details");
|
||||
}
|
||||
// if(components[2] !== "Details")
|
||||
// {
|
||||
// console.log("wtf are you doing? " + components[2] + " is something other than Details");
|
||||
// }
|
||||
var type=components[1];
|
||||
var id=components[3];
|
||||
// var id=components[3];
|
||||
|
||||
console.log("dexter impression: I am now ready to post the following content:");
|
||||
console.log(JSON.stringify(model));
|
||||
fetch(apiUrl, {
|
||||
fetch(apiUrl + type + '/', {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -48,16 +44,17 @@ function patchModel(model, apiUrl)
|
||||
});
|
||||
}
|
||||
|
||||
function deleteModel(model, apiUrl)
|
||||
function deleteModel(model, deprecated_apiUrl)
|
||||
{
|
||||
var components = window.location.pathname.split('/');
|
||||
if(components[2] !== "Details")
|
||||
{
|
||||
console.log("wtf are you doing? " + components[2] + " is something other than Details");
|
||||
}
|
||||
// if(components[2] !== "Details")
|
||||
// {
|
||||
// console.log("wtf are you doing? " + components[2] + " is something other than Details");
|
||||
// }
|
||||
var type=components[1];
|
||||
var id=components[3];
|
||||
fetch(apiUrl, {
|
||||
let result = null;
|
||||
// var id=components[3];
|
||||
fetch(apiUrl + type + '/', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@ -73,8 +70,179 @@ function deleteModel(model, apiUrl)
|
||||
.then(returnedSuccessdata => {
|
||||
// perhaps a success callback
|
||||
console.log('returnedSuccessdata:', returnedSuccessdata);
|
||||
if(callback !== null) { callback(); }
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function linkUAC_Channel(channel_guid, callback)
|
||||
{
|
||||
|
||||
var components = window.location.pathname.split('/');
|
||||
var id=components[3];
|
||||
let model={"uac_guid": id,
|
||||
"channel_guid": channel_guid};
|
||||
fetch(apiUrl + "UAC/LinkChannel/", {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(model),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not "ok". which is not ok.');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(returnedSuccessdata => {
|
||||
// perhaps a success callback
|
||||
console.log('returnedSuccessdata:', returnedSuccessdata);
|
||||
if(callback !== null) { callback(); }
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
function linkUAC_User(user_guid, callback)
|
||||
{
|
||||
var components = window.location.pathname.split('/');
|
||||
var id=components[3];
|
||||
let model={"uac_guid": id,
|
||||
"user_guid": user_guid};
|
||||
fetch(apiUrl + "UAC/LinkUser/", {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(model),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not "ok". which is not ok.');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(returnedSuccessdata => {
|
||||
// perhaps a success callback
|
||||
console.log('returnedSuccessdata:', returnedSuccessdata);
|
||||
if(callback !== null) { callback(); }
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
function linkUAC_Account(account_guid, callback)
|
||||
{
|
||||
var reuslt = null;
|
||||
var components = window.location.pathname.split('/');
|
||||
var id=components[3];
|
||||
let model={"uac_guid": id,
|
||||
"account_guid": account_guid};
|
||||
fetch(apiUrl + "UAC/LinkAccount/", {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(model),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not "ok". which is not ok.');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(returnedSuccessdata => {
|
||||
// perhaps a success callback
|
||||
console.log('returnedSuccessdata:', returnedSuccessdata);
|
||||
if(callback !== null) { callback(); }
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
function unlinkUAC_User(user_guid, callback)
|
||||
{
|
||||
var components = window.location.pathname.split('/');
|
||||
var id=components[3];
|
||||
let model={"uac_guid": id,
|
||||
"user_guid": user_guid};
|
||||
fetch(apiUrl + "UAC/UnlinkUser/", {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(model),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not "ok". which is not ok.');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(returnedSuccessdata => {
|
||||
// perhaps a success callback
|
||||
console.log('returnedSuccessdata:', returnedSuccessdata);
|
||||
if(callback !== null) { callback(); }
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
function unlinkUAC_Account(account_guid, callback)
|
||||
{
|
||||
var components = window.location.pathname.split('/');
|
||||
var id=components[3];
|
||||
let model={"uac_guid": id,
|
||||
"account_guid": account_guid};
|
||||
fetch(apiUrl + "UAC/UnlinkAccount/", {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(model),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not "ok". which is not ok.');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(returnedSuccessdata => {
|
||||
// perhaps a success callback
|
||||
console.log('returnedSuccessdata:', returnedSuccessdata);
|
||||
if(callback !== null) { callback(); }
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
function unlinkUAC_Channel(user_guid, callback)
|
||||
{
|
||||
var components = window.location.pathname.split('/');
|
||||
var id=components[3];
|
||||
let model={"uac_guid": id,
|
||||
"channel_guid": user_guid};
|
||||
fetch(apiUrl + "UAC/UnlinkChannel/", {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(model),
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Network response was not "ok". which is not ok.');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(returnedSuccessdata => {
|
||||
// perhaps a success callback
|
||||
console.log('returnedSuccessdata:', returnedSuccessdata);
|
||||
if(callback !== null) { callback(); }
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user