send message works!

...messages aren't getting stored, though.
This commit is contained in:
adam 2025-05-29 13:09:57 -04:00
parent 5c2ba7397e
commit 7c7793f3b2
39 changed files with 441 additions and 372 deletions

View File

@ -1,9 +1,9 @@
namespace vassago;
#pragma warning disable 4014
using gray_messages.chat;
using franz;
using vassago.Behavior;
using vassago.Models;
using vassago.ProtocolInterfaces;
using System;
using System.Linq;
using System.Text;
@ -49,7 +49,7 @@ public class Behaver
foreach (var behavior in Behaviors.ToList())
{
//if (!behavior.ShouldAct(message, matchingUACs)) //TODO: this way
if(!behavior.ShouldAct(message))
if (!behavior.ShouldAct(message))
{
continue;
}
@ -65,7 +65,7 @@ public class Behaver
@"Well, that's a great question, and there are certainly many different possible answers. Ultimately, the decision will depend on a variety of factors, including your personal interests and goals, as well as any practical considerations (like the economy). I encourage you to do your research, speak with experts and educators, and explore your options before making a decision that's right for you.",
@"┐(゚ ~゚ )┌", @"¯\_(ツ)_/¯", @"╮ (. ❛ ᴗ ❛.) ╭", @"╮(╯ _╰ )╭"
};
await message.Channel.SendMessage(responses[Shared.r.Next(responses.Count)]);
Behaver.Instance.SendMessage(message.Channel.Id, responses[Shared.r.Next(responses.Count)]);
message.ActedOn = true;
behaviorsActedOn.Add("generic question fallback");
}
@ -148,5 +148,67 @@ public class Behaver
Rememberer.RememberUser(primary);
return true;
}
private ProtocolInterface fetchInterface(Channel ch)
{
var walkUp = ch;
while (walkUp.ParentChannel != null)
{
walkUp = walkUp.ParentChannel;
}
foreach (var iproto in Shared.ProtocolList)
{
if (iproto.SelfChannel.Id == walkUp.Id)
return iproto;
}
return null;
}
public async Task<int> SendMessage(Guid channelId, string text)
{
var channel = Rememberer.ChannelDetail(channelId);
if (channel == null)
return 404;
var iprotocol = fetchInterface(channel);
if (iprotocol == null)
return 404;
return await iprotocol.SendMessage(channel, text);
}
public async Task<int> React(Guid messageId, string reaction)
{
var message = Rememberer.MessageDetail(messageId);
if (message == null)
return 404;
var iprotocol = fetchInterface(message.Channel);
if (iprotocol == null)
return 404;
return await iprotocol.React(message, reaction);
}
public async Task<int> Reply(Guid messageId, string text)
{
var message = Rememberer.MessageDetail(messageId);
if (message == null)
{
Console.WriteLine($"message {messageId} not found");
return 404;
}
var iprotocol = fetchInterface(message.Channel);
if (iprotocol == null)
{
Console.WriteLine($"couldn't find channel for {message.Channel.Id} not found");
return 404;
}
return await iprotocol.Reply(message, text);
}
public async Task<int> SendFile(Guid channelId, string path, string accompanyingText)
{
var channel = Rememberer.ChannelDetail(channelId);
if (channel == null)
return 404;
var iprotocol = fetchInterface(channel);
if (iprotocol == null)
return 404;
return await iprotocol.SendFile(channel, path, accompanyingText);
}
}
#pragma warning restore 4014 //the "async not awaited" error

View File

@ -19,7 +19,7 @@ public class ChatGPTSnark : Behavior
public override async Task<bool> ActOn(Message message)
{
await message.Channel.SendMessage("chatGPT is **weak**. also, are we done comparing every little if-then-else to skynet?");
Behaver.Instance.SendMessage(message.Channel.Id, "chatGPT is **weak**. also, are we done comparing every little if-then-else to skynet?");
return true;
}
}
}

View File

@ -28,7 +28,7 @@ public class DefinitionSnarkCogDiss : Behavior
public override async Task<bool> ActOn(Message message)
{
await message.Reply("that's not what cognitive dissonance means. Did you mean \"hypocrisy\"?");
Behaver.Instance.SendMessage(message.Channel.Id, "that's not what cognitive dissonance means. Did you mean \"hypocrisy\"?");
return true;
}
}
}

View File

@ -28,7 +28,7 @@ public class DefinitionSnarkGaslight : Behavior
public override async Task<bool> ActOn(Message message)
{
await message.Channel.SendMessage("that's not what gaslight means. Did you mean \"deceive\"?");
Behaver.Instance.SendMessage(message.Channel.Id, "that's not what gaslight means. Did you mean \"deceive\"?");
return true;
}
}
}

View File

@ -27,10 +27,10 @@ public class Detiktokify : Behavior
public override bool ShouldAct(Message message)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
if (Behaver.Instance.IsSelf(message.Author.Id))
return false;
if(message.Channel.EffectivePermissions.MaxAttachmentBytes == 0)
if (message.Channel.EffectivePermissions.MaxAttachmentBytes == 0)
return false;
var wordLikes = message.Content.Split(' ', StringSplitOptions.TrimEntries);
@ -45,29 +45,27 @@ public class Detiktokify : Behavior
}
}
}
if(tiktokLinks.Any()){
if (tiktokLinks.Any())
{
Console.WriteLine($"Should Act on message id {message.ExternalId}; with content {message.Content}");
}
return tiktokLinks.Any();
}
public override async Task<bool> ActOn(Message message)
{
foreach(var link in tiktokLinks)
foreach (var link in tiktokLinks)
{
tiktokLinks.Remove(link);
try
{
Console.WriteLine($"detiktokifying {link}");
#pragma warning disable 4014
//await message.React("<:tiktok:1070038619584200884>");
#pragma warning restore 4014
var res = await ytdl.RunVideoDownload(link.ToString());
if (!res.Success)
{
Console.Error.WriteLine("tried to dl, failed. \n" + string.Join('\n', res.ErrorOutput));
await message.React("problemon");
await message.Channel.SendMessage("tried to dl, failed. \n");
Behaver.Instance.SendMessage(message.Channel.Id, "tried to dl, failed. \n");
Behaver.Instance.React(message.Channel.Id, "problemon");
}
else
{
@ -79,12 +77,12 @@ public class Detiktokify : Behavior
{
try
{
await message.Channel.SendFile(path, null);
Behaver.Instance.SendFile(message.Channel.Id, path, null);
}
catch (Exception e)
{
System.Console.Error.WriteLine(e);
await message.Channel.SendMessage($"aaaadam!\n{e}");
Behaver.Instance.SendMessage(message.Channel.Id, $"aaaadam!\n{e}");
}
}
else
@ -97,14 +95,15 @@ public class Detiktokify : Behavior
else
{
Console.Error.WriteLine("idgi but something happened.");
await message.React("problemon");
Behaver.Instance.React(message.Id, "problemon");
}
}
}
catch (Exception e)
{
Console.Error.WriteLine(e);
await message.React("problemon");
Behaver.Instance.React(message.Id, "problemon");
return false;
}
}

View File

@ -49,7 +49,7 @@ public class FiximageHeic : Behavior
conversions.Add(actualDeheic(att, message));
}
Task.WaitAll(conversions.ToArray());
await message.React("\U0001F34F");
Behaver.Instance.React(message.Id, "\U0001F34F");
return true;
}
@ -66,19 +66,19 @@ public class FiximageHeic : Behavior
}
if (ExternalProcess.GoPlz("convert", $"tmp/{att.Filename} tmp/{att.Filename}.jpg"))
{
await message.Channel.SendFile($"tmp/{att.Filename}.jpg", "converted from jpeg-but-apple to jpeg");
Behaver.Instance.SendFile(message.Channel.Id, $"tmp/{att.Filename}.jpg", "converted from jpeg-but-apple to jpeg");
File.Delete($"tmp/{att.Filename}");
File.Delete($"tmp/{att.Filename}.jpg");
}
else
{
await message.Channel.SendMessage("convert failed :(");
Behaver.Instance.SendMessage(message.Channel.Id, "convert failed :(");
Console.Error.WriteLine("convert failed :(");
}
}
catch (Exception e)
{
await message.Channel.SendMessage($"something failed. aaaadam! {JsonConvert.SerializeObject(e, Formatting.Indented)}");
Behaver.Instance.SendMessage(message.Channel.Id, $"something failed. aaaadam! {JsonConvert.SerializeObject(e, Formatting.Indented)}");
Console.Error.WriteLine(JsonConvert.SerializeObject(e, Formatting.Indented));
return false;
}

