diff --git a/PluralKit.Bot/Proxy/ProxyService.cs b/PluralKit.Bot/Proxy/ProxyService.cs index 372a8c58..8693d315 100644 --- a/PluralKit.Bot/Proxy/ProxyService.cs +++ b/PluralKit.Bot/Proxy/ProxyService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading.Tasks; using App.Metrics; @@ -9,6 +10,8 @@ using DSharpPlus; using DSharpPlus.Entities; using DSharpPlus.Exceptions; +using Humanizer; + using PluralKit.Core; using Serilog; @@ -95,16 +98,66 @@ namespace PluralKit.Bot private async Task ExecuteProxy(DiscordClient shard, IPKConnection conn, DiscordMessage trigger, MessageContext ctx, ProxyMatch match, bool allowEveryone, bool allowEmbeds) { + // Create reply embed + var embeds = new List(); + if (trigger.Reference?.Channel?.Id == trigger.ChannelId) + { + var embed = await CreateReplyEmbed(trigger); + if (embed != null) + embeds.Add(embed); + } + // Send the webhook var content = match.ProxyContent; if (!allowEmbeds) content = content.BreakLinkEmbeds(); var proxyMessage = await _webhookExecutor.ExecuteWebhook(trigger.Channel, FixSingleCharacterName(match.Member.ProxyName(ctx)), match.Member.ProxyAvatar(ctx), - content, trigger.Attachments, allowEveryone); + content, trigger.Attachments, embeds, allowEveryone); await HandleProxyExecutedActions(shard, conn, ctx, trigger, proxyMessage, match); } + private async Task CreateReplyEmbed(DiscordMessage trigger) + { + DiscordMessage message; + try + { + message = await trigger.Channel.GetMessageAsync(trigger.Reference.Message.Id); + } + catch (NotFoundException) + { + _logger.Warning("Attempted to fetch reply message {ChannelId}/{MessageId} but it was not found", + trigger.Reference.Channel.Id, trigger.Reference.Message.Id); + return null; + } + catch (UnauthorizedException) + { + _logger.Warning("Attempted to fetch reply message {ChannelId}/{MessageId} but bot was not allowed to", + trigger.Reference.Channel.Id, trigger.Reference.Message.Id); + return null; + } + + var content = new StringBuilder(); + content.Append("[Reply to "); + + if (message.WebhookMessage) + content.Append($"**{message.Author.Username.EscapeMarkdown()}**"); + else + content.Append(message.Author.Mention); + + content.Append($"]({message.JumpLink}): "); + + if (message.Attachments.Count > 0) + content.Append($"{Emojis.Image} "); + + if (!string.IsNullOrWhiteSpace(message.Content)) + content.Append($"{message.Content.Truncate(100)}"); + + return new DiscordEmbedBuilder() + .WithDescription(content.ToString()) + .Build(); + } + private async Task HandleProxyExecutedActions(DiscordClient shard, IPKConnection conn, MessageContext ctx, DiscordMessage triggerMessage, DiscordMessage proxyMessage, ProxyMatch match) diff --git a/PluralKit.Bot/Services/WebhookExecutorService.cs b/PluralKit.Bot/Services/WebhookExecutorService.cs index d0917605..c307562d 100644 --- a/PluralKit.Bot/Services/WebhookExecutorService.cs +++ b/PluralKit.Bot/Services/WebhookExecutorService.cs @@ -42,13 +42,13 @@ namespace PluralKit.Bot _logger = logger.ForContext(); } - public async Task ExecuteWebhook(DiscordChannel channel, string name, string avatarUrl, string content, IReadOnlyList attachments, bool allowEveryone) + public async Task ExecuteWebhook(DiscordChannel channel, string name, string avatarUrl, string content, IReadOnlyList attachments, IReadOnlyList embeds, bool allowEveryone) { _logger.Verbose("Invoking webhook in channel {Channel}", channel.Id); // Get a webhook, execute it var webhook = await _webhookCache.GetWebhook(channel); - var webhookMessage = await ExecuteWebhookInner(channel, webhook, name, avatarUrl, content, attachments, allowEveryone); + var webhookMessage = await ExecuteWebhookInner(channel, webhook, name, avatarUrl, content, attachments, embeds, allowEveryone); // Log the relevant metrics _metrics.Measure.Meter.Mark(BotMetrics.MessagesProxied); @@ -58,17 +58,19 @@ namespace PluralKit.Bot return webhookMessage; } - private async Task ExecuteWebhookInner(DiscordChannel channel, DiscordWebhook webhook, string name, string avatarUrl, string content, - IReadOnlyList attachments, bool allowEveryone, bool hasRetried = false) + private async Task ExecuteWebhookInner( + DiscordChannel channel, DiscordWebhook webhook, string name, string avatarUrl, string content, + IReadOnlyList attachments, IReadOnlyList embeds, bool allowEveryone, bool hasRetried = false) { content = content.Truncate(2000); - + var dwb = new DiscordWebhookBuilder(); dwb.WithUsername(FixClyde(name).Truncate(80)); dwb.WithContent(content); dwb.AddMentions(content.ParseAllMentions(allowEveryone, channel.Guild)); if (!string.IsNullOrWhiteSpace(avatarUrl)) dwb.WithAvatarUrl(avatarUrl); + dwb.AddEmbeds(embeds); var attachmentChunks = ChunkAttachmentsOrThrow(attachments, 8 * 1024 * 1024); if (attachmentChunks.Count > 0) @@ -99,7 +101,7 @@ namespace PluralKit.Bot _logger.Warning("Error invoking webhook {Webhook} in channel {Channel}", webhook.Id, webhook.ChannelId); var newWebhook = await _webhookCache.InvalidateAndRefreshWebhook(channel, webhook); - return await ExecuteWebhookInner(channel, newWebhook, name, avatarUrl, content, attachments, allowEveryone, hasRetried: true); + return await ExecuteWebhookInner(channel, newWebhook, name, avatarUrl, content, attachments, embeds, allowEveryone, hasRetried: true); } throw; diff --git a/PluralKit.Core/Utils/Emojis.cs b/PluralKit.Core/Utils/Emojis.cs index 21acf293..48043277 100644 --- a/PluralKit.Core/Utils/Emojis.cs +++ b/PluralKit.Core/Utils/Emojis.cs @@ -7,5 +7,6 @@ public static readonly string ThumbsUp = "\U0001f44d"; public static readonly string RedQuestion = "\u2753"; public static readonly string Bell = "\U0001F514"; + public static readonly string Image = "\U0001F5BC"; } } \ No newline at end of file