From 1b928a1f6ba8c027949973294db728a7cd599ea9 Mon Sep 17 00:00:00 2001 From: Skillz Date: Sat, 20 Jun 2020 18:32:17 -0400 Subject: [PATCH] better channel perm handling --- handlers/channel.ts | 35 ++++++++++---------- handlers/message.ts | 42 +++++++++++------------- utils/permissions.ts | 76 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 41 deletions(-) diff --git a/handlers/channel.ts b/handlers/channel.ts index f227f55ee..ee0f3f89d 100644 --- a/handlers/channel.ts +++ b/handlers/channel.ts @@ -1,6 +1,9 @@ import { Permissions } from "../types/permission.ts"; import { Channel } from "../structures/channel.ts"; -import { botHasPermission } from "../utils/permissions.ts"; +import { + botHasPermission, + botHasChannelPermissions, +} from "../utils/permissions.ts"; import { Errors } from "../types/errors.ts"; import { RequestManager } from "../module/requestManager.ts"; import { endpoints } from "../constants/discord.ts"; @@ -43,13 +46,13 @@ export function hasChannelPermission( export async function getMessage(channel: Channel, id: string) { if (channel.guildID) { if ( - !botHasPermission(channel.guildID, [Permissions.VIEW_CHANNEL]) + !botHasChannelPermissions(channel.id, [Permissions.VIEW_CHANNEL]) ) { throw new Error(Errors.MISSING_VIEW_CHANNEL); } if ( - !botHasPermission( - channel.guildID, + !botHasChannelPermissions( + channel.id, [Permissions.READ_MESSAGE_HISTORY], ) ) { @@ -73,13 +76,13 @@ export async function getMessages( ) { if (channel.guildID) { if ( - !botHasPermission(channel.guildID, [Permissions.VIEW_CHANNEL]) + !botHasChannelPermissions(channel.id, [Permissions.VIEW_CHANNEL]) ) { throw new Error(Errors.MISSING_VIEW_CHANNEL); } if ( - !botHasPermission( - channel.guildID, + !botHasChannelPermissions( + channel.id, [Permissions.READ_MESSAGE_HISTORY], ) ) { @@ -110,16 +113,15 @@ export async function sendMessage( content: string | MessageContent, ) { if (typeof content === "string") content = { content }; - if (channel.guildID) { if ( - !botHasPermission(channel.guildID, [Permissions.SEND_MESSAGES]) + !botHasChannelPermissions(channel.id, [Permissions.SEND_MESSAGES]) ) { throw new Error(Errors.MISSING_SEND_MESSAGES); } if ( content.tts && - !botHasPermission( + !botHasChannelPermissions( channel.guildID, [Permissions.SEND_TTS_MESSAGES], ) @@ -148,7 +150,7 @@ export function deleteMessages( ) { if ( channel.guildID && - !botHasPermission(channel.guildID, [Permissions.MANAGE_MESSAGES]) + !botHasChannelPermissions(channel.id, [Permissions.MANAGE_MESSAGES]) ) { throw new Error(Errors.MISSING_MANAGE_MESSAGES); } @@ -172,7 +174,7 @@ export function deleteMessages( export function getChannelInvites(channel: Channel) { if ( channel.guildID && - !botHasPermission(channel.guildID, [Permissions.MANAGE_CHANNELS]) + !botHasChannelPermissions(channel.id, [Permissions.MANAGE_CHANNELS]) ) { throw new Error(Errors.MISSING_MANAGE_CHANNELS); } @@ -183,8 +185,8 @@ export function getChannelInvites(channel: Channel) { export function createInvite(channel: Channel, options: CreateInviteOptions) { if ( channel.guildID && - !botHasPermission( - channel.guildID, + !botHasChannelPermissions( + channel.id, [Permissions.CREATE_INSTANT_INVITE], ) ) { @@ -196,8 +198,7 @@ export function createInvite(channel: Channel, options: CreateInviteOptions) { /** Gets the webhooks for this channel. Requires MANAGE_WEBHOOKS */ export function getChannelWebhooks(channel: Channel) { if ( - channel.guildID && - !botHasPermission(channel.guildID, [Permissions.MANAGE_WEBHOOKS]) + !botHasChannelPermissions(channel.id, [Permissions.MANAGE_WEBHOOKS]) ) { throw new Error(Errors.MISSING_MANAGE_WEBHOOKS); } @@ -251,7 +252,7 @@ export function editChannel(channel: Channel, options: ChannelEditOptions) { if (!channel.guildID) throw new Error(Errors.CHANNEL_NOT_IN_GUILD); if ( - !botHasPermission(channel.guildID, [Permissions.MANAGE_CHANNELS]) + !botHasChannelPermissions(channel.id, [Permissions.MANAGE_CHANNELS]) ) { throw new Error(Errors.MISSING_MANAGE_CHANNELS); } diff --git a/handlers/message.ts b/handlers/message.ts index ba60a2167..4ffa9dc67 100644 --- a/handlers/message.ts +++ b/handlers/message.ts @@ -6,7 +6,7 @@ import { Permissions } from "../types/permission.ts"; import { Errors } from "../types/errors.ts"; import { RequestManager } from "../module/requestManager.ts"; import { endpoints } from "../constants/discord.ts"; -import { botHasPermission } from "../utils/permissions.ts"; +import { botHasChannelPermissions } from "../utils/permissions.ts"; import { MessageContent } from "../types/channel.ts"; import { UserPayload } from "../types/guild.ts"; import { MessageCreateOptions } from "../types/message.ts"; @@ -42,8 +42,7 @@ export async function deleteMessage( /** Pin a message in a channel. Requires MANAGE_MESSAGES. Max pins allowed in a channel = 50. */ export function pin(message: Message) { if ( - message.guildID && - !botHasPermission(message.guildID, [Permissions.MANAGE_MESSAGES]) + !botHasChannelPermissions(message.channelID, [Permissions.MANAGE_MESSAGES]) ) { throw new Error(Errors.MISSING_MANAGE_MESSAGES); } @@ -53,8 +52,7 @@ export function pin(message: Message) { /** Unpin a message in a channel. Requires MANAGE_MESSAGES. */ export function unpin(message: Message) { if ( - message.guildID && - !botHasPermission(message.guildID, [Permissions.MANAGE_MESSAGES]) + !botHasChannelPermissions(message.channelID, [Permissions.MANAGE_MESSAGES]) ) { throw new Error(Errors.MISSING_MANAGE_MESSAGES); } @@ -88,8 +86,7 @@ export function removeReaction(message: Message, reaction: string) { /** Removes all reactions for all emojis on this message. */ export function removeAllReactions(message: Message) { if ( - message.guildID && - !botHasPermission(message.guildID, [Permissions.MANAGE_MESSAGES]) + !botHasChannelPermissions(message.channelID, [Permissions.MANAGE_MESSAGES]) ) { throw new Error(Errors.MISSING_MANAGE_MESSAGES); } @@ -101,8 +98,7 @@ export function removeAllReactions(message: Message) { /** Removes all reactions for a single emoji on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */ export function removeReactionEmoji(message: Message, reaction: string) { if ( - message.guildID && - !botHasPermission(message.guildID, [Permissions.MANAGE_MESSAGES]) + !botHasChannelPermissions(message.channelID, [Permissions.MANAGE_MESSAGES]) ) { throw new Error(Errors.MISSING_MANAGE_MESSAGES); } @@ -136,22 +132,20 @@ export async function editMessage( if (typeof content === "string") content = { content }; - if (message.guildID) { - if ( - !botHasPermission(message.guildID, [Permissions.SEND_MESSAGES]) - ) { - throw new Error(Errors.MISSING_SEND_MESSAGES); - } + if ( + !botHasChannelPermissions(message.channelID, [Permissions.SEND_MESSAGES]) + ) { + throw new Error(Errors.MISSING_SEND_MESSAGES); + } - if ( - content.tts && - !botHasPermission( - message.guildID, - [Permissions.SEND_TTS_MESSAGES], - ) - ) { - throw new Error(Errors.MISSING_SEND_TTS_MESSAGE); - } + if ( + content.tts && + !botHasChannelPermissions( + message.channelID, + [Permissions.SEND_TTS_MESSAGES], + ) + ) { + throw new Error(Errors.MISSING_SEND_TTS_MESSAGE); } if (content.content && content.content.length > 2000) { diff --git a/utils/permissions.ts b/utils/permissions.ts index cccc7a4f0..03b24b898 100644 --- a/utils/permissions.ts +++ b/utils/permissions.ts @@ -48,6 +48,82 @@ export function botHasPermission(guildID: string, permissions: Permissions[]) { return permissions.every((permission) => permissionBits & permission); } +/** Checks if the bot has the permissions in a channel */ +export function botHasChannelPermissions(channelID: string, permissions: Permissions[]) { + return hasChannelPermissions(channelID, botID, permissions) +} + +/** Checks if a user has permissions in a channel. */ +export function hasChannelPermissions( + channelID: string, + memberID: string, + permissions: Permissions[], +) { + const channel = cache.channels.get(channelID); + if (!channel?.guildID) return true; + + const guild = cache.guilds.get(channel.guildID); + if (!guild) return false; + + if (guild.ownerID === memberID) return true; + + const member = guild.members.get(memberID); + if (!member) return false; + + const memberOverwrite = channel.permission_overwrites?.find((o) => + o.id === memberID + ); + + const rolesOverwrites = channel.permission_overwrites?.filter((o) => + member.roles.includes(o.id) + ); + + const everyoneOverwrite = channel.permission_overwrites?.find((o) => + o.id === guild.id + ); + + // One of the necessary permissions is denied + if ( + memberOverwrite && permissions.some((perm) => memberOverwrite.deny & perm) + ) { + return false; + } + + // Check the necessary permissions for roles + if (rolesOverwrites?.length) { + if ( + rolesOverwrites.some((overwrite) => + permissions.some((perm) => + (overwrite.deny & perm) && + // If another role allows these perms then they are not denied + !rolesOverwrites.some((o) => o.allow & perm) && + // Make sure the memberOverwrite does not allow this perm + !(memberOverwrite && memberOverwrite.allow & perm) + ) + ) + ) { + return false; + } + } + + // Check the necessary permissions for everyone + if ( + everyoneOverwrite + ) { + if (permissions.some((perm) => everyoneOverwrite.deny & perm)) { + return false; + } + // If all permissions are granted + if ( + permissions.every((perm) => everyoneOverwrite.allow & perm) + ) { + return true; + } + } + + return botHasPermission(guild.id, permissions) +} + export function calculatePermissions(permissionBits: number) { return Object.keys(Permissions).filter((perm) => { return permissionBits & Permissions[perm as Permission];