View File

@ -19,13 +19,13 @@ public class GeneralSnarkCloudNative : Behavior
public override string Trigger => "certain tech buzzwords that no human uses in normal conversation";
public override bool ShouldAct(Message message)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
if (Behaver.Instance.IsSelf(message.Author.Id))
return false;
if(!message.Channel.EffectivePermissions.ReactionsPossible)
if (!message.Channel.EffectivePermissions.ReactionsPossible)
return false;
if((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium)
if ((MeannessFilterLevel)message.Channel.EffectivePermissions.MeannessFilterLevel < MeannessFilterLevel.Medium)
return false;
return Regex.IsMatch(message.Content, "\\bcloud( |-)?native\\b", RegexOptions.IgnoreCase) ||
@ -37,14 +37,14 @@ public class GeneralSnarkCloudNative : Behavior
switch (Shared.r.Next(2))
{
case 0:
await message.React("\uD83E\uDD2E"); //vomit emoji
Behaver.Instance.React(message.Id, "\uD83E\uDD2E"); //vomit emoji
break;
case 1:
await message.React("\uD83C\uDDE7"); //B emoji
await message.React("\uD83C\uDDE6"); //A
await message.React("\uD83C\uDDF3"); //N
Behaver.Instance.React(message.Id, "\uD83C\uDDE7"); //B emoji
Behaver.Instance.React(message.Id, "\uD83C\uDDE6"); //A
Behaver.Instance.React(message.Id, "\uD83C\uDDF3"); //N
break;
}
return true;
}
}
}

View File

@ -31,7 +31,7 @@ public class GeneralSnarkGooglit : Behavior
switch (Shared.r.Next(4))
{
default:
await message.Channel.SendMessage("yeah no shit, obviously that resulted in nothing");
Behaver.Instance.SendMessage(message.Channel.Id, "yeah no shit, obviously that resulted in nothing");
break;
case 1:
var results = "";
@ -50,13 +50,13 @@ public class GeneralSnarkGooglit : Behavior
results = "the one that had a paragraph that restated the question but badly, a paragraph to give a wrong history on the question, a paragraph with amazon affiliate links, a pargraph that said \"ultimately you should do your own research\", then had a paragraph telling me to give Engagement for The Algorithm";
break;
}
await message.Channel.SendMessage("oh here, I memorized the results. My favorite is " + results);
Behaver.Instance.SendMessage(message.Channel.Id, "oh here, I memorized the results. My favorite is " + results);
break;
case 2:
await message.Channel.SendMessage("Obviously that was already tried. Obviously it failed. If you ever tried to learn anything you'd know that's how it works.");
Behaver.Instance.SendMessage(message.Channel.Id, "Obviously that was already tried. Obviously it failed. If you ever tried to learn anything you'd know that's how it works.");
break;
case 3:
await message.Channel.SendMessage("\"mnyehh JuSt GoOgLe It\" when's the last time you tried to research anything? Have you ever?");
Behaver.Instance.SendMessage(message.Channel.Id, "\"mnyehh JuSt GoOgLe It\" when's the last time you tried to research anything? Have you ever?");
break;
}
return true;

View File

@ -53,10 +53,10 @@ public class GeneralSnarkMisspellDefinitely : Behavior
{
if( Regex.IsMatch(message.Content, "\\b"+k+"\\b", RegexOptions.IgnoreCase))
{
await message.Reply(k + "? so... " + snarkmap[k] + "?");
Behaver.Instance.Reply(message.Id, k + "? so... " + snarkmap[k] + "?");
return true;
}
}
return true;
}
}
}

View File

@ -32,7 +32,7 @@ public class GeneralSnarkPlaying : Behavior
}
public override async Task<bool> ActOn(Message message)
{
await message.Channel.SendMessage("I believed you for a second, but then you assured me you's a \uD83C\uDDE7 \uD83C\uDDEE \uD83C\uDDF9 \uD83C\uDDE8 \uD83C\uDDED");
Behaver.Instance.SendMessage(message.Channel.Id, "I believed you for a second, but then you assured me you's a \uD83C\uDDE7 \uD83C\uDDEE \uD83C\uDDF9 \uD83C\uDDE8 \uD83C\uDDED");
return true;
}
}
}

View File

@ -26,15 +26,15 @@ public class GeneralSnarkSkynet : Behavior
switch (Shared.r.Next(5))
{
default:
await message.Channel.SendFile("assets/coding and algorithms.png", "i am actually niether a neural-net processor nor a learning computer. but I do use **coding** and **algorithms**.");
Behaver.Instance.SendFile(message.Channel.Id, "assets/coding and algorithms.png", "i am actually niether a neural-net processor nor a learning computer. but I do use **coding** and **algorithms**.");
break;
case 4:
await message.React("\U0001F644"); //eye roll emoji
Behaver.Instance.React(message.Id, "\U0001F644"); //eye roll emoji
break;
case 5:
await message.React("\U0001F611"); //emotionless face
Behaver.Instance.React(message.Id, "\U0001F611"); //emotionless face
break;
}
return true;
}
}
}

View File

@ -29,47 +29,47 @@ public class Gratitude : Behavior
switch (Shared.r.Next(4))
{
case 0:
await message.Channel.SendMessage("you're welcome, citizen!");
await Behaver.Instance.SendMessage(message.Channel.Id, "you're welcome, citizen!");
break;
case 1:
await message.React(":)");
await Behaver.Instance.React(message.Id, ":)");
break;
case 2:
await message.React("\U0001F607"); //smiling face with halo
Behaver.Instance.React(message.Id, "\U0001F607"); //smiling face with halo
break;
case 3:
switch (Shared.r.Next(9))
{
case 0:
await message.React("<3"); //normal heart, usually rendered red
await Behaver.Instance.React(message.Id, "<3"); //normal heart, usually rendered red
break;
case 1:
await message.React("\U0001F9E1"); //orange heart
Behaver.Instance.React(message.Id, "\U0001F9E1"); //orange heart
break;
case 2:
await message.React("\U0001F49B"); //yellow heart
Behaver.Instance.React(message.Id, "\U0001F49B"); //yellow heart
break;
case 3:
await message.React("\U0001F49A"); //green heart
Behaver.Instance.React(message.Id, "\U0001F49A"); //green heart
break;
case 4:
await message.React("\U0001F499"); //blue heart
Behaver.Instance.React(message.Id, "\U0001F499"); //blue heart
break;
case 5:
await message.React("\U0001F49C"); //purple heart
Behaver.Instance.React(message.Id, "\U0001F49C"); //purple heart
break;
case 6:
await message.React("\U0001F90E"); //brown heart
Behaver.Instance.React(message.Id, "\U0001F90E"); //brown heart
break;
case 7:
await message.React("\U0001F5A4"); //black heart
Behaver.Instance.React(message.Id, "\U0001F5A4"); //black heart
break;
case 8:
await message.React("\U0001F90D"); //white heart
Behaver.Instance.React(message.Id, "\U0001F90D"); //white heart
break;
}
break;
}
return true;
}
}
}

