コード例 #1
0
    private async Task ExecuteProxy(Message trigger, MessageContext ctx, AutoproxySettings autoproxySettings,
                                    ProxyMatch match, bool allowEveryone, bool allowEmbeds)
    {
        // Create reply embed
        var embeds  = new List <Embed>();
        var content = "";

        if (trigger.Type == Message.MessageType.Reply && trigger.MessageReference?.ChannelId == trigger.ChannelId)
        {
            var repliedTo = trigger.ReferencedMessage.Value;
            if (repliedTo != null)
            {
                if (trigger.Mentions.Length > 0 &&
                    repliedTo.Author.Id == trigger.Mentions[0].Id &&
                    !(trigger.Content.Contains($"<@{repliedTo.Author.Id}>") ||
                      trigger.Content.Contains($"<@!{repliedTo.Author.Id}>")))
                {
                    content = $"*<@{repliedTo.Author.Id}>*\n";
                }

                var(nickname, avatar) = await FetchReferencedMessageAuthorInfo(trigger, repliedTo);

                var embed = CreateReplyEmbed(match, trigger, repliedTo, nickname, avatar);
                if (embed != null)
                {
                    embeds.Add(embed);
                }
            }

            // TODO: have a clean error for when message can't be fetched instead of just being silent
        }

        // Send the webhook
        content += match.ProxyContent;
        if (!allowEmbeds)
        {
            content = content.BreakLinkEmbeds();
        }

        var messageChannel = await _cache.GetChannel(trigger.ChannelId);

        var rootChannel = await _cache.GetRootChannel(trigger.ChannelId);

        var threadId = messageChannel.IsThread() ? messageChannel.Id : (ulong?)null;
        var guild    = await _cache.GetGuild(trigger.GuildId.Value);

        var proxyMessage = await _webhookExecutor.ExecuteWebhook(new ProxyRequest
        {
            GuildId       = trigger.GuildId !.Value,
            ChannelId     = rootChannel.Id,
            ThreadId      = threadId,
            Name          = await FixSameName(messageChannel.Id, ctx, match.Member),
            AvatarUrl     = AvatarUtils.TryRewriteCdnUrl(match.Member.ProxyAvatar(ctx)),
            Content       = content,
            Attachments   = trigger.Attachments,
            FileSizeLimit = guild.FileSizeLimit(),
            Embeds        = embeds.ToArray(),
            Stickers      = trigger.StickerItems,
            AllowEveryone = allowEveryone
        });
コード例 #2
0
    public static async Task <PermissionSet> PermissionsFor(this IDiscordCache cache, ulong channelId, ulong userId,
                                                            GuildMemberPartial?member, bool isWebhook = false)
    {
        if (!(await cache.TryGetChannel(channelId) is Channel channel))
        {
            // todo: handle channel not found better
            return(PermissionSet.Dm);
        }

        if (channel.GuildId == null)
        {
            return(PermissionSet.Dm);
        }

        var rootChannel = await cache.GetRootChannel(channelId);

        var guild = await cache.GetGuild(channel.GuildId.Value);

        if (isWebhook)
        {
            return(EveryonePermissions(guild));
        }

        return(PermissionsFor(guild, rootChannel, userId, member));
    }
コード例 #3
0
    public async Task Handle(int shardId, MessageCreateEvent evt)
    {
        if (evt.Author.Id == await _cache.GetOwnUser())
        {
            return;
        }
        if (evt.Type != Message.MessageType.Default && evt.Type != Message.MessageType.Reply)
        {
            return;
        }
        if (IsDuplicateMessage(evt))
        {
            return;
        }

        if (!(await _cache.PermissionsIn(evt.ChannelId)).HasFlag(PermissionSet.SendMessages))
        {
            return;
        }

        // spawn off saving the private channel into another thread
        // it is not a fatal error if this fails, and it shouldn't block message processing
        _ = _dmCache.TrySavePrivateChannel(evt);

        var guild = evt.GuildId != null ? await _cache.GetGuild(evt.GuildId.Value) : null;

        var channel = await _cache.GetChannel(evt.ChannelId);

        var rootChannel = await _cache.GetRootChannel(evt.ChannelId);

        // Log metrics and message info
        _metrics.Measure.Meter.Mark(BotMetrics.MessagesReceived);
        _lastMessageCache.AddMessage(evt);

        // Get message context from DB (tracking w/ metrics)
        MessageContext ctx;

        using (_metrics.Measure.Timer.Time(BotMetrics.MessageContextQueryTime))
            ctx = await _repo.GetMessageContext(evt.Author.Id, evt.GuildId ?? default, rootChannel.Id);

        // Try each handler until we find one that succeeds
        if (await TryHandleLogClean(evt, ctx))
        {
            return;
        }

        // Only do command/proxy handling if it's a user account
        if (evt.Author.Bot || evt.WebhookId != null || evt.Author.System == true)
        {
            return;
        }

        if (await TryHandleCommand(shardId, evt, guild, channel, ctx))
        {
            return;
        }
        await TryHandleProxy(evt, guild, channel, ctx);
    }
コード例 #4
0
        public static PermissionSet PermissionsFor(this IDiscordCache cache, ulong channelId, ulong userId, ICollection <ulong>?userRoles)
        {
            var channel = cache.GetChannel(channelId);

            if (channel.GuildId == null)
            {
                return(PermissionSet.Dm);
            }

            var guild = cache.GetGuild(channel.GuildId.Value);

            return(PermissionsFor(guild, channel, userId, userRoles));
        }
コード例 #5
0
        public async Task Handle(Shard shard, MessageCreateEvent evt)
        {
            if (evt.Author.Id == shard.User?.Id)
            {
                return;
            }
            if (evt.Type != Message.MessageType.Default && evt.Type != Message.MessageType.Reply)
            {
                return;
            }
            if (IsDuplicateMessage(evt))
            {
                return;
            }

            var guild = evt.GuildId != null?_cache.GetGuild(evt.GuildId.Value) : null;

            var channel     = _cache.GetChannel(evt.ChannelId);
            var rootChannel = _cache.GetRootChannel(evt.ChannelId);

            // Log metrics and message info
            _metrics.Measure.Meter.Mark(BotMetrics.MessagesReceived);
            _lastMessageCache.AddMessage(evt);

            // Get message context from DB (tracking w/ metrics)
            MessageContext ctx;

            await using (var conn = await _db.Obtain())
                using (_metrics.Measure.Timer.Time(BotMetrics.MessageContextQueryTime))
                    ctx = await _repo.GetMessageContext(conn, evt.Author.Id, evt.GuildId ?? default, rootChannel.Id);

            // Try each handler until we find one that succeeds
            if (await TryHandleLogClean(evt, ctx))
            {
                return;
            }

            // Only do command/proxy handling if it's a user account
            if (evt.Author.Bot || evt.WebhookId != null || evt.Author.System == true)
            {
                return;
            }

            if (await TryHandleCommand(shard, evt, guild, channel, ctx))
            {
                return;
            }
            await TryHandleProxy(shard, evt, guild, channel, ctx);
        }
コード例 #6
0
        public static PermissionSet PermissionsFor(this IDiscordCache cache, ulong channelId, ulong userId, ICollection <ulong>?userRoles, bool isWebhook = false)
        {
            if (!cache.TryGetChannel(channelId, out var channel))
            {
                // todo: handle channel not found better
                return(PermissionSet.Dm);
            }

            if (channel.GuildId == null)
            {
                return(PermissionSet.Dm);
            }

            var rootChannel = cache.GetRootChannel(channelId);

            var guild = cache.GetGuild(channel.GuildId.Value);

            if (isWebhook)
            {
                return(EveryonePermissions(guild));
            }

            return(PermissionsFor(guild, rootChannel, userId, userRoles));
        }
コード例 #7
0
    private async Task <Message> ExecuteWebhookInner(Webhook webhook, ProxyRequest req, bool hasRetried = false)
    {
        var guild = await _cache.GetGuild(req.GuildId);

        var content = req.Content.Truncate(2000);

        var allowedMentions = content.ParseMentions();

        if (!req.AllowEveryone)
        {
            allowedMentions = allowedMentions.RemoveUnmentionableRoles(guild) with
            {
                // also clear @everyones
                Parse = Array.Empty <AllowedMentions.ParseType>()
            }
        }
        ;

        var webhookReq = new ExecuteWebhookRequest
        {
            Username        = FixProxyName(req.Name).Truncate(80),
            Content         = content,
            AllowedMentions = allowedMentions,
            AvatarUrl       = !string.IsNullOrWhiteSpace(req.AvatarUrl) ? req.AvatarUrl : null,
            Embeds          = req.Embeds,
            Stickers        = req.Stickers,
        };

        MultipartFile[] files            = null;
        var             attachmentChunks = ChunkAttachmentsOrThrow(req.Attachments, req.FileSizeLimit);

        if (attachmentChunks.Count > 0)
        {
            _logger.Information(
                "Invoking webhook with {AttachmentCount} attachments totalling {AttachmentSize} MiB in {AttachmentChunks} chunks",
                req.Attachments.Length, req.Attachments.Select(a => a.Size).Sum() / 1024 / 1024,
                attachmentChunks.Count);
            files = await GetAttachmentFiles(attachmentChunks[0]);

            webhookReq.Attachments = files.Select(f => new Message.Attachment
            {
                Id          = (ulong)Array.IndexOf(files, f),
                Description = f.Description,
                Filename    = f.Filename
            }).ToArray();
        }

        Message webhookMessage;

        using (_metrics.Measure.Timer.Time(BotMetrics.WebhookResponseTime))
        {
            try
            {
                webhookMessage =
                    await _rest.ExecuteWebhook(webhook.Id, webhook.Token, webhookReq, files, req.ThreadId);
            }
            catch (JsonReaderException)
            {
                // This happens sometimes when we hit a CloudFlare error (or similar) on Discord's end
                // Nothing we can do about this - happens sometimes under server load, so just drop the message and give up
                throw new WebhookExecutionErrorOnDiscordsEnd();
            }
            catch (NotFoundException e)
            {
                if (e.ErrorCode == 10015 && !hasRetried)
                {
                    // Error 10015 = "Unknown Webhook" - this likely means the webhook was deleted
                    // but is still in our cache. Invalidate, refresh, try again
                    _logger.Warning("Error invoking webhook {Webhook} in channel {Channel} (thread {ThreadId})",
                                    webhook.Id, webhook.ChannelId, req.ThreadId);

                    var newWebhook = await _webhookCache.InvalidateAndRefreshWebhook(req.ChannelId, webhook);

                    return(await ExecuteWebhookInner(newWebhook, req, true));
                }

                throw;
            }
        }

        // We don't care about whether the sending succeeds, and we don't want to *wait* for it, so we just fork it off
        var _ = TrySendRemainingAttachments(webhook, req.Name, req.AvatarUrl, attachmentChunks, req.ThreadId);

        return(webhookMessage);
    }