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 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)); }
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); }
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); }
public async Task <Message> EditWebhookMessage(ulong channelId, ulong messageId, string newContent) { var allowedMentions = newContent.ParseMentions() with { Roles = Array.Empty <ulong>(), Parse = Array.Empty <AllowedMentions.ParseType>() }; ulong?threadId = null; var root = await _cache.GetRootChannel(channelId); if (root.Id != channelId) { threadId = channelId; } var webhook = await _webhookCache.GetWebhook(root.Id); return(await _rest.EditWebhookMessage(webhook.Id, webhook.Token, messageId, new WebhookMessageEditRequest { Content = newContent, AllowedMentions = allowedMentions }, threadId)); }
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)); }