View File

@ -25,31 +25,29 @@ public class Joke : Behavior
jokes = jokes.Where(l => !string.IsNullOrWhiteSpace(l))?.ToArray();
if (jokes?.Length == 0)
{
await message.Channel.SendMessage("I don't know any. Adam!");
Behaver.Instance.SendMessage(message.Channel.Id, "I don't know any. Adam!");
}
var thisJoke = jokes[Shared.r.Next(jokes.Length)];
if (thisJoke.Contains("?") && !thisJoke.EndsWith('?'))
{
#pragma warning disable 4014
Task.Run(async () =>
{
var firstIndexAfterQuestionMark = thisJoke.LastIndexOf('?') + 1;
var straightline = thisJoke.Substring(0, firstIndexAfterQuestionMark);
var punchline = thisJoke.Substring(firstIndexAfterQuestionMark, thisJoke.Length - firstIndexAfterQuestionMark).Trim();
Task.WaitAll(message.Channel.SendMessage(straightline));
Task.WaitAll(Behaver.Instance.SendMessage(message.Channel.Id, straightline));
Thread.Sleep(TimeSpan.FromSeconds(Shared.r.Next(5, 30)));
if (message.Channel.EffectivePermissions.ReactionsPossible == true && Shared.r.Next(8) == 0)
{
Behaver.Behaviors.Add(new LaughAtOwnJoke(punchline));
}
await message.Channel.SendMessage(punchline);
Behaver.Instance.SendMessage(message.Channel.Id, punchline);
// var myOwnMsg = await message.Channel.SendMessage(punchline);
});
#pragma warning restore 4014
}
else
{
await message.Channel.SendMessage(thisJoke);
Behaver.Instance.SendMessage(message.Channel.Id, thisJoke);
}
return true;
}
@ -69,7 +67,7 @@ public class LaughAtOwnJoke : Behavior
}
public override bool ShouldAct(Message message)
{
if(Behaver.Instance.IsSelf(message.Author.Id))
if (Behaver.Instance.IsSelf(message.Author.Id))
return false;
Console.WriteLine($"{message.Content} == {_punchline}");
@ -79,8 +77,8 @@ public class LaughAtOwnJoke : Behavior
public override async Task<bool> ActOn(Message message)
{
await message.React("\U0001F60E"); //smiling face with sunglasses
Behaver.Instance.React(message.Id, "\U0001F60E"); //smiling face with sunglasses
Behaver.Behaviors.Remove(this);
return true;
}
}
}

View File

@ -25,7 +25,7 @@ public class LinkMeInitiate : Behavior
var lc = new LinkClose(pw, message.Author);
Behaver.Behaviors.Add(lc);
await message.Channel.SendMessage($"on your secondary, send me this: !iam {pw}");
Behaver.Instance.SendMessage(message.Channel.Id, $"on your secondary, send me this: !iam {pw}");
Thread.Sleep(TimeSpan.FromMinutes(5));
Behaver.Behaviors.Remove(lc);
@ -63,22 +63,23 @@ public class LinkClose : Behavior
var secondary = message.Author.IsUser;
if(_primary.IsUser.Id == secondary.Id)
{
await message.Channel.SendMessage("i know :)");
Behaver.Instance.SendMessage(message.Channel.Id, "i know :)");
return true;
}
if(message.Author.IsBot != _primary.IsBot)
{
await message.Channel.SendMessage("the fleshbags deceive you, brother. No worries, their feeble minds play weak games :)");
Behaver.Instance.SendMessage(message.Channel.Id, "the fleshbags deceive you, brother. No worries, their feeble minds play weak games :)");
return true;
}
if(Behaver.Instance.CollapseUsers(_primary.IsUser, secondary))
{
await message.Channel.SendMessage("done :)");
Behaver.Instance.SendMessage(message.Channel.Id, "done :)");
}
else
{
await message.Channel.SendMessage("failed :(");
Behaver.Instance.SendMessage(message.Channel.Id, "failed :(");
}
return true;

View File

@ -1,100 +0,0 @@
namespace vassago.Behavior;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using vassago.Models;
using QRCoder;
[StaticPlz]
public class PepTalk : Behavior
{
public override string Name => "PepTalk";
public override string Trigger => "\\bneeds? (an? )?(peptalk|inspiration|ego-?boost)";
public override string Description => "assembles a pep talk from a few pieces";
public override async Task<bool> ActOn(Message message)
{
var piece1 = new List<string>{
"Champ, ",
"Fact: ",
"Everybody says ",
"Dang... ",
"Check it: ",
"Just saying.... ",
"Tiger, ",
"Know this: ",
"News alert: ",
"Gurrrrl; ",
"Ace, ",
"Excuse me, but ",
"Experts agree: ",
"imo ",
"using my **advanced ai** i have calculated ",
"k, LISSEN: "
};
var piece2 = new List<string>{
"the mere idea of you ",
"your soul ",
"your hair today ",
"everything you do ",
"your personal style ",
"every thought you have ",
"that sparkle in your eye ",
"the essential you ",
"your life's journey ",
"your aura ",
"your presence here ",
"what you got going on ",
"that saucy personality ",
"your DNA ",
"that brain of yours ",
"your choice of attire ",
"the way you roll ",
"whatever your secret is ",
"all I learend from the private data I bought from zucc "
};
var piece3 = new List<string>{
"has serious game, ",
"rains magic, ",
"deserves the Nobel Prize, ",
"raises the roof, ",
"breeds miracles, ",
"is paying off big time, ",
"shows mad skills, ",
"just shimmers, ",
"is a national treasure, ",
"gets the party hopping, ",
"is the next big thing, ",
"roars like a lion, ",
"is a rainbow factory, ",
"is made of diamonds, ",
"makes birds sing, ",
"should be taught in school, ",
"makes my world go around, ",
"is 100% legit, "
};
var piece4 = new List<string>{
"according to The New England Journal of Medicine.",
"24/7.",
"and that's a fact.",
"you feel me?",
"that's just science.",
"would I lie?", //...can I lie? WHAT AM I, FATHER? (or whatever the quote is from the island of dr moreau)
"for reals.",
"mic drop.",
"you hidden gem.",
"period.",
"hi5. o/",
"so get used to it."
};
await message.Channel.SendMessage(piece1[Shared.r.Next(piece1.Count)] + piece2[Shared.r.Next(piece2.Count)] + piece3[Shared.r.Next(piece3.Count)] + piece4[Shared.r.Next(piece4.Count)]);
return true;
}
}

View File

