From d6234b764ecbf12ebc0a795429a6aa3a650f5a6c Mon Sep 17 00:00:00 2001 From: Souji Date: Mon, 14 Dec 2020 13:54:02 +0100 Subject: [PATCH] feat(Role): role tags (#4628) * feat(Role): role tags * fix(RoleManager): fix js docs and mark nullable * fix(Role): typings tags are be null, not undefined * fix(Role): getters should actually return a bool * fix(RoleManager): typo * fix(Role): should always return a boolean * fix(Gmrm): getter should return null * fix(RoleManager): getters should return null * fix: typing getters should return null * fix(Role): docs grammar and consistency * chore: prefer in operator over Reflect#has * feat(GmRm): botRole getter * fix(GmRm): use the actual properties * feat(RoleManager): rem myRole in pref o botRoleFor * fix(Role): remove obsolete is- getters * fix: checking tags after getter removal * chore: identifier naming consistency * chore: prefer explicit true type over boolean * fix: typo * feat(Integration): Add Integration#roles getter (#1) * fix(RoleManager): remove bot check r:partials * feat(RoleManager): robustness against uncached u * docs: possibly undefined Co-authored-by: Jan <66554238+Vaporox@users.noreply.github.com> --- src/managers/GuildMemberRoleManager.js | 20 ++++++++++++++++++++ src/managers/RoleManager.js | 21 +++++++++++++++++++++ src/structures/Integration.js | 10 ++++++++++ src/structures/Role.js | 20 ++++++++++++++++++++ typings/index.d.ts | 13 ++++++++++++- 5 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/managers/GuildMemberRoleManager.js b/src/managers/GuildMemberRoleManager.js index ccbd31f52b6b..396f3d5b89ca 100644 --- a/src/managers/GuildMemberRoleManager.js +++ b/src/managers/GuildMemberRoleManager.js @@ -72,6 +72,26 @@ class GuildMemberRoleManager { return this._roles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this._roles.first()); } + /** + * The premium subscriber role of the guild, if present on the member + * @type {?Role} + * @readonly + */ + get premiumSubscriberRole() { + return this.cache.find(role => role.tags && role.tags.premiumSubscriberRole) || null; + } + + /** + * The managed role this member created when joining the guild, if any + * Only ever available on bots + * @type {?Role} + * @readonly + */ + get botRole() { + if (!this.member.user.bot) return null; + return this.cache.find(role => role.tags && role.tags.botID === this.member.user.id) || null; + } + /** * Adds a role (or multiple roles) to the member. * @param {RoleResolvable|RoleResolvable[]|Collection} roleOrRoles The role or roles to add diff --git a/src/managers/RoleManager.js b/src/managers/RoleManager.js index 50bd0f195207..0dfbefb03b39 100644 --- a/src/managers/RoleManager.js +++ b/src/managers/RoleManager.js @@ -126,6 +126,18 @@ class RoleManager extends BaseManager { }); } + /** + * Gets the managed role a user created when joining the guild, if any + * Only ever available for bots + * @param {UserResolvable} user The user to access the bot role for + * @returns {?Role} + */ + botRoleFor(user) { + const userID = this.client.users.resolveID(user); + if (!userID) return null; + return this.cache.find(role => role.tags && role.tags.botID === userID) || null; + } + /** * The `@everyone` role of the guild * @type {Role} @@ -135,6 +147,15 @@ class RoleManager extends BaseManager { return this.cache.get(this.guild.id); } + /** + * The premium subscriber role of the guild, if any + * @type {?Role} + * @readonly + */ + get premiumSubscriberRole() { + return this.cache.find(role => role.tags && role.tags.premiumSubscriberRole) || null; + } + /** * The role with the highest position in the cache * @type {Role} diff --git a/src/structures/Integration.js b/src/structures/Integration.js index a48019a61b3f..2fc005cf45a3 100644 --- a/src/structures/Integration.js +++ b/src/structures/Integration.js @@ -83,6 +83,16 @@ class Integration extends Base { this._patch(data); } + /** + * All roles that are managed by this integration + * @type {Collection} + * @readonly + */ + get roles() { + const roles = this.guild.roles.cache; + return roles.filter(role => role.tags && role.tags.integrationID === this.id); + } + _patch(data) { /** * The behavior of expiring subscribers diff --git a/src/structures/Role.js b/src/structures/Role.js index 4d2474ca07e0..d14db3639386 100644 --- a/src/structures/Role.js +++ b/src/structures/Role.js @@ -82,6 +82,26 @@ class Role extends Base { * @type {boolean} */ this.deleted = false; + + /** + * The tags this role has + * @type {?Object} + * @property {Snowflake} [botID] The id of the bot this role belongs to + * @property {Snowflake} [integrationID] The id of the integration this role belongs to + * @property {true} [premiumSubscriberRole] Whether this is the guild's premium subscription role + */ + this.tags = data.tags ? {} : null; + if (data.tags) { + if ('bot_id' in data.tags) { + this.tags.botID = data.tags.bot_id; + } + if ('integration_id' in data.tags) { + this.tags.integrationID = data.tags.integration_id; + } + if ('premium_subscriber' in data.tags) { + this.tags.premiumSubscriberRole = true; + } + } } /** diff --git a/typings/index.d.ts b/typings/index.d.ts index c1b4d6a24864..45da4e4e41bf 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -904,6 +904,7 @@ declare module 'discord.js' { public id: Snowflake; public name: string; public role: Role; + public readonly roles: Collection; public syncedAt: number; public syncing: boolean; public type: string; @@ -1287,6 +1288,7 @@ declare module 'discord.js' { public permissions: Readonly; public readonly position: number; public rawPosition: number; + public tags: RoleTagData | null; public comparePositionTo(role: Role): number; public delete(reason?: string): Promise; public edit(data: RoleData, reason?: string): Promise; @@ -1954,6 +1956,8 @@ declare module 'discord.js' { public readonly hoist: Role | null; public readonly color: Role | null; public readonly highest: Role; + public readonly premiumSubscriberRole: Role | null; + public readonly botRole: Role | null; public member: GuildMember; public guild: Guild; @@ -2015,7 +2019,8 @@ declare module 'discord.js' { public readonly everyone: Role; public readonly highest: Role; public guild: Guild; - + public readonly premiumSubscriberRole: Role | null; + public botRoleFor(user: UserResolvable): Role | null; public create(options?: { data?: RoleData; reason?: string }): Promise; public fetch(id: Snowflake, cache?: boolean, force?: boolean): Promise; public fetch(id?: Snowflake, cache?: boolean, force?: boolean): Promise>; @@ -3115,6 +3120,12 @@ declare module 'discord.js' { type RoleResolvable = Role | string; + interface RoleTagData { + botID?: Snowflake; + integrationID?: Snowflake; + premiumSubscriberRole?: true; + } + type ShardingManagerMode = 'process' | 'worker'; type Snowflake = string;