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 ;
2021-09-25 19:04:06 +00:00
private readonly Cluster _cluster ;
2020-12-23 01:19:02 +00:00
private readonly DiscordApiClient _rest ;
2020-05-01 23:52:52 +00:00
2021-09-25 19:04:06 +00:00
public ReactionAdded ( ILogger logger , IDatabase db , ModelRepository repo , CommandMessageService commandMessageService , IDiscordCache cache , Bot bot , Cluster cluster , 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 ;
2021-09-25 19:04:06 +00:00
_cluster = cluster ;
2020-12-23 01:19:02 +00:00
_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 )
2021-08-27 15:03:47 +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...
2021-11-18 18:11:02 +00:00
if ( ! ( await _cache . TryGetUser ( evt . UserId ) is User user ) )
2020-12-23 01:19:02 +00:00
return ;
2021-09-25 19:04:06 +00:00
// ignore any reactions added by *us*
2021-11-22 00:42:35 +00:00
if ( evt . UserId = = await _cache . GetOwnUser ( ) )
2021-09-25 19:04:06 +00:00
return ;
// Ignore reactions from bots (we can't DM them anyway)
if ( user . Bot ) return ;
2021-11-18 01:41:02 +00:00
var channel = await _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" )
{
2021-09-13 06:13:36 +00:00
// in DMs, allow deleting any PK message
if ( channel . GuildId = = null )
{
await HandleCommandDeleteReaction ( evt , null ) ;
return ;
}
2021-09-30 01:51:38 +00:00
var commandMsg = await _commandMessageService . GetCommandMessage ( evt . MessageId ) ;
2020-11-04 16:30:00 +00:00
if ( commandMsg ! = null )
{
await HandleCommandDeleteReaction ( evt , commandMsg ) ;
return ;
}
}
2021-07-08 13:17:35 +00:00
// Proxied messages only exist in guild text channels, so skip checking if we're elsewhere
2021-07-08 16:45:59 +00:00
if ( ! DiscordUtils . IsValidGuildChannel ( channel ) ) return ;
2020-11-04 16:30:00 +00:00
2020-06-11 21:20:46 +00:00
switch ( evt . Emoji . Name )
{
// Message deletion
case "\u274C" : // Red X
2021-08-27 15:03:47 +00:00
{
2021-09-03 20:20:07 +00:00
var msg = await _db . Execute ( c = > _repo . GetMessage ( c , evt . MessageId ) ) ;
2021-08-27 15:03:47 +00:00
if ( msg ! = null )
await HandleProxyDeleteReaction ( evt , msg ) ;
break ;
}
2020-06-11 21:20:46 +00:00
case "\u2753" : // Red question mark
case "\u2754" : // White question mark
2021-08-27 15:03:47 +00:00
{
2021-09-03 20:20:07 +00:00
var msg = await _db . Execute ( c = > _repo . GetMessage ( c , evt . MessageId ) ) ;
2021-08-27 15:03:47 +00:00
if ( msg ! = null )
await HandleQueryReaction ( evt , msg ) ;
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
2021-08-27 15:03:47 +00:00
{
2021-09-03 20:20:07 +00:00
var msg = await _db . Execute ( c = > _repo . GetMessage ( c , evt . MessageId ) ) ;
2021-08-27 15:03:47 +00:00
if ( msg ! = null )
await HandlePingReaction ( evt , msg ) ;
break ;
}
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
{
2021-11-22 00:42:35 +00:00
if ( ! ( await _cache . PermissionsIn ( evt . ChannelId ) ) . HasFlag ( PermissionSet . ManageMessages ) )
2020-12-23 01:19:02 +00:00
return ;
2021-08-27 15:03:47 +00:00
2021-09-30 01:51:38 +00:00
var system = await _repo . GetSystemByAccount ( evt . UserId ) ;
2021-07-27 15:39:37 +00:00
2020-06-11 21:20:46 +00:00
// Can only delete your own message
2021-09-03 20:20:07 +00:00
if ( msg . System . Id ! = system ? . Id ) 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
}
2021-09-30 01:51:38 +00:00
await _repo . DeleteMessage ( evt . MessageId ) ;
2020-06-11 21:20:46 +00:00
}
2021-09-13 06:13:36 +00:00
private async ValueTask HandleCommandDeleteReaction ( MessageReactionAddEvent evt , CommandMessage ? msg )
2020-10-18 05:59:36 +00:00
{
// Can only delete your own message
2021-09-13 06:13:36 +00:00
// (except in DMs, where msg will be null)
if ( msg ! = null & & 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
{
2021-11-18 01:41:02 +00:00
var guild = await _cache . GetGuild ( evt . GuildId ! . Value ) ;
2021-08-27 15:03:47 +00:00
2020-06-11 21:20:46 +00:00
// Try to DM the user info about the message
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 )
} ) ;
2021-08-27 15:03:47 +00:00
2020-12-25 12:58:45 +00:00
await _rest . CreateMessage ( dm . Id , new MessageRequest
{
2021-11-11 04:46:16 +00:00
Embed = await _embeds . CreateMessageInfoEmbed ( msg , true )
2020-12-25 12:58:45 +00:00
} ) ;
2020-06-11 21:20:46 +00:00
}
2021-03-18 10:38:28 +00:00
catch ( ForbiddenException ) { } // No permissions to DM, can't check for this :(
2021-08-27 15:03:47 +00:00
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
{
2021-11-22 00:42:35 +00:00
if ( ! ( await _cache . PermissionsIn ( evt . ChannelId ) ) . HasFlag ( PermissionSet . ManageMessages ) )
2020-12-23 01:19:02 +00:00
return ;
2021-08-27 15:03:47 +00:00
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 ;
2021-11-18 01:41:02 +00:00
if ( member = = null | | ! ( await _cache . PermissionsFor ( evt . ChannelId , member ) ) . HasFlag ( requiredPerms ) ) return ;
2021-08-27 15:03:47 +00:00
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
await _rest . CreateMessage ( evt . ChannelId , new ( )
{
2021-06-11 16:17:08 +00:00
Content = $"Psst, **{msg.Member.DisplayName()}** (<@{msg.Message.Sender}>), you have been pinged by <@{evt.UserId}>." ,
2021-08-27 15:03:47 +00:00
Components = new [ ]
2021-06-11 16:17:08 +00:00
{
new MessageComponent
{
Type = ComponentType . ActionRow ,
Components = new [ ]
{
new MessageComponent
{
Style = ButtonStyle . Link ,
Type = ComponentType . Button ,
Label = "Jump" ,
Url = evt . JumpLink ( )
}
}
}
} ,
2021-08-27 15:03:47 +00:00
AllowedMentions = new AllowedMentions { Users = new [ ] { msg . Message . Sender } }
2020-12-23 01:19:02 +00:00
} ) ;
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:"
} ) ;
2021-08-27 15:03:47 +00:00
await _rest . CreateMessage ( dm . Id , new MessageRequest { Content = $"<@{msg.Message.Sender}>" . AsCode ( ) } ) ;
2020-06-11 21:20:46 +00:00
}
2021-03-18 10:38:28 +00:00
catch ( ForbiddenException ) { }
2020-06-11 21:20:46 +00:00
}
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
{
2021-11-22 00:42:35 +00:00
if ( ( await _cache . PermissionsIn ( evt . ChannelId ) ) . HasFlag ( PermissionSet . ManageMessages ) )
2021-01-31 16:56:44 +00:00
await _rest . DeleteUserReaction ( evt . ChannelId , evt . MessageId , evt . Emoji , evt . UserId ) ;
2020-05-01 23:52:52 +00:00
}
}
}