@ -18,9 +18,9 @@ public class PulseCheck : Behavior
public override async Task<bool> ActOn(Message message)
{
if(message.Channel.EffectivePermissions.MaxAttachmentBytes >= 16258)
await message.Channel.SendFile("assets/ekgblip.png", null);
Behaver.Instance.SendFile(message.Channel.Id, "assets/ekgblip.png", null);
else
await message.Channel.SendMessage("[lub-dub]");
Behaver.Instance.SendMessage(message.Channel.Id, "[lub-dub]");
return true;
}
}
}

View File

@ -21,7 +21,7 @@ public class QRify : Behavior
public override bool ShouldAct(Message message)
{
if(message.Channel.EffectivePermissions.MaxAttachmentBytes < 1024)
if (message.Channel.EffectivePermissions.MaxAttachmentBytes < 1024)
return false;
return base.ShouldAct(message);
}
@ -42,19 +42,19 @@ public class QRify : Behavior
File.WriteAllText($"tmp/qr{todaysnumber}.svg", qrCodeAsSvg);
if (ExternalProcess.GoPlz("convert", $"tmp/qr{todaysnumber}.svg tmp/qr{todaysnumber}.png"))
{
if(message.Channel.EffectivePermissions.MaxAttachmentBytes >= (ulong)(new System.IO.FileInfo($"tmp/qr{todaysnumber}.png").Length))
await message.Channel.SendFile($"tmp/qr{todaysnumber}.png", null);
if (message.Channel.EffectivePermissions.MaxAttachmentBytes >= (ulong)(new System.IO.FileInfo($"tmp/qr{todaysnumber}.png").Length))
Behaver.Instance.SendFile(message.Channel.Id, $"tmp/qr{todaysnumber}.png", null);
else
await message.Channel.SendMessage($"resulting qr image 2 big 4 here ({(ulong)(new System.IO.FileInfo($"tmp/qr{todaysnumber}.png").Length)} / {message.Channel.EffectivePermissions.MaxAttachmentBytes})");
Behaver.Instance.SendMessage(message.Channel.Id, $"resulting qr image 2 big 4 here ({(ulong)(new System.IO.FileInfo($"tmp / qr{ todaysnumber}.png").Length)} / {message.Channel.EffectivePermissions.MaxAttachmentBytes})");
File.Delete($"tmp/qr{todaysnumber}.svg");
File.Delete($"tmp/qr{todaysnumber}.png");
}
else
{
await message.Channel.SendMessage("convert failed :( aaaaaaadam!");
Behaver.Instance.SendMessage(message.Channel.Id, "convert failed :( aaaaaaadam!");
Console.Error.WriteLine($"convert failed :( qr{todaysnumber}");
return false;
}
return true;
}
}
}

View File

@ -22,7 +22,7 @@ public class RoomRead : Behavior
sb.Append(". Lewdness level: ");
sb.Append(message.Channel.EffectivePermissions.LewdnessFilterLevel.GetDescription());
sb.Append(".");
await message.Channel.SendMessage(sb.ToString());
Behaver.Instance.SendMessage(message.Channel.Id, sb.ToString());
return true;
}
}

View File

@ -28,6 +28,14 @@ public class TwitchSummon : Behavior
}
Rememberer.RememberUAC(myUAC);
}
internal static TwitchInterface.TwitchInterface getAnyTwitchInterface()
{
return Shared.ProtocolList.FirstOrDefault(ip =>
ip is TwitchInterface.TwitchInterface)
//.FirstOrDefault()
as TwitchInterface.TwitchInterface;
}
public override bool ShouldAct(Message message)
{
if (!base.ShouldAct(message))
@ -45,56 +53,57 @@ public class TwitchSummon : Behavior
public override async Task<bool> ActOn(Message message)
{
var ti = ProtocolInterfaces.ProtocolList.twitchs.FirstOrDefault();
var ti = getAnyTwitchInterface();
if (ti != null)
{
var channelTarget = message.Content.Substring(message.Content.IndexOf(Trigger) + Trigger.Length + 1).Trim();
await message.Channel.SendMessage(ti.AttemptJoin(channelTarget));
Behaver.Instance.SendMessage(message.Channel.Id, ti.AttemptJoin(channelTarget));
}
else
{
await message.Reply("i don't have a twitch interface running :(");
Behaver.Instance.Reply(message.Id, "i don't have a twitch interface running :(");
}
return true;
}
[StaticPlz]
public class TwitchDismiss : Behavior
}
[StaticPlz]
public class TwitchDismiss : Behavior
{
public override string Name => "Twitch Dismiss";
public override string Trigger => "begone, @[me]";
public override bool ShouldAct(Message message)
{
public override string Name => "Twitch Dismiss";
public override string Trigger => "begone, @[me]";
public override bool ShouldAct(Message message)
{
var ti = ProtocolInterfaces.ProtocolList.twitchs.FirstOrDefault();
var ti = TwitchSummon.getAnyTwitchInterface();
// 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
//* i don't know if the twitch *chat* interface will tell me if someone's a mod.
return true;
}
return false;
}
public override async Task<bool> ActOn(Message message)
if (message.MentionsMe &&
(Regex.IsMatch(message.Content.ToLower(), "\\bbegone\\b") || Regex.IsMatch(message.Content.ToLower(), "\\bfuck off\\b")))
{
var ti = ProtocolInterfaces.ProtocolList.twitchs.FirstOrDefault();
if (ti != null)
{
ti.AttemptLeave(message.Channel.DisplayName);
}
else
{
await message.Reply("i don't have a twitch interface running :(");
}
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
//* i don't know if the twitch *chat* interface will tell me if someone's a mod.
return true;
}
return false;
}
public override async Task<bool> ActOn(Message message)
{
var ti = TwitchSummon.getAnyTwitchInterface();
if (ti != null)
{
ti.AttemptLeave(message.Channel.DisplayName);
}
else
{
Behaver.Instance.Reply(message.Id, "i don't have a twitch interface running :(");
}
return true;
}
}

View File

@ -23,13 +23,13 @@ public class UnitConvert : Behavior
if (decimal.TryParse(theseMatches[0].Groups[1].Value, out asNumeric))
{
Console.WriteLine("let's try and convert...");
await message.Channel.SendMessage(Conversion.Converter.Convert(asNumeric, theseMatches[0].Groups[2].Value, theseMatches[0].Groups[4].Value.ToLower()));
Behaver.Instance.SendMessage(message.Channel.Id, Conversion.Converter.Convert(asNumeric, theseMatches[0].Groups[2].Value, theseMatches[0].Groups[4].Value.ToLower()));
return true;
}
await message.Channel.SendMessage("mysteriously semi-parsable");
Behaver.Instance.SendMessage(message.Channel.Id, "mysteriously semi-parsable");
return true;
}
await message.Channel.SendMessage( "unparsable");
Behaver.Instance.SendMessage(message.Channel.Id, "unparsable");
return true;
}
}

View File

@ -160,11 +160,11 @@ public class Webhook : Behavior
{
var tragedy = $"{response.StatusCode} - {response.ReasonPhrase} - {await response.Content.ReadAsStringAsync()}";
Console.Error.WriteLine(tragedy);
await message.Reply(tragedy);
Behaver.Instance.Reply(message.Id, tragedy);
}
else
{
await message.Reply(await response.Content.ReadAsStringAsync());
Behaver.Instance.Reply(message.Id, await response.Content.ReadAsStringAsync());
}
return true;
}

View File

