diff options
Diffstat (limited to 'node_modules/discord.js/src/managers')
15 files changed, 1795 insertions, 0 deletions
diff --git a/node_modules/discord.js/src/managers/BaseManager.js b/node_modules/discord.js/src/managers/BaseManager.js new file mode 100644 index 0000000..c11d191 --- /dev/null +++ b/node_modules/discord.js/src/managers/BaseManager.js @@ -0,0 +1,81 @@ +'use strict'; + +const Collection = require('../util/Collection'); +let Structures; + +/** + * Manages the API methods of a data model and holds its cache. + * @abstract + */ +class BaseManager { + constructor(client, iterable, holds, cacheType = Collection, ...cacheOptions) { + if (!Structures) Structures = require('../util/Structures'); + /** + * The data structure belonging to this manager + * @name BaseManager#holds + * @type {Function} + * @private + * @readonly + */ + Object.defineProperty(this, 'holds', { value: Structures.get(holds.name) || holds }); + + /** + * The client that instantiated this Manager + * @name BaseManager#client + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: client }); + + /** + * The type of Collection of the Manager + * @type {Collection} + */ + this.cacheType = cacheType; + + /** + * Holds the cache for the data model + * @type {Collection} + */ + this.cache = new cacheType(...cacheOptions); + if (iterable) for (const i of iterable) this.add(i); + } + + add(data, cache = true, { id, extras = [] } = {}) { + const existing = this.cache.get(id || data.id); + if (existing && existing._patch && cache) existing._patch(data); + if (existing) return existing; + + const entry = this.holds ? new this.holds(this.client, data, ...extras) : data; + if (cache) this.cache.set(id || entry.id, entry); + return entry; + } + + /** + * Resolves a data entry to a data Object. + * @param {string|Object} idOrInstance The id or instance of something in this Manager + * @returns {?Object} An instance from this Manager + */ + resolve(idOrInstance) { + if (idOrInstance instanceof this.holds) return idOrInstance; + if (typeof idOrInstance === 'string') return this.cache.get(idOrInstance) || null; + return null; + } + + /** + * Resolves a data entry to a instance ID. + * @param {string|Object} idOrInstance The id or instance of something in this Manager + * @returns {?Snowflake} + */ + resolveID(idOrInstance) { + if (idOrInstance instanceof this.holds) return idOrInstance.id; + if (typeof idOrInstance === 'string') return idOrInstance; + return null; + } + + valueOf() { + return this.cache; + } +} + +module.exports = BaseManager; diff --git a/node_modules/discord.js/src/managers/ChannelManager.js b/node_modules/discord.js/src/managers/ChannelManager.js new file mode 100644 index 0000000..99fde10 --- /dev/null +++ b/node_modules/discord.js/src/managers/ChannelManager.js @@ -0,0 +1,93 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const Channel = require('../structures/Channel'); +const { Events } = require('../util/Constants'); + +/** + * A manager of channels belonging to a client + * @extends {BaseManager} + */ +class ChannelManager extends BaseManager { + constructor(client, iterable) { + super(client, iterable, Channel); + } + + /** + * The cache of Channels + * @type {Collection<Snowflake, Channel>} + * @name ChannelManager#cache + */ + + add(data, guild, cache = true) { + const existing = this.cache.get(data.id); + if (existing) { + if (existing._patch && cache) existing._patch(data); + if (guild) guild.channels.add(existing); + return existing; + } + + const channel = Channel.create(this.client, data, guild); + + if (!channel) { + this.client.emit(Events.DEBUG, `Failed to find guild, or unknown type for channel ${data.id} ${data.type}`); + return null; + } + + if (cache) this.cache.set(channel.id, channel); + + return channel; + } + + remove(id) { + const channel = this.cache.get(id); + if (channel.guild) channel.guild.channels.cache.delete(id); + this.cache.delete(id); + } + + /** + * Data that can be resolved to give a Channel object. This can be: + * * A Channel object + * * A Snowflake + * @typedef {Channel|Snowflake} ChannelResolvable + */ + + /** + * Resolves a ChannelResolvable to a Channel object. + * @method resolve + * @memberof ChannelManager + * @instance + * @param {ChannelResolvable} channel The channel resolvable to resolve + * @returns {?Channel} + */ + + /** + * Resolves a ChannelResolvable to a channel ID string. + * @method resolveID + * @memberof ChannelManager + * @instance + * @param {ChannelResolvable} channel The channel resolvable to resolve + * @returns {?Snowflake} + */ + + /** + * Obtains a channel from Discord, or the channel cache if it's already available. + * @param {Snowflake} id ID of the channel + * @param {boolean} [cache=true] Whether to cache the new channel object if it isn't already + * @returns {Promise<Channel>} + * @example + * // Fetch a channel by its id + * client.channels.fetch('222109930545610754') + * .then(channel => console.log(channel.name)) + * .catch(console.error); + */ + async fetch(id, cache = true) { + const existing = this.cache.get(id); + if (existing && !existing.partial) return existing; + + const data = await this.client.api.channels(id).get(); + return this.add(data, null, cache); + } +} + +module.exports = ChannelManager; diff --git a/node_modules/discord.js/src/managers/GuildChannelManager.js b/node_modules/discord.js/src/managers/GuildChannelManager.js new file mode 100644 index 0000000..bc72e9a --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildChannelManager.js @@ -0,0 +1,131 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const GuildChannel = require('../structures/GuildChannel'); +const PermissionOverwrites = require('../structures/PermissionOverwrites'); +const { ChannelTypes } = require('../util/Constants'); + +/** + * Manages API methods for GuildChannels and stores their cache. + * @extends {BaseManager} + */ +class GuildChannelManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, GuildChannel); + + /** + * The guild this Manager belongs to + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The cache of this Manager + * @type {Collection<Snowflake, GuildChannel>} + * @name GuildChannelManager#cache + */ + + add(channel) { + const existing = this.cache.get(channel.id); + if (existing) return existing; + this.cache.set(channel.id, channel); + return channel; + } + + /** + * Data that can be resolved to give a Guild Channel object. This can be: + * * A GuildChannel object + * * A Snowflake + * @typedef {GuildChannel|Snowflake} GuildChannelResolvable + */ + + /** + * Resolves a GuildChannelResolvable to a Channel object. + * @method resolve + * @memberof GuildChannelManager + * @instance + * @param {GuildChannelResolvable} channel The GuildChannel resolvable to resolve + * @returns {?Channel} + */ + + /** + * Resolves a GuildChannelResolvable to a channel ID string. + * @method resolveID + * @memberof GuildChannelManager + * @instance + * @param {GuildChannelResolvable} channel The GuildChannel resolvable to resolve + * @returns {?Snowflake} + */ + + /** + * Creates a new channel in the guild. + * @param {string} name The name of the new channel + * @param {Object} [options] Options + * @param {string} [options.type='text'] The type of the new channel, either `text`, `voice`, or `category` + * @param {string} [options.topic] The topic for the new channel + * @param {boolean} [options.nsfw] Whether the new channel is nsfw + * @param {number} [options.bitrate] Bitrate of the new channel in bits (only voice) + * @param {number} [options.userLimit] Maximum amount of users allowed in the new channel (only voice) + * @param {ChannelResolvable} [options.parent] Parent of the new channel + * @param {OverwriteResolvable[]|Collection<Snowflake, OverwriteResolvable>} [options.permissionOverwrites] + * Permission overwrites of the new channel + * @param {number} [options.position] Position of the new channel + * @param {number} [options.rateLimitPerUser] The ratelimit per user for the channel + * @param {string} [options.reason] Reason for creating the channel + * @returns {Promise<GuildChannel>} + * @example + * // Create a new text channel + * guild.channels.create('new-general', { reason: 'Needed a cool new channel' }) + * .then(console.log) + * .catch(console.error); + * @example + * // Create a new channel with permission overwrites + * guild.channels.create('new-voice', { + * type: 'voice', + * permissionOverwrites: [ + * { + * id: message.author.id, + * deny: ['VIEW_CHANNEL'], + * }, + * ], + * }) + */ + async create(name, options = {}) { + let { + type, + topic, + nsfw, + bitrate, + userLimit, + parent, + permissionOverwrites, + position, + rateLimitPerUser, + reason, + } = options; + if (parent) parent = this.client.channels.resolveID(parent); + if (permissionOverwrites) { + permissionOverwrites = permissionOverwrites.map(o => PermissionOverwrites.resolve(o, this.guild)); + } + + const data = await this.client.api.guilds(this.guild.id).channels.post({ + data: { + name, + topic, + type: type ? ChannelTypes[type.toUpperCase()] : ChannelTypes.TEXT, + nsfw, + bitrate, + user_limit: userLimit, + parent_id: parent, + position, + permission_overwrites: permissionOverwrites, + rate_limit_per_user: rateLimitPerUser, + }, + reason, + }); + return this.client.actions.ChannelCreate.handle(data).channel; + } +} + +module.exports = GuildChannelManager; diff --git a/node_modules/discord.js/src/managers/GuildEmojiManager.js b/node_modules/discord.js/src/managers/GuildEmojiManager.js new file mode 100644 index 0000000..c13ad13 --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildEmojiManager.js @@ -0,0 +1,129 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { TypeError } = require('../errors'); +const GuildEmoji = require('../structures/GuildEmoji'); +const ReactionEmoji = require('../structures/ReactionEmoji'); +const Collection = require('../util/Collection'); +const DataResolver = require('../util/DataResolver'); + +/** + * Manages API methods for GuildEmojis and stores their cache. + * @extends {BaseManager} + */ +class GuildEmojiManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, GuildEmoji); + /** + * The guild this manager belongs to + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The cache of GuildEmojis + * @type {Collection<Snowflake, GuildEmoji>} + * @name GuildEmojiManager#cache + */ + + add(data, cache) { + return super.add(data, cache, { extras: [this.guild] }); + } + + /** + * Creates a new custom emoji in the guild. + * @param {BufferResolvable|Base64Resolvable} attachment The image for the emoji + * @param {string} name The name for the emoji + * @param {Object} [options] Options + * @param {Collection<Snowflake, Role>|RoleResolvable[]} [options.roles] Roles to limit the emoji to + * @param {string} [options.reason] Reason for creating the emoji + * @returns {Promise<Emoji>} The created emoji + * @example + * // Create a new emoji from a url + * guild.emojis.create('https://i.imgur.com/w3duR07.png', 'rip') + * .then(emoji => console.log(`Created new emoji with name ${emoji.name}!`)) + * .catch(console.error); + * @example + * // Create a new emoji from a file on your computer + * guild.emojis.create('./memes/banana.png', 'banana') + * .then(emoji => console.log(`Created new emoji with name ${emoji.name}!`)) + * .catch(console.error); + */ + async create(attachment, name, { roles, reason } = {}) { + attachment = await DataResolver.resolveImage(attachment); + if (!attachment) throw new TypeError('REQ_RESOURCE_TYPE'); + + const data = { image: attachment, name }; + if (roles) { + data.roles = []; + for (let role of roles instanceof Collection ? roles.values() : roles) { + role = this.guild.roles.resolve(role); + if (!role) { + return Promise.reject( + new TypeError('INVALID_TYPE', 'options.roles', 'Array or Collection of Roles or Snowflakes', true), + ); + } + data.roles.push(role.id); + } + } + + return this.client.api + .guilds(this.guild.id) + .emojis.post({ data, reason }) + .then(emoji => this.client.actions.GuildEmojiCreate.handle(this.guild, emoji).emoji); + } + + /** + * Data that can be resolved into an GuildEmoji object. This can be: + * * A custom emoji ID + * * A GuildEmoji object + * * A ReactionEmoji object + * @typedef {Snowflake|GuildEmoji|ReactionEmoji} EmojiResolvable + */ + + /** + * Resolves an EmojiResolvable to an Emoji object. + * @param {EmojiResolvable} emoji The Emoji resolvable to identify + * @returns {?GuildEmoji} + */ + resolve(emoji) { + if (emoji instanceof ReactionEmoji) return super.resolve(emoji.id); + return super.resolve(emoji); + } + + /** + * Resolves an EmojiResolvable to an Emoji ID string. + * @param {EmojiResolvable} emoji The Emoji resolvable to identify + * @returns {?Snowflake} + */ + resolveID(emoji) { + if (emoji instanceof ReactionEmoji) return emoji.id; + return super.resolveID(emoji); + } + + /** + * Data that can be resolved to give an emoji identifier. This can be: + * * The unicode representation of an emoji + * * An EmojiResolvable + * @typedef {string|EmojiResolvable} EmojiIdentifierResolvable + */ + + /** + * Resolves an EmojiResolvable to an emoji identifier. + * @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve + * @returns {?string} + */ + resolveIdentifier(emoji) { + const emojiResolvable = this.resolve(emoji); + if (emojiResolvable) return emojiResolvable.identifier; + if (emoji instanceof ReactionEmoji) return emoji.identifier; + if (typeof emoji === 'string') { + if (!emoji.includes('%')) return encodeURIComponent(emoji); + else return emoji; + } + return null; + } +} + +module.exports = GuildEmojiManager; diff --git a/node_modules/discord.js/src/managers/GuildEmojiRoleManager.js b/node_modules/discord.js/src/managers/GuildEmojiRoleManager.js new file mode 100644 index 0000000..038571f --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildEmojiRoleManager.js @@ -0,0 +1,119 @@ +'use strict'; + +const { TypeError } = require('../errors'); +const Collection = require('../util/Collection'); + +/** + * Manages API methods for roles belonging to emojis and stores their cache. + */ +class GuildEmojiRoleManager { + constructor(emoji) { + /** + * The emoji belonging to this manager + * @type {GuildEmoji} + */ + this.emoji = emoji; + /** + * The guild belonging to this manager + * @type {Guild} + */ + this.guild = emoji.guild; + /** + * The client belonging to this manager + * @type {Client} + * @readonly + */ + Object.defineProperty(this, 'client', { value: emoji.client }); + } + + /** + * The filtered collection of roles of the guild emoji + * @type {Collection<Snowflake, Role>} + * @private + * @readonly + */ + get _roles() { + return this.guild.roles.cache.filter(role => this.emoji._roles.includes(role.id)); + } + + /** + * The cache of roles belonging to this emoji + * @type {Collection<Snowflake, Role>} + * @readonly + */ + get cache() { + return this._roles; + } + + /** + * Adds a role (or multiple roles) to the list of roles that can use this emoji. + * @param {RoleResolvable|RoleResolvable[]|Collection<Snowflake, Role>} roleOrRoles The role or roles to add + * @returns {Promise<GuildEmoji>} + */ + add(roleOrRoles) { + if (roleOrRoles instanceof Collection) return this.add(roleOrRoles.keyArray()); + if (!Array.isArray(roleOrRoles)) return this.add([roleOrRoles]); + roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolve(r)); + + if (roleOrRoles.includes(null)) { + return Promise.reject(new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true)); + } + + const newRoles = [...new Set(roleOrRoles.concat(...this._roles.values()))]; + return this.set(newRoles); + } + + /** + * Removes a role (or multiple roles) from the list of roles that can use this emoji. + * @param {RoleResolvable|RoleResolvable[]|Collection<Snowflake, Role>} roleOrRoles The role or roles to remove + * @returns {Promise<GuildEmoji>} + */ + remove(roleOrRoles) { + if (roleOrRoles instanceof Collection) return this.remove(roleOrRoles.keyArray()); + if (!Array.isArray(roleOrRoles)) return this.remove([roleOrRoles]); + roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolveID(r)); + + if (roleOrRoles.includes(null)) { + return Promise.reject(new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true)); + } + + const newRoles = this._roles.keyArray().filter(role => !roleOrRoles.includes(role)); + return this.set(newRoles); + } + + /** + * Sets the role(s) that can use this emoji. + * @param {Collection<Snowflake, Role>|RoleResolvable[]} roles The roles or role IDs to apply + * @returns {Promise<GuildEmoji>} + * @example + * // Set the emoji's roles to a single role + * guildEmoji.roles.set(['391156570408615936']) + * .then(console.log) + * .catch(console.error); + * @example + * // Remove all roles from an emoji + * guildEmoji.roles.set([]) + * .then(console.log) + * .catch(console.error); + */ + set(roles) { + return this.emoji.edit({ roles }); + } + + clone() { + const clone = new this.constructor(this.emoji); + clone._patch(this._roles.keyArray().slice()); + return clone; + } + + /** + * Patches the roles for this manager's cache + * @param {Snowflake[]} roles The new roles + * @private + */ + _patch(roles) { + this.emoji._roles = roles; + } +} + +module.exports = GuildEmojiRoleManager; diff --git a/node_modules/discord.js/src/managers/GuildManager.js b/node_modules/discord.js/src/managers/GuildManager.js new file mode 100644 index 0000000..42cbcd4 --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildManager.js @@ -0,0 +1,216 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const Guild = require('../structures/Guild'); +const GuildChannel = require('../structures/GuildChannel'); +const GuildEmoji = require('../structures/GuildEmoji'); +const GuildMember = require('../structures/GuildMember'); +const Invite = require('../structures/Invite'); +const Role = require('../structures/Role'); +const { + Events, + VerificationLevels, + DefaultMessageNotifications, + ExplicitContentFilterLevels, +} = require('../util/Constants'); +const DataResolver = require('../util/DataResolver'); +const Permissions = require('../util/Permissions'); +const { resolveColor } = require('../util/Util'); + +/** + * Manages API methods for Guilds and stores their cache. + * @extends {BaseManager} + */ +class GuildManager extends BaseManager { + constructor(client, iterable) { + super(client, iterable, Guild); + } + + /** + * The cache of this Manager + * @type {Collection<Snowflake, Guild>} + * @name GuildManager#cache + */ + + /** + * Data that resolves to give a Guild object. This can be: + * * A Guild object + * * A GuildChannel object + * * A GuildEmoji object + * * A Role object + * * A Snowflake + * * An Invite object + * @typedef {Guild|GuildChannel|GuildMember|GuildEmoji|Role|Snowflake|Invite} GuildResolvable + */ + + /** + * Partial data for a Role. + * @typedef {Object} PartialRoleData + * @property {number} [id] The ID for this role, used to set channel overrides, + * this is a placeholder and will be replaced by the API after consumption + * @property {string} [name] The name of the role + * @property {ColorResolvable} [color] The color of the role, either a hex string or a base 10 number + * @property {boolean} [hoist] Whether or not the role should be hoisted + * @property {number} [position] The position of the role + * @property {PermissionResolvable|number} [permissions] The permissions of the role + * @property {boolean} [mentionable] Whether or not the role should be mentionable + */ + + /** + * Partial overwrite data. + * @typedef {Object} PartialOverwriteData + * @property {number|Snowflake} id The Role or User ID for this overwrite + * @property {string} [type] The type of this overwrite + * @property {PermissionResolvable} [allow] The permissions to allow + * @property {PermissionResolvable} [deny] The permissions to deny + */ + + /** + * Partial data for a Channel. + * @typedef {Object} PartialChannelData + * @property {number} [id] The ID for this channel, used to set its parent, + * this is a placeholder and will be replaced by the API after consumption + * @property {number} [parentID] The parent ID for this channel + * @property {string} [type] The type of the channel + * @property {string} name The name of the channel + * @property {string} [topic] The topic of the text channel + * @property {boolean} [nsfw] Whether the channel is NSFW + * @property {number} [bitrate] The bitrate of the voice channel + * @property {number} [userLimit] The user limit of the channel + * @property {PartialOverwriteData} [permissionOverwrites] + * Overwrites of the channel + * @property {number} [rateLimitPerUser] The rate limit per user of the channel in seconds + */ + + /** + * Resolves a GuildResolvable to a Guild object. + * @method resolve + * @memberof GuildManager + * @instance + * @param {GuildResolvable} guild The guild resolvable to identify + * @returns {?Guild} + */ + resolve(guild) { + if ( + guild instanceof GuildChannel || + guild instanceof GuildMember || + guild instanceof GuildEmoji || + guild instanceof Role || + (guild instanceof Invite && guild.guild) + ) { + return super.resolve(guild.guild); + } + return super.resolve(guild); + } + + /** + * Resolves a GuildResolvable to a Guild ID string. + * @method resolveID + * @memberof GuildManager + * @instance + * @param {GuildResolvable} guild The guild resolvable to identify + * @returns {?Snowflake} + */ + resolveID(guild) { + if ( + guild instanceof GuildChannel || + guild instanceof GuildMember || + guild instanceof GuildEmoji || + guild instanceof Role || + (guild instanceof Invite && guild.guild) + ) { + return super.resolveID(guild.guild.id); + } + return super.resolveID(guild); + } + + /** + * Creates a guild. + * <warn>This is only available to bots in fewer than 10 guilds.</warn> + * @param {string} name The name of the guild + * @param {Object} [options] Options for the creating + * @param {PartialChannelData[]} [options.channels] The channels for this guild + * @param {DefaultMessageNotifications} [options.defaultMessageNotifications] The default message notifications + * for the guild + * @param {ExplicitContentFilterLevel} [options.explicitContentFilter] The explicit content filter level for the guild + * @param {BufferResolvable|Base64Resolvable} [options.icon=null] The icon for the guild + * @param {string} [options.region] The region for the server, defaults to the closest one available + * @param {PartialRoleData[]} [options.roles] The roles for this guild, + * the first element of this array is used to change properties of the guild's everyone role. + * @param {VerificationLevel} [options.verificationLevel] The verification level for the guild + * @returns {Promise<Guild>} The guild that was created + */ + async create( + name, + { + channels = [], + defaultMessageNotifications, + explicitContentFilter, + icon = null, + region, + roles = [], + verificationLevel, + } = {}, + ) { + icon = await DataResolver.resolveImage(icon); + if (typeof verificationLevel !== 'undefined' && typeof verificationLevel !== 'number') { + verificationLevel = VerificationLevels.indexOf(verificationLevel); + } + if (typeof defaultMessageNotifications !== 'undefined' && typeof defaultMessageNotifications !== 'number') { + defaultMessageNotifications = DefaultMessageNotifications.indexOf(defaultMessageNotifications); + } + if (typeof explicitContentFilter !== 'undefined' && typeof explicitContentFilter !== 'number') { + explicitContentFilter = ExplicitContentFilterLevels.indexOf(explicitContentFilter); + } + for (const channel of channels) { + channel.parent_id = channel.parentID; + delete channel.parentID; + if (!channel.permissionOverwrites) continue; + for (const overwrite of channel.permissionOverwrites) { + if (overwrite.allow) overwrite.allow = Permissions.resolve(overwrite.allow); + if (overwrite.deny) overwrite.deny = Permissions.resolve(overwrite.deny); + } + channel.permission_overwrites = channel.permissionOverwrites; + delete channel.permissionOverwrites; + } + for (const role of roles) { + if (role.color) role.color = resolveColor(role.color); + if (role.permissions) role.permissions = Permissions.resolve(role.permissions); + } + return new Promise((resolve, reject) => + this.client.api.guilds + .post({ + data: { + name, + region, + icon, + verification_level: verificationLevel, + default_message_notifications: defaultMessageNotifications, + explicit_content_filter: explicitContentFilter, + channels, + roles, + }, + }) + .then(data => { + if (this.client.guilds.cache.has(data.id)) return resolve(this.client.guilds.cache.get(data.id)); + + const handleGuild = guild => { + if (guild.id === data.id) { + this.client.removeListener(Events.GUILD_CREATE, handleGuild); + this.client.clearTimeout(timeout); + resolve(guild); + } + }; + this.client.on(Events.GUILD_CREATE, handleGuild); + + const timeout = this.client.setTimeout(() => { + this.client.removeListener(Events.GUILD_CREATE, handleGuild); + resolve(this.client.guilds.add(data)); + }, 10000); + return undefined; + }, reject), + ); + } +} + +module.exports = GuildManager; diff --git a/node_modules/discord.js/src/managers/GuildMemberManager.js b/node_modules/discord.js/src/managers/GuildMemberManager.js new file mode 100644 index 0000000..e1387e8 --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildMemberManager.js @@ -0,0 +1,272 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { Error, TypeError } = require('../errors'); +const GuildMember = require('../structures/GuildMember'); +const Collection = require('../util/Collection'); +const { Events, OPCodes } = require('../util/Constants'); + +/** + * Manages API methods for GuildMembers and stores their cache. + * @extends {BaseManager} + */ +class GuildMemberManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, GuildMember); + /** + * The guild this manager belongs to + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The cache of this Manager + * @type {Collection<Snowflake, GuildMember>} + * @name GuildMemberManager#cache + */ + + add(data, cache = true) { + return super.add(data, cache, { id: data.user.id, extras: [this.guild] }); + } + + /** + * Data that resolves to give a GuildMember object. This can be: + * * A GuildMember object + * * A User resolvable + * @typedef {GuildMember|UserResolvable} GuildMemberResolvable + */ + + /** + * Resolves a GuildMemberResolvable to a GuildMember object. + * @param {GuildMemberResolvable} member The user that is part of the guild + * @returns {?GuildMember} + */ + resolve(member) { + const memberResolvable = super.resolve(member); + if (memberResolvable) return memberResolvable; + const userResolvable = this.client.users.resolveID(member); + if (userResolvable) return super.resolve(userResolvable); + return null; + } + + /** + * Resolves a GuildMemberResolvable to a member ID string. + * @param {GuildMemberResolvable} member The user that is part of the guild + * @returns {?Snowflake} + */ + resolveID(member) { + const memberResolvable = super.resolveID(member); + if (memberResolvable) return memberResolvable; + const userResolvable = this.client.users.resolveID(member); + return this.cache.has(userResolvable) ? userResolvable : null; + } + + /** + * Options used to fetch a single member from a guild. + * @typedef {Object} FetchMemberOptions + * @property {UserResolvable} user The user to fetch + * @property {boolean} [cache=true] Whether or not to cache the fetched member + */ + + /** + * Options used to fetch multiple members from a guild. + * @typedef {Object} FetchMembersOptions + * @property {UserResolvable|UserResolvable[]} user The user(s) to fetch + * @property {?string} query Limit fetch to members with similar usernames + * @property {number} [limit=0] Maximum number of members to request + * @property {boolean} [withPresences=false] Whether or not to include the presences + * @property {number} [time=120e3] Timeout for receipt of members + */ + + /** + * Fetches member(s) from Discord, even if they're offline. + * @param {UserResolvable|FetchMemberOptions|FetchMembersOptions} [options] If a UserResolvable, the user to fetch. + * If undefined, fetches all members. + * If a query, it limits the results to users with similar usernames. + * @returns {Promise<GuildMember>|Promise<Collection<Snowflake, GuildMember>>} + * @example + * // Fetch all members from a guild + * guild.members.fetch() + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch a single member + * guild.members.fetch('66564597481480192') + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch a single member without caching + * guild.members.fetch({ user, cache: false }) + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch by an array of users including their presences + * guild.members.fetch({ user: ['66564597481480192', '191615925336670208'], withPresences: true }) + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch by query + * guild.members.fetch({ query: 'hydra', limit: 1 }) + * .then(console.log) + * .catch(console.error); + */ + fetch(options) { + if (!options) return this._fetchMany(); + const user = this.client.users.resolveID(options); + if (user) return this._fetchSingle({ user, cache: true }); + if (options.user) { + if (Array.isArray(options.user)) { + options.user = options.user.map(u => this.client.users.resolveID(u)); + return this._fetchMany(options); + } else { + options.user = this.client.users.resolveID(options.user); + } + if (!options.limit && !options.withPresences) return this._fetchSingle(options); + } + return this._fetchMany(options); + } + + /** + * Prunes members from the guild based on how long they have been inactive. + * <info>It's recommended to set options.count to `false` for large guilds.</info> + * @param {Object} [options] Prune options + * @param {number} [options.days=7] Number of days of inactivity required to kick + * @param {boolean} [options.dry=false] Get number of users that will be kicked, without actually kicking them + * @param {boolean} [options.count=true] Whether or not to return the number of users that have been kicked. + * @param {string} [options.reason] Reason for this prune + * @returns {Promise<number|null>} The number of members that were/will be kicked + * @example + * // See how many members will be pruned + * guild.members.prune({ dry: true }) + * .then(pruned => console.log(`This will prune ${pruned} people!`)) + * .catch(console.error); + * @example + * // Actually prune the members + * guild.members.prune({ days: 1, reason: 'too many people!' }) + * .then(pruned => console.log(`I just pruned ${pruned} people!`)) + * .catch(console.error); + */ + prune({ days = 7, dry = false, count = true, reason } = {}) { + if (typeof days !== 'number') throw new TypeError('PRUNE_DAYS_TYPE'); + return this.client.api + .guilds(this.guild.id) + .prune[dry ? 'get' : 'post']({ + query: { + days, + compute_prune_count: count, + }, + reason, + }) + .then(data => data.pruned); + } + + /** + * Bans a user from the guild. + * @param {UserResolvable} user The user to ban + * @param {Object} [options] Options for the ban + * @param {number} [options.days=0] Number of days of messages to delete + * @param {string} [options.reason] Reason for banning + * @returns {Promise<GuildMember|User|Snowflake>} Result object will be resolved as specifically as possible. + * If the GuildMember cannot be resolved, the User will instead be attempted to be resolved. If that also cannot + * be resolved, the user ID will be the result. + * @example + * // Ban a user by ID (or with a user/guild member object) + * guild.members.ban('84484653687267328') + * .then(user => console.log(`Banned ${user.username || user.id || user} from ${guild.name}`)) + * .catch(console.error); + */ + ban(user, options = { days: 0 }) { + if (options.days) options['delete-message-days'] = options.days; + const id = this.client.users.resolveID(user); + if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID', true)); + return this.client.api + .guilds(this.guild.id) + .bans[id].put({ query: options }) + .then(() => { + if (user instanceof GuildMember) return user; + const _user = this.client.users.resolve(id); + if (_user) { + const member = this.resolve(_user); + return member || _user; + } + return id; + }); + } + + /** + * Unbans a user from the guild. + * @param {UserResolvable} user The user to unban + * @param {string} [reason] Reason for unbanning user + * @returns {Promise<User>} + * @example + * // Unban a user by ID (or with a user/guild member object) + * guild.members.unban('84484653687267328') + * .then(user => console.log(`Unbanned ${user.username} from ${guild.name}`)) + * .catch(console.error); + */ + unban(user, reason) { + const id = this.client.users.resolveID(user); + if (!id) return Promise.reject(new Error('BAN_RESOLVE_ID')); + return this.client.api + .guilds(this.guild.id) + .bans[id].delete({ reason }) + .then(() => this.client.users.resolve(user)); + } + + _fetchSingle({ user, cache }) { + const existing = this.cache.get(user); + if (existing && !existing.partial) return Promise.resolve(existing); + return this.client.api + .guilds(this.guild.id) + .members(user) + .get() + .then(data => this.add(data, cache)); + } + + _fetchMany({ limit = 0, withPresences: presences = false, user: user_ids, query, time = 120e3 } = {}) { + return new Promise((resolve, reject) => { + if (this.guild.memberCount === this.cache.size && !query && !limit && !presences && !user_ids) { + resolve(this.cache); + return; + } + if (!query && !user_ids) query = ''; + this.guild.shard.send({ + op: OPCodes.REQUEST_GUILD_MEMBERS, + d: { + guild_id: this.guild.id, + presences, + user_ids, + query, + limit, + }, + }); + const fetchedMembers = new Collection(); + const option = query || limit || presences || user_ids; + const handler = (members, guild) => { + if (guild.id !== this.guild.id) return; + timeout.refresh(); + for (const member of members.values()) { + if (option) fetchedMembers.set(member.id, member); + } + if ( + this.guild.memberCount <= this.cache.size || + (option && members.size < 1000) || + (limit && fetchedMembers.size >= limit) + ) { + this.guild.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler); + let fetched = option ? fetchedMembers : this.cache; + if (user_ids && !Array.isArray(user_ids) && fetched.size) fetched = fetched.first(); + resolve(fetched); + } + }; + const timeout = this.guild.client.setTimeout(() => { + this.guild.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler); + reject(new Error('GUILD_MEMBERS_TIMEOUT')); + }, time); + this.guild.client.on(Events.GUILD_MEMBERS_CHUNK, handler); + }); + } +} + +module.exports = GuildMemberManager; diff --git a/node_modules/discord.js/src/managers/GuildMemberRoleManager.js b/node_modules/discord.js/src/managers/GuildMemberRoleManager.js new file mode 100644 index 0000000..f4f530d --- /dev/null +++ b/node_modules/discord.js/src/managers/GuildMemberRoleManager.js @@ -0,0 +1,166 @@ +'use strict'; + +const { TypeError } = require('../errors'); +const Collection = require('../util/Collection'); + +/** + * Manages API methods for roles of a GuildMember and stores their cache. + */ +class GuildMemberRoleManager { + constructor(member) { + /** + * The GuildMember this manager belongs to + * @type {GuildMember} + */ + this.member = member; + /** + * The Guild this manager belongs to + * @type {Guild} + */ + this.guild = member.guild; + Object.defineProperty(this, 'client', { value: member.client }); + } + + /** + * The filtered collection of roles of the member + * @type {Collection<Snowflake, Role>} + * @private + * @readonly + */ + get _roles() { + const everyone = this.guild.roles.everyone; + return this.guild.roles.cache.filter(role => this.member._roles.includes(role.id)).set(everyone.id, everyone); + } + + /** + * The roles of this member + * @type {Collection<Snowflake, Role>} + * @readonly + */ + get cache() { + return this._roles; + } + + /** + * The role of the member used to hoist them in a separate category in the users list + * @type {?Role} + * @readonly + */ + get hoist() { + const hoistedRoles = this._roles.filter(role => role.hoist); + if (!hoistedRoles.size) return null; + return hoistedRoles.reduce((prev, role) => (!prev || role.comparePositionTo(prev) > 0 ? role : prev)); + } + + /** + * The role of the member used to set their color + * @type {?Role} + * @readonly + */ + get color() { + const coloredRoles = this._roles.filter(role => role.color); + if (!coloredRoles.size) return null; + return coloredRoles.reduce((prev, role) => (!prev || role.comparePositionTo(prev) > 0 ? role : prev)); + } + + /** + * The role of the member with the highest position + * @type {Role} + * @readonly + */ + get highest() { + return this._roles.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this._roles.first()); + } + + /** + * Adds a role (or multiple roles) to the member. + * @param {RoleResolvable|RoleResolvable[]|Collection<Snowflake, Role>} roleOrRoles The role or roles to add + * @param {string} [reason] Reason for adding the role(s) + * @returns {Promise<GuildMember>} + */ + async add(roleOrRoles, reason) { + if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) { + roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolve(r)); + if (roleOrRoles.includes(null)) { + throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true); + } + + const newRoles = [...new Set(roleOrRoles.concat(...this._roles.values()))]; + return this.set(newRoles, reason); + } else { + roleOrRoles = this.guild.roles.resolve(roleOrRoles); + if (roleOrRoles === null) { + throw new TypeError( + 'INVALID_TYPE', + 'roles', + 'Role, Snowflake or Array or Collection of Roles or Snowflakes', + true, + ); + } + + await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles.id].put({ reason }); + + const clone = this.member._clone(); + clone._roles = [...this._roles.keys(), roleOrRoles.id]; + return clone; + } + } + + /** + * Removes a role (or multiple roles) from the member. + * @param {RoleResolvable|RoleResolvable[]|Collection<Snowflake, Role>} roleOrRoles The role or roles to remove + * @param {string} [reason] Reason for removing the role(s) + * @returns {Promise<GuildMember>} + */ + async remove(roleOrRoles, reason) { + if (roleOrRoles instanceof Collection || Array.isArray(roleOrRoles)) { + roleOrRoles = roleOrRoles.map(r => this.guild.roles.resolve(r)); + if (roleOrRoles.includes(null)) { + throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true); + } + + const newRoles = this._roles.filter(role => !roleOrRoles.includes(role)); + return this.set(newRoles, reason); + } else { + roleOrRoles = this.guild.roles.resolve(roleOrRoles); + if (roleOrRoles === null) { + throw new TypeError('INVALID_TYPE', 'roles', 'Array or Collection of Roles or Snowflakes', true); + } + + await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles.id].delete({ reason }); + + const clone = this.member._clone(); + const newRoles = this._roles.filter(role => role.id !== roleOrRoles.id); + clone._roles = [...newRoles.keys()]; + return clone; + } + } + + /** + * Sets the roles applied to the member. + * @param {Collection<Snowflake, Role>|RoleResolvable[]} roles The roles or role IDs to apply + * @param {string} [reason] Reason for applying the roles + * @returns {Promise<GuildMember>} + * @example + * // Set the member's roles to a single role + * guildMember.roles.set(['391156570408615936']) + * .then(console.log) + * .catch(console.error); + * @example + * // Remove all the roles from a member + * guildMember.roles.set([]) + * .then(member => console.log(`Member roles is now of ${member.roles.cache.size} size`)) + * .catch(console.error); + */ + set(roles, reason) { + return this.member.edit({ roles }, reason); + } + + clone() { + const clone = new this.constructor(this.member); + clone.member._roles = [...this._roles.keyArray()]; + return clone; + } +} + +module.exports = GuildMemberRoleManager; diff --git a/node_modules/discord.js/src/managers/MessageManager.js b/node_modules/discord.js/src/managers/MessageManager.js new file mode 100644 index 0000000..6d51bcf --- /dev/null +++ b/node_modules/discord.js/src/managers/MessageManager.js @@ -0,0 +1,145 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const Message = require('../structures/Message'); +const Collection = require('../util/Collection'); +const LimitedCollection = require('../util/LimitedCollection'); + +/** + * Manages API methods for Messages and holds their cache. + * @extends {BaseManager} + */ +class MessageManager extends BaseManager { + constructor(channel, iterable) { + super(channel.client, iterable, Message, LimitedCollection, channel.client.options.messageCacheMaxSize); + /** + * The channel that the messages belong to + * @type {TextBasedChannel} + */ + this.channel = channel; + } + + /** + * The cache of Messages + * @type {Collection<Snowflake, Message>} + * @name MessageManager#cache + */ + + add(data, cache) { + return super.add(data, cache, { extras: [this.channel] }); + } + + /** + * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and + * `after` are mutually exclusive. All the parameters are optional. + * @typedef {Object} ChannelLogsQueryOptions + * @property {number} [limit=50] Number of messages to acquire + * @property {Snowflake} [before] ID of a message to get the messages that were posted before it + * @property {Snowflake} [after] ID of a message to get the messages that were posted after it + * @property {Snowflake} [around] ID of a message to get the messages that were posted around it + */ + + /** + * Gets a message, or messages, from this channel. + * <info>The returned Collection does not contain reaction users of the messages if they were not cached. + * Those need to be fetched separately in such a case.</info> + * @param {Snowflake|ChannelLogsQueryOptions} [message] The ID of the message to fetch, or query parameters. + * @param {boolean} [cache=true] Whether to cache the message(s) + * @returns {Promise<Message>|Promise<Collection<Snowflake, Message>>} + * @example + * // Get message + * channel.messages.fetch('99539446449315840') + * .then(message => console.log(message.content)) + * .catch(console.error); + * @example + * // Get messages + * channel.messages.fetch({ limit: 10 }) + * .then(messages => console.log(`Received ${messages.size} messages`)) + * .catch(console.error); + * @example + * // Get messages and filter by user ID + * channel.messages.fetch() + * .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`)) + * .catch(console.error); + */ + fetch(message, cache = true) { + return typeof message === 'string' ? this._fetchId(message, cache) : this._fetchMany(message, cache); + } + + /** + * Fetches the pinned messages of this channel and returns a collection of them. + * <info>The returned Collection does not contain any reaction data of the messages. + * Those need to be fetched separately.</info> + * @param {boolean} [cache=true] Whether to cache the message(s) + * @returns {Promise<Collection<Snowflake, Message>>} + * @example + * // Get pinned messages + * channel.fetchPinned() + * .then(messages => console.log(`Received ${messages.size} messages`)) + * .catch(console.error); + */ + fetchPinned(cache = true) { + return this.client.api.channels[this.channel.id].pins.get().then(data => { + const messages = new Collection(); + for (const message of data) messages.set(message.id, this.add(message, cache)); + return messages; + }); + } + + /** + * Data that can be resolved to a Message object. This can be: + * * A Message + * * A Snowflake + * @typedef {Message|Snowflake} MessageResolvable + */ + + /** + * Resolves a MessageResolvable to a Message object. + * @method resolve + * @memberof MessageManager + * @instance + * @param {MessageResolvable} message The message resolvable to resolve + * @returns {?Message} + */ + + /** + * Resolves a MessageResolvable to a Message ID string. + * @method resolveID + * @memberof MessageManager + * @instance + * @param {MessageResolvable} message The message resolvable to resolve + * @returns {?Snowflake} + */ + + /** + * Deletes a message, even if it's not cached. + * @param {MessageResolvable} message The message to delete + * @param {string} [reason] Reason for deleting this message, if it does not belong to the client user + * @returns {Promise<void>} + */ + async delete(message, reason) { + message = this.resolveID(message); + if (message) { + await this.client.api + .channels(this.channel.id) + .messages(message) + .delete({ reason }); + } + } + + async _fetchId(messageID, cache) { + const existing = this.cache.get(messageID); + if (existing && !existing.partial) return existing; + const data = await this.client.api.channels[this.channel.id].messages[messageID].get(); + return this.add(data, cache); + } + + async _fetchMany(options = {}, cache) { + const data = await this.client.api.channels[this.channel.id].messages.get({ query: options }); + const messages = new Collection(); + for (const message of data) messages.set(message.id, this.add(message, cache)); + return messages; + } +} + +module.exports = MessageManager; diff --git a/node_modules/discord.js/src/managers/PresenceManager.js b/node_modules/discord.js/src/managers/PresenceManager.js new file mode 100644 index 0000000..c02fe04 --- /dev/null +++ b/node_modules/discord.js/src/managers/PresenceManager.js @@ -0,0 +1,59 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { Presence } = require('../structures/Presence'); + +/** + * Manages API methods for Presences and holds their cache. + * @extends {BaseManager} + */ +class PresenceManager extends BaseManager { + constructor(client, iterable) { + super(client, iterable, Presence); + } + + /** + * The cache of Presences + * @type {Collection<Snowflake, Presence>} + * @name PresenceManager#cache + */ + + add(data, cache) { + const existing = this.cache.get(data.user.id); + return existing ? existing.patch(data) : super.add(data, cache, { id: data.user.id }); + } + + /** + * Data that can be resolved to a Presence object. This can be: + * * A Presence + * * A UserResolvable + * * A Snowflake + * @typedef {Presence|UserResolvable|Snowflake} PresenceResolvable + */ + + /** + * Resolves a PresenceResolvable to a Presence object. + * @param {PresenceResolvable} presence The presence resolvable to resolve + * @returns {?Presence} + */ + resolve(presence) { + const presenceResolvable = super.resolve(presence); + if (presenceResolvable) return presenceResolvable; + const UserResolvable = this.client.users.resolveID(presence); + return super.resolve(UserResolvable) || null; + } + + /** + * Resolves a PresenceResolvable to a Presence ID string. + * @param {PresenceResolvable} presence The presence resolvable to resolve + * @returns {?Snowflake} + */ + resolveID(presence) { + const presenceResolvable = super.resolveID(presence); + if (presenceResolvable) return presenceResolvable; + const userResolvable = this.client.users.resolveID(presence); + return this.cache.has(userResolvable) ? userResolvable : null; + } +} + +module.exports = PresenceManager; diff --git a/node_modules/discord.js/src/managers/ReactionManager.js b/node_modules/discord.js/src/managers/ReactionManager.js new file mode 100644 index 0000000..b46d187 --- /dev/null +++ b/node_modules/discord.js/src/managers/ReactionManager.js @@ -0,0 +1,69 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const MessageReaction = require('../structures/MessageReaction'); + +/** + * Manages API methods for reactions and holds their cache. + * @extends {BaseManager} + */ +class ReactionManager extends BaseManager { + constructor(message, iterable) { + super(message.client, iterable, MessageReaction); + + /** + * The message that this manager belongs to + * @type {Message} + */ + this.message = message; + } + + add(data, cache) { + return super.add(data, cache, { id: data.emoji.id || data.emoji.name, extras: [this.message] }); + } + + /** + * The reaction cache of this manager + * @type {Collection<Snowflake, MessageReaction>} + * @name ReactionManager#cache + */ + + /** + * Data that can be resolved to a MessageReaction object. This can be: + * * A MessageReaction + * * A Snowflake + * @typedef {MessageReaction|Snowflake} MessageReactionResolvable + */ + + /** + * Resolves a MessageReactionResolvable to a MessageReaction object. + * @method resolve + * @memberof ReactionManager + * @instance + * @param {MessageReactionResolvable} reaction The MessageReaction to resolve + * @returns {?MessageReaction} + */ + + /** + * Resolves a MessageReactionResolvable to a MessageReaction ID string. + * @method resolveID + * @memberof ReactionManager + * @instance + * @param {MessageReactionResolvable} reaction The MessageReaction to resolve + * @returns {?Snowflake} + */ + + /** + * Removes all reactions from a message. + * @returns {Promise<Message>} + */ + removeAll() { + return this.client.api + .channels(this.message.channel.id) + .messages(this.message.id) + .reactions.delete() + .then(() => this.message); + } +} + +module.exports = ReactionManager; diff --git a/node_modules/discord.js/src/managers/ReactionUserManager.js b/node_modules/discord.js/src/managers/ReactionUserManager.js new file mode 100644 index 0000000..1a6ce14 --- /dev/null +++ b/node_modules/discord.js/src/managers/ReactionUserManager.js @@ -0,0 +1,66 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { Error } = require('../errors'); +const Collection = require('../util/Collection'); + +/** + * Manages API methods for users who reacted to a reaction and stores their cache. + * @extends {BaseManager} + */ +class ReactionUserManager extends BaseManager { + constructor(client, iterable, reaction) { + super(client, iterable, { name: 'User' }); + /** + * The reaction that this manager belongs to + * @type {MessageReaction} + */ + this.reaction = reaction; + } + + /** + * The cache of this manager + * @type {Collection<Snowflake, User>} + * @name ReactionUserManager#cache + */ + + /** + * Fetches all the users that gave this reaction. Resolves with a collection of users, mapped by their IDs. + * @param {Object} [options] Options for fetching the users + * @param {number} [options.limit=100] The maximum amount of users to fetch, defaults to 100 + * @param {Snowflake} [options.before] Limit fetching users to those with an id lower than the supplied id + * @param {Snowflake} [options.after] Limit fetching users to those with an id greater than the supplied id + * @returns {Promise<Collection<Snowflake, User>>} + */ + async fetch({ limit = 100, after, before } = {}) { + const message = this.reaction.message; + const data = await this.client.api.channels[message.channel.id].messages[message.id].reactions[ + this.reaction.emoji.identifier + ].get({ query: { limit, before, after } }); + const users = new Collection(); + for (const rawUser of data) { + const user = this.client.users.add(rawUser); + this.cache.set(user.id, user); + users.set(user.id, user); + } + return users; + } + + /** + * Removes a user from this reaction. + * @param {UserResolvable} [user=this.reaction.message.client.user] The user to remove the reaction of + * @returns {Promise<MessageReaction>} + */ + remove(user = this.reaction.message.client.user) { + const message = this.reaction.message; + const userID = message.client.users.resolveID(user); + if (!userID) return Promise.reject(new Error('REACTION_RESOLVE_USER')); + return message.client.api.channels[message.channel.id].messages[message.id].reactions[ + this.reaction.emoji.identifier + ][userID === message.client.user.id ? '@me' : userID] + .delete() + .then(() => this.reaction); + } +} + +module.exports = ReactionUserManager; diff --git a/node_modules/discord.js/src/managers/RoleManager.js b/node_modules/discord.js/src/managers/RoleManager.js new file mode 100644 index 0000000..7c5bf51 --- /dev/null +++ b/node_modules/discord.js/src/managers/RoleManager.js @@ -0,0 +1,145 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const Role = require('../structures/Role'); +const Permissions = require('../util/Permissions'); +const { resolveColor } = require('../util/Util'); + +/** + * Manages API methods for roles and stores their cache. + * @extends {BaseManager} + */ +class RoleManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, Role); + /** + * The guild belonging to this manager + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The role cache of this manager + * @type {Collection<Snowflake, Role>} + * @name RoleManager#cache + */ + + add(data, cache) { + return super.add(data, cache, { extras: [this.guild] }); + } + + /** + * Obtains one or more roles from Discord, or the role cache if they're already available. + * @param {Snowflake} [id] ID or IDs of the role(s) + * @param {boolean} [cache=true] Whether to cache the new roles objects if it weren't already + * @returns {Promise<Role|RoleManager>} + * @example + * // Fetch all roles from the guild + * message.guild.roles.fetch() + * .then(roles => console.log(`There are ${roles.cache.size} roles.`)) + * .catch(console.error); + * @example + * // Fetch a single role + * message.guild.roles.fetch('222078108977594368') + * .then(role => console.log(`The role color is: ${role.color}`)) + * .catch(console.error); + */ + async fetch(id, cache = true) { + if (id) { + const existing = this.cache.get(id); + if (existing) return existing; + } + + // We cannot fetch a single role, as of this commit's date, Discord API throws with 405 + const roles = await this.client.api.guilds(this.guild.id).roles.get(); + for (const role of roles) this.add(role, cache); + return id ? this.cache.get(id) || null : this; + } + + /** + * Data that can be resolved to a Role object. This can be: + * * A Role + * * A Snowflake + * @typedef {Role|Snowflake} RoleResolvable + */ + + /** + * Resolves a RoleResolvable to a Role object. + * @method resolve + * @memberof RoleManager + * @instance + * @param {RoleResolvable} role The role resolvable to resolve + * @returns {?Role} + */ + + /** + * Resolves a RoleResolvable to a role ID string. + * @method resolveID + * @memberof RoleManager + * @instance + * @param {RoleResolvable} role The role resolvable to resolve + * @returns {?Snowflake} + */ + + /** + * Creates a new role in the guild with given information. + * <warn>The position will silently reset to 1 if an invalid one is provided, or none.</warn> + * @param {Object} [options] Options + * @param {RoleData} [options.data] The data to create the role with + * @param {string} [options.reason] Reason for creating this role + * @returns {Promise<Role>} + * @example + * // Create a new role + * guild.roles.create() + * .then(console.log) + * .catch(console.error); + * @example + * // Create a new role with data and a reason + * guild.roles.create({ + * data: { + * name: 'Super Cool People', + * color: 'BLUE', + * }, + * reason: 'we needed a role for Super Cool People', + * }) + * .then(console.log) + * .catch(console.error); + */ + create({ data = {}, reason } = {}) { + if (data.color) data.color = resolveColor(data.color); + if (data.permissions) data.permissions = Permissions.resolve(data.permissions); + + return this.guild.client.api + .guilds(this.guild.id) + .roles.post({ data, reason }) + .then(r => { + const { role } = this.client.actions.GuildRoleCreate.handle({ + guild_id: this.guild.id, + role: r, + }); + if (data.position) return role.setPosition(data.position, reason); + return role; + }); + } + + /** + * The `@everyone` role of the guild + * @type {Role} + * @readonly + */ + get everyone() { + return this.cache.get(this.guild.id); + } + + /** + * The role with the highest position in the cache + * @type {Role} + * @readonly + */ + get highest() { + return this.cache.reduce((prev, role) => (role.comparePositionTo(prev) > 0 ? role : prev), this.cache.first()); + } +} + +module.exports = RoleManager; diff --git a/node_modules/discord.js/src/managers/UserManager.js b/node_modules/discord.js/src/managers/UserManager.js new file mode 100644 index 0000000..ebd7fdf --- /dev/null +++ b/node_modules/discord.js/src/managers/UserManager.js @@ -0,0 +1,68 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const GuildMember = require('../structures/GuildMember'); +const Message = require('../structures/Message'); +const User = require('../structures/User'); + +/** + * Manages API methods for users and stores their cache. + * @extends {BaseManager} + */ +class UserManager extends BaseManager { + constructor(client, iterable) { + super(client, iterable, User); + } + + /** + * The cache of this manager + * @type {Collection<Snowflake, User>} + * @name UserManager#cache + */ + + /** + * Data that resolves to give a User object. This can be: + * * A User object + * * A Snowflake + * * A Message object (resolves to the message author) + * * A GuildMember object + * @typedef {User|Snowflake|Message|GuildMember} UserResolvable + */ + + /** + * Resolves a UserResolvable to a User object. + * @param {UserResolvable} user The UserResolvable to identify + * @returns {?User} + */ + resolve(user) { + if (user instanceof GuildMember) return user.user; + if (user instanceof Message) return user.author; + return super.resolve(user); + } + + /** + * Resolves a UserResolvable to a user ID string. + * @param {UserResolvable} user The UserResolvable to identify + * @returns {?Snowflake} + */ + resolveID(user) { + if (user instanceof GuildMember) return user.user.id; + if (user instanceof Message) return user.author.id; + return super.resolveID(user); + } + + /** + * Obtains a user from Discord, or the user cache if it's already available. + * @param {Snowflake} id ID of the user + * @param {boolean} [cache=true] Whether to cache the new user object if it isn't already + * @returns {Promise<User>} + */ + async fetch(id, cache = true) { + const existing = this.cache.get(id); + if (existing && !existing.partial) return existing; + const data = await this.client.api.users(id).get(); + return this.add(data, cache); + } +} + +module.exports = UserManager; diff --git a/node_modules/discord.js/src/managers/VoiceStateManager.js b/node_modules/discord.js/src/managers/VoiceStateManager.js new file mode 100644 index 0000000..4a26b30 --- /dev/null +++ b/node_modules/discord.js/src/managers/VoiceStateManager.js @@ -0,0 +1,36 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const VoiceState = require('../structures/VoiceState'); + +/** + * Manages API methods for VoiceStates and stores their cache. + * @extends {BaseManager} + */ +class VoiceStateManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, VoiceState); + /** + * The guild this manager belongs to + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The cache of this manager + * @type {Collection<Snowflake, VoiceState>} + * @name VoiceStateManager#cache + */ + + add(data, cache = true) { + const existing = this.cache.get(data.user_id); + if (existing) return existing._patch(data); + + const entry = new VoiceState(this.guild, data); + if (cache) this.cache.set(data.user_id, entry); + return entry; + } +} + +module.exports = VoiceStateManager; |