vassago/DiscordInterface/DiscordInterface.cs

323 lines
12 KiB
C#
Raw Normal View History

2023-06-01 00:03:23 -04:00
//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;
2023-06-05 14:55:48 -04:00
using vassago.Models;
2023-06-01 00:03:23 -04:00
using vassago.Behavior;
2023-06-05 14:55:48 -04:00
using Discord.Rest;
2023-06-01 00:03:23 -04:00
namespace vassago.DiscordInterface;
public class DiscordInterface
{
2023-06-05 14:55:48 -04:00
internal const string PROTOCOL = "discord";
internal DiscordSocketClient client;
2023-06-01 00:03:23 -04:00
private bool eventsSignedUp = false;
private ChattingContext _db;
public DiscordInterface()
{
_db = Shared.dbContext;
}
public async Task Init(string token)
{
2023-06-05 14:55:48 -04:00
//var c = _db.Channels.FirstOrDefault(ci => ci.ExternalId == channel.Id);
//Todo: find protocol reference in DB.
//TODO: should protocol be shared across mutliple accounts on that protocol? should protocol be a per-vassago-account thing?
//TODO: should protocol be associated with a connection token? how would that be updated?
2023-06-01 00:03:23 -04:00
2023-06-05 14:55:48 -04:00
client = new DiscordSocketClient(new DiscordSocketConfig() { GatewayIntents = GatewayIntents.All });
client.Log += (msg) =>
2023-06-01 00:03:23 -04:00
{
Console.WriteLine(msg.ToString());
return Task.CompletedTask;
};
2023-06-05 14:55:48 -04:00
client.Ready += () => Task.Run(() =>
2023-06-01 00:03:23 -04:00
{
if (!eventsSignedUp)
{
eventsSignedUp = true;
Console.WriteLine("Bot is connected! going to sign up for message received and user joined in client ready");
2023-06-05 14:55:48 -04:00
client.MessageReceived += MessageReceived;
2023-06-01 00:03:23 -04:00
// _client.MessageUpdated +=
2023-06-05 14:55:48 -04:00
client.UserJoined += UserJoined;
client.SlashCommandExecuted += SlashCommandHandler;
2023-06-01 00:03:23 -04:00
// _client.ChannelCreated +=
// _client.ChannelDestroyed +=
// _client.ChannelUpdated +=
// _client.GuildMemberUpdated +=
// _client.UserBanned +=
// _client.UserLeft +=
// _client.ThreadCreated +=
// _client.ThreadUpdated +=
// _client.ThreadDeleted +=
// _client.JoinedGuild +=
// _client.GuildUpdated +=
// _client.LeftGuild +=
2023-06-05 14:55:48 -04:00
SlashCommandsHelper.Register(client).GetAwaiter().GetResult();
2023-06-01 00:03:23 -04:00
}
else
{
Console.WriteLine("bot appears to be RE connected, so I'm not going to sign up twice");
}
});
2023-06-05 14:55:48 -04:00
await client.LoginAsync(TokenType.Bot, token);
await client.StartAsync();
2023-06-01 00:03:23 -04:00
}
#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;
2023-06-05 14:55:48 -04:00
if (suMessage == null)
2023-06-01 00:03:23 -04:00
{
2023-06-05 14:55:48 -04:00
Console.WriteLine($"{messageParam.Content}, but not a user message");
2023-06-05 15:25:43 -04:00
return;
2023-06-01 00:03:23 -04:00
}
Console.WriteLine($"#{suMessage.Channel}[{DateTime.Now}][{suMessage.Author.Username} [id={suMessage.Author.Id}]][msg id: {suMessage.Id}] {suMessage.Content}");
2023-06-05 14:55:48 -04:00
var m = UpsertMessage(suMessage);
2023-06-01 00:03:23 -04:00
2023-06-05 14:55:48 -04:00
if (suMessage.MentionedUsers?.FirstOrDefault(muid => muid.Id == client.CurrentUser.Id) != null)
2023-06-01 00:03:23 -04:00
{
2023-06-05 14:55:48 -04:00
var mentionOfMe = "<@" + client.CurrentUser.Id + ">";
2023-06-01 00:03:23 -04:00
m.MentionsMe = true;
}
2023-06-05 15:25:43 -04:00
if ((suMessage.Author.Id != client.CurrentUser.Id))
2023-06-01 00:03:23 -04:00
{
2023-06-05 15:25:43 -04:00
if (await thingmanagementdoer.Instance.ActOn(m))
{
m.ActedOn = true;
Console.WriteLine("survived a savechanges: 103");
}
2023-06-01 00:03:23 -04:00
}
2023-06-05 14:55:48 -04:00
_db.SaveChanges();
}
2023-06-01 00:03:23 -04:00
private Task UserJoined(SocketGuildUser arg)
{
var guild = UpsertChannel(arg.Guild);
var defaultChannel = UpsertChannel(arg.Guild.DefaultChannel);
defaultChannel.ParentChannel = guild;
var u = UpsertUser(arg);
2023-06-05 14:55:48 -04:00
//TODO: seen in channels
// 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();
Console.WriteLine("survived a savechanges: 123");
// }
2023-06-01 00:03:23 -04:00
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;
}
}
2023-06-05 14:55:48 -04:00
internal vassago.Models.Attachment UpsertAttachment(IAttachment dAttachment)
{
2023-06-05 15:25:43 -04:00
var addPlease = false;
2023-06-05 14:55:48 -04:00
var a = _db.Attachments.FirstOrDefault(ai => ai.ExternalId == dAttachment.Id);
if (a == null)
{
2023-06-05 15:25:43 -04:00
addPlease = true;
a = new vassago.Models.Attachment();
2023-06-05 14:55:48 -04:00
}
a.ContentType = dAttachment.ContentType;
a.Description = dAttachment.Description;
a.Filename = dAttachment.Filename;
a.Size = dAttachment.Size;
a.Source = new Uri(dAttachment.Url);
2023-06-05 15:25:43 -04:00
if (addPlease)
{
_db.Attachments.Add(a);
}
2023-06-05 14:55:48 -04:00
return a;
}
internal Message UpsertMessage(IUserMessage dMessage)
2023-06-01 00:03:23 -04:00
{
2023-06-05 14:55:48 -04:00
var addPlease = false;
var m = _db.Messages.FirstOrDefault(mi => mi.ExternalId == dMessage.Id);
if (m == null)
{
addPlease = true;
m = new Message();
}
2023-06-05 15:25:43 -04:00
m.Attachments = m.Attachments ?? new List<vassago.Models.Attachment>();
2023-06-05 14:55:48 -04:00
if (dMessage.Attachments?.Any() == true)
2023-06-01 00:03:23 -04:00
{
2023-06-05 14:55:48 -04:00
m.Attachments = new List<vassago.Models.Attachment>();
foreach (var da in dMessage.Attachments)
{
m.Attachments.Add(UpsertAttachment(da));
}
2023-06-01 00:03:23 -04:00
}
2023-06-05 14:55:48 -04:00
m.Author = UpsertUser(dMessage.Author);
m.Channel = UpsertChannel(dMessage.Channel);
m.Content = dMessage.Content;
m.ExternalId = dMessage.Id;
m.Timestamp = dMessage.EditedTimestamp ?? dMessage.CreatedAt;
if (dMessage.MentionedUserIds?.FirstOrDefault(muid => muid == client.CurrentUser.Id) != null)
{
m.MentionsMe = true;
}
if (addPlease)
{
_db.Messages.Add(m);
}
2023-06-05 15:25:43 -04:00
m.Reply = (t) => { return dMessage.ReplyAsync(t); };
m.React = (e) => { return dMessage.AddReactionAsync(Emote.Parse(e)); };
2023-06-05 14:55:48 -04:00
return m;
}
internal Channel UpsertChannel(IMessageChannel channel)
{
var addPlease = false;
Channel c = _db.Channels.FirstOrDefault(ci => ci.ExternalId == channel.Id);
if (c == null)
{
addPlease = true;
c = new Channel();
}
c.DisplayName = channel.Name;
c.ExternalId = channel.Id;
c.IsDM = channel is IPrivateChannel;
c.Messages = c.Messages ?? new List<Message>();
//c.Messages = await channel.GetMessagesAsync(); //TODO: this, but only on startup or channel join
//c.OtherUsers = c.OtherUsers ?? new List<User>();
//c.OtherUsers = await channel.GetUsersAsync(); //TODO: this, but only on startup or channel join
c.Protocol = PROTOCOL;
if (channel is IGuildChannel)
2023-06-01 00:03:23 -04:00
{
c.ParentChannel = UpsertChannel((channel as IGuildChannel).Guild);
}
else if (channel is IPrivateChannel)
{
2023-06-05 14:55:48 -04:00
c.ParentChannel = null;
2023-06-01 00:03:23 -04:00
}
else
{
2023-06-05 14:55:48 -04:00
c.ParentChannel = null;
Console.Error.WriteLine($"trying to upsert channel {channel.Id}/{channel.Name}, but it's neither guildchannel nor private channel. shrug.jpg");
2023-06-01 00:03:23 -04:00
}
2023-06-05 14:55:48 -04:00
c.ParentChannel.SubChannels.Add(c);
c.SubChannels = c.SubChannels ?? new List<Channel>();
if (addPlease)
{
_db.Channels.Add(c);
}
c.SendMessage = (t) => { return channel.SendMessageAsync(t); };
c.SendFile = (f, t) => { return channel.SendFileAsync(f, t); };
return c;
2023-06-01 00:03:23 -04:00
}
2023-06-05 14:55:48 -04:00
internal Channel UpsertChannel(IGuild channel)
2023-06-01 00:03:23 -04:00
{
2023-06-05 14:55:48 -04:00
var addPlease = false;
Channel c = _db.Channels.FirstOrDefault(ci => ci.ExternalId == channel.Id);
if (c == null)
{
addPlease = true;
c = new Channel();
}
c.DisplayName = channel.Name;
c.ExternalId = channel.Id;
c.IsDM = false;
c.Messages = c.Messages ?? new List<Message>();
//c.Messages = await channel.GetMessagesAsync(); //TODO: this, but only on startup or channel join
//c.OtherUsers = c.OtherUsers ?? new List<User>();
//c.OtherUsers = await channel.GetUsersAsync(); //TODO: this, but only on startup or channel join
c.Protocol = PROTOCOL;
c.ParentChannel = null;
c.SubChannels = c.SubChannels ?? new List<Channel>();
if (addPlease)
2023-06-01 00:03:23 -04:00
{
2023-06-05 14:55:48 -04:00
_db.Channels.Add(c);
2023-06-01 00:03:23 -04:00
}
2023-06-05 14:55:48 -04:00
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"); };
2023-06-01 00:03:23 -04:00
return c;
}
2023-06-05 14:55:48 -04:00
internal User UpsertUser(IUser user)
2023-06-01 00:03:23 -04:00
{
2023-06-05 14:55:48 -04:00
var addPlease = false;
2023-06-01 00:03:23 -04:00
var u = _db.Users.FirstOrDefault(ui => ui.ExternalId == user.Id);
2023-06-05 14:55:48 -04:00
if (u == null)
2023-06-01 00:03:23 -04:00
{
2023-06-05 14:55:48 -04:00
addPlease = true;
u = new User();
2023-06-01 00:03:23 -04:00
}
2023-06-05 14:55:48 -04:00
u.Username = user.Username;
u.ExternalId = user.Id;
u.IsBot = user.IsBot || user.IsWebhook;
u.Protocol = PROTOCOL;
if (addPlease)
{
_db.Users.Add(u);
}
2023-06-01 00:03:23 -04:00
return u;
}
}