feat: add bannable, kickable, moderatable, manageable to GuildMember and update shorters

This commit is contained in:
MARCROCK22 2024-03-27 13:57:45 -04:00
parent 39b6334f7e
commit b46c16cd50
8 changed files with 77 additions and 24 deletions

View File

@ -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));

View File

@ -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<ContextOptionsResolved> = {
@ -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));
}

View File

@ -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;

View File

@ -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);

View File

@ -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)));
}
}

View File

@ -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<Omit<APIGuildMember, 'user' |
*/
export class GuildMember extends BaseGuildMember {
user: User;
private __me?: GuildMember;
constructor(
client: UsingClient,
data: GuildMemberData,
@ -192,9 +195,34 @@ export class GuildMember extends BaseGuildMember {
return this.user.bannerURL(options);
}
async fetchPermissions() {
async fetchPermissions(force = false) {
if ('permissions' in this) return this.permissions as PermissionsBitField;
return this.roles.permissions();
return this.roles.permissions(force);
}
async manageable(force = false) {
this.__me = await this.client.guilds.fetchSelf(this.guildId, force);
const ownerId = (await this.client.guilds.fetch(this.guildId, force)).ownerId;
if (this.user.id === ownerId) return false;
if (this.user.id === this.client.botId) return false;
if (this.client.botId === ownerId) return true;
return (await this.__me!.roles.highest()).position > (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')
);
}
}

View File

@ -70,7 +70,7 @@ export class BitField<T extends object> {
}
}
keys(...bits: BitFieldResolvable<T>[]) {
keys(bits: BitFieldResolvable<T>[] = [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<T extends object> {
}, [] as string[]);
}
values(...bits: BitFieldResolvable<T>[]) {
values(bits: BitFieldResolvable<T>[] = [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])) {

View File

@ -6,7 +6,7 @@ export class PermissionsBitField extends BitField<typeof PermissionFlagsBits> {
Flags = PermissionFlagsBits;
static All = Object.values(PermissionFlagsBits).reduce((acc, value) => acc | value, 0n);
declare keys: (...bits: BitFieldResolvable<typeof PermissionFlagsBits>[]) => PermissionStrings;
declare keys: (bits: BitFieldResolvable<typeof PermissionFlagsBits>[]) => PermissionStrings;
has(...bits: BitFieldResolvable<typeof PermissionFlagsBits>[]) {
return super.has(...bits) || super.has('Administrator');