connects to twitch. twitchsummon happens.
I think twitchunsummon doesn't?
This commit is contained in:
parent
5ff601a60c
commit
1d70074d71
@ -24,7 +24,7 @@ public class Message
|
|||||||
public Channel Channel { get; set; }
|
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]
|
[NonSerialized]
|
||||||
public Func<string, Task> Reply;
|
public Func<string, Task> Reply;
|
||||||
|
|
||||||
|
@ -15,16 +15,22 @@ using System.Reactive.Linq;
|
|||||||
|
|
||||||
namespace vassago.ProtocolInterfaces.DiscordInterface;
|
namespace vassago.ProtocolInterfaces.DiscordInterface;
|
||||||
|
|
||||||
|
//data received
|
||||||
|
//translate data to internal type
|
||||||
|
//store
|
||||||
|
//ship off to behaver
|
||||||
|
|
||||||
public class DiscordInterface
|
public class DiscordInterface
|
||||||
{
|
{
|
||||||
internal const string PROTOCOL = "discord";
|
internal static string PROTOCOL { get => "discord"; }
|
||||||
internal DiscordSocketClient client;
|
internal DiscordSocketClient client;
|
||||||
private bool eventsSignedUp = false;
|
private bool eventsSignedUp = false;
|
||||||
private static readonly SemaphoreSlim discordChannelSetup = new(1, 1);
|
private static readonly SemaphoreSlim discordChannelSetup = new(1, 1);
|
||||||
private Channel protocolAsChannel;
|
private Channel protocolAsChannel;
|
||||||
|
|
||||||
public async Task Init(string token)
|
public async Task Init(string config)
|
||||||
{
|
{
|
||||||
|
var token = config;
|
||||||
await SetupDiscordChannel();
|
await SetupDiscordChannel();
|
||||||
client = new DiscordSocketClient(new DiscordSocketConfig() { GatewayIntents = GatewayIntents.All });
|
client = new DiscordSocketClient(new DiscordSocketConfig() { GatewayIntents = GatewayIntents.All });
|
||||||
|
|
||||||
@ -147,6 +153,7 @@ public class DiscordInterface
|
|||||||
}
|
}
|
||||||
await Behaver.Instance.ActOn(m);
|
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.
|
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)
|
private Task UserJoined(SocketGuildUser arg)
|
||||||
@ -312,7 +319,8 @@ public class DiscordInterface
|
|||||||
|
|
||||||
c = Rememberer.RememberChannel(c);
|
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)
|
if(selfAccountInChannel == null)
|
||||||
{
|
{
|
||||||
selfAccountInChannel = UpsertAccount(client.CurrentUser, c);
|
selfAccountInChannel = UpsertAccount(client.CurrentUser, c);
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
|
using RestSharp;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using RestSharp;
|
|
||||||
using TwitchLib.Api;
|
|
||||||
using TwitchLib.Api.Helix.Models.Users.GetUsers;
|
using TwitchLib.Api.Helix.Models.Users.GetUsers;
|
||||||
using TwitchLib.Client;
|
using TwitchLib.Api;
|
||||||
using TwitchLib.Client.Events;
|
using TwitchLib.Client.Events;
|
||||||
using TwitchLib.Client.Models;
|
using TwitchLib.Client.Models;
|
||||||
|
using TwitchLib.Client;
|
||||||
using TwitchLib.Communication.Clients;
|
using TwitchLib.Communication.Clients;
|
||||||
using TwitchLib.Communication.Models;
|
using TwitchLib.Communication.Models;
|
||||||
using vassago.Behavior;
|
using vassago.Behavior;
|
||||||
@ -13,27 +13,26 @@ using vassago.Models;
|
|||||||
|
|
||||||
namespace vassago.TwitchInterface;
|
namespace vassago.TwitchInterface;
|
||||||
|
|
||||||
|
internal class unifiedTwitchMessage
|
||||||
|
{
|
||||||
|
public unifiedTwitchMessage(ChatMessage chatMessage){}
|
||||||
|
}
|
||||||
|
|
||||||
public class TwitchInterface
|
public class TwitchInterface
|
||||||
{
|
{
|
||||||
internal const string PROTOCOL = "twitch";
|
internal const string PROTOCOL = "twitch";
|
||||||
private bool eventsSignedUp = false;
|
private static SemaphoreSlim channelSetupSemaphpore = new SemaphoreSlim(1, 1);
|
||||||
private ChattingContext _db;
|
|
||||||
private static SemaphoreSlim twitchChannelSetup = new SemaphoreSlim(1, 1);
|
|
||||||
private Channel protocolAsChannel;
|
private Channel protocolAsChannel;
|
||||||
|
private Account selfAccountInProtocol;
|
||||||
TwitchClient client;
|
TwitchClient client;
|
||||||
TwitchAPI api;
|
|
||||||
|
|
||||||
public TwitchInterface()
|
|
||||||
{
|
|
||||||
_db = new ChattingContext();
|
|
||||||
}
|
|
||||||
private async Task SetupTwitchChannel()
|
private async Task SetupTwitchChannel()
|
||||||
{
|
{
|
||||||
await twitchChannelSetup.WaitAsync();
|
await channelSetupSemaphpore.WaitAsync();
|
||||||
|
|
||||||
try
|
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)
|
if (protocolAsChannel == null)
|
||||||
{
|
{
|
||||||
protocolAsChannel = new Channel()
|
protocolAsChannel = new Channel()
|
||||||
@ -47,17 +46,23 @@ public class TwitchInterface
|
|||||||
ReactionsPossible = false,
|
ReactionsPossible = false,
|
||||||
ExternalId = null,
|
ExternalId = null,
|
||||||
Protocol = PROTOCOL,
|
Protocol = PROTOCOL,
|
||||||
SubChannels = new List<Channel>()
|
SubChannels = []
|
||||||
};
|
};
|
||||||
|
protocolAsChannel.DisplayName = "twitch (itself)";
|
||||||
protocolAsChannel.SendMessage = (t) => { throw new InvalidOperationException($"twitch itself cannot accept text"); };
|
protocolAsChannel.SendMessage = (t) => { throw new InvalidOperationException($"twitch itself cannot accept text"); };
|
||||||
protocolAsChannel.SendFile = (f, t) => { throw new InvalidOperationException($"twitch itself cannot send file"); };
|
protocolAsChannel.SendFile = (f, t) => { throw new InvalidOperationException($"twitch itself cannot send file"); };
|
||||||
_db.Channels.Add(protocolAsChannel);
|
protocolAsChannel = Rememberer.RememberChannel(protocolAsChannel);
|
||||||
_db.SaveChanges();
|
Console.WriteLine($"protocol as channle added; {protocolAsChannel}");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine($"twitch, channel with id {protocolAsChannel.Id}, already exists");
|
||||||
|
}
|
||||||
|
//protocolAsChan
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
twitchChannelSetup.Release();
|
channelSetupSemaphpore.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,69 +86,46 @@ public class TwitchInterface
|
|||||||
client.OnWhisperReceived += Client_OnWhisperReceivedAsync;
|
client.OnWhisperReceived += Client_OnWhisperReceivedAsync;
|
||||||
client.OnConnected += Client_OnConnected;
|
client.OnConnected += Client_OnConnected;
|
||||||
|
|
||||||
Console.WriteLine("twitch client 1 connecting...");
|
|
||||||
client.Connect();
|
client.Connect();
|
||||||
Console.WriteLine("twitch client 1 connected");
|
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)
|
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}");
|
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 Behaver.Instance.ActOn(m);
|
||||||
await _db.SaveChangesAsync();
|
m.ActedOn = true;
|
||||||
|
//TODO: remember it again?
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Client_OnMessageReceivedAsync(object sender, OnMessageReceivedArgs e)
|
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}");
|
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 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}");
|
Console.WriteLine($"twitch marking selfaccount as seeninchannel {protocolAsChannel.Id}");
|
||||||
var selfAccount = UpsertAccount(e.BotUsername, protocolAsChannel.Id);
|
selfAccountInProtocol = UpsertAccount(e.BotUsername, protocolAsChannel);
|
||||||
Behaver.Instance.MarkSelf(selfAccount);
|
selfAccountInProtocol.DisplayName = e.BotUsername;
|
||||||
|
Behaver.Instance.MarkSelf(selfAccountInProtocol);
|
||||||
await _db.SaveChangesAsync();
|
|
||||||
|
|
||||||
Console.WriteLine($"Connected to {e.AutoJoinChannel}");
|
Console.WriteLine($"Connected to {e.AutoJoinChannel}");
|
||||||
}
|
}
|
||||||
@ -158,61 +140,95 @@ public class TwitchInterface
|
|||||||
Console.WriteLine($"{e.DateTime.ToString()}: {e.BotUsername} - {e.Data}");
|
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 = Rememberer.SearchAccount(ui => ui.ExternalId == username && ui.SeenInChannel.ExternalId == inChannel.ToString());
|
||||||
var acc = _db.Accounts.FirstOrDefault(ui => ui.ExternalId == username && ui.SeenInChannel.Id == inChannel);
|
Console.WriteLine($"upserting account, retrieved {acc?.Id}.");
|
||||||
if (acc == null)
|
if (acc != null)
|
||||||
{
|
{
|
||||||
acc = new Account();
|
Console.WriteLine($"acc's usser: {acc.IsUser?.Id}");
|
||||||
acc.SeenInChannel = seenInChannel;
|
|
||||||
_db.Accounts.Add(acc);
|
|
||||||
}
|
}
|
||||||
|
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.Username = username;
|
||||||
acc.ExternalId = 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.Protocol = PROTOCOL;
|
||||||
|
acc.SeenInChannel = inChannel;
|
||||||
|
|
||||||
acc.IsUser = _db.Users.FirstOrDefault(u => u.Accounts.Any(a => a.ExternalId == acc.ExternalId && a.Protocol == acc.Protocol));
|
Console.WriteLine($"we asked rememberer to search for acc's user. {acc.IsUser?.Id}");
|
||||||
if (acc.IsUser == null)
|
if (acc.IsUser != null)
|
||||||
{
|
{
|
||||||
acc.IsUser = new vassago.Models.User() { Accounts = new List<Account>() { acc } };
|
Console.WriteLine($"user has record of {acc.IsUser.Accounts?.Count ?? 0} accounts");
|
||||||
_db.Users.Add(acc.IsUser);
|
}
|
||||||
|
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;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Channel UpsertChannel(string channelName)
|
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)
|
if (c == null)
|
||||||
{
|
{
|
||||||
c = new Channel();
|
Console.WriteLine($"couldn't find channel under protocol {PROTOCOL} with externalId {channelName}");
|
||||||
_db.Channels.Add(c);
|
c = new Channel()
|
||||||
|
{
|
||||||
|
Users = []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
c.DisplayName = channelName;
|
c.DisplayName = channelName;
|
||||||
c.ExternalId = channelName;
|
c.ExternalId = channelName;
|
||||||
c.ChannelType = vassago.Models.Enumerations.ChannelType.Normal;
|
c.ChannelType = vassago.Models.Enumerations.ChannelType.Normal;
|
||||||
c.Messages = c.Messages ?? new List<Message>();
|
c.Messages ??= [];
|
||||||
c.Protocol = PROTOCOL;
|
c.Protocol = PROTOCOL;
|
||||||
c.ParentChannel = protocolAsChannel;
|
c.ParentChannel = protocolAsChannel;
|
||||||
c.SubChannels = c.SubChannels ?? new List<Channel>();
|
c.SubChannels = c.SubChannels ?? new List<Channel>();
|
||||||
c.SendMessage = (t) => { return Task.Run(() => { client.SendMessage(channelName, t); }); };
|
c.SendMessage = (t) => { return Task.Run(() => { client.SendMessage(channelName, t); }); };
|
||||||
c.SendFile = (f, t) => { throw new InvalidOperationException($"twitch cannot send files"); };
|
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.ToString());
|
||||||
|
if(selfAccountInChannel == null)
|
||||||
|
{
|
||||||
|
selfAccountInChannel = UpsertAccount(selfAccountInProtocol.Username, c);
|
||||||
|
}
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
private Channel UpsertDMChannel(string whisperWith)
|
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)
|
if (c == null)
|
||||||
{
|
{
|
||||||
c = new Channel();
|
Console.WriteLine($"couldn't find channel under protocol {PROTOCOL}, whisper with {whisperWith}");
|
||||||
_db.Channels.Add(c);
|
c = new Channel()
|
||||||
|
{
|
||||||
|
Users = []
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
c.DisplayName = $"Whisper: {whisperWith}";
|
c.DisplayName = $"Whisper: {whisperWith}";
|
||||||
c.ExternalId = $"w_{whisperWith}";
|
c.ExternalId = $"w_{whisperWith}";
|
||||||
c.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
c.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
||||||
c.Messages = c.Messages ?? new List<Message>();
|
c.Messages ??= [];
|
||||||
c.Protocol = PROTOCOL;
|
c.Protocol = PROTOCOL;
|
||||||
c.ParentChannel = protocolAsChannel;
|
c.ParentChannel = protocolAsChannel;
|
||||||
c.SubChannels = c.SubChannels ?? new List<Channel>();
|
c.SubChannels = c.SubChannels ?? new List<Channel>();
|
||||||
@ -229,50 +245,59 @@ public class TwitchInterface
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
c.SendFile = (f, t) => { throw new InvalidOperationException($"twitch cannot send files"); };
|
c.SendFile = (f, t) => { throw new InvalidOperationException($"twitch cannot send files"); };
|
||||||
return c;
|
c = Rememberer.RememberChannel(c);
|
||||||
}
|
|
||||||
|
|
||||||
private Message UpsertMessage(ChatMessage chatMessage)
|
var selfAccountInChannel = c.Users.FirstOrDefault(a => a.ExternalId == selfAccountInProtocol.ExternalId.ToString());
|
||||||
{
|
if(selfAccountInChannel == null)
|
||||||
var m = _db.Messages.FirstOrDefault(mi => mi.ExternalId == chatMessage.Id);
|
|
||||||
if (m == null)
|
|
||||||
{
|
{
|
||||||
m = new Message();
|
selfAccountInChannel = UpsertAccount(selfAccountInChannel.Username, c);
|
||||||
m.Protocol = PROTOCOL;
|
|
||||||
_db.Messages.Add(m);
|
|
||||||
m.Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.Content = chatMessage.Message;
|
||||||
m.ExternalId = chatMessage.Id;
|
m.ExternalId = chatMessage.Id;
|
||||||
m.Channel = UpsertChannel(chatMessage.Channel);
|
m.Channel = UpsertChannel(chatMessage.Channel);
|
||||||
m.Author = UpsertAccount(chatMessage.Username, m.Channel.Id);
|
m.Author = UpsertAccount(chatMessage.Username, m.Channel); //TODO: m.channel, instead, for consistency
|
||||||
m.Author.SeenInChannel = m.Channel;
|
m.Author.SeenInChannel = m.Channel;//TODO: should be handled in UpsertAccount
|
||||||
|
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.Reply = (t) => { return Task.Run(() => { client.SendReply(chatMessage.Channel, chatMessage.Id, t); }); };
|
||||||
m.React = (e) => { throw new InvalidOperationException($"twitch cannot react"); };
|
m.React = (e) => { throw new InvalidOperationException($"twitch cannot react"); };
|
||||||
|
Rememberer.RememberMessage(m);
|
||||||
return 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)
|
private Message UpsertMessage(WhisperMessage whisperMessage)
|
||||||
{
|
{
|
||||||
var m = _db.Messages.FirstOrDefault(mi => mi.ExternalId == whisperMessage.MessageId);
|
//WhisperMessage.Id corresponds to chatMessage.Id. \*eye twitch*
|
||||||
if (m == null)
|
var m = Rememberer.SearchMessage(mi => mi.ExternalId == whisperMessage.MessageId && mi.Protocol == PROTOCOL)
|
||||||
{
|
?? new()
|
||||||
m = new Message();
|
{
|
||||||
m.Protocol = PROTOCOL;
|
Protocol = PROTOCOL,
|
||||||
_db.Messages.Add(m);
|
Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)
|
||||||
m.Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
m.Content = whisperMessage.Message;
|
m.Content = whisperMessage.Message;
|
||||||
m.ExternalId = whisperMessage.MessageId;
|
m.ExternalId = whisperMessage.MessageId;
|
||||||
m.Channel = UpsertDMChannel(whisperMessage.Username);
|
m.Channel = UpsertDMChannel(whisperMessage.Username);
|
||||||
m.Channel.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
|
m.Author = UpsertAccount(whisperMessage.Username, m.Channel);
|
||||||
m.Author = UpsertAccount(whisperMessage.Username, m.Channel.Id);
|
m.MentionsMe = Regex.IsMatch(m.Content?.ToLower(), $"\\b@{selfAccountInProtocol.Username.ToLower()}\\b");
|
||||||
m.Author.SeenInChannel = m.Channel;
|
|
||||||
|
|
||||||
m.Reply = (t) => { return Task.Run(() => { client.SendWhisper(whisperMessage.Username, t); }); };
|
m.Reply = (t) => { return Task.Run(() => { client.SendWhisper(whisperMessage.Username, t); }); };
|
||||||
m.React = (e) => { throw new InvalidOperationException($"twitch cannot react"); };
|
m.React = (e) => { throw new InvalidOperationException($"twitch cannot react"); };
|
||||||
|
Rememberer.RememberMessage(m);
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,105 +6,160 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
public static class Rememberer
|
public static class Rememberer
|
||||||
{
|
{
|
||||||
|
private static readonly SemaphoreSlim dbAccessSemaphore = new(1, 1);
|
||||||
private static readonly ChattingContext db = new();
|
private static readonly ChattingContext db = new();
|
||||||
|
|
||||||
public static Account SearchAccount(Expression<Func<Account, bool>> predicate)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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.Update(toRemember.IsUser);
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
dbAccessSemaphore.Release();
|
||||||
}
|
}
|
||||||
public static void RememberAttachment(Attachment toRemember)
|
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.Update(toRemember.Message);
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
dbAccessSemaphore.Release();
|
||||||
}
|
}
|
||||||
public static Channel RememberChannel(Channel toRemember)
|
public static Channel RememberChannel(Channel toRemember)
|
||||||
{
|
{
|
||||||
|
dbAccessSemaphore.Wait();
|
||||||
db.Update(toRemember);
|
db.Update(toRemember);
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
dbAccessSemaphore.Release();
|
||||||
return toRemember;
|
return toRemember;
|
||||||
}
|
}
|
||||||
public static void RememberMessage(Message toRemember)
|
public static void RememberMessage(Message toRemember)
|
||||||
{
|
{
|
||||||
toRemember.Channel ??= new (){ Messages = [toRemember] };
|
dbAccessSemaphore.Wait();
|
||||||
|
toRemember.Channel ??= new() { Messages = [toRemember] };
|
||||||
db.Update(toRemember.Channel);
|
db.Update(toRemember.Channel);
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
dbAccessSemaphore.Release();
|
||||||
}
|
}
|
||||||
public static void RememberUser(User toRemember)
|
public static void RememberUser(User toRemember)
|
||||||
{
|
{
|
||||||
|
dbAccessSemaphore.Wait();
|
||||||
db.Users.Update(toRemember);
|
db.Users.Update(toRemember);
|
||||||
|
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
dbAccessSemaphore.Release();
|
||||||
}
|
}
|
||||||
public static void ForgetAccount(Account toForget)
|
public static void ForgetAccount(Account toForget)
|
||||||
{
|
{
|
||||||
var user = toForget.IsUser;
|
var user = toForget.IsUser;
|
||||||
var usersOnlyAccount = user.Accounts?.Count == 1;
|
var usersOnlyAccount = user.Accounts?.Count == 1;
|
||||||
|
|
||||||
if(usersOnlyAccount)
|
if (usersOnlyAccount)
|
||||||
{
|
{
|
||||||
Rememberer.ForgetUser(user);
|
Rememberer.ForgetUser(user);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
dbAccessSemaphore.Wait();
|
||||||
db.Accounts.Remove(toForget);
|
db.Accounts.Remove(toForget);
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
dbAccessSemaphore.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static void ForgetChannel(Channel toForget)
|
public static void ForgetChannel(Channel toForget)
|
||||||
{
|
{
|
||||||
|
dbAccessSemaphore.Wait();
|
||||||
db.Channels.Remove(toForget);
|
db.Channels.Remove(toForget);
|
||||||
|
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
dbAccessSemaphore.Release();
|
||||||
}
|
}
|
||||||
public static void ForgetUser(User toForget)
|
public static void ForgetUser(User toForget)
|
||||||
{
|
{
|
||||||
|
dbAccessSemaphore.Wait();
|
||||||
db.Users.Remove(toForget);
|
db.Users.Remove(toForget);
|
||||||
|
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
dbAccessSemaphore.Release();
|
||||||
}
|
}
|
||||||
public static List<Account> AccountsOverview()
|
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()
|
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)
|
public static Channel ChannelDetail(Guid Id)
|
||||||
{
|
{
|
||||||
return db.Channels.Find(Id);
|
Channel toReturn;
|
||||||
// .Include(u => u.SubChannels)
|
dbAccessSemaphore.Wait();
|
||||||
// .Include(u => u.Users)
|
toReturn = db.Channels.Find(Id);
|
||||||
// .Include(u => u.ParentChannel);
|
dbAccessSemaphore.Release();
|
||||||
|
return toReturn;
|
||||||
|
// .Include(u => u.SubChannels)
|
||||||
|
// .Include(u => u.Users)
|
||||||
|
// .Include(u => u.ParentChannel);
|
||||||
}
|
}
|
||||||
public static List<User> UsersOverview()
|
public static List<User> UsersOverview()
|
||||||
{
|
{
|
||||||
return db.Users.ToList();
|
List<User> toReturn;
|
||||||
|
dbAccessSemaphore.Wait();
|
||||||
|
toReturn = db.Users.ToList();
|
||||||
|
dbAccessSemaphore.Release();
|
||||||
|
return toReturn;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,6 @@ using System.Diagnostics;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using vassago.Models;
|
using vassago.Models;
|
||||||
using vassago.ProtocolInterfaces.DiscordInterface;
|
|
||||||
|
|
||||||
namespace vassago.Controllers.api;
|
namespace vassago.Controllers.api;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user