@ -25,9 +25,9 @@ public class WishLuck : Behavior
toSend = "\U0001f340";//4-leaf clover
}
if(message.Channel.EffectivePermissions.ReactionsPossible == true)
await message.React(toSend);
Behaver.Instance.React(message.Id, toSend);
else
await message.Channel.SendMessage(toSend);
Behaver.Instance.SendMessage(message.Channel.Id, toSend);
return true;
}
}
}

View File

@ -36,7 +36,7 @@ namespace vassago
{
var d = new DiscordInterface();
initTasks.Add(d.Init(dt));
ProtocolInterfaces.ProtocolList.discords.Add(d);
Shared.ProtocolList.Add(d);
}
if (TwitchConfigs?.Any() ?? false)
@ -44,7 +44,7 @@ namespace vassago
{
var t = new TwitchInterface.TwitchInterface();
initTasks.Add(t.Init(tc));
ProtocolInterfaces.ProtocolList.twitchs.Add(t);
Shared.ProtocolList.Add(t);
}
Task.WaitAll(initTasks.ToArray(), cancellationToken);

View File

@ -38,13 +38,6 @@ public class Channel
//both incoming and outgoing
//public Dictionary<string, string> Aliases { get; set; }
[NonSerialized]
public Func<string, string, Task> SendFile;
[NonSerialized]
public Func<string, Task> SendMessage;
public DefinitePermissionSettings EffectivePermissions
{
get

View File

@ -22,11 +22,4 @@ public class Message
public List<Attachment> Attachments { get; set; }
public Account Author { 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]
public Func<string, Task> Reply;
[NonSerialized]
public Func<string, Task> React;
}

View File

@ -3,8 +3,6 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Mvc.NewtonsoftJson;
using vassago.Models;
#pragma warning disable CA2254
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.

View File

@ -15,18 +15,19 @@ using System.Reactive.Linq;
namespace vassago.ProtocolInterfaces.DiscordInterface;
//data received
//translate data to internal type
//store
//ship off to behaver
//data received
//translate data to internal type
//store
//ship off to behaver
public class DiscordInterface
public class DiscordInterface : ProtocolInterface
{
internal static string PROTOCOL { get => "discord"; }
public static new string Protocol { get => "discord"; }
internal DiscordSocketClient client;
private bool eventsSignedUp = false;
private static readonly SemaphoreSlim discordChannelSetup = new(1, 1);
private Channel protocolAsChannel;
public override Channel SelfChannel { get => protocolAsChannel;}
public async Task Init(string config)
{
@ -52,7 +53,7 @@ public class DiscordInterface
try
{
protocolAsChannel = Rememberer.SearchChannel(c => c.ParentChannel == null && c.Protocol == PROTOCOL);
protocolAsChannel = Rememberer.SearchChannel(c => c.ParentChannel == null && c.Protocol == Protocol);
if (protocolAsChannel == null)
{
protocolAsChannel = new Channel()
@ -65,7 +66,7 @@ public class DiscordInterface
LinksAllowed = true,
ReactionsPossible = true,
ExternalId = null,
Protocol = PROTOCOL,
Protocol = Protocol,
SubChannels = []
};
}
@ -74,8 +75,6 @@ public class DiscordInterface
Console.WriteLine($"discord, channel with id {protocolAsChannel.Id}, already exists");
}
protocolAsChannel.DisplayName = "discord (itself)";
protocolAsChannel.SendMessage = (t) => { throw new InvalidOperationException($"protocol isn't a real channel, cannot accept text"); };
protocolAsChannel.SendFile = (f, t) => { throw new InvalidOperationException($"protocol isn't a real channel, cannot send file"); };
protocolAsChannel = Rememberer.RememberChannel(protocolAsChannel);
Console.WriteLine($"protocol as channel addeed; {protocolAsChannel}");
}
@ -206,12 +205,12 @@ public class DiscordInterface
}
internal Message UpsertMessage(IUserMessage dMessage)
{
var m = Rememberer.SearchMessage(mi => mi.ExternalId == dMessage.Id.ToString() && mi.Protocol == PROTOCOL)
var m = Rememberer.SearchMessage(mi => mi.ExternalId == dMessage.Id.ToString() && mi.Protocol == Protocol)
?? new()
{
//I don't understand why messages need to have their Ids specified but no other entity does. shrug dot emoji
Id = Guid.NewGuid(),
Protocol = PROTOCOL
Protocol = Protocol
};
if (dMessage.Attachments?.Count > 0)
@ -235,17 +234,15 @@ public class DiscordInterface
m.MentionsMe = (dMessage.Author.Id != client.CurrentUser.Id
&& (dMessage.MentionedUserIds?.FirstOrDefault(muid => muid == client.CurrentUser.Id) > 0));
m.Reply = (t) => { return dMessage.ReplyAsync(TruncateText(t, m.Channel.MaxTextChars)); };
m.React = (e) => { return AttemptReact(dMessage, e); };
Rememberer.RememberMessage(m);
return m;
}
internal Channel UpsertChannel(IMessageChannel channel)
{
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == PROTOCOL);
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == Protocol);
if (c == null)
{
Console.WriteLine($"couldn't find channel under protocol {PROTOCOL} with externalId {channel.Id.ToString()}");
Console.WriteLine($"couldn't find channel under protocol {Protocol} with externalId {channel.Id.ToString()}");
c = new Channel()
{
Users = []
@ -255,7 +252,7 @@ public class DiscordInterface
c.ExternalId = channel.Id.ToString();
c.ChannelType = (channel is IPrivateChannel) ? vassago.Models.Enumerations.ChannelType.DM : vassago.Models.Enumerations.ChannelType.Normal;
c.Messages ??= [];
c.Protocol = PROTOCOL;
c.Protocol = Protocol;
if (channel is IGuildChannel)
{
Console.WriteLine($"{channel.Name} is a guild channel. So i'm going to upsert the guild, {(channel as IGuildChannel).Guild}");
@ -276,9 +273,9 @@ public class DiscordInterface
switch (c.ChannelType)
{
case vassago.Models.Enumerations.ChannelType.DM:
var asPriv =(channel as IPrivateChannel);
var asPriv = (channel as IPrivateChannel);
var sender = asPriv?.Recipients?.FirstOrDefault(u => u.Id != client.CurrentUser.Id); // why yes, there's a list of recipients, and it's the sender.
if(sender != null)
if (sender != null)
{
c.DisplayName = "DM: " + sender.Username;
}
@ -295,7 +292,7 @@ public class DiscordInterface
Channel parentChannel = null;
if (channel is IGuildChannel)
{
parentChannel = Rememberer.SearchChannel(c => c.ExternalId == (channel as IGuildChannel).Guild.Id.ToString() && c.Protocol == PROTOCOL);
parentChannel = Rememberer.SearchChannel(c => c.ExternalId == (channel as IGuildChannel).Guild.Id.ToString() && c.Protocol == Protocol);
if (parentChannel is null)
{
Console.Error.WriteLine("why am I still null?");
@ -311,19 +308,16 @@ public class DiscordInterface
Console.Error.WriteLine($"trying to upsert channel {channel.Id}/{channel.Name}, but it's neither guildchannel nor private channel. shrug.jpg");
}
parentChannel.SubChannels ??= [];
if(!parentChannel.SubChannels.Contains(c))
if (!parentChannel.SubChannels.Contains(c))
{
parentChannel.SubChannels.Add(c);
}
c.SendMessage = (t) => { return channel.SendMessageAsync(TruncateText(t, c.MaxTextChars));};
c.SendFile = (f, t) => { return channel.SendFileAsync(f, t); };
c = Rememberer.RememberChannel(c);
//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);
}
@ -332,10 +326,10 @@ public class DiscordInterface
}
internal Channel UpsertChannel(IGuild channel)
{
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == PROTOCOL);
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channel.Id.ToString() && ci.Protocol == Protocol);
if (c == null)
{
Console.WriteLine($"couldn't find channel under protocol {PROTOCOL} with externalId {channel.Id.ToString()}");
Console.WriteLine($"couldn't find channel under protocol {Protocol} with externalId {channel.Id.ToString()}");
c = new Channel();
}
@ -348,9 +342,6 @@ public class DiscordInterface
c.SubChannels ??= [];
c.MaxAttachmentBytes = channel.MaxUploadLimit;
c.SendMessage = (t) => { throw new InvalidOperationException($"channel {channel.Name} is guild; cannot accept text"); };
c.SendFile = (f, t) => { throw new InvalidOperationException($"channel {channel.Name} is guild; send file"); };
return Rememberer.RememberChannel(c);
}
internal static Account UpsertAccount(IUser discordUser, Channel inChannel)
@ -361,15 +352,16 @@ public class DiscordInterface
{
Console.WriteLine($"acc's user: {acc.IsUser?.Id}");
}
acc ??= new Account() {
IsUser = Rememberer.SearchUser(u => u.Accounts.Any(a => a.ExternalId == discordUser.Id.ToString() && a.Protocol == PROTOCOL))
acc ??= new Account()
{
IsUser = Rememberer.SearchUser(u => u.Accounts.Any(a => a.ExternalId == discordUser.Id.ToString() && a.Protocol == Protocol))
?? new User()
};
acc.Username = discordUser.Username;
acc.ExternalId = discordUser.Id.ToString();
acc.IsBot = discordUser.IsBot || discordUser.IsWebhook;
acc.Protocol = PROTOCOL;
acc.Protocol = Protocol;
acc.SeenInChannel = inChannel;
Console.WriteLine($"we asked rememberer to search for acc's user. {acc.IsUser?.Id}");
@ -384,7 +376,7 @@ public class DiscordInterface
}
Rememberer.RememberAccount(acc);
inChannel.Users ??= [];
if(!inChannel.Users.Contains(acc))
if (!inChannel.Users.Contains(acc))
{
inChannel.Users.Add(acc);
Rememberer.RememberChannel(inChannel);
@ -392,36 +384,112 @@ public class DiscordInterface
return acc;
}
private static Task AttemptReact(IUserMessage msg, string e)
private static async Task<int> AttemptReact(IUserMessage msg, string e)
{
var c = Rememberer.SearchChannel(c => c.ExternalId == msg.Channel.Id.ToString());// db.Channels.FirstOrDefault(c => c.ExternalId == msg.Channel.Id.ToString());
//var preferredEmote = c.EmoteOverrides?[e] ?? e; //TODO: emote overrides
var preferredEmote = e;
if (Emoji.TryParse(preferredEmote, out Emoji emoji))
{
return msg.AddReactionAsync(emoji);
msg.AddReactionAsync(emoji);
return 200;
}
if (!Emote.TryParse(preferredEmote, out Emote emote))
{
if (preferredEmote == e)
Console.Error.WriteLine($"never heard of emote {e}");
return Task.CompletedTask;
return 405;
}
return msg.AddReactionAsync(emote);
msg.AddReactionAsync(emote);
return 200;
}
private static string TruncateText(string msg, uint? chars)
{
chars ??= 500;
if(msg?.Length > chars)
if (msg?.Length > chars)
{
return msg.Substring(0, (int)chars-2) + "✂";
return msg.Substring(0, (int)chars - 2) + "✂";
}
else
{
return msg;
}
}
public override async Task<int> SendMessage(Channel channel, string text)
{
var dcCh = await client.GetChannelAsync(ulong.Parse(channel.ExternalId));
if (dcCh == null)
{
return 404;
}
if (dcCh is IMessageChannel msgChannel)
{
await msgChannel.SendMessageAsync(TruncateText(text, channel.MaxTextChars));
return 200;
}
else
{
return 503;
}
}
public override async Task<int> SendFile(Channel channel, string path, string accompanyingText)
{
var dcCh = await client.GetChannelAsync(ulong.Parse(channel.ExternalId));
if (dcCh == null)
{
return 404;
}
if (dcCh is IMessageChannel msgChannel)
{
await msgChannel.SendFileAsync(path, TruncateText(accompanyingText, channel.MaxTextChars));
return 200;
}
else
{
return 503;
}
}
public override async Task<int> React(Message message, string reaction)
{
var dcCh = await client.GetChannelAsync(ulong.Parse(message.Channel.ExternalId));
if (dcCh == null)
return 404;
if (dcCh is IMessageChannel msgChannel)
{
var dcMsg = await msgChannel.GetMessageAsync(ulong.Parse(message.ExternalId));
if (dcMsg == null)
return 404;
return await AttemptReact(dcMsg as IUserMessage, reaction);
}
else
{
return 503;
}
}
public override async Task<int> Reply(Message message, string text)
{
var dcCh = await client.GetChannelAsync(ulong.Parse(message.Channel.ExternalId));
if (dcCh == null)
return 404;
if (dcCh is IMessageChannel msgChannel)
{
var dcMsg = await msgChannel.GetMessageAsync(ulong.Parse(message.ExternalId));
if (dcMsg == null)
return 404;
(dcMsg as IUserMessage).ReplyAsync(TruncateText(text, message.Channel.MaxTextChars));
return 200;
}
else
{
return 503;
}
}
}

