Compare commits

...
This repository has been archived on 2023-06-01. You can view files and clone it, but cannot push or open issues or pull requests.

3 Commits

Author SHA1 Message Date
3031779e24 fork: vassago
All checks were successful
greyn/vassago/pipeline/head This commit looks good
gitea/vassago/pipeline/head This commit looks good
gitea.arg.rip/vassago/pipeline/head This commit looks good
2023-06-01 00:03:23 -04:00
bfe7f582b2 start to structure code for multiplatofrm 2023-05-23 19:03:58 -04:00
a87fcd6ad9 start to structure code for multiplatofrm 2023-05-22 00:58:36 -04:00
31 changed files with 2117 additions and 331 deletions

12
.config/dotnet-tools.json Normal file
View File

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "7.0.5",
"commands": [
"dotnet-ef"
]
}
}
}

2
.vscode/launch.json vendored
View File

@ -10,7 +10,7 @@
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/bin/Debug/net7.0/shtikbot-discord.dll", "program": "${workspaceFolder}/bin/Debug/net7.0/vassago.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console

6
.vscode/tasks.json vendored
View File

@ -7,7 +7,7 @@
"type": "process", "type": "process",
"args": [ "args": [
"build", "build",
"${workspaceFolder}/shtikbot-discord.csproj", "${workspaceFolder}/vassago.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary"
], ],
@ -19,7 +19,7 @@
"type": "process", "type": "process",
"args": [ "args": [
"publish", "publish",
"${workspaceFolder}/shtikbot-discord.csproj", "${workspaceFolder}/vassago.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary"
], ],
@ -32,7 +32,7 @@
"args": [ "args": [
"watch", "watch",
"run", "run",
"${workspaceFolder}/shtikbot-discord.csproj", "${workspaceFolder}/vassago.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary" "/consoleloggerparameters:NoSummary"
], ],

320
Behavior/Features.cs Normal file
View File

@ -0,0 +1,320 @@
namespace vassago.Behavior;
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using QRCoder;
using vassago.Models;
public static class Features
{
public static Random r = new Random();
public static async void detiktokify(Uri link, Message message)
{
//yes, even if there is a problem later.
#pragma warning disable 4014
message.React("tiktokbad");
#pragma warning restore 4014
var ytdl = new YoutubeDLSharp.YoutubeDL();
ytdl.YoutubeDLPath = "yt-dlp";
ytdl.FFmpegPath = "ffmpeg";
ytdl.OutputFolder = "";
ytdl.OutputFileTemplate = "tiktokbad.%(ext)s";
try
{
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" + string.Join('\n', res.ErrorOutput));
}
else
{
string path = res.Data;
if (File.Exists(path))
{
var bytesize = new System.IO.FileInfo(path).Length;
if (bytesize < 1024 * 1024 * 10)
{
try
{
await message.Channel.SendFile(path);
}
catch (Exception e)
{
System.Console.Error.WriteLine(e);
await message.Channel.SendMessage($"aaaadam!\n{e}");
}
}
else
{
Console.WriteLine($"file appears too big ({bytesize} bytes ({bytesize / (1024 * 1024)}MB)), not posting");
}
File.Delete(path);
}
else
{
Console.Error.WriteLine("idgi but something happened.");
await message.React("problemon");
}
}
}
catch (Exception e)
{
Console.Error.WriteLine(e);
await message.React("problemon");
}
}
public static async void deheic(Message message, Attachment att)
{
try
{
if (!Directory.Exists("tmp"))
{
Directory.CreateDirectory("tmp");
}
var cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
using (Stream output = File.OpenWrite("tmp/" + att.Filename))
{
(await Shared.HttpClient.GetAsync(att.Source))
.Content.CopyTo(output, null, token);
}
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");
File.Delete($"tmp/{att.Filename}");
File.Delete($"tmp/{att.Filename}.jpg");
}
else
{
await message.Channel.SendMessage("convert failed :(");
Console.Error.WriteLine("convert failed :(");
}
}
catch (Exception e)
{
await message.Channel.SendMessage($"something failed. aaaadam! {JsonConvert.SerializeObject(e, Formatting.Indented)}");
Console.Error.WriteLine(JsonConvert.SerializeObject(e, Formatting.Indented));
}
}
internal static async void mock(string contentWithoutMention, Message message)
{
var toPost = new StringBuilder();
for (int i = 0; i < contentWithoutMention.Length; i++)
{
if (i % 2 == 0)
{
toPost.Append(contentWithoutMention[i].ToString().ToUpper());
}
else
{
toPost.Append(contentWithoutMention[i].ToString().ToLower());
}
}
await message.Reply(toPost.ToString());
}
public static async void qrify(string qrContent, Message message)
{
Console.WriteLine($"qring: {qrContent}");
QRCodeGenerator qrGenerator = new QRCodeGenerator();
QRCodeData qrCodeData = qrGenerator.CreateQrCode(qrContent, QRCodeGenerator.ECCLevel.Q);
SvgQRCode qrCode = new SvgQRCode(qrCodeData);
string qrCodeAsSvg = qrCode.GetGraphic(20);
int todaysnumber = Shared.r.Next();
if (!Directory.Exists("tmp"))
{
Directory.CreateDirectory("tmp");
}
File.WriteAllText($"tmp/qr{todaysnumber}.svg", qrCodeAsSvg);
if (ExternalProcess.GoPlz("convert", $"tmp/qr{todaysnumber}.svg tmp/qr{todaysnumber}.png"))
{
await message.Channel.SendFile($"tmp/qr{todaysnumber}.png");
File.Delete($"tmp/qr{todaysnumber}.svg");
File.Delete($"tmp/qr{todaysnumber}.png");
}
else
{
await message.Channel.SendMessage("convert failed :( aaaaaaadam!");
Console.Error.WriteLine($"convert failed :( qr{todaysnumber}");
}
}
public static async void Convert(Message message, string contentWithoutMention)
{
await message.Channel.SendMessage(Conversion.Converter.convert(contentWithoutMention));
}
public static async void Joke(Message message)
{
var jokes = File.ReadAllLines("assets/jokes.txt");
jokes = jokes.Where(l => !string.IsNullOrWhiteSpace(l))?.ToArray();
if (jokes?.Length == 0)
{
await message.Channel.SendMessage("I don't know any. Adam!");
}
var thisJoke = jokes[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);
Task.WaitAll(message.Channel.SendMessage(straightline));
Thread.Sleep(TimeSpan.FromSeconds(r.Next(5, 30)));
var myOwnMsg = await message.Channel.SendMessage(punchline);
if (r.Next(8) == 0)
{
await myOwnMsg.React("\U0001F60E"); //smiling face with sunglasses
}
});
#pragma warning restore 4014
}
else
{
await message.Channel.SendMessage(thisJoke);
}
}
public static async void Recipe(Message message)
{
var sb = new StringBuilder();
var snarkSeg1 = new string[] { "ew", "gross", "that seems a bit hard for you" };
sb.AppendLine(snarkSeg1[r.Next(snarkSeg1.Length)]);
var snarkSeg2 = new string[]{@"here's an easier recipe for you:
Ingredients:
- Corn flakes cereal
- Milk
Instructions:
1. Pour some corn flakes into a bowl.
2. Pour some milk into the bowl until it covers the corn flakes.
3. Use a spoon to mix the corn flakes and milk together.
4. Enjoy your delicious cereal!
Hope that's a bit better for you! 🥣",
@"here's an easier recipe for you:
Ingredients:
- Bread
- Peanut butter
- Jelly or jam
Instructions:
1. Take two slices of bread and put them on a plate or cutting board.
2. Using a spoon or knife, spread peanut butter on one slice of bread.
3. Using a separate spoon or knife, spread jelly or jam on the other slice of bread.
4. Put the two slices of bread together with the peanut butter and jelly sides facing each other.
5. Cut the sandwich in half (optional!).
6. Enjoy your yummy sandwich!
I hope you have fun making and eating your PB&J 🥪!",
"just order pizza instead"
};
sb.AppendLine(snarkSeg2[r.Next(snarkSeg2.Length)]);
await message.Channel.SendMessage(sb.ToString());
}
public static async void Skynet(Message message)
{
switch (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**.");
break;
case 4:
await message.React("\U0001F644"); //eye roll emoji
break;
case 5:
await message.React("\U0001F611"); //emotionless face
break;
}
}
public static async void peptalk(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[r.Next(piece1.Count)] + piece2[r.Next(piece2.Count)] + piece3[r.Next(piece3.Count)] + piece4[r.Next(piece4.Count)]);
}
}

