public async Task <bool> HandleIncomingMessage(MessageCreateEvent message, MessageContext ctx, Guild guild, Channel channel, bool allowAutoproxy, PermissionSet botPermissions) { if (!ShouldProxy(channel, message, ctx)) { return(false); } var autoproxySettings = await _repo.GetAutoproxySettings(ctx.SystemId.Value, guild.Id, null); if (autoproxySettings.AutoproxyMode == AutoproxyMode.Latch && IsUnlatch(message)) { // "unlatch" await _repo.UpdateAutoproxy(ctx.SystemId.Value, guild.Id, null, new() { AutoproxyMember = null }); return(false); } var rootChannel = await _cache.GetRootChannel(message.ChannelId); List <ProxyMember> members; // Fetch members and try to match to a specific member using (_metrics.Measure.Timer.Time(BotMetrics.ProxyMembersQueryTime)) members = (await _repo.GetProxyMembers(message.Author.Id, message.GuildId !.Value)).ToList(); if (!_matcher.TryMatch(ctx, autoproxySettings, members, out var match, message.Content, message.Attachments.Length > 0, allowAutoproxy)) { return(false); } // this is hopefully temporary, so not putting it into a separate method if (message.Content != null && message.Content.Length > 2000) { throw new PKError("PluralKit cannot proxy messages over 2000 characters in length."); } // Permission check after proxy match so we don't get spammed when not actually proxying if (!CheckBotPermissionsOrError(botPermissions, rootChannel.Id)) { return(false); } // this method throws, so no need to wrap it in an if statement CheckProxyNameBoundsOrError(match.Member.ProxyName(ctx)); // Check if the sender account can mention everyone/here + embed links // we need to "mirror" these permissions when proxying to prevent exploits var senderPermissions = PermissionExtensions.PermissionsFor(guild, rootChannel, message); var allowEveryone = senderPermissions.HasFlag(PermissionSet.MentionEveryone); var allowEmbeds = senderPermissions.HasFlag(PermissionSet.EmbedLinks); // Everything's in order, we can execute the proxy! await ExecuteProxy(message, ctx, autoproxySettings, match, allowEveryone, allowEmbeds); return(true); }
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}"); } }