2020-05-01 23:52:52 +00:00
using System.Threading.Tasks ;
2020-12-23 01:19:02 +00:00
using Myriad.Builders ;
using Myriad.Cache ;
using Myriad.Extensions ;
2020-12-22 12:15:26 +00:00
using Myriad.Gateway ;
2020-12-23 01:19:02 +00:00
using Myriad.Rest ;
using Myriad.Rest.Exceptions ;
using Myriad.Rest.Types ;
2020-12-25 12:58:45 +00:00
using Myriad.Rest.Types.Requests ;
2020-12-23 01:19:02 +00:00
using Myriad.Types ;
2020-12-22 12:15:26 +00:00
2020-06-11 21:20:46 +00:00
using PluralKit.Core ;
2020-05-01 23:52:52 +00:00
2020-07-18 11:05:22 +00:00
using Serilog ;
2020-05-01 23:52:52 +00:00
namespace PluralKit.Bot
{
2020-12-22 12:15:26 +00:00
public class ReactionAdded : IEventHandler < MessageReactionAddEvent >
2020-05-01 23:52:52 +00:00
{
2020-08-29 11:46:27 +00:00
private readonly IDatabase _db ;
private readonly ModelRepository _repo ;
2020-10-23 10:18:28 +00:00
private readonly CommandMessageService _commandMessageService ;
2020-08-29 11:46:27 +00:00
private readonly ILogger _logger ;
2020-12-23 01:19:02 +00:00
private readonly IDiscordCache _cache ;
2020-12-25 12:58:45 +00:00
private readonly EmbedService _embeds ;
2020-12-23 01:19:02 +00:00
private readonly Bot _bot ;
private readonly DiscordApiClient _rest ;
2020-05-01 23:52:52 +00:00
2020-12-25 12:58:45 +00:00
public ReactionAdded ( ILogger logger , IDatabase db , ModelRepository repo , CommandMessageService commandMessageService , IDiscordCache cache , Bot bot , DiscordApiClient rest , EmbedService embeds )
2020-05-01 23:52:52 +00:00
{
2020-08-29 11:46:27 +00:00
_db = db ;
_repo = repo ;
2020-10-23 10:18:28 +00:00
_commandMessageService = commandMessageService ;
2020-12-23 01:19:02 +00:00
_cache = cache ;
_bot = bot ;
_rest = rest ;
2020-12-25 12:58:45 +00:00
_embeds = embeds ;
2020-07-18 11:05:22 +00:00
_logger = logger . ForContext < ReactionAdded > ( ) ;
2020-05-01 23:52:52 +00:00
}
2020-12-22 12:15:26 +00:00
public async Task Handle ( Shard shard , MessageReactionAddEvent evt )
2020-06-11 21:20:46 +00:00
{
2020-12-23 01:19:02 +00:00
await TryHandleProxyMessageReactions ( evt ) ;
2020-06-11 21:20:46 +00:00
}
2020-12-23 01:19:02 +00:00
private async ValueTask TryHandleProxyMessageReactions ( MessageReactionAddEvent evt )
2020-06-11 21:20:46 +00:00
{
2020-06-24 14:47:34 +00:00
// Sometimes we get events from users that aren't in the user cache
// We just ignore all of those for now, should be quite rare...
2020-12-23 01:19:02 +00:00
if ( ! _cache . TryGetUser ( evt . UserId , out var user ) )
return ;
var channel = _cache . GetChannel ( evt . ChannelId ) ;
2020-11-04 16:30:00 +00:00
// check if it's a command message first
// since this can happen in DMs as well
if ( evt . Emoji . Name = = "\u274c" )
{
await using var conn = await _db . Obtain ( ) ;
2020-12-23 01:19:02 +00:00
var commandMsg = await _commandMessageService . GetCommandMessage ( conn , evt . MessageId ) ;
2020-11-04 16:30:00 +00:00
if ( commandMsg ! = null )
{
await HandleCommandDeleteReaction ( evt , commandMsg ) ;
return ;
}
}
// Only proxies in guild text channels
2020-12-23 01:19:02 +00:00
if ( channel . Type ! = Channel . ChannelType . GuildText ) return ;
2020-11-04 16:30:00 +00:00
2020-06-18 15:49:43 +00:00
// Ignore reactions from bots (we can't DM them anyway)
2020-12-23 01:19:02 +00:00
if ( user . Bot ) return ;
2020-10-23 10:18:28 +00:00
2020-06-11 21:20:46 +00:00
switch ( evt . Emoji . Name )
{
// Message deletion
case "\u274C" : // Red X
2020-10-23 10:18:28 +00:00
{
await using var conn = await _db . Obtain ( ) ;
2020-12-23 01:19:02 +00:00
var msg = await _repo . GetMessage ( conn , evt . MessageId ) ;
2020-10-23 10:18:28 +00:00
if ( msg ! = null )
await HandleProxyDeleteReaction ( evt , msg ) ;
2020-11-04 16:30:00 +00:00
2020-10-23 10:18:28 +00:00
break ;
}
2020-06-11 21:20:46 +00:00
case "\u2753" : // Red question mark
case "\u2754" : // White question mark
2020-10-23 10:18:28 +00:00
{
await using var conn = await _db . Obtain ( ) ;
2020-12-23 01:19:02 +00:00
var msg = await _repo . GetMessage ( conn , evt . MessageId ) ;
2020-10-23 10:18:28 +00:00
if ( msg ! = null )
2020-12-23 01:19:02 +00:00
await HandleQueryReaction ( evt , msg ) ;
2020-10-23 10:18:28 +00:00
2020-06-11 21:20:46 +00:00
break ;
2020-10-23 10:18:28 +00:00
}
2020-06-11 21:20:46 +00:00
case "\U0001F514" : // Bell
case "\U0001F6CE" : // Bellhop bell
case "\U0001F3D3" : // Ping pong paddle (lol)
case "\u23F0" : // Alarm clock
case "\u2757" : // Exclamation mark
2020-10-23 10:18:28 +00:00
{
await using var conn = await _db . Obtain ( ) ;
2020-12-23 01:19:02 +00:00
var msg = await _repo . GetMessage ( conn , evt . MessageId ) ;
2020-10-23 10:18:28 +00:00
if ( msg ! = null )
2020-06-11 21:20:46 +00:00
await HandlePingReaction ( evt , msg ) ;
break ;
2020-10-23 10:18:28 +00:00
}
2020-06-11 21:20:46 +00:00
}
}
2020-12-23 01:19:02 +00:00
private async ValueTask HandleProxyDeleteReaction ( MessageReactionAddEvent evt , FullMessage msg )
2020-06-11 21:20:46 +00:00
{
2020-12-23 01:19:02 +00:00
if ( ! _bot . PermissionsIn ( evt . ChannelId ) . HasFlag ( PermissionSet . ManageMessages ) )
return ;
2020-06-11 21:20:46 +00:00
// Can only delete your own message
2020-12-23 01:19:02 +00:00
if ( msg . Message . Sender ! = evt . UserId ) return ;
2020-06-11 21:20:46 +00:00
try
{
2020-12-23 01:19:02 +00:00
await _rest . DeleteMessage ( evt . ChannelId , evt . MessageId ) ;
2020-06-11 21:20:46 +00:00
}
catch ( NotFoundException )
{
// Message was deleted by something/someone else before we got to it
}
2020-12-23 01:19:02 +00:00
await _db . Execute ( c = > _repo . DeleteMessage ( c , evt . MessageId ) ) ;
2020-06-11 21:20:46 +00:00
}
2020-12-23 01:19:02 +00:00
private async ValueTask HandleCommandDeleteReaction ( MessageReactionAddEvent evt , CommandMessage msg )
2020-10-18 05:59:36 +00:00
{
2020-12-23 01:19:02 +00:00
// TODO: why does the bot need manage messages if it's deleting its own messages??
if ( ! _bot . PermissionsIn ( evt . ChannelId ) . HasFlag ( PermissionSet . ManageMessages ) )
2020-10-23 10:18:28 +00:00
return ;
2020-10-18 05:59:36 +00:00
// Can only delete your own message
2020-12-23 01:19:02 +00:00
if ( msg . AuthorId ! = evt . UserId )
2020-10-23 10:18:28 +00:00
return ;
2020-10-18 05:59:36 +00:00
try
{
2020-12-23 01:19:02 +00:00
await _rest . DeleteMessage ( evt . ChannelId , evt . MessageId ) ;
2020-10-18 05:59:36 +00:00
}
catch ( NotFoundException )
{
// Message was deleted by something/someone else before we got to it
}
// No need to delete database row here, it'll get deleted by the once-per-minute scheduled task.
}
2020-12-23 01:19:02 +00:00
private async ValueTask HandleQueryReaction ( MessageReactionAddEvent evt , FullMessage msg )
2020-06-11 21:20:46 +00:00
{
2020-12-25 12:58:45 +00:00
var guild = _cache . GetGuild ( evt . GuildId ! . Value ) ;
2020-06-11 21:20:46 +00:00
// Try to DM the user info about the message
2020-12-23 01:19:02 +00:00
// var member = await evt.Guild.GetMember(evt.User.Id);
2020-06-11 21:20:46 +00:00
try
{
2020-12-25 12:58:45 +00:00
var dm = await _cache . GetOrCreateDmChannel ( _rest , evt . UserId ) ;
await _rest . CreateMessage ( dm . Id , new MessageRequest
{
Embed = await _embeds . CreateMemberEmbed ( msg . System , msg . Member , guild , LookupContext . ByNonOwner )
} ) ;
await _rest . CreateMessage ( dm . Id , new MessageRequest
{
Embed = await _embeds . CreateMessageInfoEmbed ( msg )
} ) ;
2020-06-11 21:20:46 +00:00
}
catch ( UnauthorizedException ) { } // No permissions to DM, can't check for this :(
2020-07-18 11:05:22 +00:00
await TryRemoveOriginalReaction ( evt ) ;
2020-06-11 21:20:46 +00:00
}
2020-12-23 01:19:02 +00:00
private async ValueTask HandlePingReaction ( MessageReactionAddEvent evt , FullMessage msg )
2020-05-01 23:52:52 +00:00
{
2020-12-23 01:19:02 +00:00
if ( ! _bot . PermissionsIn ( evt . ChannelId ) . HasFlag ( PermissionSet . ManageMessages ) )
return ;
2020-06-11 21:20:46 +00:00
// Check if the "pinger" has permission to send messages in this channel
// (if not, PK shouldn't send messages on their behalf)
2020-12-23 01:19:02 +00:00
var member = await _rest . GetGuildMember ( evt . GuildId ! . Value , evt . UserId ) ;
var requiredPerms = PermissionSet . ViewChannel | PermissionSet . SendMessages ;
if ( member = = null | | ! _cache . PermissionsFor ( evt . ChannelId , member ) . HasFlag ( requiredPerms ) ) return ;
2020-06-11 21:20:46 +00:00
if ( msg . System . PingsEnabled )
{
// If the system has pings enabled, go ahead
2020-12-23 01:19:02 +00:00
var embed = new EmbedBuilder ( ) . Description ( $"[Jump to pinged message]({evt.JumpLink()})" ) ;
await _rest . CreateMessage ( evt . ChannelId , new ( )
{
Content =
$"Psst, **{msg.Member.DisplayName()}** (<@{msg.Message.Sender}>), you have been pinged by <@{evt.UserId}>." ,
Embed = embed . Build ( ) ,
AllowedMentions = new AllowedMentions { Users = new [ ] { msg . Message . Sender } }
} ) ;
2020-06-11 21:20:46 +00:00
}
else
{
// If not, tell them in DMs (if we can)
try
{
2020-12-25 12:58:45 +00:00
var dm = await _cache . GetOrCreateDmChannel ( _rest , evt . UserId ) ;
await _rest . CreateMessage ( dm . Id , new MessageRequest
{
Content = $"{Emojis.Error} {msg.Member.DisplayName()}'s system has disabled reaction pings. If you want to mention them anyway, you can copy/paste the following message:"
} ) ;
await _rest . CreateMessage ( dm . Id , new MessageRequest { Content = $"<@{msg.Message.Sender}>" . AsCode ( ) } ) ;
2020-06-11 21:20:46 +00:00
}
catch ( UnauthorizedException ) { }
}
2020-07-18 11:05:22 +00:00
await TryRemoveOriginalReaction ( evt ) ;
}
2020-12-23 01:19:02 +00:00
private async Task TryRemoveOriginalReaction ( MessageReactionAddEvent evt )
2020-07-18 11:05:22 +00:00
{
2020-12-23 01:19:02 +00:00
if ( _bot . PermissionsIn ( evt . ChannelId ) . HasFlag ( PermissionSet . ManageMessages ) )
await _rest . DeleteOwnReaction ( evt . ChannelId , evt . MessageId , evt . Emoji ) ;
2020-05-01 23:52:52 +00:00
}
}
}