Example #1
0
    public async Task EditMessage(Context ctx)
    {
        var msg = await GetMessageToEdit(ctx);

        if (ctx.System.Id != msg.System?.Id)
        {
            throw new PKError("Can't edit a message sent by a different system.");
        }

        if (!ctx.HasNext())
        {
            throw new PKSyntaxError("You need to include the message to edit in.");
        }

        var newContent = ctx.RemainderOrNull().NormalizeLineEndSpacing();

        if (newContent.Length > 2000)
        {
            throw new PKError("PluralKit cannot proxy messages over 2000 characters in length.");
        }

        var originalMsg = await _rest.GetMessageOrNull(msg.Message.Channel, msg.Message.Mid);

        if (originalMsg == null)
        {
            throw new PKError("Could not edit message.");
        }

        try
        {
            var editedMsg =
                await _webhookExecutor.EditWebhookMessage(msg.Message.Channel, msg.Message.Mid, newContent);

            if (ctx.Guild == null)
            {
                await _rest.CreateReaction(ctx.Channel.Id, ctx.Message.Id, new Emoji { Name = Emojis.Success });
            }

            if ((await ctx.BotPermissions).HasFlag(PermissionSet.ManageMessages))
            {
                await _rest.DeleteMessage(ctx.Channel.Id, ctx.Message.Id);
            }

            await _logChannel.LogMessage(ctx.MessageContext, msg.Message, ctx.Message, editedMsg,
                                         originalMsg !.Content !);
        }
        catch (NotFoundException)
        {
            throw new PKError("Could not edit message.");
        }
    }
Example #2
0
    public async Task <Embed> CreateMessageInfoEmbed(FullMessage msg, bool showContent)
    {
        var channel = await _cache.GetOrFetchChannel(_rest, msg.Message.Channel);

        var ctx = LookupContext.ByNonOwner;

        var serverMsg = await _rest.GetMessageOrNull(msg.Message.Channel, msg.Message.Mid);

        // Need this whole dance to handle cases where:
        // - the user is deleted (userInfo == null)
        // - the bot's no longer in the server we're querying (channel == null)
        // - the member is no longer in the server we're querying (memberInfo == null)
        // TODO: optimize ordering here a bit with new cache impl; and figure what happens if bot leaves server -> channel still cached -> hits this bit and 401s?
        GuildMemberPartial memberInfo = null;
        User userInfo = null;

        if (channel != null)
        {
            GuildMember member = null;
            try
            {
                member = await _rest.GetGuildMember(channel.GuildId !.Value, msg.Message.Sender);
            }
            catch (ForbiddenException)
            {
                // no permission, couldn't fetch, oh well
            }

            if (member != null)
            {
                // Don't do an extra request if we already have this info from the member lookup
                userInfo = member.User;
            }
            memberInfo = member;
        }

        if (userInfo == null)
        {
            userInfo = await _cache.GetOrFetchUser(_rest, msg.Message.Sender);
        }

        // Calculate string displayed under "Sent by"
        string userStr;

        if (showContent && memberInfo != null && memberInfo.Nick != null)
        {
            userStr = $"**Username:** {userInfo.NameAndMention()}\n**Nickname:** {memberInfo.Nick}";
        }
        else if (userInfo != null)
        {
            userStr = userInfo.NameAndMention();
        }
        else
        {
            userStr = $"*(deleted user {msg.Message.Sender})*";
        }

        var content = serverMsg?.Content?.NormalizeLineEndSpacing();

        if (content == null || !showContent)
        {
            content = "*(message contents deleted or inaccessible)*";
        }

        // Put it all together
        var eb = new EmbedBuilder()
                 .Author(new Embed.EmbedAuthor(msg.Member?.NameFor(ctx) ?? "(deleted member)",
                                               IconUrl: msg.Member?.AvatarFor(ctx).TryGetCleanCdnUrl()))
                 .Description(content)
                 .Image(showContent ? new Embed.EmbedImage(serverMsg?.Attachments?.FirstOrDefault()?.Url) : null)
                 .Field(new Embed.Field("System",
                                        msg.System == null
                    ? "*(deleted or unknown system)*"
                    : msg.System.Name != null ? $"{msg.System.Name} (`{msg.System.Hid}`)" : $"`{msg.System.Hid}`"
                                        , true))
                 .Field(new Embed.Field("Member",
                                        msg.Member == null
                    ? "*(deleted member)*"
                    : $"{msg.Member.NameFor(ctx)} (`{msg.Member.Hid}`)"
                                        , true))
                 .Field(new Embed.Field("Sent by", userStr, true))
                 .Timestamp(DiscordUtils.SnowflakeToInstant(msg.Message.Mid).ToDateTimeOffset().ToString("O"));

        var roles = memberInfo?.Roles?.ToList();

        if (roles != null && roles.Count > 0 && showContent)
        {
            var rolesString = string.Join(", ", (await Task.WhenAll(roles
                                                                    .Select(async id =>
            {
                var role = await _cache.TryGetRole(id);
                if (role != null)
                {
                    return(role);
                }
                return(new Role {
                    Name = "*(unknown role)*", Position = 0
                });
            })))
                                          .OrderByDescending(role => role.Position)
                                          .Select(role => role.Name));
            eb.Field(new Embed.Field($"Account roles ({roles.Count})", rolesString.Truncate(1024)));
        }

        return(eb.Build());
    }