View File

@ -0,0 +1,244 @@
namespace vassago.Behavior;
#pragma warning disable 4014 //the "not awaited" error
using vassago.Models;
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Collections.Generic;
//TODO: better name
public class thingmanagementdoer
{
internal thingmanagementdoer() { }
static thingmanagementdoer() { }
private static readonly thingmanagementdoer _instance = new thingmanagementdoer();
public static thingmanagementdoer Instance
{
get { return _instance; }
}
public async Task<bool> ActOn(Message message)
{
var didThing = false;
var contentWithoutMention = message.Content;
var mentionedMe = false;
// if (message.Author.Id == 159985870458322944) //MEE6
// {
// if (message.Content?.Contains("you just advanced") == true)
// {
// var newText = Regex.Replace(message.Content, "<[^>]*>", message.Author.Username);
// newText = Regex.Replace(newText, "level [\\d]+", "level -1");
// Features.mock(newText, message);
// didThing = true;
// }
// }
var wordLikes = message.Content.Split(' ', StringSplitOptions.TrimEntries);
var links = wordLikes?.Where(wl => Uri.IsWellFormedUriString(wl, UriKind.Absolute)).Select(wl => new Uri(wl));
if (links != null && links.Count() > 0)
{
foreach (var link in links)
{
if (link.Host.EndsWith(".tiktok.com"))
{
Features.detiktokify(link, message);
didThing = true;
}
}
}
if (message.Attachments?.Count() > 0)
{
Console.WriteLine($"{message.Attachments.Count()} attachments");
var appleReactions = false;
foreach (var att in message.Attachments)
{
if (att.Filename?.EndsWith(".heic") == true)
{
Features.deheic(message, att);
appleReactions = true;
didThing = true;
}
}
if (appleReactions)
{
message.React("\U0001F34F");
}
}
var msgText = message.Content?.ToLower();
if (!string.IsNullOrWhiteSpace(msgText))
{
if (Regex.IsMatch(msgText, "\\bcloud( |-)?native\\b", RegexOptions.IgnoreCase) ||
Regex.IsMatch(msgText, "\\benterprise( |-)?(level|solution)\\b", RegexOptions.IgnoreCase))
{
switch (Shared.r.Next(2))
{
case 0:
await message.React("\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
break;
}
didThing = true;
}
if (Regex.IsMatch(msgText, "^(s?he|(yo)?u|y'?all) thinks? i'?m (playin|jokin|kiddin)g?$", RegexOptions.IgnoreCase))
{
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");
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bskynet\\b", RegexOptions.IgnoreCase))
{
Features.Skynet(message);
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bchatgpt\\b", RegexOptions.IgnoreCase))
{
message.Channel.SendMessage("chatGPT is **weak**. also, are we done comparing every little if-then-else to skynet?");
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bi need (an? )?(peptalk|inspiration|ego-?boost)\\b", RegexOptions.IgnoreCase))
{
Console.WriteLine("peptalk");
Features.peptalk(message);
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bwish me luck\\b", RegexOptions.IgnoreCase))
{
if (Shared.r.Next(20) == 0)
{
await message.React("\U0001f340");//4-leaf clover
}
else
{
await message.React("☘️");
}
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bgaslight(ing)?\\b", RegexOptions.IgnoreCase))
{
message.Channel.SendMessage("that's not what gaslight means. Did you mean \"say something that (you believe) is wrong\"?");
didThing = true;
}
if (msgText.Contains("!qrplz "))
{
Features.qrify(message.Content.Substring("!qrplz ".Length + msgText.IndexOf("!qrplz ")), message);
didThing = true;
}
if (msgText.Contains("!freedomunits "))
{
Features.Convert(message, contentWithoutMention);
didThing = true;
}
if (Regex.IsMatch(msgText, "!joke\\b"))
{
Features.Joke(message);
didThing = true;
}
if (Regex.IsMatch(msgText, "!pulse ?check\\b"))
{
message.Channel.SendFile("assets/ekgblip.png");
Console.WriteLine(Conversion.Converter.DebugInfo());
didThing = true;
}
if (mentionedMe && (Regex.IsMatch(msgText, "\\brecipe for .+") || Regex.IsMatch(msgText, ".+ recipe\\b")))
{
Features.Recipe(message);
didThing = true;
}
if (msgText.Contains("cognitive dissonance") == true)
{
message.Reply("that's not what cognitive dissonance means. Did you mean \"hypocrisy\"?");
didThing = true;
}
if (mentionedMe && Regex.IsMatch(msgText, "what'?s the longest (six|6)(-| )?letter word( in english)?\\b"))
{
Task.Run(async () =>
{
await message.Channel.SendMessage("mother.");
await Task.Delay(3000);
await message.Channel.SendMessage("oh, longest? I thought you said fattest.");
});
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bthank (yo)?u\\b", RegexOptions.IgnoreCase) &&
(mentionedMe || Regex.IsMatch(msgText, "\\b(sh?tik)?bot\\b", RegexOptions.IgnoreCase)))
{
switch (Shared.r.Next(4))
{
case 0:
message.Channel.SendMessage("you're welcome, citizen!");
break;
case 1:
message.React("☺");
break;
case 2:
message.React("\U0001F607"); //smiling face with halo
break;
case 3:
switch (Shared.r.Next(9))
{
case 0:
message.React("❤"); //normal heart, usually rendered red
break;
case 1:
message.React("\U0001F9E1"); //orange heart
break;
case 2:
message.React("\U0001F49B"); //yellow heart
break;
case 3:
message.React("\U0001F49A"); //green heart
break;
case 4:
message.React("\U0001F499"); //blue heart
break;
case 5:
message.React("\U0001F49C"); //purple heart
break;
case 6:
message.React("\U0001F90E"); //brown heart
break;
case 7:
message.React("\U0001F5A4"); //black heart
break;
case 8:
message.React("\U0001F90D"); //white heart
break;
}
break;
}
didThing = true;
#pragma warning restore 4014
}
// if (didThing == false && mentionedMe && contentWithoutMention.Contains("how long has that been there?"))
// {
// await message.Channel.SendMessage("text", false, null, null, null, null, new ComponentBuilder().WithButton("label", "custom-id").Build());
// didThing = true;
// }
if (didThing == false && mentionedMe && contentWithoutMention.Contains('?'))
{
Console.WriteLine("providing bullshit nonanswer / admitting uselessness");
var responses = new List<string>(){
@"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)]);
didThing = true;
}
}
return didThing;
}
internal Task OnJoin(User u, Channel defaultChannel)
{
throw new NotImplementedException();
}
}
#pragma warning restore 4014 //the "async not awaited" error

46
Configuration.cs Normal file
View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace vassago
{
public class Configuration
{
public string ExchangePairsLocation {get;set;}
public IEnumerable<string> DiscordTokens { get; set; }
public string DBConnectionString{get;set;}
private Configuration(){}
public static Configuration Parse(string configurationPath)
{
if(string.IsNullOrWhiteSpace(configurationPath))
return null;
if (!File.Exists(configurationPath))
{
File.WriteAllText("sample-appsettings.json", JsonConvert.SerializeObject(new Configuration(), Formatting.Indented));
throw new ConfigurationException($"could not find configuration at {configurationPath}! copying sample to that spot.");
}
var fileContents = File.ReadAllText(configurationPath);
if (string.IsNullOrWhiteSpace(fileContents))
{
File.WriteAllText("sample-appsettings.json", JsonConvert.SerializeObject(new Configuration(), Formatting.Indented));
throw new ConfigurationException($"configuration file at {configurationPath} was empty! overwriting with sample settings.");
}
var conf = JsonConvert.DeserializeObject<Configuration>(fileContents);
if (conf == null)
{
File.WriteAllText("sample-appsettings.json", JsonConvert.SerializeObject(new Configuration(), Formatting.Indented));
throw new ConfigurationException($"configuration file at {configurationPath} was empty! overwriting with sample settings.");
}
return conf;
}
public class ConfigurationException : Exception
{
public ConfigurationException(string message) : base(message){}
}
}
}

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace silverworker_discord.Conversion namespace vassago.Conversion
{ {
public class ConversionConfig public class ConversionConfig
{ {

View File

@ -12,7 +12,7 @@ using Discord.WebSocket;
using Newtonsoft.Json; using Newtonsoft.Json;
using QRCoder; using QRCoder;
namespace silverworker_discord.Conversion namespace vassago.Conversion
{ {
public static class Converter public static class Converter
{ {

View File

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace silverworker_discord.Conversion namespace vassago.Conversion
{ {
public class ExchangePairs public class ExchangePairs
{ {

View File

@ -0,0 +1,216 @@
//https://discord.com/oauth2/authorize?client_id=913003037348491264&permissions=274877942784&scope=bot%20messages.read
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using vassago.Models;
using vassago.DiscordInterface.Models;
using vassago.Behavior;
namespace vassago.DiscordInterface;
public class DiscordInterface
{
private DiscordSocketClient _client;
private DiscordProtocol protocolInterface;
private bool eventsSignedUp = false;
private ChattingContext _db;
public DiscordInterface()
{
_db = Shared.dbContext;
}
public async Task Init(string token)
{
_client = new DiscordSocketClient(new DiscordSocketConfig() { GatewayIntents = GatewayIntents.All });
_client.Log += (msg) =>
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
};
_client.Ready += () => Task.Run(() =>
{
if (!eventsSignedUp)
{
eventsSignedUp = true;
Console.WriteLine("Bot is connected! going to sign up for message received and user joined in client ready");
_client.MessageReceived += MessageReceived;
// _client.MessageUpdated +=
_client.UserJoined += UserJoined;
_client.SlashCommandExecuted += SlashCommandHandler;
// _client.ChannelCreated +=
// _client.ChannelDestroyed +=
// _client.ChannelUpdated +=
// _client.GuildMemberUpdated +=
// _client.UserBanned +=
// _client.UserLeft +=
// _client.ThreadCreated +=
// _client.ThreadUpdated +=
// _client.ThreadDeleted +=
// _client.JoinedGuild +=
// _client.GuildUpdated +=
// _client.LeftGuild +=
SlashCommandsHelper.Register(_client).GetAwaiter().GetResult();
}
else
{
Console.WriteLine("bot appears to be RE connected, so I'm not going to sign up twice");
}
});
await _client.LoginAsync(TokenType.Bot, token);
await _client.StartAsync();
}
#pragma warning disable 4014 //the "you're not awaiting this" warning. yeah I know, that's the beauty of an async method lol
#pragma warning disable 1998 //the "it's async but you're not awaiting anything".
private async Task MessageReceived(SocketMessage messageParam)
#pragma warning restore 1998
{
var suMessage = messageParam as SocketUserMessage;
if (suMessage == null) return;
var m = _db.Messages.FirstOrDefault(mi => mi.ExternalId == suMessage.Id) as DiscordMessage;
if(m == null)
{
m = _db.Messages.Add(new DiscordMessage(suMessage)).Entity as DiscordMessage;
}
m.Intake(suMessage, _client.CurrentUser.Id);
m.Channel = UpsertChannel(suMessage.Channel);
m.Author = UpsertUser(suMessage.Author);
_db.SaveChanges();
Console.WriteLine($"#{suMessage.Channel}[{DateTime.Now}][{suMessage.Author.Username} [id={suMessage.Author.Id}]][msg id: {suMessage.Id}] {suMessage.Content}");
if (suMessage.Author.Id == _client.CurrentUser.Id) return;
if (suMessage.MentionedUsers?.FirstOrDefault(muid => muid.Id == _client.CurrentUser.Id) != null)
{
var mentionOfMe = "<@" + _client.CurrentUser.Id + ">";
m.MentionsMe = true;
}
//TODO: standardize content
if(await thingmanagementdoer.Instance.ActOn(m))
{
m.ActedOn = true;
_db.SaveChanges();
}
}
private Task UserJoined(SocketGuildUser arg)
{
var guild = UpsertChannel(arg.Guild);
var defaultChannel = UpsertChannel(arg.Guild.DefaultChannel);
defaultChannel.ParentChannel = guild;
var u = UpsertUser(arg);
if(u.SeenInChannels == null) u.SeenInChannels = new List<Channel>();
var sighting = u.SeenInChannels?.FirstOrDefault(c => c.ExternalId == arg.Guild.Id);
if(sighting == null)
{
var seenIn = u.SeenInChannels as List<Channel>;
seenIn.Add(guild);
seenIn.Add(defaultChannel);
u.SeenInChannels = seenIn;
_db.SaveChanges();
}
return thingmanagementdoer.Instance.OnJoin(u, defaultChannel);
// Console.WriteLine($"user joined: {arg.Nickname}. Guid: {arg.Guild.Id}. Channel: {arg.Guild.DefaultChannel}");
// var abbreviatedNickname = arg.Nickname;
// if (arg.Nickname.Length > 3)
// {
// abbreviatedNickname = arg.Nickname.Substring(0, arg.Nickname.Length / 3);
// }
// Console.WriteLine($"imma call him {abbreviatedNickname}");
// return arg.Guild.DefaultChannel.SendMessageAsync($"oh hey {abbreviatedNickname}- IPLAYTHESEALOFORICHALCOS <:ORICHALCOS:852749196633309194>");
}
private async Task ButtonHandler(SocketMessageComponent component)
{
switch (component.Data.CustomId)
{
case "custom-id":
await component.RespondAsync($"{component.User.Mention}, it's been here the whole time!");
break;
}
}
internal static async Task SlashCommandHandler(SocketSlashCommand command)
{
switch (command.CommandName)
{
case "freedomunits":
try
{
var amt = Convert.ToDecimal((double)(command.Data.Options.First(o => o.Name == "amount").Value));
var src = (string)command.Data.Options.First(o => o.Name == "src-unit").Value;
var dest = (string)command.Data.Options.First(o => o.Name == "dest-unit").Value;
var conversionResult = Conversion.Converter.Convert(amt, src, dest);
await command.RespondAsync($"> {amt} {src} -> {dest}\n{conversionResult}");
}
catch (Exception e)
{
await command.RespondAsync($"error: {e.Message}. aaadam!");
}
break;
default:
await command.RespondAsync($"\\*smiles and nods*\n");
await command.Channel.SendFileAsync($"assets/loud sweating.gif");
Console.Error.WriteLine($"can't understand command name: {command.CommandName}");
break;
}
}
private Channel UpsertChannel(ISocketMessageChannel channel)
{
var c = _db.Channels.FirstOrDefault(ci => ci.ExternalId == channel.Id);
if(c == null)
{
c = _db.Channels.Add(new DiscordChannel()).Entity;
_db.SaveChanges();
}
if(channel is IGuildChannel)
{
c.ParentChannel = UpsertChannel((channel as IGuildChannel).Guild);
}
else if (channel is IPrivateChannel)
{
c.ParentChannel = protocolInterface;
}
else
{
c.ParentChannel = protocolInterface;
Console.WriteLine($"trying to upsert channel {channel.Id}/{channel.Name}, but it's neither guildchannel nor private channel. shrug.jpg");
}
return c;
}
private Channel UpsertChannel(IGuild channel)
{
var c = _db.Channels.FirstOrDefault(ci => ci.ExternalId == channel.Id);
if(c == null)
{
c = _db.Channels.Add(new DiscordChannel()).Entity;
_db.SaveChanges();
}
c.ParentChannel = protocolInterface;
return c;
}
private User UpsertUser(SocketUser user)
{
var u = _db.Users.FirstOrDefault(ui => ui.ExternalId == user.Id);
if(u == null)
{
u = _db.Users.Add(new DiscordUser()).Entity;
_db.SaveChanges();
}
return u;
}
}

View File

@ -0,0 +1,4 @@
namespace vassago.DiscordInterface.Models;
using vassago.Models;
public class DiscordAttachment : Attachment {}

View File

@ -0,0 +1,18 @@
namespace vassago.DiscordInterface.Models;
using System.Threading.Tasks;
using vassago.Models;
public class DiscordChannel : Channel
{
public override Task<Message> SendFile(string path, string messageText = null)
{
throw new System.NotImplementedException();
}
public override Task<Message> SendMessage(string text)
{
throw new System.NotImplementedException();
}
}

View File

@ -0,0 +1,40 @@
namespace vassago.DiscordInterface.Models;
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord.WebSocket;
using Newtonsoft.Json;
using vassago.Models;
public class DiscordMessage : Message
{
private SocketUserMessage _externalEntity;
public DiscordMessage(SocketUserMessage suMessage)
{
_externalEntity = suMessage;
}
public override Task React(string reaction)
{
return _externalEntity.AddReactionAsync(Discord.Emote.Parse(reaction));
}
public override Task Reply(string message)
{
return _externalEntity.Channel.SendMessageAsync(message, messageReference: new Discord.MessageReference(_externalEntity.Id));
}
internal void Intake(SocketUserMessage suMessage, ulong currentUserId)
{
this.Content = suMessage.Content;
this.ExternalId = suMessage.Id;
this.Timestamp = suMessage.EditedTimestamp ?? suMessage.CreatedAt;
if (suMessage.MentionedUsers?.FirstOrDefault(muid => muid.Id == currentUserId) != null)
{
this.MentionsMe = true;
}
}
}

View File

@ -0,0 +1,20 @@
namespace vassago.DiscordInterface.Models;
using vassago.Models;
using Discord;
using Discord.WebSocket;
using System.Threading.Tasks;
public class DiscordProtocol : Protocol
{
public DiscordSocketClient Client {get;set;}
public override Task<Message> SendFile(string path, string messageText = null)
{
throw new System.InvalidOperationException("can't send a file to \"discord\", pick a channel");
}
public override Task<Message> SendMessage(string message)
{
throw new System.InvalidOperationException("can't send a message to \"discord\", pick a channel");
}
}

View File

@ -0,0 +1,6 @@
namespace vassago.DiscordInterface.Models;
using vassago.Models;
using Discord;
using Discord.WebSocket;
public class DiscordUser : User { }

View File

@ -0,0 +1,124 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Discord.WebSocket;
using Discord;
using Discord.Net;
namespace vassago.DiscordInterface
{
public static class SlashCommandsHelper
{
private static List<CommandSetup> slashCommands = new List<CommandSetup>()
{
new CommandSetup(){
Id = "freedomunits",
UpdatedAt = new DateTime(2023, 5, 21, 13, 3, 0),
guild = 825293851110801428,
register = register_FreedomUnits
}
};
public static async Task Register(DiscordSocketClient client)
{
var commandsInContext = await client.GetGlobalApplicationCommandsAsync();
await Register(client, commandsInContext, null);
foreach (var guild in client.Guilds)
{
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}");
}
}
}
private static async Task Register(DiscordSocketClient client, IEnumerable<SocketApplicationCommand> commandsInContext, SocketGuild guild)
{
foreach (var existingCommand in commandsInContext)
{
var myVersion = slashCommands.FirstOrDefault(c => c.Id == existingCommand.Name && c.guild == guild?.Id);
if (myVersion == null)
{
Console.WriteLine($"deleting command {existingCommand.Name} - (created at {existingCommand.CreatedAt}, it's in guild {existingCommand.Guild?.Id} while I'm in {guild?.Id})");
await existingCommand.DeleteAsync();
Console.WriteLine("survived");
}
else
{
Console.WriteLine(existingCommand.CreatedAt);
if (myVersion.UpdatedAt > existingCommand.CreatedAt)
{
Console.WriteLine($"overwriting command {existingCommand.Name}");
await myVersion.register(false, client, guild);
Console.WriteLine($"survived");
}
myVersion.alreadyRegistered = true;
}
}
foreach (var remaining in slashCommands.Where(sc => sc.alreadyRegistered == false && sc.guild == guild?.Id))
{
Console.WriteLine($"creating new command {remaining.Id} ({(remaining.guild == null ? "global" : $"for guild {remaining.guild}")})");
await remaining.register(true, client, guild);
Console.WriteLine($"survived");
}
}
private static async Task register_FreedomUnits(bool isNew, DiscordSocketClient client, SocketGuild guild)
{
var builtCommand = new SlashCommandBuilder()
.WithName("freedomunits")
.WithDescription("convert between misc units (currency: iso 4217 code)")
.AddOption("amount", ApplicationCommandOptionType.Number, "source amount", isRequired: true)
.AddOption(new SlashCommandOptionBuilder()
.WithName("src-unit")
.WithDescription("unit converting FROM")
.WithRequired(true)
.WithType(ApplicationCommandOptionType.String))
.AddOption(new SlashCommandOptionBuilder()
.WithName("dest-unit")
.WithDescription("unit converting TO")
.WithRequired(true)
.WithType(ApplicationCommandOptionType.String))
.Build();
try
{
if (guild != null)
{
if (isNew)
await guild.CreateApplicationCommandAsync(builtCommand);
else
await guild.BulkOverwriteApplicationCommandAsync(new ApplicationCommandProperties[] { builtCommand });
}
else
{
if (isNew)
await client.CreateGlobalApplicationCommandAsync(builtCommand);
else
await client.BulkOverwriteGlobalApplicationCommandsAsync(new ApplicationCommandProperties[] { builtCommand });
}
}
catch (HttpException exception)
{
var json = JsonConvert.SerializeObject(exception.Errors, Formatting.Indented);
Console.Error.WriteLine(json);
}
}
private class CommandSetup
{
public string Id { get; set; }
//the date/time you updated yours IN UTC.
public DateTimeOffset UpdatedAt { get; set; }
public Registration register { get; set; }
public ulong? guild { get; set; }
public bool alreadyRegistered {get;set; } = false;
public delegate Task Registration(bool isNew, DiscordSocketClient client, SocketGuild guild);
}
}
}

View File

@ -0,0 +1,316 @@
// <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("20230601033836_initial")]
partial class initial
{
/// <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("ChannelUser", b =>
{
b.Property<Guid>("OtherUsersId")
.HasColumnType("uuid");
b.Property<Guid>("SeenInChannelsId")
.HasColumnType("uuid");
b.HasKey("OtherUsersId", "SeenInChannelsId");
b.HasIndex("SeenInChannelsId");
b.ToTable("ChannelUser");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
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<string>("Discriminator")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<bool>("IsDM")
.HasColumnType("boolean");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<int?>("PermissionsOverridesId")
.HasColumnType("integer");
b.Property<Guid?>("ProtocolId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.HasIndex("PermissionsOverridesId");
b.HasIndex("ProtocolId");
b.ToTable("Channels");
b.HasDiscriminator<string>("Discriminator").HasValue("Channel");
b.UseTphMappingStrategy();
});
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<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("ExternalRepresentation")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
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.PermissionSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<long?>("MaxAttachmentBytes")
.HasColumnType("bigint");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("PermissionSettings");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("External")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("ProtocolId")
.HasColumnType("uuid");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("ProtocolId");
b.HasIndex("UserId");
b.ToTable("Users");
});
modelBuilder.Entity("vassago.Models.Protocol", b =>
{
b.HasBaseType("vassago.Models.Channel");
b.Property<string>("ConnectionToken")
.HasColumnType("text");
b.HasDiscriminator().HasValue("Protocol");
});
modelBuilder.Entity("ChannelUser", b =>
{
b.HasOne("vassago.Models.User", null)
.WithMany()
.HasForeignKey("OtherUsersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.Channel", null)
.WithMany()
.HasForeignKey("SeenInChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId");
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId");
b.HasOne("vassago.Models.PermissionSettings", "PermissionsOverrides")
.WithMany()
.HasForeignKey("PermissionsOverridesId");
b.HasOne("vassago.Models.Protocol", "Protocol")
.WithMany()
.HasForeignKey("ProtocolId");
b.Navigation("ParentChannel");
b.Navigation("PermissionsOverrides");
b.Navigation("Protocol");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.User", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId");
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.HasOne("vassago.Models.Protocol", "Protocol")
.WithMany()
.HasForeignKey("ProtocolId");
b.HasOne("vassago.Models.User", null)
.WithMany("KnownAliases")
.HasForeignKey("UserId");
b.Navigation("Protocol");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("KnownAliases");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,235 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace vassago.Migrations
{
/// <inheritdoc />
public partial class initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PermissionSettings",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
MaxAttachmentBytes = table.Column<long>(type: "bigint", nullable: true),
MaxTextChars = table.Column<long>(type: "bigint", nullable: true),
LinksAllowed = table.Column<bool>(type: "boolean", nullable: true),
LewdnessFilterLevel = table.Column<int>(type: "integer", nullable: true),
MeannessFilterLevel = table.Column<int>(type: "integer", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PermissionSettings", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Channels",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
ExternalId = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
DisplayName = table.Column<string>(type: "text", nullable: true),
IsDM = table.Column<bool>(type: "boolean", nullable: false),
PermissionsOverridesId = table.Column<int>(type: "integer", nullable: true),
ParentChannelId = table.Column<Guid>(type: "uuid", nullable: true),
ProtocolId = table.Column<Guid>(type: "uuid", nullable: true),
Discriminator = table.Column<string>(type: "text", nullable: false),
ConnectionToken = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Channels", x => x.Id);
table.ForeignKey(
name: "FK_Channels_Channels_ParentChannelId",
column: x => x.ParentChannelId,
principalTable: "Channels",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Channels_Channels_ProtocolId",
column: x => x.ProtocolId,
principalTable: "Channels",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Channels_PermissionSettings_PermissionsOverridesId",
column: x => x.PermissionsOverridesId,
principalTable: "PermissionSettings",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
ExternalId = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
DisplayName = table.Column<string>(type: "text", nullable: true),
IsBot = table.Column<bool>(type: "boolean", nullable: false),
ProtocolId = table.Column<Guid>(type: "uuid", nullable: true),
External = table.Column<string>(type: "text", nullable: true),
UserId = table.Column<Guid>(type: "uuid", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
table.ForeignKey(
name: "FK_Users_Channels_ProtocolId",
column: x => x.ProtocolId,
principalTable: "Channels",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Users_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "ChannelUser",
columns: table => new
{
OtherUsersId = table.Column<Guid>(type: "uuid", nullable: false),
SeenInChannelsId = table.Column<Guid>(type: "uuid", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ChannelUser", x => new { x.OtherUsersId, x.SeenInChannelsId });
table.ForeignKey(
name: "FK_ChannelUser_Channels_SeenInChannelsId",
column: x => x.SeenInChannelsId,
principalTable: "Channels",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ChannelUser_Users_OtherUsersId",
column: x => x.OtherUsersId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Messages",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
ExternalId = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
Content = table.Column<string>(type: "text", nullable: true),
MentionsMe = table.Column<bool>(type: "boolean", nullable: false),
Timestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
ActedOn = table.Column<bool>(type: "boolean", nullable: false),
ExternalRepresentation = table.Column<string>(type: "text", nullable: true),
AuthorId = table.Column<Guid>(type: "uuid", nullable: true),
ChannelId = table.Column<Guid>(type: "uuid", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Messages", x => x.Id);
table.ForeignKey(
name: "FK_Messages_Channels_ChannelId",
column: x => x.ChannelId,
principalTable: "Channels",
principalColumn: "Id");
table.ForeignKey(
name: "FK_Messages_Users_AuthorId",
column: x => x.AuthorId,
principalTable: "Users",
principalColumn: "Id");
});
migrationBuilder.CreateTable(
name: "Attachments",
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false),
ExternalId = table.Column<decimal>(type: "numeric(20,0)", nullable: true),
Source = table.Column<string>(type: "text", nullable: true),
Content = table.Column<byte[]>(type: "bytea", nullable: true),
Filename = table.Column<string>(type: "text", nullable: true),
MessageId = table.Column<Guid>(type: "uuid", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Attachments", x => x.Id);
table.ForeignKey(
name: "FK_Attachments_Messages_MessageId",
column: x => x.MessageId,
principalTable: "Messages",
principalColumn: "Id");
});
migrationBuilder.CreateIndex(
name: "IX_Attachments_MessageId",
table: "Attachments",
column: "MessageId");
migrationBuilder.CreateIndex(
name: "IX_Channels_ParentChannelId",
table: "Channels",
column: "ParentChannelId");
migrationBuilder.CreateIndex(
name: "IX_Channels_PermissionsOverridesId",
table: "Channels",
column: "PermissionsOverridesId");
migrationBuilder.CreateIndex(
name: "IX_Channels_ProtocolId",
table: "Channels",
column: "ProtocolId");
migrationBuilder.CreateIndex(
name: "IX_ChannelUser_SeenInChannelsId",
table: "ChannelUser",
column: "SeenInChannelsId");
migrationBuilder.CreateIndex(
name: "IX_Messages_AuthorId",
table: "Messages",
column: "AuthorId");
migrationBuilder.CreateIndex(
name: "IX_Messages_ChannelId",
table: "Messages",
column: "ChannelId");
migrationBuilder.CreateIndex(
name: "IX_Users_ProtocolId",
table: "Users",
column: "ProtocolId");
migrationBuilder.CreateIndex(
name: "IX_Users_UserId",
table: "Users",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Attachments");
migrationBuilder.DropTable(
name: "ChannelUser");
migrationBuilder.DropTable(
name: "Messages");
migrationBuilder.DropTable(
name: "Users");
migrationBuilder.DropTable(
name: "Channels");
migrationBuilder.DropTable(
name: "PermissionSettings");
}
}
}

View File

@ -0,0 +1,313 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using vassago.Models;
#nullable disable
namespace vassago.Migrations
{
[DbContext(typeof(ChattingContext))]
partial class ChattingContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.5")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("ChannelUser", b =>
{
b.Property<Guid>("OtherUsersId")
.HasColumnType("uuid");
b.Property<Guid>("SeenInChannelsId")
.HasColumnType("uuid");
b.HasKey("OtherUsersId", "SeenInChannelsId");
b.HasIndex("SeenInChannelsId");
b.ToTable("ChannelUser");
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<byte[]>("Content")
.HasColumnType("bytea");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("Filename")
.HasColumnType("text");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
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<string>("Discriminator")
.IsRequired()
.HasColumnType("text");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<bool>("IsDM")
.HasColumnType("boolean");
b.Property<Guid?>("ParentChannelId")
.HasColumnType("uuid");
b.Property<int?>("PermissionsOverridesId")
.HasColumnType("integer");
b.Property<Guid?>("ProtocolId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("ParentChannelId");
b.HasIndex("PermissionsOverridesId");
b.HasIndex("ProtocolId");
b.ToTable("Channels");
b.HasDiscriminator<string>("Discriminator").HasValue("Channel");
b.UseTphMappingStrategy();
});
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<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<string>("ExternalRepresentation")
.HasColumnType("text");
b.Property<bool>("MentionsMe")
.HasColumnType("boolean");
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.PermissionSettings", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("LewdnessFilterLevel")
.HasColumnType("integer");
b.Property<bool?>("LinksAllowed")
.HasColumnType("boolean");
b.Property<long?>("MaxAttachmentBytes")
.HasColumnType("bigint");
b.Property<long?>("MaxTextChars")
.HasColumnType("bigint");
b.Property<int?>("MeannessFilterLevel")
.HasColumnType("integer");
b.HasKey("Id");
b.ToTable("PermissionSettings");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
b.Property<string>("DisplayName")
.HasColumnType("text");
b.Property<string>("External")
.HasColumnType("text");
b.Property<decimal?>("ExternalId")
.HasColumnType("numeric(20,0)");
b.Property<bool>("IsBot")
.HasColumnType("boolean");
b.Property<Guid?>("ProtocolId")
.HasColumnType("uuid");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.HasKey("Id");
b.HasIndex("ProtocolId");
b.HasIndex("UserId");
b.ToTable("Users");
});
modelBuilder.Entity("vassago.Models.Protocol", b =>
{
b.HasBaseType("vassago.Models.Channel");
b.Property<string>("ConnectionToken")
.HasColumnType("text");
b.HasDiscriminator().HasValue("Protocol");
});
modelBuilder.Entity("ChannelUser", b =>
{
b.HasOne("vassago.Models.User", null)
.WithMany()
.HasForeignKey("OtherUsersId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("vassago.Models.Channel", null)
.WithMany()
.HasForeignKey("SeenInChannelsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("vassago.Models.Attachment", b =>
{
b.HasOne("vassago.Models.Message", "Message")
.WithMany("Attachments")
.HasForeignKey("MessageId");
b.Navigation("Message");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.HasOne("vassago.Models.Channel", "ParentChannel")
.WithMany("SubChannels")
.HasForeignKey("ParentChannelId");
b.HasOne("vassago.Models.PermissionSettings", "PermissionsOverrides")
.WithMany()
.HasForeignKey("PermissionsOverridesId");
b.HasOne("vassago.Models.Protocol", "Protocol")
.WithMany()
.HasForeignKey("ProtocolId");
b.Navigation("ParentChannel");
b.Navigation("PermissionsOverrides");
b.Navigation("Protocol");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.HasOne("vassago.Models.User", "Author")
.WithMany()
.HasForeignKey("AuthorId");
b.HasOne("vassago.Models.Channel", "Channel")
.WithMany("Messages")
.HasForeignKey("ChannelId");
b.Navigation("Author");
b.Navigation("Channel");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.HasOne("vassago.Models.Protocol", "Protocol")
.WithMany()
.HasForeignKey("ProtocolId");
b.HasOne("vassago.Models.User", null)
.WithMany("KnownAliases")
.HasForeignKey("UserId");
b.Navigation("Protocol");
});
modelBuilder.Entity("vassago.Models.Channel", b =>
{
b.Navigation("Messages");
b.Navigation("SubChannels");
});
modelBuilder.Entity("vassago.Models.Message", b =>
{
b.Navigation("Attachments");
});
modelBuilder.Entity("vassago.Models.User", b =>
{
b.Navigation("KnownAliases");
});
#pragma warning restore 612, 618
}
}
}

12
Models/Attachment.cs Normal file
View File

@ -0,0 +1,12 @@
namespace vassago.Models;
using System;
public class Attachment
{
public Guid Id { get; set; }
public ulong? ExternalId { get; set; }
public Uri Source { get; set; }
public byte[] Content { get; set; }
public string Filename { get; set; }
public Message Message { get; set; }
}

28
Models/Channel.cs Normal file
View File

@ -0,0 +1,28 @@
namespace vassago.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class Channel
{
public Guid Id { get; set; }
public ulong? ExternalId { get; set; }
public string DisplayName { get; set; }
public bool IsDM { get; set; }
public IEnumerable<User> OtherUsers { get; set; }
public PermissionSettings PermissionsOverrides { get; set; }
public IEnumerable<Channel> SubChannels { get; set; }
public Channel ParentChannel { get; set; }
public Protocol Protocol { get; set; }
public IEnumerable<Message> Messages { get; set; }
public virtual Task<Message> SendMessage(string text)
{
throw new NotImplementedException("derive from me");
}
public virtual Task<Message> SendFile(string path, string messageText = null)
{
throw new NotImplementedException("derive from me");
}
}

17
Models/ChattingContext.cs Normal file
View File

@ -0,0 +1,17 @@
namespace vassago.Models;
using Microsoft.EntityFrameworkCore;
public class ChattingContext : DbContext
{
public DbSet<Attachment> Attachments { get; set; }
public DbSet<Channel> Channels { get; set; }
//public DbSet<Emoji> Emoji {get;set;}
public DbSet<Message> Messages { get; set; }
public DbSet<PermissionSettings> PermissionSettings{get;set;}
public DbSet<Protocol> Protocols { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql(Shared.DBConnectionString);
}

33
Models/Enums.cs Normal file
View File

@ -0,0 +1,33 @@
namespace vassago.Models;
public static class Enumerations
{
public static string LewdnessFilterLevel(int level)
{
switch (level)
{
case 0:
return "this is a christian minecraft server 🙏";
case 1:
return "G-Rated";
case 2:
return "polite company";
case 3:
return ";) ;) ;)";
default:
return "ERROR";
}
}
public static string MeannessFilterLevel(int level)
{
switch (level)
{
case 0:
return "good vibes only";
case 1:
return "387.44 million miles of printed circuits, etc";
default:
return "ERROR";
}
}
}

30
Models/Message.cs Normal file
View File

@ -0,0 +1,30 @@
namespace vassago.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Discord.WebSocket;
public class Message
{
public Guid Id { get; set; }
public ulong? ExternalId { get; set; }
public string Content { get; set; }
public bool MentionsMe { get; set; }
public DateTimeOffset Timestamp { get; set; }
public bool ActedOn { get; set; }
///however it came from the protocol.
public string ExternalRepresentation { get; set; }
public IEnumerable<Attachment> Attachments { get; set; }
public User Author { get; set; }
public Channel Channel { get; set; }
public virtual Task Reply(string message)
{
throw new NotImplementedException("derive from me");
}
public virtual Task React(string reaction)
{
throw new NotImplementedException("derive from me");
}
}

View File

@ -0,0 +1,12 @@
namespace vassago.Models;
using System;
public class PermissionSettings
{
public int Id { get; set; }
public uint? MaxAttachmentBytes { get; set; }
public uint? MaxTextChars { get; set; }
public bool? LinksAllowed { get; set; }
public int? LewdnessFilterLevel { get; set; }
public int? MeannessFilterLevel { get; set; }
}

13
Models/Protocol.cs Normal file
View File

@ -0,0 +1,13 @@
namespace vassago.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class Protocol : Channel
{
//log in, log out, observe events?
//doesn't actually have to be a token, but it should be how an interface can find itself in the DB
public string ConnectionToken { get; set; }
}

16
Models/User.cs Normal file
View File

@ -0,0 +1,16 @@
namespace vassago.Models;
using System;
using System.Collections.Generic;
public class User //more like "user's account - no concept of the person outside of the protocol. (yet?)
{
public Guid Id { get; set; }
public ulong? ExternalId { get; set; }
public string DisplayName { get; set; }
public bool IsBot { get; set; } //webhook counts
public IEnumerable<Channel> SeenInChannels { get; set; }
public IEnumerable<User> KnownAliases { get; set; }
public Protocol Protocol { get; set; }
public string External { get; set; }
}

View File

@ -1,4 +1,3 @@
//https://discord.com/oauth2/authorize?client_id=913003037348491264&permissions=274877942784&scope=bot%20messages.read
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -6,336 +5,38 @@ using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord;
using Discord.WebSocket;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Diagnostics; using System.Diagnostics;
using Discord.Net; using vassago.Models;
namespace silverworker_discord namespace vassago
{ {
class Program class Program
{ {
private DiscordSocketClient _client; Configuration config = Configuration.Parse("appsettings.json");
private bool eventsSignedUp = false; private List<DiscordInterface.DiscordInterface> discords = new List<DiscordInterface.DiscordInterface>();
private Random r = new Random();
IConfigurationRoot config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", true, true)
.Build();
public static void Main(string[] args) public static void Main(string[] args)
=> new Program().MainAsync().GetAwaiter().GetResult(); => new Program().MainAsync().GetAwaiter().GetResult();
private Task Log(LogMessage msg)
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
}
public async Task MainAsync() public async Task MainAsync()
{ {
#if !DEBUG Shared.DBConnectionString = config.DBConnectionString;
Process[] processes = Process.GetProcesses(); Shared.dbContext = new ChattingContext();
Process currentProc = Process.GetCurrentProcess(); {
Console.WriteLine("Current proccess: {0}", currentProc.ProcessName); Shared.dbContext.Database.EnsureCreated();
foreach (Process process in processes)
{
if (currentProc.ProcessName == process.ProcessName && currentProc.Id != process.Id)
{
Console.Error.WriteLine($"{DateTime.Now} - Another instance of this process is already running: {process.Id} (I'm {currentProc.Id})");
return;
}
} }
#endif Conversion.Converter.Load(config.ExchangePairsLocation);
Conversion.Converter.Load(config["exchangePairsLocation"]); if(config.DiscordTokens.Any())
foreach(var dt in config.DiscordTokens)
_client = new DiscordSocketClient(new DiscordSocketConfig(){GatewayIntents = GatewayIntents.All});
_client.Log += Log;
_client.Ready += () => Task.Run(() =>
{
if (!eventsSignedUp)
{ {
eventsSignedUp = true; var d = new DiscordInterface.DiscordInterface();
Console.WriteLine("Bot is connected! going to sign up for message received and user joined in client ready"); await d.Init(dt);
discords.Add(d);
}
_client.MessageReceived += MessageReceived;
_client.UserJoined += UserJoined;
//_client.ButtonExecuted += MyButtonHandler;
_client.SlashCommandExecuted += SlashCommandsHelper.SlashCommandHandler;
SlashCommandsHelper.Register(_client).GetAwaiter().GetResult();
}
else
{
Console.WriteLine("bot appears to be RE connected, so I'm not going to sign up twice");
}
});
await _client.LoginAsync(TokenType.Bot, config["token"]);
await _client.StartAsync();
// Block this task until the program is closed.
await Task.Delay(-1); await Task.Delay(-1);
} }
#pragma warning disable 4014 //the "you're not awaiting this" warning. yeah I know, that's the beauty of an async method lol
#pragma warning disable 1998 //the "it's async but you're not awaiting anything".
private async Task MessageReceived(SocketMessage messageParam)
#pragma warning restore 1998
{
var message = messageParam as SocketUserMessage;
if (message == null) return;
if (message.Author.Id == _client.CurrentUser.Id) return;
Console.WriteLine($"#{message.Channel}[{DateTime.Now}][{message.Author.Username} [id={message.Author.Id}]][msg id: {message.Id}] {message.Content}");
if (message.Author.IsWebhook || message.Author.IsBot)
{
if (message.Author.Id == 159985870458322944) //MEE6
{
if (message.Content?.Contains("you just advanced") == true)
{
var newText = Regex.Replace(message.Content, "<[^>]*>", message.Author.Username);
newText = Regex.Replace(newText, "level [\\d]+", "level -1");
Features.mock(newText, message);
}
}
}
else
{
var didThing = false;
var contentWithoutMention = message.Content;
var mentionedMe = false;
if (message.MentionedUsers?.FirstOrDefault(muid => muid.Id == _client.CurrentUser.Id) != null)
{
var mentionOfMe = "<@" + _client.CurrentUser.Id + ">";
contentWithoutMention = message.Content.Replace(mentionOfMe + " ", null);
contentWithoutMention = contentWithoutMention.Replace(mentionOfMe, null);
mentionedMe = true;
}
var wordLikes = message.Content.Split(' ', StringSplitOptions.TrimEntries);
var links = wordLikes?.Where(wl => Uri.IsWellFormedUriString(wl, UriKind.Absolute)).Select(wl => new Uri(wl));
if (links != null && links.Count() > 0)
{
foreach (var link in links)
{
if (link.Host.EndsWith(".tiktok.com"))
{
Features.detiktokify(link, message);
didThing = true;
}
}
}
if (message.Attachments?.Count > 0)
{
Console.WriteLine($"{message.Attachments.Count} attachments");
var appleReactions = false;
foreach (var att in message.Attachments)
{
if (att.Filename?.EndsWith(".heic") == true)
{
Features.deheic(message, att);
appleReactions = true;
didThing = true;
}
}
if (appleReactions)
{
message.AddReactionAsync(new Emoji("\U0001F34F"));
}
}
var msgText = message.Content?.ToLower();
if (!string.IsNullOrWhiteSpace(msgText))
{
if (Regex.IsMatch(msgText, "\\bcloud( |-)?native\\b", RegexOptions.IgnoreCase) ||
Regex.IsMatch(msgText, "\\benterprise( |-)?(level|solution)\\b", RegexOptions.IgnoreCase))
{
switch (r.Next(2))
{
case 0:
await message.AddReactionAsync(new Emoji("\uD83E\uDD2E")); //vomit emoji
break;
case 1:
await message.AddReactionAsync(new Emoji("\uD83C\uDDE7")); //B emoji
await message.AddReactionAsync(new Emoji("\uD83C\uDDE6")); //A
await message.AddReactionAsync(new Emoji("\uD83C\uDDF3")); //N
break;
}
didThing = true;
}
if (Regex.IsMatch(msgText, "^(s?he|(yo)?u|y'?all) thinks? i'?m (playin|jokin|kiddin)g?$", RegexOptions.IgnoreCase))
{
await message.Channel.SendMessageAsync("I believed you for a second, but then you assured me you's a \uD83C\uDDE7 \uD83C\uDDEE \uD83C\uDDF9 \uD83C\uDDE8 \uD83C\uDDED");
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bskynet\\b", RegexOptions.IgnoreCase))
{
Features.Skynet(message);
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bchatgpt\\b", RegexOptions.IgnoreCase))
{
message.Channel.SendMessageAsync("chatGPT is **weak**. also, are we done comparing every little if-then-else to skynet?");
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bi need (an? )?(peptalk|inspiration|ego-?boost)\\b", RegexOptions.IgnoreCase))
{
Console.WriteLine("peptalk");
Features.peptalk(message);
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bwish me luck\\b", RegexOptions.IgnoreCase))
{
if (r.Next(20) == 0)
{
await message.AddReactionAsync(new Emoji("\U0001f340"));//4-leaf clover
}
else
{
await message.AddReactionAsync(new Emoji("☘️"));
}
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bgaslight(ing)?\\b", RegexOptions.IgnoreCase))
{
message.Channel.SendMessageAsync("that's not what gaslight means. Did you mean \"say something that (you believe) is wrong\"?");
didThing = true;
}
if (msgText.Contains("!qrplz "))
{
Features.qrify(message.Content.Substring("!qrplz ".Length + msgText.IndexOf("!qrplz ")), message);
didThing = true;
}
if (msgText.Contains("!freedomunits "))
{
Features.Convert(message, contentWithoutMention);
didThing = true;
}
if (Regex.IsMatch(msgText, "!joke\\b"))
{
Features.Joke(message);
didThing = true;
}
if (Regex.IsMatch(msgText, "!pulse ?check\\b"))
{
message.Channel.SendFileAsync("assets/ekgblip.png");
Console.WriteLine(Conversion.Converter.DebugInfo());
didThing = true;
}
if (mentionedMe && (Regex.IsMatch(msgText, "\\brecipe for .+") || Regex.IsMatch(msgText, ".+ recipe\\b")))
{
Features.Recipe(message);
didThing = true;
}
if (msgText.Contains("cognitive dissonance") == true)
{
message.ReplyAsync("that's not what cognitive dissonance means. Did you mean \"hypocrisy\"?");
didThing = true;
}
if (mentionedMe && Regex.IsMatch(msgText, "what'?s the longest (six|6)(-| )?letter word( in english)?\\b"))
{
Task.Run(async () =>
{
await message.Channel.SendMessageAsync("mother.");
await Task.Delay(3000);
await message.Channel.SendMessageAsync("oh, longest? I thought you said fattest.");
});
didThing = true;
}
if (Regex.IsMatch(msgText, "\\bthank (yo)?u\\b", RegexOptions.IgnoreCase) &&
(mentionedMe || Regex.IsMatch(msgText, "\\b(sh?tik)?bot\\b", RegexOptions.IgnoreCase)))
{
switch (r.Next(4))
{
case 0:
message.Channel.SendMessageAsync("you're welcome, citizen!");
break;
case 1:
message.AddReactionAsync(new Emoji("☺"));
break;
case 2:
message.AddReactionAsync(new Emoji("\U0001F607")); //smiling face with halo
break;
case 3:
switch (r.Next(9))
{
case 0:
message.AddReactionAsync(new Emoji("❤")); //normal heart, usually rendered red
break;
case 1:
message.AddReactionAsync(new Emoji("\U0001F9E1")); //orange heart
break;
case 2:
message.AddReactionAsync(new Emoji("\U0001F49B")); //yellow heart
break;
case 3:
message.AddReactionAsync(new Emoji("\U0001F49A")); //green heart
break;
case 4:
message.AddReactionAsync(new Emoji("\U0001F499")); //blue heart
break;
case 5:
message.AddReactionAsync(new Emoji("\U0001F49C")); //purple heart
break;
case 6:
message.AddReactionAsync(new Emoji("\U0001F90E")); //brown heart
break;
case 7:
message.AddReactionAsync(new Emoji("\U0001F5A4")); //black heart
break;
case 8:
message.AddReactionAsync(new Emoji("\U0001F90D")); //white heart
break;
}
break;
}
didThing = true;
#pragma warning restore 4014
}
// if (didThing == false && mentionedMe && contentWithoutMention.Contains("how long has that been there?"))
// {
// await message.Channel.SendMessageAsync("text", false, null, null, null, null, new ComponentBuilder().WithButton("label", "custom-id").Build());
// didThing = true;
// }
if (didThing == false && mentionedMe && contentWithoutMention.Contains('?'))
{
Console.WriteLine("providing bullshit nonanswer / admitting uselessness");
var responses = new List<string>(){
@"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.SendMessageAsync(responses[r.Next(responses.Count)]);
didThing = true;
}
}
}
}
private Task UserJoined(SocketGuildUser arg)
{
Console.WriteLine($"user joined: {arg.Nickname}. Guid: {arg.Guild.Id}. Channel: {arg.Guild.DefaultChannel}");
var abbreviatedNickname = arg.Nickname;
if (arg.Nickname.Length > 3)
{
abbreviatedNickname = arg.Nickname.Substring(0, arg.Nickname.Length / 3);
}
Console.WriteLine($"imma call him {abbreviatedNickname}");
return arg.Guild.DefaultChannel.SendMessageAsync($"oh hey {abbreviatedNickname}- IPLAYTHESEALOFORICHALCOS <:ORICHALCOS:852749196633309194>");
}
private async Task ButtonHandler(SocketMessageComponent component)
{
switch(component.Data.CustomId)
{
case "custom-id":
await component.RespondAsync($"{component.User.Mention}, it's been here the whole time!");
break;
}
}
} }
} }

View File

@ -1,10 +1,14 @@
namespace vassago;
using System; using System;
using System.Net.Http;
using vassago.Models;
namespace silverworker_discord
public static class Shared
{ {
public static class Shared public static Random r = new Random();
{ public static string DBConnectionString { get; set; }
public static Random r = new Random(); public static ChattingContext dbContext { get; set; }
} public static HttpClient HttpClient { get; internal set; } = new HttpClient();
} }

View File

@ -7,7 +7,7 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace silverworker_discord namespace vassago
{ {
public class ExternalProcess public class ExternalProcess
{ {

View File

@ -1,16 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<RootNamespace>silverworker_discord</RootNamespace> <RootNamespace>vassago</RootNamespace>
<UserSecretsId>1b425139-0de4-443f-91e6-a8eec44855dd</UserSecretsId>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="discord.net" Version="3.10.0" /> <PackageReference Include="discord.net" Version="3.10.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" /> <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="QRCoder" Version="1.4.2" />
<PackageReference Include="youtubedlsharp" Version="0.3.1" /> <PackageReference Include="youtubedlsharp" Version="0.3.1" />
</ItemGroup> </ItemGroup>
@ -32,7 +38,7 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="appsettings.json"> <None Update="appsettings.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>
</Project> </Project>