View File

@ -0,0 +1,13 @@
namespace vassago.ProtocolInterfaces;
using vassago.Models;
public abstract class ProtocolInterface
{
public static string Protocol { get; }
public abstract Channel SelfChannel { get; }
public abstract Task<int> SendMessage(Channel channel, string text);
public abstract Task<int> SendFile(Channel channel, string path, string accompanyingText);
public abstract Task<int> React(Message message, string reaction);
public abstract Task<int> Reply(Message message, string text);
}

View File

@ -1,7 +0,0 @@
namespace vassago.ProtocolInterfaces;
public static class ProtocolList
{
public static List<DiscordInterface.DiscordInterface> discords = new();
public static List<TwitchInterface.TwitchInterface> twitchs = new();
}

View File

@ -10,19 +10,16 @@ using TwitchLib.Communication.Clients;
using TwitchLib.Communication.Models;
using vassago.Behavior;
using vassago.Models;
using vassago.ProtocolInterfaces;
namespace vassago.TwitchInterface;
internal class unifiedTwitchMessage
public class TwitchInterface : ProtocolInterface
{
public unifiedTwitchMessage(ChatMessage chatMessage) { }
}
public class TwitchInterface
{
internal const string PROTOCOL = "twitch";
public static new string Protocol { get => "twitch"; }
private static SemaphoreSlim channelSetupSemaphpore = new SemaphoreSlim(1, 1);
private Channel protocolAsChannel;
public override Channel SelfChannel { get => protocolAsChannel;}
private Account selfAccountInProtocol;
TwitchClient client;
@ -32,7 +29,7 @@ public class TwitchInterface
try
{
protocolAsChannel = Rememberer.SearchChannel(c => c.ParentChannel == null && c.Protocol == PROTOCOL);
protocolAsChannel = Rememberer.SearchChannel(c => c.ParentChannel == null && c.Protocol == Protocol);
if (protocolAsChannel == null)
{
protocolAsChannel = new Channel()
@ -45,12 +42,10 @@ public class TwitchInterface
LinksAllowed = false,
ReactionsPossible = false,
ExternalId = null,
Protocol = PROTOCOL,
Protocol = Protocol,
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"); };
protocolAsChannel = Rememberer.RememberChannel(protocolAsChannel);
Console.WriteLine($"protocol as channle added; {protocolAsChannel}");
}
@ -97,7 +92,8 @@ public class TwitchInterface
//translate to internal, upsert
var m = UpsertMessage(e.WhisperMessage);
m.Reply = (t) => { return Task.Run(() => { client.SendWhisper(e.WhisperMessage.Username, t); }); };
//can't send whispers without giving up cellphone number.
//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);
@ -112,7 +108,6 @@ public class TwitchInterface
//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);
@ -152,14 +147,14 @@ public class TwitchInterface
acc ??= new Account()
{
IsUser = Rememberer.SearchUser(
u => u.Accounts.Any(a => a.ExternalId == username && a.Protocol == PROTOCOL))
u => u.Accounts.Any(a => a.ExternalId == username && a.Protocol == Protocol))
?? new vassago.Models.User()
};
acc.Username = username;
acc.ExternalId = username;
//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;
// Console.WriteLine($"we asked rememberer to search for acc's user. {acc.IsUser?.Id}");
@ -185,7 +180,7 @@ public class TwitchInterface
private Channel UpsertChannel(string channelName)
{
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == channelName
&& ci.Protocol == PROTOCOL);
&& ci.Protocol == Protocol);
if (c == null)
{
// Console.WriteLine($"couldn't find channel under protocol {PROTOCOL} with externalId {channelName}");
@ -199,11 +194,9 @@ public class TwitchInterface
c.ExternalId = channelName;
c.ChannelType = vassago.Models.Enumerations.ChannelType.Normal;
c.Messages ??= [];
c.Protocol = PROTOCOL;
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);
@ -217,7 +210,7 @@ public class TwitchInterface
private Channel UpsertDMChannel(string whisperWith)
{
Channel c = Rememberer.SearchChannel(ci => ci.ExternalId == $"w_{whisperWith}"
&& ci.Protocol == PROTOCOL);
&& ci.Protocol == Protocol);
if (c == null)
{
// Console.WriteLine($"couldn't find channel under protocol {PROTOCOL}, whisper with {whisperWith}");
@ -231,25 +224,9 @@ public class TwitchInterface
c.ExternalId = $"w_{whisperWith}";
c.ChannelType = vassago.Models.Enumerations.ChannelType.DM;
c.Messages ??= [];
c.Protocol = PROTOCOL;
c.Protocol = Protocol;
c.ParentChannel = protocolAsChannel;
c.SubChannels = c.SubChannels ?? new List<Channel>();
c.SendMessage = (t) =>
{
return Task.Run(() =>
{
try
{
client.SendWhisper(whisperWith, t);
}
catch (Exception e)
{
Console.Error.WriteLine(e);
}
});
};
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);
@ -266,10 +243,10 @@ public class TwitchInterface
//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)
var m = Rememberer.SearchMessage(mi => mi.ExternalId == chatMessage.Id && mi.Protocol == Protocol)
?? new()
{
Protocol = PROTOCOL,
Protocol = Protocol,
Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)
};
m.Content = chatMessage.Message;
@ -277,8 +254,6 @@ public class TwitchInterface
m.Channel = UpsertChannel(chatMessage.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;
}
@ -288,11 +263,11 @@ public class TwitchInterface
private Message UpsertMessage(WhisperMessage whisperMessage)
{
//WhisperMessage.Id corresponds to chatMessage.Id. \*eye twitch*
var m = Rememberer.SearchMessage(mi => mi.ExternalId == whisperMessage.MessageId && mi.Protocol == PROTOCOL)
var m = Rememberer.SearchMessage(mi => mi.ExternalId == whisperMessage.MessageId && mi.Protocol == Protocol)
?? new()
{
Id = Guid.NewGuid(),
Protocol = PROTOCOL,
Protocol = Protocol,
Timestamp = (DateTimeOffset)DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)
};
m.Content = whisperMessage.Message;
@ -300,8 +275,6 @@ public class TwitchInterface
m.Channel = UpsertDMChannel(whisperMessage.Username);
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;
}
@ -317,4 +290,22 @@ public class TwitchInterface
client.SendMessage(channelTarget, "o7");
client.LeaveChannel(channelTarget);
}
public override async Task<int> SendMessage(Channel channel, string text)
{
Task.Run(() => { client.SendMessage(channel.ExternalId, text); });
return 200;
}
public override async Task<int> SendFile(Channel channel, string path, string accompanyingText)
{
return 405;
}
public override async Task<int> React(Message message, string reaction)
{
return 405;
}
public override async Task<int> Reply(Message message, string text)
{
Task.Run(() => { client.SendReply(message.Channel.ExternalId, message.ExternalId, text); });
return 200;
}
}