Example #3
0
    public async Task EditMessage(Context ctx)
    {
        var msg = await GetMessageToEdit(ctx, EditTimeout, false);

        if (ctx.System.Id != msg.System?.Id)
        {
            throw new PKError("Can't edit a message sent by a different system.");
        }

        if (!ctx.HasNext())
        {
            throw new PKSyntaxError("You need to include the message to edit in.");
        }

        var originalMsg = await _rest.GetMessageOrNull(msg.Message.Channel, msg.Message.Mid);

        if (originalMsg == null)
        {
            throw new PKError("Could not edit message.");
        }

        // Check if we should append or prepend
        var append  = ctx.MatchFlag("append");
        var prepend = ctx.MatchFlag("prepend");

        // Grab the original message content and new message content
        var originalContent = originalMsg.Content;
        var newContent      = ctx.RemainderOrNull().NormalizeLineEndSpacing();

        // Append or prepend the new content to the original message content if needed.
        // If no flag is supplied, the new contents will completly overwrite the old contents
        // If both flags are specified. the message will be prepended AND appended
        if (append && prepend)
        {
            newContent = $"{newContent} {originalContent} {newContent}";
        }
        else if (append)
        {
            newContent = originalContent + " " + newContent;
        }
        else if (prepend)
        {
            newContent = newContent + " " + originalContent;
        }

        if (newContent.Length > 2000)
        {
            throw new PKError("PluralKit cannot proxy messages over 2000 characters in length.");
        }

        try
        {
            var editedMsg =
                await _webhookExecutor.EditWebhookMessage(msg.Message.Channel, msg.Message.Mid, newContent);

            if (ctx.Guild == null)
            {
                await _rest.CreateReaction(ctx.Channel.Id, ctx.Message.Id, new Emoji { Name = Emojis.Success });
            }

            if ((await ctx.BotPermissions).HasFlag(PermissionSet.ManageMessages))
            {
                await _rest.DeleteMessage(ctx.Channel.Id, ctx.Message.Id);
            }

            await _logChannel.LogMessage(ctx.MessageContext, msg.Message, ctx.Message, editedMsg,
                                         originalMsg !.Content !);
        }
        catch (NotFoundException)
        {
            throw new PKError("Could not edit message.");
        }
    }
Example #4
0
    public async Task MessageProxyCheck(Context ctx)
    {
        if (!ctx.HasNext() && ctx.Message.MessageReference == null)
        {
            throw new PKSyntaxError("You need to specify a message.");
        }

        var failedToGetMessage =
            "Could not find a valid message to check, was not able to fetch the message, or the message was not sent by you.";

        var(messageId, channelId) = ctx.MatchMessage(false);
        if (messageId == null || channelId == null)
        {
            throw new PKError(failedToGetMessage);
        }

        var proxiedMsg = await ctx.Database.Execute(conn => ctx.Repository.GetMessage(conn, messageId.Value));

        if (proxiedMsg != null)
        {
            await ctx.Reply($"{Emojis.Success} This message was proxied successfully.");

            return;
        }

        // get the message info
        var msg = await _rest.GetMessageOrNull(channelId.Value, messageId.Value);

        if (msg == null)
        {
            throw new PKError(failedToGetMessage);
        }

        // if user is fetching a message in a different channel sent by someone else, throw a generic error message
        if (msg == null || msg.Author.Id != ctx.Author.Id && msg.ChannelId != ctx.Channel.Id)
        {
            throw new PKError(failedToGetMessage);
        }

        if ((_botConfig.Prefixes ?? BotConfig.DefaultPrefixes).Any(p => msg.Content.StartsWith(p)))
        {
            await ctx.Reply("This message starts with the bot's prefix, and was parsed as a command.");

            return;
        }
        if (msg.Author.Bot)
        {
            throw new PKError("You cannot check messages sent by a bot.");
        }
        if (msg.WebhookId != null)
        {
            throw new PKError("You cannot check messages sent by a webhook.");
        }
        if (msg.Author.Id != ctx.Author.Id && !ctx.CheckBotAdmin())
        {
            throw new PKError("You can only check your own messages.");
        }

        // get the channel info
        var channel = await _rest.GetChannelOrNull(channelId.Value);

        if (channel == null)
        {
            throw new PKError("Unable to get the channel associated with this message.");
        }

        // using channel.GuildId here since _rest.GetMessage() doesn't return the GuildId
        var context = await ctx.Repository.GetMessageContext(msg.Author.Id, channel.GuildId.Value, msg.ChannelId);

        var members = (await ctx.Repository.GetProxyMembers(msg.Author.Id, channel.GuildId.Value)).ToList();

        // for now this is just server
        var autoproxySettings = await ctx.Repository.GetAutoproxySettings(ctx.System.Id, channel.GuildId.Value, null);

        // todo: match unlatch

        // Run everything through the checks, catch the ProxyCheckFailedException, and reply with the error message.
        try
        {
            _proxy.ShouldProxy(channel, msg, context);
            _matcher.TryMatch(context, autoproxySettings, members, out var match, msg.Content, msg.Attachments.Length > 0,
                              context.AllowAutoproxy);

            await ctx.Reply("I'm not sure why this message was not proxied, sorry.");
        }
        catch (ProxyService.ProxyChecksFailedException e)
        {
            await ctx.Reply($"{e.Message}");
        }
    }