Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add modify function to the GuildMemberRoleManager for adding/removing roles at once. #10355

Closed
wants to merge 12 commits into from
71 changes: 49 additions & 22 deletions packages/discord.js/src/managers/GuildMemberRoleManager.js
Original file line number Diff line number Diff line change
@@ -107,17 +107,7 @@ class GuildMemberRoleManager extends DataManager {
*/
async add(roleOrRoles, reason) {
scottbucher marked this conversation as resolved.
Show resolved Hide resolved
if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) {
const resolvedRoles = [];
for (const role of roleOrRoles.values()) {
const resolvedRole = this.guild.roles.resolveId(role);
if (!resolvedRole) {
throw new DiscordjsTypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role);
}
resolvedRoles.push(resolvedRole);
}

const newRoles = [...new Set(resolvedRoles.concat(...this.cache.keys()))];
return this.set(newRoles, reason);
return this.modify({ rolesToAdd: roleOrRoles, reason });
} else {
roleOrRoles = this.guild.roles.resolveId(roleOrRoles);
if (roleOrRoles === null) {
@@ -144,17 +134,7 @@ class GuildMemberRoleManager extends DataManager {
*/
async remove(roleOrRoles, reason) {
if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) {
const resolvedRoles = [];
for (const role of roleOrRoles.values()) {
const resolvedRole = this.guild.roles.resolveId(role);
if (!resolvedRole) {
throw new DiscordjsTypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role);
}
resolvedRoles.push(resolvedRole);
}

const newRoles = this.cache.filter(role => !resolvedRoles.includes(role.id));
return this.set(newRoles, reason);
return this.modify({ rolesToRemove: roleOrRoles, reason });
} else {
roleOrRoles = this.guild.roles.resolveId(roleOrRoles);
if (roleOrRoles === null) {
@@ -174,6 +154,53 @@ class GuildMemberRoleManager extends DataManager {
}
}

/**
* @typedef {Object} ModifyGuildMemberRolesOptions
* @property {Readonly<RoleResolvable[]> | ReadonlyCollection<Snowflake, Role>} [rolesToAdd] The roles to add
* @property {Readonly<RoleResolvable[]> | ReadonlyCollection<Snowflake, Role>} [rolesToRemove] The roles to remove
* @property {Readonly<RoleResolvable[]> | ReadonlyCollection<Snowflake, Role>} [reason] Reason for modifying
* the roles
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we have a typedef for ReadonlyCollection and Readonly

*/

/**
* Modifies the roles of the member.
* @param {ModifyGuildMemberRolesOptions} [options] Options for modifying the roles
* @returns {GuildMember}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @returns {GuildMember}
* @returns {Promise<GuildMember>}

*/
modify(options) {
const resolvedRolesToAdd = this.resolveRoles(options.rolesToAdd);
const resolvedRolesToRemove = this.resolveRoles(options.rolesToRemove);
scottbucher marked this conversation as resolved.
Show resolved Hide resolved

const currentRoles = new Set(this.member.roles.cache.keys());
for (const role of resolvedRolesToAdd) {
currentRoles.add(role.id);
}
for (const role of resolvedRolesToRemove) {
currentRoles.delete(role.id);
}

return this.member.roles.set([...currentRoles], options.reason);
}

/**
* Resolves roles from the input.
* @param {Readonly<RoleResolvable[]> | ReadonlyCollection<Snowflake, Role>} rolesToResolve The roles to resolve
* @returns {Role[]} The resolved roles
Copy link
Contributor

@NotSugden NotSugden Jun 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not what is returned by this function.

an array of role ids is returned.

as this is only used to resolve role ids and is private we should keep it as returning role ids as makes more sense. This will require a small change to the modify function as well.

* @private
*/
resolveRoles(rolesToResolve) {
scottbucher marked this conversation as resolved.
Show resolved Hide resolved
if (!rolesToResolve) return [];
const resolvedRoles = [];
for (const role of rolesToResolve.values()) {
const resolvedRole = this.guild.roles.resolveId(role);
if (!resolvedRole) {
throw new DiscordjsTypeError(ErrorCodes.InvalidElement, 'Array or Collection', 'roles', role);
}
resolvedRoles.push(resolvedRole);
}
return resolvedRoles;
}

/**
* Sets the roles applied to the member.
* @param {Collection<Snowflake, Role>|RoleResolvable[]} roles The roles or role ids to apply
8 changes: 8 additions & 0 deletions packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
@@ -4393,6 +4393,12 @@ export class GuildStickerManager extends CachedManager<Snowflake, Sticker, Stick
public fetchUser(sticker: StickerResolvable): Promise<User | null>;
}

export interface ModifyGuildMemberRolesOptions {
Copy link
Contributor Author

@scottbucher scottbucher Jun 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the right place to put this? What is the preference for ordering in the this typings file?

rolesToAdd?: readonly RoleResolvable[] | ReadonlyCollection<Snowflake, Role>;
rolesToRemove?: readonly RoleResolvable[] | ReadonlyCollection<Snowflake, Role>;
reason?: string;
}

export class GuildMemberRoleManager extends DataManager<Snowflake, Role, RoleResolvable> {
private constructor(member: GuildMember);
public get hoist(): Role | null;
@@ -4408,6 +4414,7 @@ export class GuildMemberRoleManager extends DataManager<Snowflake, Role, RoleRes
roleOrRoles: RoleResolvable | readonly RoleResolvable[] | ReadonlyCollection<Snowflake, Role>,
reason?: string,
): Promise<GuildMember>;
public modify(options: ModifyGuildMemberRolesOptions): GuildMember;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public modify(options: ModifyGuildMemberRolesOptions): GuildMember;
public modify(options: ModifyGuildMemberRolesOptions): Promise<GuildMember>;

public set(
roles: readonly RoleResolvable[] | ReadonlyCollection<Snowflake, Role>,
reason?: string,
@@ -4416,6 +4423,7 @@ export class GuildMemberRoleManager extends DataManager<Snowflake, Role, RoleRes
roleOrRoles: RoleResolvable | readonly RoleResolvable[] | ReadonlyCollection<Snowflake, Role>,
reason?: string,
): Promise<GuildMember>;
private resolveRoles(rolesToResolve: readonly RoleResolvable[] | ReadonlyCollection<Snowflake, Role>): Role[];
}

export interface FetchPollAnswerVotersOptions extends BaseFetchPollAnswerVotersOptions {