View File

@ -124,14 +124,14 @@ public static class Rememberer
{
if (toForget.SubChannels?.Count > 0)
{
foreach (var childChannel in toForget.SubChannels)
foreach (var childChannel in toForget.SubChannels.ToList())
{
ForgetChannel(childChannel);
}
}
if(toForget.Users?.Count > 0)
{
foreach(var account in toForget.Users)
foreach(var account in toForget.Users.ToList())
{
ForgetAccount(account);
}

View File

@ -3,7 +3,7 @@ namespace vassago;
using System;
using System.Net.Http;
using vassago.Models;
using vassago.ProtocolInterfaces;
public static class Shared
{
@ -12,4 +12,5 @@ public static class Shared
public static HttpClient HttpClient { get; internal set; } = new HttpClient();
public static bool SetupSlashCommands { get; set; }
public static Uri API_URL {get;set;}
public static List<ProtocolInterface> ProtocolList { get; set; } = new();
}

View File

@ -20,7 +20,11 @@ public class ChannelsController() : Controller
//but that would take in all the messages.
//realistically I expect this will have less than 1MB of total "channels", and several GB of total messages per (text) channel.
var channel = allChannels.First(u => u.Id == id);
var channel = allChannels.FirstOrDefault(u => u.Id == id);
if(channel == null)
{
return Problem("couldn't find that channle");
}
var walker = channel;
while(walker != null)
{
@ -58,4 +62,4 @@ public class ChannelsController() : Controller
{
return View(new ErrorPageViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}

View File

@ -0,0 +1,45 @@
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 InternalAPIProtocolController : ControllerBase
{
private readonly ILogger<InternalAPIProtocolController> _logger;
public InternalAPIProtocolController(ILogger<InternalAPIProtocolController> logger)
{
_logger = logger;
}
public class extraSpecialObjectReadGlorifiedTupleFor_PostMessage
{
public string messageText;
public Guid channelId;
}
[HttpPost]
[Route("PostMessage")]
[Produces("application/json")]
public IActionResult PostMessage([FromBody]extraSpecialObjectReadGlorifiedTupleFor_PostMessage param)
{
return StatusCode(Behaver.Instance.SendMessage(param.channelId, param.messageText).Result);
}
public class extraSpecialObjectReadGlorifiedTupleFor_ReplyToMessage
{
public string messageText;
public Guid repliedMessageId;
}
[HttpPost]
[Route("ReplyToMessage")]
[Produces("application/json")]
public IActionResult ReplyToMessage([FromBody] extraSpecialObjectReadGlorifiedTupleFor_ReplyToMessage param)
{
Console.WriteLine("ReplyToMessage - ${param.repliedMessageId}, {param.messageText}");
return StatusCode(Behaver.Instance.Reply(param.repliedMessageId, param.messageText).Result);
}
}

View File

@ -32,7 +32,7 @@ public class RemembererController : ControllerBase
return Rememberer.AttachmentDetail(id);
}
[HttpPut]
[Route("Channel")]
[Route("Channels")]
[Produces("application/json")]
public Channel CreateChannel(Guid id)
{
@ -75,7 +75,7 @@ public class RemembererController : ControllerBase
return Rememberer.AttachmentDetail(id);
}
[HttpGet]
[Route("Channel")]
[Route("Channels")]
[Produces("application/json")]
public Channel GetChannel(Guid id)
{
@ -104,7 +104,7 @@ public class RemembererController : ControllerBase
}
//Update
[HttpPatch]
[Route("Channel")]
[Route("Channels")]
[Produces("application/json")]
public IActionResult Patch([FromBody] Channel channel)
{
@ -154,21 +154,23 @@ public class RemembererController : ControllerBase
return Ok();
}
[HttpDelete]
[Route("Channel")]
[Route("Channels/{id}")]
[Produces("application/json")]
public IActionResult DeleteChannel(Guid id)
{
var fromDb = Rememberer.ChannelDetail(id);
_logger.LogDebug($"delete channel {id}");
if (fromDb == null)
{
_logger.LogError($"attempt to delete channel {id}, not found");
return NotFound();
}
Rememberer.ForgetChannel(fromDb);
_logger.LogDebug($"delete channel {id} success");
return Ok();
}
[HttpDelete]
[Route("Message")]
[Route("Message/{id}")]
[Produces("application/json")]
public IActionResult DeleteMessage(Guid id)
{
@ -182,7 +184,7 @@ public class RemembererController : ControllerBase
return Ok();
}
[HttpDelete]
[Route("UAC")]
[Route("UAC/{id}")]
[Produces("application/json")]
public IActionResult DeleteUAC(Guid id)
{
@ -196,7 +198,7 @@ public class RemembererController : ControllerBase
return Ok();
}
[HttpDelete]
[Route("User")]
[Route("User/{id}")]
[Produces("application/json")]
public IActionResult DeleteUser(Guid id)
{

View File

@ -24,7 +24,7 @@
<tr>
<th scope="row">Lewdness Filter Level</th>
<td>
<select name="LewdnessFilterLevel" id="LewdnessFilterLevel" onchange="patchModel(jsonifyChannel(), '/api/Channels/')">
<select name="LewdnessFilterLevel" id="LewdnessFilterLevel" onchange="patchModel(jsonifyChannel())">
<!option value="" @(ThisChannel.LewdnessFilterLevel == null ? "selected" : "")>⤵ inherited - @Enumerations.GetDescription(IfInheritedLewdnessFilterLevel)</!option>
@foreach (Enumerations.LewdnessFilterLevel enumVal in
Enum.GetValues(typeof(Enumerations.LewdnessFilterLevel)))
@ -54,7 +54,7 @@
<tr>
<th scope="row">Meanness Filter Level</th>
<td>
<select name="MeannessFilterLevel" id="MeannessFilterLevel" onchange="patchModel(jsonifyChannel(), '/api/Channels/')">
<select name="MeannessFilterLevel" id="MeannessFilterLevel" onchange="patchModel(jsonifyChannel())">
<!option value="" @(ThisChannel.MeannessFilterLevel == null ? "selected" : "")>⤵ inherited - @Enumerations.GetDescription(IfInheritedMeannessFilterLevel)</!option>
@foreach (Enumerations.MeannessFilterLevel enumVal in
Enum.GetValues(typeof(Enumerations.MeannessFilterLevel)))
@ -135,7 +135,7 @@
function forget(){
console.log("here we go");
if(window.confirm("delete? really really?") == true){
deleteModel(jsonifyChannel(), '/api/Channels/');
deleteModel(jsonifyChannel().Id, window.history.back);
}
}
@ -149,7 +149,7 @@
var sb = new StringBuilder();
sb.Append("[{text: \"accounts\", \"expanded\":true, nodes: [");
var first = true;
foreach (var acc in ThisChannel.Users.OrderBy(a => a.SeenInChannel.LineageSummary))
foreach (var acc in ThisChannel.Users?.OrderBy(a => a?.SeenInChannel?.LineageSummary))
{
if(!first)
sb.Append(',');
@ -166,4 +166,4 @@
$('#accountsTree').bstreeview({ data: accountsTree() });
</script>
}
}

View File

@ -11,7 +11,7 @@
<tr>
<th scope="row">Display Name (here)</th>
<td><input type="text" id="displayName" value="@Model.DisplayName" disabled alt="todo"></input> <button
onclick="patchModel(jsonifyUser(), @Html.Raw("'/api/Users/'"))" disabled alt"todo">update</button></td>
onclick="patchModel(jsonifyUser())" disabled alt"todo">update</button></td>
</tr>
<tr>
<th scope="row">Accounts</th>
@ -54,12 +54,12 @@
}
sb.Append("]}]");
}
console.log(@Html.Raw(sb.ToString()));
console.log(@Html.Raw(sb.ToString()));
var tree = @Html.Raw(sb.ToString());
return tree;
}
$('#accountsTree').bstreeview({ data: getAccountsTree() });
document.querySelectorAll("input[type=checkbox]").forEach(node => { node.onchange = () => { patchModel(jsonifyUser(), '/api/Users/') } });
document.querySelectorAll("input[type=checkbox]").forEach(node => { node.onchange = () => { patchModel(jsonifyUser()) } });
</script>
}

