Skip to content
This repository was archived by the owner on Apr 27, 2024. It is now read-only.

Commit 5a4ff9d

Browse files
kyranetbdistin
andauthored
feat(GuildMember): added getters for easier moderation (#212)
* feat(GuildMember): added getters for easier moderation * fix(GuildMember): permissions should check for ADMINISTRATOR * Permissions#has no longer needs checkAdmin, clean up permissionsIn * changes kyra can do the docs * fix logic * forgot to save * fix the logic right this time * whoops, had that wrong * docs(GuildMember): updated the docs Co-authored-by: bdistin <bdistin@gmail.com>
1 parent 5c0cf63 commit 5a4ff9d

File tree

5 files changed

+94
-16
lines changed

5 files changed

+94
-16
lines changed

src/lib/caching/stores/GuildMemberRoleStore.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,14 @@ export class GuildMemberRoleStore extends ProxyCache<string, Role> {
3535
this.member = member;
3636
}
3737

38+
/**
39+
* Gets the highest role from this store.
40+
* @since 0.0.1
41+
*/
42+
public highest(): Role | undefined {
43+
return this.reduce((highest, role) => highest.position > role.position ? highest : role, this.firstValue as Role);
44+
}
45+
3846
/**
3947
* The {@link Guild guild} this store belongs to.
4048
* @since 0.0.1

src/lib/caching/stores/RoleStore.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ export class RoleStore extends DataStore<Role> {
3030
this.guild = guild;
3131
}
3232

33+
/**
34+
* Gets the highest role from this store.
35+
* @since 0.0.1
36+
*/
37+
public highest(): Role | undefined {
38+
return this.reduce((highest, role) => highest.position > role.position ? highest : role, this.firstValue as Role);
39+
}
40+
3341
/**
3442
* Creates a new {@link Role role} for the {@link Guild guild}.
3543
* @since 0.0.1

src/lib/caching/structures/guilds/GuildMember.ts

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,81 @@ export class GuildMember extends Structure {
8585
return this.client.users.get(this.id) ?? null;
8686
}
8787

88+
/**
89+
* The calculated permissions from the member's {@link GuildMemberRoleStore roles}.
90+
* @since 0.0.1
91+
*/
92+
public get permissions(): Readonly<Permissions> {
93+
if (this.id === this.guild.ownerID) return new Permissions(Permissions.ALL).freeze();
94+
95+
const permissions = new Permissions(this.roles.map(role => role.permissions));
96+
return (permissions.has(Permissions.FLAGS.ADMINISTRATOR) ? permissions.add(Permissions.ALL) : permissions).freeze();
97+
}
98+
99+
/**
100+
* Whether or not the {@link ClientUser client user} can kick this member.
101+
* @since 0.0.1
102+
* @returns `null` when the {@link ClientUser client user}'s member is not cached (or when {@link Client#user} is null),
103+
* or a boolean specifying whether or not the conditions are met.
104+
*/
105+
public get kickable(): boolean | null {
106+
return (this.id !== this.client.user?.id && this._manageable && (this.guild.me as GuildMember).permissions.has(Permissions.FLAGS.KICK_MEMBERS)) ?? null;
107+
}
108+
109+
/**
110+
* Whether or not the {@link ClientUser client user} can ban this member.
111+
* @since 0.0.1
112+
* @returns `null` when the {@link ClientUser client user}'s member is not cached (or when {@link Client#user} is null),
113+
* or a boolean specifying whether or not the conditions are met.
114+
*/
115+
public get bannable(): boolean | null {
116+
return (this.id !== this.client.user?.id && this._manageable && (this.guild.me as GuildMember).permissions.has(Permissions.FLAGS.BAN_MEMBERS)) ?? null;
117+
}
118+
119+
/**
120+
* Whether or not the {@link ClientUser client user} can manage the member's nickname.
121+
* @since 0.0.1
122+
* @returns `null` when the {@link ClientUser client user}'s member is not cached (or when {@link Client#user} is null),
123+
* or a boolean specifying whether or not the conditions are met.
124+
*/
125+
public get manageNicknames(): boolean | null {
126+
return (this._manageable && (this.guild.me as GuildMember).permissions.has(Permissions.FLAGS.MANAGE_NICKNAMES)) ?? null;
127+
}
128+
129+
/**
130+
* Whether or not the {@link ClientUser client user} can manage the member's roles.
131+
* @since 0.0.1
132+
* @returns `null` when the {@link ClientUser client user}'s member is not cached (or when {@link Client#user} is null),
133+
* or a boolean specifying whether or not the conditions are met.
134+
*/
135+
public get manageRoles(): boolean | null {
136+
return (this._manageable && (this.guild.me as GuildMember).permissions.has(Permissions.FLAGS.MANAGE_ROLES)) ?? null;
137+
}
138+
139+
/**
140+
* Whether or not the {@link ClientUser client user} can manage this member. This is based on:
141+
* - The member is not the {@link Guild#owner guild owner}.
142+
* - The {@link ClientUser client user} is the owner of the {@link Guild}.
143+
* - The {@link ClientUser client user}'s {@link GuildMemberRoleStore#highest highest role} is higher than the member's.
144+
* @since 0.0.1
145+
* @returns `true` when any of the conditions are met, `null` when the {@link ClientUser client user}'s member is not
146+
* cached (or when {@link Client#user} is null), or `false` otherwise.
147+
*/
148+
protected get _manageable(): boolean | null {
149+
// If the client user's member instance is not cached, return null.
150+
const { me } = this.guild;
151+
if (!this.client.user || !me) return null;
152+
153+
// If the client is the owner, then it can manage itself
154+
if (this.guild.ownerID === this.client.user.id) return true;
155+
156+
// If this is the owner (and we have already determined we are not the owner), then it can't manage
157+
if (this.id === this.guild.ownerID) return false;
158+
159+
// If the clients highest role is higher than this roles highest role
160+
return me.roles.highest > this.roles.highest;
161+
}
162+
88163
/**
89164
* Modifies the settings for the member.
90165
* @param data The settings to be set.
@@ -101,11 +176,9 @@ export class GuildMember extends Structure {
101176
* @param channel The channel to check permissions in
102177
*/
103178
public permissionsIn(channel: GuildChannel): Readonly<Permissions> {
104-
if (this.id === this.guild.ownerID) return new Permissions(Permissions.ALL).freeze();
105-
106-
const permissions = new Permissions(this.roles.map(role => role.permissions));
179+
const { permissions } = this;
107180

108-
if (permissions.has(Permissions.FLAGS.ADMINISTRATOR)) return new Permissions(Permissions.ALL).freeze();
181+
if (permissions.equals(Permissions.ALL)) return permissions;
109182

110183
const overwrites = channel.permissionOverwrites.for(this);
111184

src/lib/caching/structures/guilds/Role.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export class Role extends Structure {
8787
public permissionsIn(channel: GuildChannel): Readonly<Permissions> {
8888
const { permissions } = this;
8989

90-
if (this.permissions.has(Permissions.FLAGS.ADMINISTRATOR)) return new Permissions(Permissions.ALL).freeze();
90+
if (permissions.has(Permissions.FLAGS.ADMINISTRATOR)) return new Permissions(Permissions.ALL).freeze();
9191

9292
const overwrites = channel.permissionOverwrites.for(this);
9393

src/lib/util/bitfields/Permissions.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,6 @@ export type PermissionsResolvable = keyof typeof Permissions.FLAGS | number | Bi
99
*/
1010
export class Permissions extends BitField<PermissionsResolvable> {
1111

12-
/**
13-
* Determines if this Permissions includes a permission or permissions
14-
* @param permission The permission/s to check for
15-
* @param checkAdmin Whether this should account for implied Administrator permissions
16-
*/
17-
public has(permission: PermissionsResolvable, checkAdmin = false): boolean {
18-
const constructor = this.constructor as typeof Permissions;
19-
if (checkAdmin && super.has(constructor.FLAGS.ADMINISTRATOR)) return true;
20-
return super.has(permission);
21-
}
22-
2312
/**
2413
* The Permissions flags
2514
*/

0 commit comments

Comments
 (0)