diff --git a/src/client/oninteractioncreate.ts b/src/client/oninteractioncreate.ts index 1027ff4..79a9cd3 100644 --- a/src/client/oninteractioncreate.ts +++ b/src/client/oninteractioncreate.ts @@ -94,7 +94,7 @@ export async function onInteractionCreate( try { if (command.botPermissions && interaction.appPermissions) { const permissions = interaction.appPermissions.missings( - ...interaction.appPermissions.values(command.botPermissions), + ...interaction.appPermissions.values([command.botPermissions]), ); if (permissions.length) { return command.onPermissionsFail?.(context, interaction.appPermissions.keys(permissions)); @@ -158,7 +158,7 @@ export async function onInteractionCreate( try { if (command.botPermissions && interaction.appPermissions) { const permissions = interaction.appPermissions.missings( - ...interaction.appPermissions.values(command.botPermissions), + ...interaction.appPermissions.values([command.botPermissions]), ); if (permissions.length) { return command.onPermissionsFail?.(context, interaction.appPermissions.keys(permissions)); diff --git a/src/client/onmessagecreate.ts b/src/client/onmessagecreate.ts index 2167b2a..206ad91 100644 --- a/src/client/onmessagecreate.ts +++ b/src/client/onmessagecreate.ts @@ -90,13 +90,7 @@ export async function onMessageCreate( if (!command) return; if (!command.run) return self.logger.warn(`${fullCommandName} command does not have 'run' callback`); - if ( - [InteractionContextTypes.BOT_DM, InteractionContextTypes.PRIVATE_CHANNEL].some(i => - command.contexts?.includes(i), - ) && - !message.guildId - ) - return; + if (!command.contexts?.includes(InteractionContextTypes.BOT_DM) && !message.guildId) return; if (command.guild_id && !command.guild_id?.includes(message.guildId!)) return; const resolved: MakeRequired = { @@ -118,7 +112,7 @@ export async function onMessageCreate( const meMember = await self.cache.members?.get(self.botId, message.guildId); if (!meMember) return; //enable member cache and "Guilds" intent, lol const appPermissions = await meMember.fetchPermissions(); - const permissions = appPermissions.missings(...appPermissions.values(command.botPermissions)); + const permissions = appPermissions.missings(...appPermissions.values([command.botPermissions])); if (permissions.length) { return command.onPermissionsFail?.(context, appPermissions.keys(permissions)); } diff --git a/src/common/shorters/channels.ts b/src/common/shorters/channels.ts index e031b86..2f8c56f 100644 --- a/src/common/shorters/channels.ts +++ b/src/common/shorters/channels.ts @@ -128,7 +128,7 @@ export class ChannelShorter extends BaseShorter { for (const overwrite of channelOverwrites) { if (overwrite.id === member.guildId) { everyoneOverwrites = overwrite; - } else if (member.roles.values.includes(overwrite.id)) { + } else if (member.roles.keys.includes(overwrite.id)) { roleOverwrites.push(overwrite); } else if (overwrite.id === member.id) { memberOverwrites = overwrite; diff --git a/src/common/shorters/guilds.ts b/src/common/shorters/guilds.ts index c0a7e86..c674e09 100644 --- a/src/common/shorters/guilds.ts +++ b/src/common/shorters/guilds.ts @@ -74,7 +74,11 @@ export class GuildShorter extends BaseShorter { .then(guilds => guilds.map(guild => new AnonymousGuild(this.client, { ...guild, splash: null }))); } - async fetchSelf(id: string) { + async fetchSelf(id: string, force = false) { + if (!force) { + const self = await this.client.cache.members?.get(this.client.botId, id); + if (self) return self; + } const self = await this.client.proxy.guilds(id).members(this.client.botId).get(); await this.client.cache.members?.patch(self.user!.id, id, self); return new GuildMember(this.client, self, self.user!, id); diff --git a/src/common/shorters/members.ts b/src/common/shorters/members.ts index 68dde13..d84b68a 100644 --- a/src/common/shorters/members.ts +++ b/src/common/shorters/members.ts @@ -9,6 +9,7 @@ import { type RESTPutAPIGuildMemberJSONBody, } from '..'; import { GuildMember } from '../../structures'; +import { PermissionsBitField } from '../../structures/extra/Permissions'; import { BaseShorter } from './base'; export class MemberShorter extends BaseShorter { @@ -181,4 +182,30 @@ export class MemberShorter extends BaseShorter { removeRole(guildId: string, memberId: string, id: string) { return this.client.proxy.guilds(guildId).members(memberId).roles(id).delete(); } + + async listRoles(guildId: string, memberId: string, force = false) { + if (!force) { + const member = await this.client.cache.members?.get(memberId, guildId); + if (member) { + const roles = (await this.client.cache.roles?.bulk(member.roles.keys)) ?? []; + if (roles.length) return roles; + } + } + + const member = await this.client.members.fetch(guildId, memberId, force); + const allRoles = await this.client.roles.list(guildId, force); + const rolesId = member.roles.keys.concat(guildId); + return allRoles.filter(role => rolesId.includes(role.id)); + } + + async sortRoles(guildId: string, memberId: string, force = false) { + const roles = await this.listRoles(guildId, memberId, force); + return roles.sort((a, b) => b.position - a.position); + } + + async permissions(guildId: string, memberId: string, force = false) { + const roles = await this.listRoles(guildId, memberId, force); + + return new PermissionsBitField(roles.map(x => BigInt(x.permissions.bits))); + } } diff --git a/src/structures/GuildMember.ts b/src/structures/GuildMember.ts index 684bcf9..d7ceba6 100644 --- a/src/structures/GuildMember.ts +++ b/src/structures/GuildMember.ts @@ -95,15 +95,17 @@ export class BaseGuildMember extends DiscordBase { get roles() { return { - values: Object.freeze(this._roles), + keys: Object.freeze(this._roles.concat(this.guildId)) as string[], + list: (force = false) => + this.client.roles + .list(this.guildId, force) + .then(roles => roles.filter(role => this.roles.keys.includes(role.id))), add: (id: string) => this.client.members.addRole(this.guildId, this.id, id), remove: (id: string) => this.client.members.removeRole(this.guildId, this.id, id), - permissions: async () => - new PermissionsBitField( - ((await this.cache.roles?.bulk(this.roles.values as string[])) ?? []) - .filter(x => x) - .map(x => BigInt(x!.permissions.bits)), - ), + permissions: (force = false) => + this.roles.list(force).then(roles => new PermissionsBitField(roles.map(x => BigInt(x.permissions.bits)))), + sorted: (force = false) => this.roles.list(force).then(roles => roles.sort((a, b) => b.position - a.position)), + highest: (force = false) => this.roles.sorted(force).then(roles => roles[0]), }; } @@ -132,6 +134,7 @@ export interface GuildMember extends ObjectToLower (await this.roles.highest(force)).position; + } + + async bannable(force = false) { + return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has('BanMembers'); + } + + async kickable(force = false) { + return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has('KickMembers'); + } + + async moderatable(force = false) { + return ( + !(await this.roles.permissions(force)).has('Administrator') && + (await this.manageable(force)) && + (await this.__me!.fetchPermissions(force)).has('KickMembers') + ); } } diff --git a/src/structures/extra/BitField.ts b/src/structures/extra/BitField.ts index 67e7728..b13d6df 100644 --- a/src/structures/extra/BitField.ts +++ b/src/structures/extra/BitField.ts @@ -70,7 +70,7 @@ export class BitField { } } - keys(...bits: BitFieldResolvable[]) { + keys(bits: BitFieldResolvable[] = [this.bits]) { const bitsResolved = bits.map(bit => BigInt(this.resolve(bit))); return Object.entries(this.Flags).reduce((acc, value) => { if (bitsResolved.some(bit => (bit & value[1]) === value[1])) { @@ -81,7 +81,7 @@ export class BitField { }, [] as string[]); } - values(...bits: BitFieldResolvable[]) { + values(bits: BitFieldResolvable[] = [this.bits]) { const bitsResolved = bits.map(bit => BigInt(this.resolve(bit))); return Object.entries(this.Flags).reduce((acc, value) => { if (bitsResolved.some(bit => (bit & value[1]) === value[1])) { diff --git a/src/structures/extra/Permissions.ts b/src/structures/extra/Permissions.ts index 297ca6c..f3e67b3 100644 --- a/src/structures/extra/Permissions.ts +++ b/src/structures/extra/Permissions.ts @@ -6,7 +6,7 @@ export class PermissionsBitField extends BitField { Flags = PermissionFlagsBits; static All = Object.values(PermissionFlagsBits).reduce((acc, value) => acc | value, 0n); - declare keys: (...bits: BitFieldResolvable[]) => PermissionStrings; + declare keys: (bits: BitFieldResolvable[]) => PermissionStrings; has(...bits: BitFieldResolvable[]) { return super.has(...bits) || super.has('Administrator');