diff --git a/src/utils/permissions.ts b/src/utils/permissions.ts index 6de579299..bce60a360 100644 --- a/src/utils/permissions.ts +++ b/src/utils/permissions.ts @@ -2,6 +2,7 @@ import { cacheHandlers } from "../controllers/cache.ts"; import { botID } from "../module/client.ts"; import type { Guild } from "../structures/guild.ts"; import type { Role } from "../structures/role.ts"; +import { RawOverwrite } from "../types/guild.ts"; import type { Permission } from "../types/permission.ts"; import { Permissions } from "../types/permission.ts"; @@ -104,87 +105,77 @@ export async function hasChannelPermissions( const member = guild.members.get(memberID); if (!member) return false; - const memberOverwrite = channel.permission_overwrites?.find((o) => - o.id === memberID - ); + let memberOverwrite: RawOverwrite | undefined = undefined; + let everyoneOverwrite: RawOverwrite | undefined = undefined; + let rolesOverwrites: RawOverwrite[] = []; - const rolesOverwrites = channel.permission_overwrites?.filter((o) => - member.roles.includes(o.id) - ); - - const everyoneOverwrite = channel.permission_overwrites?.find((o) => - o.id === guild.id - ); + for (const overwrite of channel.permission_overwrites || []) { + // If the overwrite on this channel is specific to this member + if (overwrite.id === memberID) memberOverwrite = overwrite; + // If it is the everyone role overwrite + if (overwrite.id === guild.id) everyoneOverwrite = overwrite; + // If it is one of the roles the member has + if (member.roles.includes(overwrite.id)) rolesOverwrites.push(overwrite); + } const allowedPermissions = new Set(); + // Member perms override everything so we must check them first if (memberOverwrite) { - // One of the necessary permissions is denied - if ( - permissions.some((perm) => BigInt(memberOverwrite.deny) & BigInt(perm)) - ) { - return false; - } - permissions.forEach((perm) => { + for (const perm of permissions) { + // One of the necessary permissions is denied. Since this is main permission we can cancel if its denied. + if (BigInt(memberOverwrite.deny) & BigInt(perm)) return false; // Already allowed perm - if (allowedPermissions.has(perm)) return; + if (allowedPermissions.has(perm)) continue; // This perm is allowed so we save it if (BigInt(memberOverwrite.allow) & BigInt(perm)) { allowedPermissions.add(perm); } - }); + } } // Check the necessary permissions for roles - if (rolesOverwrites?.length) { - if ( - rolesOverwrites.some((overwrite) => - permissions.some((perm) => - (BigInt(overwrite.deny) & BigInt(perm)) && - // If another role allows these perms then they are not denied - !rolesOverwrites.some((o) => BigInt(o.allow) & BigInt(perm)) && - // Make sure the memberOverwrite does not allow this perm - !(memberOverwrite && BigInt(memberOverwrite.allow) & BigInt(perm)) - ) - ) - ) { - return false; - } + for (const perm of permissions) { + // If this is already allowed, skip + if (allowedPermissions.has(perm)) continue; - permissions.forEach((perm) => { + for (const overwrite of rolesOverwrites) { + // This perm is allowed so we save it + if (BigInt(overwrite.allow) & BigInt(perm)) { + allowedPermissions.add(perm); + break; + } + + // If this role denies it we need to save and check if another role allows it, allows > deny + if (BigInt(overwrite.deny) & BigInt(perm)) { + // This role denies his perm, but before denying we need to check all other roles if any allow as allow > deny + const isAllowed = rolesOverwrites.some((o) => + BigInt(o.allow) & BigInt(perm) + ); + if (isAllowed) continue; + // This permission is in fact denied. Since Roles overrule everything below here we can cancel ou here + return false; + } + } + } + + if (everyoneOverwrite) { + for (const perm of permissions) { // Already allowed perm - if (allowedPermissions.has(perm)) return; - rolesOverwrites.forEach((overwrite) => { - // This perm is allowed so we save it - if (BigInt(overwrite.allow) & BigInt(perm)) { - allowedPermissions.add(perm); - } - }); - }); - } - - // Check the necessary permissions for everyone - if ( - everyoneOverwrite - ) { - if ( - permissions.some((perm) => - BigInt(everyoneOverwrite.deny) & BigInt(perm) && - !allowedPermissions.has(perm) - ) - ) { - return false; - } - // If all permissions are granted - if ( - permissions.every((perm) => - BigInt(everyoneOverwrite.allow) & BigInt(perm) - ) - ) { - return true; + if (allowedPermissions.has(perm)) continue; + // One of the necessary permissions is denied. Since everyone overwrite overrides role perms we can cancel here + if (BigInt(everyoneOverwrite.deny) & BigInt(perm)) return false; + // This perm is allowed so we save it + if (BigInt(everyoneOverwrite.allow) & BigInt(perm)) { + allowedPermissions.add(perm); + } } } + // Is there any remaining permission to check role perms or can we determine that permissions are allowed + if (permissions.every((perm) => allowedPermissions.has(perm))) return true; + + // Some permission was not explicitly allowed so we default to checking role perms directly return botHasPermission(guild.id, permissions); }