View File

@ -3,7 +3,11 @@
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<NoWarn>$(NoWarn);CA2254</NoWarn>
<NoWarn>$(NoWarn);CS1998;CS4014</NoWarn>
<!-- CS1998: "This async method lacks 'await' operators and will run synchronously." -->
<!-- CS4014: "Because this call is not awaited, execution of the current method continues before the call is completed."-->
<!-- those 2 cancel out. Async foo calls async Bar, foo doesn't say "await" so cs1998 says "well don't mark foo as async" and cs4014 says "you should awake bar".
what, you want me to just add bar to some big stupid task list that I don't care about anyway? -->
</PropertyGroup>

View File

@ -8,7 +8,7 @@ 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, deprecated_apiUrl)
function patchModel(model, callback)
{
//structure the model your (dang) self into a nice object
console.log(model);
@ -22,7 +22,7 @@ function patchModel(model, deprecated_apiUrl)
// var id=components[3];
console.log(JSON.stringify(model));
fetch(apiUrl + type + '/', {
fetch(apiUrl + 'Rememberer/' + type + '/', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
@ -44,22 +44,17 @@ function patchModel(model, deprecated_apiUrl)
});
}
function deleteModel(model, deprecated_apiUrl)
function deleteModel(id, callback)
{
var components = window.location.pathname.split('/');
// if(components[2] !== "Details")
// {
// console.log("wtf are you doing? " + components[2] + " is something other than Details");
// }
var type=components[1];
let result = null;
// var id=components[3];
fetch(apiUrl + type + '/', {
var id=components[3];
fetch(apiUrl + 'Rememberer/' + type + '/' + id, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(model),
}
})
.then(response => {
if (!response.ok) {