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;