Add files via upload

This commit is contained in:
Tropicallism 2022-04-11 04:12:35 +02:00 committed by GitHub
parent 6af816d457
commit f1fc882d35
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 1413 additions and 1410 deletions

View File

@ -1,223 +1,223 @@
'use strict'; 'use strict';
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager'); const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager');
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const ApplicationCommand = require('../structures/ApplicationCommand'); const ApplicationCommand = require('../structures/ApplicationCommand');
const { ApplicationCommandTypes } = require('../util/Constants'); const { ApplicationCommandTypes } = require('../util/Constants');
/** /**
* Manages API methods for application commands and stores their cache. * Manages API methods for application commands and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class ApplicationCommandManager extends CachedManager { class ApplicationCommandManager extends CachedManager {
constructor(client, iterable, user) { constructor(client, iterable, user) {
super(client, ApplicationCommand, iterable); super(client, ApplicationCommand, iterable);
/** /**
* The manager for permissions of arbitrary commands on arbitrary guilds * The manager for permissions of arbitrary commands on arbitrary guilds
* @type {ApplicationCommandPermissionsManager} * @type {ApplicationCommandPermissionsManager}
*/ */
this.permissions = new ApplicationCommandPermissionsManager(this, user); this.permissions = new ApplicationCommandPermissionsManager(this, user);
this.user = user; this.user = user;
} }
/** /**
* The cache of this manager * The cache of this manager
* @type {Collection<Snowflake, ApplicationCommand>} * @type {Collection<Snowflake, ApplicationCommand>}
* @name ApplicationCommandManager#cache * @name ApplicationCommandManager#cache
*/ */
_add(data, cache, guildId) { _add(data, cache, guildId) {
return super._add(data, cache, { extras: [this.guild, guildId] }); return super._add(data, cache, { extras: [this.guild, guildId] });
} }
/** /**
* The APIRouter path to the commands * The APIRouter path to the commands
* @param {Snowflake} [options.id] The application command's id * @param {Snowflake} [options.id] The application command's id
* @param {Snowflake} [options.guildId] The guild's id to use in the path, * @param {Snowflake} [options.guildId] The guild's id to use in the path,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Object} * @returns {Object}
* @private * @private
*/ */
commandPath({ id, guildId } = {}) { commandPath({ id, guildId } = {}) {
let path = this.client.api.applications(this.user.id); let path = this.client.api.applications(this.user.id);
if (this.guild ?? guildId) path = path.guilds(this.guild?.id ?? guildId); if (this.guild ?? guildId) path = path.guilds(this.guild?.id ?? guildId);
return id ? path.commands(id) : path.commands; return id ? path.commands(id) : path.commands;
} }
/** /**
* Data that resolves to give an ApplicationCommand object. This can be: * Data that resolves to give an ApplicationCommand object. This can be:
* * An ApplicationCommand object * * An ApplicationCommand object
* * A Snowflake * * A Snowflake
* @typedef {ApplicationCommand|Snowflake} ApplicationCommandResolvable * @typedef {ApplicationCommand|Snowflake} ApplicationCommandResolvable
*/ */
/** /**
* Options used to fetch data from Discord * Options used to fetch data from Discord
* @typedef {Object} BaseFetchOptions * @typedef {Object} BaseFetchOptions
* @property {boolean} [cache=true] Whether to cache the fetched data if it wasn't already * @property {boolean} [cache=true] Whether to cache the fetched data if it wasn't already
* @property {boolean} [force=false] Whether to skip the cache check and request the API * @property {boolean} [force=false] Whether to skip the cache check and request the API
*/ */
/** /**
* Options used to fetch Application Commands from Discord * Options used to fetch Application Commands from Discord
* @typedef {BaseFetchOptions} FetchApplicationCommandOptions * @typedef {BaseFetchOptions} FetchApplicationCommandOptions
* @property {Snowflake} [guildId] The guild's id to fetch commands for, for when the guild is not cached * @property {Snowflake} [guildId] The guild's id to fetch commands for, for when the guild is not cached
*/ */
/** /**
* Obtains one or multiple application commands from Discord, or the cache if it's already available. * Obtains one or multiple application commands from Discord, or the cache if it's already available.
* @param {Snowflake} [id] The application command's id * @param {Snowflake} [id] The application command's id
* @param {FetchApplicationCommandOptions} [options] Additional options for this fetch * @param {FetchApplicationCommandOptions} [options] Additional options for this fetch
* @returns {Promise<ApplicationCommand|Collection<Snowflake, ApplicationCommand>>} * @returns {Promise<ApplicationCommand|Collection<Snowflake, ApplicationCommand>>}
* @example * @example
* // Fetch a single command * // Fetch a single command
* client.application.commands.fetch('123456789012345678') * client.application.commands.fetch('123456789012345678')
* .then(command => console.log(`Fetched command ${command.name}`)) * .then(command => console.log(`Fetched command ${command.name}`))
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Fetch all commands * // Fetch all commands
* guild.commands.fetch() * guild.commands.fetch()
* .then(commands => console.log(`Fetched ${commands.size} commands`)) * .then(commands => console.log(`Fetched ${commands.size} commands`))
* .catch(console.error); * .catch(console.error);
*/ */
async fetch(id, { guildId, cache = true, force = false } = {}) { async fetch(id, { guildId, cache = true, force = false } = {}) {
await this.user.createDM().catch(() => {}); await this.user.createDM().catch(() => {});
if (typeof id === 'object') { if (typeof id === 'object') {
({ guildId, cache = true } = id); ({ guildId, cache = true } = id);
} else if (id) { } else if (id) {
if (!force) { if (!force) {
const existing = this.cache.get(id); const existing = this.cache.get(id);
if (existing) return existing; if (existing) return existing;
} }
const command = await this.commandPath({ id, guildId }).get(); const command = await this.commandPath({ id, guildId }).get();
return this._add(command, cache); return this._add(command, cache);
} }
const data = await this.commandPath({ guildId }).get(); const data = await this.commandPath({ guildId }).get();
return data.reduce((coll, command) => coll.set(command.id, this._add(command, cache, guildId)), new Collection()); return data.reduce((coll, command) => coll.set(command.id, this._add(command, cache, guildId)), new Collection());
} }
/** /**
* Creates an application command. * Creates an application command.
* @param {ApplicationCommandData|APIApplicationCommand} command The command * @param {ApplicationCommandData|APIApplicationCommand} command The command
* @param {Snowflake} [guildId] The guild's id to create this command in, * @param {Snowflake} [guildId] The guild's id to create this command in,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<ApplicationCommand>} * @returns {Promise<ApplicationCommand>}
* @example * @example
* // Create a new command * // Create a new command
* client.application.commands.create({ * client.application.commands.create({
* name: 'test', * name: 'test',
* description: 'A test command', * description: 'A test command',
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async create(command, guildId) { async create(command, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD");
const data = await this.commandPath({ guildId }).post({ const data = await this.commandPath({ guildId }).post({
data: this.constructor.transformCommand(command), data: this.constructor.transformCommand(command),
}); });
return this._add(data, true, guildId); return this._add(data, true, guildId);
} }
/** /**
* Sets all the commands for this application or guild. * Sets all the commands for this application or guild.
* @param {ApplicationCommandData[]|APIApplicationCommand[]} commands The commands * @param {ApplicationCommandData[]|APIApplicationCommand[]} commands The commands
* @param {Snowflake} [guildId] The guild's id to create the commands in, * @param {Snowflake} [guildId] The guild's id to create the commands in,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<Collection<Snowflake, ApplicationCommand>>} * @returns {Promise<Collection<Snowflake, ApplicationCommand>>}
* @example * @example
* // Set all commands to just this one * // Set all commands to just this one
* client.application.commands.set([ * client.application.commands.set([
* { * {
* name: 'test', * name: 'test',
* description: 'A test command', * description: 'A test command',
* }, * },
* ]) * ])
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Remove all commands * // Remove all commands
* guild.commands.set([]) * guild.commands.set([])
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async set(commands, guildId) { async set(commands, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD");
const data = await this.commandPath({ guildId }).put({ const data = await this.commandPath({ guildId }).put({
data: commands.map(c => this.constructor.transformCommand(c)), data: commands.map(c => this.constructor.transformCommand(c)),
}); });
return data.reduce((coll, command) => coll.set(command.id, this._add(command, true, guildId)), new Collection()); return data.reduce((coll, command) => coll.set(command.id, this._add(command, true, guildId)), new Collection());
} }
/** /**
* Edits an application command. * Edits an application command.
* @param {ApplicationCommandResolvable} command The command to edit * @param {ApplicationCommandResolvable} command The command to edit
* @param {ApplicationCommandData|APIApplicationCommand} data The data to update the command with * @param {ApplicationCommandData|APIApplicationCommand} data The data to update the command with
* @param {Snowflake} [guildId] The guild's id where the command registered, * @param {Snowflake} [guildId] The guild's id where the command registered,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<ApplicationCommand>} * @returns {Promise<ApplicationCommand>}
* @example * @example
* // Edit an existing command * // Edit an existing command
* client.application.commands.edit('123456789012345678', { * client.application.commands.edit('123456789012345678', {
* description: 'New description', * description: 'New description',
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async edit(command, data, guildId) { async edit(command, data, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD");
const id = this.resolveId(command); const id = this.resolveId(command);
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
const patched = await this.commandPath({ id, guildId }).patch({ const patched = await this.commandPath({ id, guildId }).patch({
data: this.constructor.transformCommand(data), data: this.constructor.transformCommand(data),
}); });
return this._add(patched, true, guildId); return this._add(patched, true, guildId);
} }
/** /**
* Deletes an application command. * Deletes an application command.
* @param {ApplicationCommandResolvable} command The command to delete * @param {ApplicationCommandResolvable} command The command to delete
* @param {Snowflake} [guildId] The guild's id where the command is registered, * @param {Snowflake} [guildId] The guild's id where the command is registered,
* ignored when using a {@link GuildApplicationCommandManager} * ignored when using a {@link GuildApplicationCommandManager}
* @returns {Promise<?ApplicationCommand>} * @returns {Promise<?ApplicationCommand>}
* @example * @example
* // Delete a command * // Delete a command
* guild.commands.delete('123456789012345678') * guild.commands.delete('123456789012345678')
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async delete(command, guildId) { async delete(command, guildId) {
if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD"); if(!this.client.user.bot) throw new Error("INVALID_USER_METHOD");
const id = this.resolveId(command); const id = this.resolveId(command);
if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!id) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
await this.commandPath({ id, guildId }).delete(); await this.commandPath({ id, guildId }).delete();
const cached = this.cache.get(id); const cached = this.cache.get(id);
this.cache.delete(id); this.cache.delete(id);
return cached ?? null; return cached ?? null;
} }
/** /**
* Transforms an {@link ApplicationCommandData} object into something that can be used with the API. * Transforms an {@link ApplicationCommandData} object into something that can be used with the API.
* @param {ApplicationCommandData|APIApplicationCommand} command The command to transform * @param {ApplicationCommandData|APIApplicationCommand} command The command to transform
* @returns {APIApplicationCommand} * @returns {APIApplicationCommand}
* @private * @private
*/ */
static transformCommand(command) { static transformCommand(command) {
return { return {
name: command.name, name: command.name,
description: command.description, description: command.description,
type: typeof command.type === 'number' ? command.type : ApplicationCommandTypes[command.type], type: typeof command.type === 'number' ? command.type : ApplicationCommandTypes[command.type],
options: command.options?.map(o => ApplicationCommand.transformOption(o)), options: command.options?.map(o => ApplicationCommand.transformOption(o)),
default_permission: command.defaultPermission ?? command.default_permission, default_permission: command.defaultPermission ?? command.default_permission,
}; };
} }
} }
module.exports = ApplicationCommandManager; module.exports = ApplicationCommandManager;

View File

@ -1,422 +1,422 @@
'use strict'; 'use strict';
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const BaseManager = require('./BaseManager'); const BaseManager = require('./BaseManager');
const { Error, TypeError } = require('../errors'); const { Error, TypeError } = require('../errors');
const { ApplicationCommandPermissionTypes, APIErrors } = require('../util/Constants'); const { ApplicationCommandPermissionTypes, APIErrors } = require('../util/Constants');
/** /**
* Manages API methods for permissions of Application Commands. * Manages API methods for permissions of Application Commands.
* @extends {BaseManager} * @extends {BaseManager}
*/ */
class ApplicationCommandPermissionsManager extends BaseManager { class ApplicationCommandPermissionsManager extends BaseManager {
constructor(manager, user) { constructor(manager, user) {
super(manager.client); super(manager.client);
/** /**
* The manager or command that this manager belongs to * The manager or command that this manager belongs to
* @type {ApplicationCommandManager|ApplicationCommand} * @type {ApplicationCommandManager|ApplicationCommand}
* @private * @private
*/ */
this.manager = manager; this.manager = manager;
/** /**
* The guild that this manager acts on * The guild that this manager acts on
* @type {?Guild} * @type {?Guild}
*/ */
this.guild = manager.guild ?? null; this.guild = manager.guild ?? null;
/** /**
* The id of the guild that this manager acts on * The id of the guild that this manager acts on
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.guildId = manager.guildId ?? manager.guild?.id ?? null; this.guildId = manager.guildId ?? manager.guild?.id ?? null;
/** /**
* The id of the command this manager acts on * The id of the command this manager acts on
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.commandId = manager.id ?? null; this.commandId = manager.id ?? null;
this.user = user; this.user = user;
} }
/** /**
* The APIRouter path to the commands * The APIRouter path to the commands
* @param {Snowflake} guildId The guild's id to use in the path, * @param {Snowflake} guildId The guild's id to use in the path,
* @param {Snowflake} [commandId] The application command's id * @param {Snowflake} [commandId] The application command's id
* @returns {Object} * @returns {Object}
* @private * @private
*/ */
permissionsPath(guildId, commandId) { permissionsPath(guildId, commandId) {
return this.client.api.applications(typeof this.user == 'string' ? this.user : this.user.id).guilds(guildId).commands(commandId).permissions; return this.client.api.applications(typeof this.user == 'string' ? this.user : this.user.id).guilds(guildId).commands(commandId).permissions;
} }
/** /**
* Data for setting the permissions of an application command. * Data for setting the permissions of an application command.
* @typedef {Object} ApplicationCommandPermissionData * @typedef {Object} ApplicationCommandPermissionData
* @property {Snowflake} id The role or user's id * @property {Snowflake} id The role or user's id
* @property {ApplicationCommandPermissionType|number} type Whether this permission is for a role or a user * @property {ApplicationCommandPermissionType|number} type Whether this permission is for a role or a user
* @property {boolean} permission Whether the role or user has the permission to use this command * @property {boolean} permission Whether the role or user has the permission to use this command
*/ */
/** /**
* The object returned when fetching permissions for an application command. * The object returned when fetching permissions for an application command.
* @typedef {Object} ApplicationCommandPermissions * @typedef {Object} ApplicationCommandPermissions
* @property {Snowflake} id The role or user's id * @property {Snowflake} id The role or user's id
* @property {ApplicationCommandPermissionType} type Whether this permission is for a role or a user * @property {ApplicationCommandPermissionType} type Whether this permission is for a role or a user
* @property {boolean} permission Whether the role or user has the permission to use this command * @property {boolean} permission Whether the role or user has the permission to use this command
*/ */
/** /**
* Options for managing permissions for one or more Application Commands * Options for managing permissions for one or more Application Commands
* <warn>When passing these options to a manager where `guildId` is `null`, * <warn>When passing these options to a manager where `guildId` is `null`,
* `guild` is a required parameter</warn> * `guild` is a required parameter</warn>
* @typedef {Object} BaseApplicationCommandPermissionsOptions * @typedef {Object} BaseApplicationCommandPermissionsOptions
* @property {GuildResolvable} [guild] The guild to modify / check permissions for * @property {GuildResolvable} [guild] The guild to modify / check permissions for
* <warn>Ignored when the manager has a non-null `guildId` property</warn> * <warn>Ignored when the manager has a non-null `guildId` property</warn>
* @property {ApplicationCommandResolvable} [command] The command to modify / check permissions for * @property {ApplicationCommandResolvable} [command] The command to modify / check permissions for
* <warn>Ignored when the manager has a non-null `commandId` property</warn> * <warn>Ignored when the manager has a non-null `commandId` property</warn>
*/ */
/** /**
* Fetches the permissions for one or multiple commands. * Fetches the permissions for one or multiple commands.
* @param {BaseApplicationCommandPermissionsOptions} [options] Options used to fetch permissions * @param {BaseApplicationCommandPermissionsOptions} [options] Options used to fetch permissions
* @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>} * @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>}
* @example * @example
* // Fetch permissions for one command * // Fetch permissions for one command
* guild.commands.permissions.fetch({ command: '123456789012345678' }) * guild.commands.permissions.fetch({ command: '123456789012345678' })
* .then(perms => console.log(`Fetched permissions for ${perms.length} users`)) * .then(perms => console.log(`Fetched permissions for ${perms.length} users`))
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Fetch permissions for all commands in a guild * // Fetch permissions for all commands in a guild
* client.application.commands.permissions.fetch({ guild: '123456789012345678' }) * client.application.commands.permissions.fetch({ guild: '123456789012345678' })
* .then(perms => console.log(`Fetched permissions for ${perms.size} commands`)) * .then(perms => console.log(`Fetched permissions for ${perms.size} commands`))
* .catch(console.error); * .catch(console.error);
*/ */
async fetch({ guild, command } = {}) { async fetch({ guild, command } = {}) {
const { guildId, commandId } = this._validateOptions(guild, command); const { guildId, commandId } = this._validateOptions(guild, command);
if (commandId) { if (commandId) {
const data = await this.permissionsPath(guildId, commandId).get(); const data = await this.permissionsPath(guildId, commandId).get();
return data.permissions.map(perm => this.constructor.transformPermissions(perm, true)); return data.permissions.map(perm => this.constructor.transformPermissions(perm, true));
} }
const data = await this.permissionsPath(guildId).get(); const data = await this.permissionsPath(guildId).get();
return data.reduce( return data.reduce(
(coll, perm) => (coll, perm) =>
coll.set( coll.set(
perm.id, perm.id,
perm.permissions.map(p => this.constructor.transformPermissions(p, true)), perm.permissions.map(p => this.constructor.transformPermissions(p, true)),
), ),
new Collection(), new Collection(),
); );
} }
/** /**
* Data used for overwriting the permissions for all application commands in a guild. * Data used for overwriting the permissions for all application commands in a guild.
* @typedef {Object} GuildApplicationCommandPermissionData * @typedef {Object} GuildApplicationCommandPermissionData
* @property {Snowflake} id The command's id * @property {Snowflake} id The command's id
* @property {ApplicationCommandPermissionData[]} permissions The permissions for this command * @property {ApplicationCommandPermissionData[]} permissions The permissions for this command
*/ */
/** /**
* Options used to set permissions for one or more Application Commands in a guild * Options used to set permissions for one or more Application Commands in a guild
* <warn>One of `command` AND `permissions`, OR `fullPermissions` is required. * <warn>One of `command` AND `permissions`, OR `fullPermissions` is required.
* `fullPermissions` is not a valid option when passing to a manager where `commandId` is non-null</warn> * `fullPermissions` is not a valid option when passing to a manager where `commandId` is non-null</warn>
* @typedef {BaseApplicationCommandPermissionsOptions} SetApplicationCommandPermissionsOptions * @typedef {BaseApplicationCommandPermissionsOptions} SetApplicationCommandPermissionsOptions
* @property {ApplicationCommandPermissionData[]} [permissions] The new permissions for the command * @property {ApplicationCommandPermissionData[]} [permissions] The new permissions for the command
* @property {GuildApplicationCommandPermissionData[]} [fullPermissions] The new permissions for all commands * @property {GuildApplicationCommandPermissionData[]} [fullPermissions] The new permissions for all commands
* in a guild <warn>When this parameter is set, `permissions` and `command` are ignored</warn> * in a guild <warn>When this parameter is set, `permissions` and `command` are ignored</warn>
*/ */
/** /**
* Sets the permissions for one or more commands. * Sets the permissions for one or more commands.
* @param {SetApplicationCommandPermissionsOptions} options Options used to set permissions * @param {SetApplicationCommandPermissionsOptions} options Options used to set permissions
* @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>} * @returns {Promise<ApplicationCommandPermissions[]|Collection<Snowflake, ApplicationCommandPermissions[]>>}
* @example * @example
* // Set the permissions for one command * // Set the permissions for one command
* client.application.commands.permissions.set({ guild: '892455839386304532', command: '123456789012345678', * client.application.commands.permissions.set({ guild: '892455839386304532', command: '123456789012345678',
* permissions: [ * permissions: [
* { * {
* id: '876543210987654321', * id: '876543210987654321',
* type: 'USER', * type: 'USER',
* permission: false, * permission: false,
* }, * },
* ]}) * ]})
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Set the permissions for all commands * // Set the permissions for all commands
* guild.commands.permissions.set({ fullPermissions: [ * guild.commands.permissions.set({ fullPermissions: [
* { * {
* id: '123456789012345678', * id: '123456789012345678',
* permissions: [{ * permissions: [{
* id: '876543210987654321', * id: '876543210987654321',
* type: 'USER', * type: 'USER',
* permission: false, * permission: false,
* }], * }],
* }, * },
* ]}) * ]})
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async set({ guild, command, permissions, fullPermissions } = {}) { async set({ guild, command, permissions, fullPermissions } = {}) {
if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD"); if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD");
const { guildId, commandId } = this._validateOptions(guild, command); const { guildId, commandId } = this._validateOptions(guild, command);
if (commandId) { if (commandId) {
if (!Array.isArray(permissions)) { if (!Array.isArray(permissions)) {
throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true); throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true);
} }
const data = await this.permissionsPath(guildId, commandId).put({ const data = await this.permissionsPath(guildId, commandId).put({
data: { permissions: permissions.map(perm => this.constructor.transformPermissions(perm)) }, data: { permissions: permissions.map(perm => this.constructor.transformPermissions(perm)) },
}); });
return data.permissions.map(perm => this.constructor.transformPermissions(perm, true)); return data.permissions.map(perm => this.constructor.transformPermissions(perm, true));
} }
if (!Array.isArray(fullPermissions)) { if (!Array.isArray(fullPermissions)) {
throw new TypeError('INVALID_TYPE', 'fullPermissions', 'Array of GuildApplicationCommandPermissionData', true); throw new TypeError('INVALID_TYPE', 'fullPermissions', 'Array of GuildApplicationCommandPermissionData', true);
} }
const APIPermissions = []; const APIPermissions = [];
for (const perm of fullPermissions) { for (const perm of fullPermissions) {
if (!Array.isArray(perm.permissions)) throw new TypeError('INVALID_ELEMENT', 'Array', 'fullPermissions', perm); if (!Array.isArray(perm.permissions)) throw new TypeError('INVALID_ELEMENT', 'Array', 'fullPermissions', perm);
APIPermissions.push({ APIPermissions.push({
id: perm.id, id: perm.id,
permissions: perm.permissions.map(p => this.constructor.transformPermissions(p)), permissions: perm.permissions.map(p => this.constructor.transformPermissions(p)),
}); });
} }
const data = await this.permissionsPath(guildId).put({ const data = await this.permissionsPath(guildId).put({
data: APIPermissions, data: APIPermissions,
}); });
return data.reduce( return data.reduce(
(coll, perm) => (coll, perm) =>
coll.set( coll.set(
perm.id, perm.id,
perm.permissions.map(p => this.constructor.transformPermissions(p, true)), perm.permissions.map(p => this.constructor.transformPermissions(p, true)),
), ),
new Collection(), new Collection(),
); );
} }
/** /**
* Options used to add permissions to a command * Options used to add permissions to a command
* <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn> * <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
* @typedef {BaseApplicationCommandPermissionsOptions} AddApplicationCommandPermissionsOptions * @typedef {BaseApplicationCommandPermissionsOptions} AddApplicationCommandPermissionsOptions
* @property {ApplicationCommandPermissionData[]} permissions The permissions to add to the command * @property {ApplicationCommandPermissionData[]} permissions The permissions to add to the command
*/ */
/** /**
* Add permissions to a command. * Add permissions to a command.
* @param {AddApplicationCommandPermissionsOptions} options Options used to add permissions * @param {AddApplicationCommandPermissionsOptions} options Options used to add permissions
* @returns {Promise<ApplicationCommandPermissions[]>} * @returns {Promise<ApplicationCommandPermissions[]>}
* @example * @example
* // Block a role from the command permissions * // Block a role from the command permissions
* guild.commands.permissions.add({ command: '123456789012345678', permissions: [ * guild.commands.permissions.add({ command: '123456789012345678', permissions: [
* { * {
* id: '876543211234567890', * id: '876543211234567890',
* type: 'ROLE', * type: 'ROLE',
* permission: false * permission: false
* }, * },
* ]}) * ]})
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async add({ guild, command, permissions }) { async add({ guild, command, permissions }) {
if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD"); if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD");
const { guildId, commandId } = this._validateOptions(guild, command); const { guildId, commandId } = this._validateOptions(guild, command);
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
if (!Array.isArray(permissions)) { if (!Array.isArray(permissions)) {
throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true); throw new TypeError('INVALID_TYPE', 'permissions', 'Array of ApplicationCommandPermissionData', true);
} }
let existing = []; let existing = [];
try { try {
existing = await this.fetch({ guild: guildId, command: commandId }); existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) { } catch (error) {
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error; if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
} }
const newPermissions = permissions.slice(); const newPermissions = permissions.slice();
for (const perm of existing) { for (const perm of existing) {
if (!newPermissions.some(x => x.id === perm.id)) { if (!newPermissions.some(x => x.id === perm.id)) {
newPermissions.push(perm); newPermissions.push(perm);
} }
} }
return this.set({ guild: guildId, command: commandId, permissions: newPermissions }); return this.set({ guild: guildId, command: commandId, permissions: newPermissions });
} }
/** /**
* Options used to remove permissions from a command * Options used to remove permissions from a command
* <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn> * <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
* @typedef {BaseApplicationCommandPermissionsOptions} RemoveApplicationCommandPermissionsOptions * @typedef {BaseApplicationCommandPermissionsOptions} RemoveApplicationCommandPermissionsOptions
* @property {UserResolvable|UserResolvable[]} [users] The user(s) to remove from the command permissions * @property {UserResolvable|UserResolvable[]} [users] The user(s) to remove from the command permissions
* <warn>One of `users` or `roles` is required</warn> * <warn>One of `users` or `roles` is required</warn>
* @property {RoleResolvable|RoleResolvable[]} [roles] The role(s) to remove from the command permissions * @property {RoleResolvable|RoleResolvable[]} [roles] The role(s) to remove from the command permissions
* <warn>One of `users` or `roles` is required</warn> * <warn>One of `users` or `roles` is required</warn>
*/ */
/** /**
* Remove permissions from a command. * Remove permissions from a command.
* @param {RemoveApplicationCommandPermissionsOptions} options Options used to remove permissions * @param {RemoveApplicationCommandPermissionsOptions} options Options used to remove permissions
* @returns {Promise<ApplicationCommandPermissions[]>} * @returns {Promise<ApplicationCommandPermissions[]>}
* @example * @example
* // Remove a user permission from this command * // Remove a user permission from this command
* guild.commands.permissions.remove({ command: '123456789012345678', users: '876543210123456789' }) * guild.commands.permissions.remove({ command: '123456789012345678', users: '876543210123456789' })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Remove multiple roles from this command * // Remove multiple roles from this command
* guild.commands.permissions.remove({ * guild.commands.permissions.remove({
* command: '123456789012345678', roles: ['876543210123456789', '765432101234567890'] * command: '123456789012345678', roles: ['876543210123456789', '765432101234567890']
* }) * })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async remove({ guild, command, users, roles }) { async remove({ guild, command, users, roles }) {
if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD"); if(!this.manager.client.user.bot) throw new Error("INVALID_USER_METHOD");
const { guildId, commandId } = this._validateOptions(guild, command); const { guildId, commandId } = this._validateOptions(guild, command);
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
if (!users && !roles) throw new TypeError('INVALID_TYPE', 'users OR roles', 'Array or Resolvable', true); if (!users && !roles) throw new TypeError('INVALID_TYPE', 'users OR roles', 'Array or Resolvable', true);
let resolvedIds = []; let resolvedIds = [];
if (Array.isArray(users)) { if (Array.isArray(users)) {
users.forEach(user => { users.forEach(user => {
const userId = this.client.users.resolveId(user); const userId = this.client.users.resolveId(user);
if (!userId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', user); if (!userId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', user);
resolvedIds.push(userId); resolvedIds.push(userId);
}); });
} else if (users) { } else if (users) {
const userId = this.client.users.resolveId(users); const userId = this.client.users.resolveId(users);
if (!userId) { if (!userId) {
throw new TypeError('INVALID_TYPE', 'users', 'Array or UserResolvable'); throw new TypeError('INVALID_TYPE', 'users', 'Array or UserResolvable');
} }
resolvedIds.push(userId); resolvedIds.push(userId);
} }
if (Array.isArray(roles)) { if (Array.isArray(roles)) {
roles.forEach(role => { roles.forEach(role => {
if (typeof role === 'string') { if (typeof role === 'string') {
resolvedIds.push(role); resolvedIds.push(role);
return; return;
} }
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE'); if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
const roleId = this.guild.roles.resolveId(role); const roleId = this.guild.roles.resolveId(role);
if (!roleId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', role); if (!roleId) throw new TypeError('INVALID_ELEMENT', 'Array', 'users', role);
resolvedIds.push(roleId); resolvedIds.push(roleId);
}); });
} else if (roles) { } else if (roles) {
if (typeof roles === 'string') { if (typeof roles === 'string') {
resolvedIds.push(roles); resolvedIds.push(roles);
} else { } else {
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE'); if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
const roleId = this.guild.roles.resolveId(roles); const roleId = this.guild.roles.resolveId(roles);
if (!roleId) { if (!roleId) {
throw new TypeError('INVALID_TYPE', 'users', 'Array or RoleResolvable'); throw new TypeError('INVALID_TYPE', 'users', 'Array or RoleResolvable');
} }
resolvedIds.push(roleId); resolvedIds.push(roleId);
} }
} }
let existing = []; let existing = [];
try { try {
existing = await this.fetch({ guild: guildId, command: commandId }); existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) { } catch (error) {
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error; if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
} }
const permissions = existing.filter(perm => !resolvedIds.includes(perm.id)); const permissions = existing.filter(perm => !resolvedIds.includes(perm.id));
return this.set({ guild: guildId, command: commandId, permissions }); return this.set({ guild: guildId, command: commandId, permissions });
} }
/** /**
* Options used to check the existence of permissions on a command * Options used to check the existence of permissions on a command
* <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn> * <warn>The `command` parameter is not optional when the managers `commandId` is `null`</warn>
* @typedef {BaseApplicationCommandPermissionsOptions} HasApplicationCommandPermissionsOptions * @typedef {BaseApplicationCommandPermissionsOptions} HasApplicationCommandPermissionsOptions
* @property {UserResolvable|RoleResolvable} permissionId The user or role to check if a permission exists for * @property {UserResolvable|RoleResolvable} permissionId The user or role to check if a permission exists for
* on this command. * on this command.
*/ */
/** /**
* Check whether a permission exists for a user or role * Check whether a permission exists for a user or role
* @param {AddApplicationCommandPermissionsOptions} options Options used to check permissions * @param {AddApplicationCommandPermissionsOptions} options Options used to check permissions
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
* @example * @example
* // Check whether a user has permission to use a command * // Check whether a user has permission to use a command
* guild.commands.permissions.has({ command: '123456789012345678', permissionId: '876543210123456789' }) * guild.commands.permissions.has({ command: '123456789012345678', permissionId: '876543210123456789' })
* .then(console.log) * .then(console.log)
* .catch(console.error); * .catch(console.error);
*/ */
async has({ guild, command, permissionId }) { async has({ guild, command, permissionId }) {
const { guildId, commandId } = this._validateOptions(guild, command); const { guildId, commandId } = this._validateOptions(guild, command);
if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable'); if (!commandId) throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable');
if (!permissionId) throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable'); if (!permissionId) throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable');
let resolvedId = permissionId; let resolvedId = permissionId;
if (typeof permissionId !== 'string') { if (typeof permissionId !== 'string') {
resolvedId = this.client.users.resolveId(permissionId); resolvedId = this.client.users.resolveId(permissionId);
if (!resolvedId) { if (!resolvedId) {
if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE'); if (!this.guild) throw new Error('GUILD_UNCACHED_ROLE_RESOLVE');
resolvedId = this.guild.roles.resolveId(permissionId); resolvedId = this.guild.roles.resolveId(permissionId);
} }
if (!resolvedId) { if (!resolvedId) {
throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable'); throw new TypeError('INVALID_TYPE', 'permissionId', 'UserResolvable or RoleResolvable');
} }
} }
let existing = []; let existing = [];
try { try {
existing = await this.fetch({ guild: guildId, command: commandId }); existing = await this.fetch({ guild: guildId, command: commandId });
} catch (error) { } catch (error) {
if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error; if (error.code !== APIErrors.UNKNOWN_APPLICATION_COMMAND_PERMISSIONS) throw error;
} }
return existing.some(perm => perm.id === resolvedId); return existing.some(perm => perm.id === resolvedId);
} }
_validateOptions(guild, command) { _validateOptions(guild, command) {
const guildId = this.guildId ?? this.client.guilds.resolveId(guild); const guildId = this.guildId ?? this.client.guilds.resolveId(guild);
if (!guildId) throw new Error('GLOBAL_COMMAND_PERMISSIONS'); if (!guildId) throw new Error('GLOBAL_COMMAND_PERMISSIONS');
let commandId = this.commandId; let commandId = this.commandId;
if (command && !commandId) { if (command && !commandId) {
commandId = this.manager.resolveId?.(command); commandId = this.manager.resolveId?.(command);
if (!commandId && this.guild) { if (!commandId && this.guild) {
commandId = this.guild.commands.resolveId(command); commandId = this.guild.commands.resolveId(command);
} }
commandId ??= this.client.application?.commands.resolveId(command); commandId ??= this.client.application?.commands.resolveId(command);
if (!commandId) { if (!commandId) {
throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable', true); throw new TypeError('INVALID_TYPE', 'command', 'ApplicationCommandResolvable', true);
} }
} }
return { guildId, commandId }; return { guildId, commandId };
} }
/** /**
* Transforms an {@link ApplicationCommandPermissionData} object into something that can be used with the API. * Transforms an {@link ApplicationCommandPermissionData} object into something that can be used with the API.
* @param {ApplicationCommandPermissionData} permissions The permissions to transform * @param {ApplicationCommandPermissionData} permissions The permissions to transform
* @param {boolean} [received] Whether these permissions have been received from Discord * @param {boolean} [received] Whether these permissions have been received from Discord
* @returns {APIApplicationCommandPermissions} * @returns {APIApplicationCommandPermissions}
* @private * @private
*/ */
static transformPermissions(permissions, received) { static transformPermissions(permissions, received) {
return { return {
id: permissions.id, id: permissions.id,
permission: permissions.permission, permission: permissions.permission,
type: type:
typeof permissions.type === 'number' && !received typeof permissions.type === 'number' && !received
? permissions.type ? permissions.type
: ApplicationCommandPermissionTypes[permissions.type], : ApplicationCommandPermissionTypes[permissions.type],
}; };
} }
} }
module.exports = ApplicationCommandPermissionsManager; module.exports = ApplicationCommandPermissionsManager;
/* eslint-disable max-len */ /* eslint-disable max-len */
/** /**
* @external APIApplicationCommandPermissions * @external APIApplicationCommandPermissions
* @see {@link https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure} * @see {@link https://discord.com/developers/docs/interactions/application-commands#application-command-permissions-object-application-command-permissions-structure}
*/ */

View File

@ -1,75 +1,75 @@
'use strict'; 'use strict';
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const GuildMember = require('../structures/GuildMember'); const GuildMember = require('../structures/GuildMember');
const Message = require('../structures/Message'); const Message = require('../structures/Message');
const ThreadMember = require('../structures/ThreadMember'); const ThreadMember = require('../structures/ThreadMember');
const User = require('../structures/User'); const User = require('../structures/User');
/** /**
* Manages API methods for users and stores their cache. * Manages API methods for users and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class BlockedManager extends CachedManager { class BlockedManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable) {
super(client, User, iterable); super(client, User, iterable);
} }
/** /**
* The cache of this manager * The cache of this manager
* @type {Collection<Snowflake, User>} * @type {Collection<Snowflake, User>}
* @name BlockedManager#cache * @name BlockedManager#cache
*/ */
/** /**
* Data that resolves to give a User object. This can be: * Data that resolves to give a User object. This can be:
* * A User object * * A User object
* * A Snowflake * * A Snowflake
* * A Message object (resolves to the message author) * * A Message object (resolves to the message author)
* * A GuildMember object * * A GuildMember object
* * A ThreadMember object * * A ThreadMember object
* @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable * @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable
*/ */
/** /**
* Resolves a {@link UserResolvable} to a {@link User} object. * Resolves a {@link UserResolvable} to a {@link User} object.
* @param {UserResolvable} user The UserResolvable to identify * @param {UserResolvable} user The UserResolvable to identify
* @returns {?User} * @returns {?User}
*/ */
resolve(user) { resolve(user) {
if (user instanceof GuildMember || user instanceof ThreadMember) return user.user; if (user instanceof GuildMember || user instanceof ThreadMember) return user.user;
if (user instanceof Message) return user.author; if (user instanceof Message) return user.author;
return super.resolve(user); return super.resolve(user);
} }
/** /**
* Resolves a {@link UserResolvable} to a {@link User} id. * Resolves a {@link UserResolvable} to a {@link User} id.
* @param {UserResolvable} user The UserResolvable to identify * @param {UserResolvable} user The UserResolvable to identify
* @returns {?Snowflake} * @returns {?Snowflake}
*/ */
resolveId(user) { resolveId(user) {
if (user instanceof ThreadMember) return user.id; if (user instanceof ThreadMember) return user.id;
if (user instanceof GuildMember) return user.user.id; if (user instanceof GuildMember) return user.user.id;
if (user instanceof Message) return user.author.id; if (user instanceof Message) return user.author.id;
return super.resolveId(user); return super.resolveId(user);
} }
/** /**
* Obtains a user from Discord, or the user cache if it's already available. * Obtains a user from Discord, or the user cache if it's already available.
* @param {UserResolvable} user The user to fetch * @param {UserResolvable} user The user to fetch
* @param {BaseFetchOptions} [options] Additional options for this fetch * @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<User>} * @returns {Promise<User>}
*/ */
async fetch(user, { cache = true, force = false } = {}) { async fetch(user, { cache = true, force = false } = {}) {
const id = this.resolveId(user); const id = this.resolveId(user);
if (!force) { if (!force) {
const existing = this.cache.get(id); const existing = this.cache.get(id);
if (existing && !existing.partial) return existing; if (existing && !existing.partial) return existing;
} }
const data = await this.client.api.users(id).get(); const data = await this.client.api.users(id).get();
return this._add(data, cache); return this._add(data, cache);
} }
} }
module.exports = BlockedManager; module.exports = BlockedManager;

View File

@ -1,364 +1,367 @@
'use strict'; 'use strict';
const { default: Collection } = require('@discordjs/collection');
const { Error, TypeError } = require('../errors/DJSError'); const CachedManager = require('./CachedManager');
const { remove } = require('lodash'); const { default: Collection } = require('@discordjs/collection');
const { localeObject, DMScanLevel, stickerAnimationMode } = require('../util/Constants') const { Error, TypeError } = require('../errors/DJSError');
/** const { remove } = require('lodash');
* Manages API methods for users. const { localeObject, DMScanLevel, stickerAnimationMode } = require('../util/Constants')
*/ /**
class ClientUserSettingManager { * Manages API methods for users and stores their cache.
constructor(client, iterable) { * @extends {CachedManager}
this.client = client; */
// Raw data class ClientUserSettingManager extends CachedManager {
this.rawSetting = {}; constructor(client, iterable) {
// Language super(client);
this.locale = null; // Raw data
// Setting => ACTIVITY SETTINGS => Activity Status => Display current activity as a status message this.rawSetting = {};
this.activityDisplay = null; // Language
// this.locale = null;
this.disableDMfromServer = new Collection(); // Setting => ACTIVITY SETTINGS => Activity Status => Display current activity as a status message
// Allow direct messages from server members this.activityDisplay = null;
this.DMfromServerMode = null; //
// this.disableDMfromServer = new Collection();
this.displayImage = null; // Allow direct messages from server members
// this.DMfromServerMode = null;
this.linkedImageDisplay = null; //
// Setting => APP SETTINGS => Accessibility => Automatically play GIFs when Discord is focused. this.displayImage = null;
this.autoplayGIF = null; //
// Show embeds and preview website links pasted into chat this.linkedImageDisplay = null;
this.previewLink = null; // Setting => APP SETTINGS => Accessibility => Automatically play GIFs when Discord is focused.
// Setting => APP SETTINGS => Accessibility => Play Animated Emojis this.autoplayGIF = null;
this.animatedEmojis = null; // Show embeds and preview website links pasted into chat
// Setting => APP SETTINGS => Accessibility => Text-to-speech => Allow playback this.previewLink = null;
this.allowTTS = null; // Setting => APP SETTINGS => Accessibility => Play Animated Emojis
// Setting => APP SETTINGS => Appearance => Message Display => Compact Mode [OK] this.animatedEmojis = null;
this.compactMode = null; // Setting => APP SETTINGS => Accessibility => Text-to-speech => Allow playback
// Setting => APP SETTINGS => Text & Images => Emoji => Convert Emoticons this.allowTTS = null;
this.convertEmoticons = null; // Setting => APP SETTINGS => Appearance => Message Display => Compact Mode [OK]
// SAFE DIRECT MESSAGING this.compactMode = null;
this.DMScanLevel = null; // Setting => APP SETTINGS => Text & Images => Emoji => Convert Emoticons
// Setting => APP SETTINGS => Appearance => Theme [OK] this.convertEmoticons = null;
this.theme = ''; // SAFE DIRECT MESSAGING
// this.DMScanLevel = null;
this.developerMode = null; // Setting => APP SETTINGS => Appearance => Theme [OK]
// this.theme = '';
this.afkTimeout = null; //
// this.developerMode = null;
this.stickerAnimationMode = null; //
// WHO CAN ADD YOU AS A FRIEND ? this.afkTimeout = null;
this.addFriendFrom = { //
all: null, this.stickerAnimationMode = null;
mutual_friends: null, // WHO CAN ADD YOU AS A FRIEND ?
mutual_guilds: null, this.addFriendFrom = {
}; all: null,
// Setting => APP SETTINGS => Text & Images => Emoji => Show emoji reactions mutual_friends: null,
this.showEmojiReactions = null; mutual_guilds: null,
// Custom Stauts [It's not working now] };
this.customStatus = null; // Setting => APP SETTINGS => Text & Images => Emoji => Show emoji reactions
// Guild folder and position this.showEmojiReactions = null;
this.guildMetadata = new Collection(); // Custom Stauts [It's not working now]
// Todo: add new method from Discum this.customStatus = null;
} // Guild folder and position
/** this.guildMetadata = new Collection();
* // Todo: add new method from Discum
* @param {Object} data Raw Data to patch }
* @extends https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/discum/user/user.py /**
* @private *
*/ * @param {Object} data Raw Data to patch
_patch(data) { * @extends https://github.com/Merubokkusu/Discord-S.C.U.M/blob/master/discum/user/user.py
this.rawSetting = data; * @private
if ('locale' in data) { */
this.locale = localeObject[data.locale]; _patch(data) {
} this.rawSetting = data;
if ('show_current_game' in data) { if ('locale' in data) {
this.activityDisplay = data.show_current_game; this.locale = localeObject[data.locale];
} }
if ('default_guilds_restricted' in data) { if ('show_current_game' in data) {
this.DMfromServerMode = data.default_guilds_restricted; this.activityDisplay = data.show_current_game;
} }
if ('inline_attachment_media' in data) { if ('default_guilds_restricted' in data) {
this.displayImage = data.inline_attachment_media; this.DMfromServerMode = data.default_guilds_restricted;
} }
if ('inline_embed_media' in data) { if ('inline_attachment_media' in data) {
this.linkedImageDisplay = data.inline_embed_media; this.displayImage = data.inline_attachment_media;
} }
if ('gif_auto_play' in data) { if ('inline_embed_media' in data) {
this.autoplayGIF = data.gif_auto_play; this.linkedImageDisplay = data.inline_embed_media;
} }
if ('render_embeds' in data) { if ('gif_auto_play' in data) {
this.previewLink = data.render_embeds; this.autoplayGIF = data.gif_auto_play;
} }
if ('animate_emoji' in data) { if ('render_embeds' in data) {
this.animatedEmojis = data.animate_emoji; this.previewLink = data.render_embeds;
} }
if ('enable_tts_command' in data) { if ('animate_emoji' in data) {
this.allowTTS = data.enable_tts_command; this.animatedEmojis = data.animate_emoji;
} }
if ('message_display_compact' in data) { if ('enable_tts_command' in data) {
this.compactMode = data.message_display_compact; this.allowTTS = data.enable_tts_command;
} }
if ('convert_emoticons' in data) { if ('message_display_compact' in data) {
this.convertEmoticons = data.convert_emoticons; this.compactMode = data.message_display_compact;
} }
if ('explicit_content_filter' in data) { if ('convert_emoticons' in data) {
this.DMScanLevel = DMScanLevel[data.explicit_content_filter]; this.convertEmoticons = data.convert_emoticons;
} }
if ('theme' in data) { if ('explicit_content_filter' in data) {
this.theme = data.theme; this.DMScanLevel = DMScanLevel[data.explicit_content_filter];
} }
if ('developer_mode' in data) { if ('theme' in data) {
this.developerMode = data.developer_mode; this.theme = data.theme;
} }
if ('afk_timeout' in data) { if ('developer_mode' in data) {
this.afkTimeout = data.afk_timeout * 1000; // second => milisecond this.developerMode = data.developer_mode;
} }
if ('animate_stickers' in data) { if ('afk_timeout' in data) {
this.stickerAnimationMode = stickerAnimationMode[data.animate_stickers]; this.afkTimeout = data.afk_timeout * 1000; // second => milisecond
} }
if ('render_reactions' in data) { if ('animate_stickers' in data) {
this.showEmojiReactions = data.render_reactions; this.stickerAnimationMode = stickerAnimationMode[data.animate_stickers];
} }
if ('custom_status' in data) { if ('render_reactions' in data) {
this.customStatus = data.custom_status || {}; // Thanks PinkDuwc._#3443 reported this issue this.showEmojiReactions = data.render_reactions;
this.customStatus.status = data.status; }
} if ('custom_status' in data) {
if ('friend_source_flags' in data) { this.customStatus = data.custom_status || {}; // Thanks PinkDuwc._#3443 reported this issue
this.addFriendFrom = { this.customStatus.status = data.status;
all: data.friend_source_flags.all || false, }
mutual_friends: if ('friend_source_flags' in data) {
data.friend_source_flags.all ? true : data.friend_source_flags.mutual_friends, this.addFriendFrom = {
mutual_guilds: all: data.friend_source_flags.all || false,
data.friend_source_flags.all ? true : data.friend_source_flags.mutual_guilds, mutual_friends:
}; data.friend_source_flags.all ? true : data.friend_source_flags.mutual_friends,
} mutual_guilds:
if ('guild_folders' in data) { data.friend_source_flags.all ? true : data.friend_source_flags.mutual_guilds,
const data_ = data.guild_positions.map((guildId, i) => { };
// Find folder }
const folderIndex = data.guild_folders.findIndex((obj) => if ('guild_folders' in data) {
obj.guild_ids.includes(guildId), const data_ = data.guild_positions.map((guildId, i) => {
); // Find folder
const metadata = { const folderIndex = data.guild_folders.findIndex((obj) =>
guildId: guildId, obj.guild_ids.includes(guildId),
guildIndex: i, );
folderId: data.guild_folders[folderIndex]?.id, const metadata = {
folderIndex, guildId: guildId,
folderName: data.guild_folders[folderIndex]?.name, guildIndex: i,
folderColor: data.guild_folders[folderIndex]?.color, folderId: data.guild_folders[folderIndex]?.id,
folderGuilds: data.guild_folders[folderIndex]?.guild_ids, folderIndex,
}; folderName: data.guild_folders[folderIndex]?.name,
return [guildId, metadata]; folderColor: data.guild_folders[folderIndex]?.color,
}); folderGuilds: data.guild_folders[folderIndex]?.guild_ids,
this.guildMetadata = new Collection(data_); };
} return [guildId, metadata];
if ('restricted_guilds' in data) { });
data.restricted_guilds.map((guildId) => { this.guildMetadata = new Collection(data_);
const guild = this.client.guilds.cache.get(guildId); }
if (!guild) return; if ('restricted_guilds' in data) {
guild.disableDM = true; data.restricted_guilds.map((guildId) => {
this.disableDMfromServer.set(guildId, true); const guild = this.client.guilds.cache.get(guildId);
}); if (!guild) return;
} guild.disableDM = true;
} this.disableDMfromServer.set(guildId, true);
async fetch() { });
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); }
try { }
const data = await this.client.api.users('@me').settings.get(); async fetch() {
this._patch(data); if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
return this; try {
} catch (e) { const data = await this.client.api.users('@me').settings.get();
throw e; this._patch(data);
} return this;
} } catch (e) {
/** throw e;
* Edit data }
* @param {Object} data Data to edit }
* @private /**
*/ * Edit data
async edit(data) { * @param {Object} data Data to edit
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); * @private
try { */
const res = await this.client.api.users('@me').settings.patch({ data }); async edit(data) {
this._patch(res); if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
return this; try {
} catch (e) { const res = await this.client.api.users('@me').settings.patch({ data });
throw e; this._patch(res);
} return this;
} } catch (e) {
/** throw e;
* Set compact mode }
* @param {Boolean | null} value Compact mode enable or disable }
* @returns {Boolean} /**
*/ * Set compact mode
async setDisplayCompactMode(value) { * @param {Boolean | null} value Compact mode enable or disable
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); * @returns {Boolean}
if ( */
typeof value !== 'boolean' && async setDisplayCompactMode(value) {
typeof value !== 'null' && if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
typeof value !== 'undefined' if (
) typeof value !== 'boolean' &&
throw new TypeError( typeof value !== 'null' &&
'INVALID_TYPE', typeof value !== 'undefined'
'value', )
'boolean | null | undefined', throw new TypeError(
true, 'INVALID_TYPE',
); 'value',
if (!value) value = !this.compactMode; 'boolean | null | undefined',
if (value !== this.compactMode) { true,
await this.edit({ message_display_compact: value }); );
} if (!value) value = !this.compactMode;
return this.compactMode; if (value !== this.compactMode) {
} await this.edit({ message_display_compact: value });
/** }
* Discord Theme return this.compactMode;
* @param {null |dark |light} value Theme to set }
* @returns {theme} /**
*/ * Discord Theme
async setTheme(value) { * @param {null |dark |light} value Theme to set
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); * @returns {theme}
const validValues = ['dark', 'light']; */
if ( async setTheme(value) {
typeof value !== 'string' && if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
typeof value !== 'null' && const validValues = ['dark', 'light'];
typeof value !== 'undefined' if (
) typeof value !== 'string' &&
throw new TypeError( typeof value !== 'null' &&
'INVALID_TYPE', typeof value !== 'undefined'
'value', )
'string | null | undefined', throw new TypeError(
true, 'INVALID_TYPE',
); 'value',
if (!validValues.includes(value)) { 'string | null | undefined',
value == validValues[0] true,
? (value = validValues[1]) );
: (value = validValues[0]); if (!validValues.includes(value)) {
} value == validValues[0]
if (value !== this.theme) { ? (value = validValues[1])
await this.edit({ theme: value }); : (value = validValues[0]);
} }
return this.theme; if (value !== this.theme) {
} await this.edit({ theme: value });
/** }
* * Locale Setting, must be one of: return this.theme;
* * `DANISH` }
* * `GERMAN` /**
* * `ENGLISH_UK` * * Locale Setting, must be one of:
* * `ENGLISH_US` * * `DANISH`
* * `SPANISH` * * `GERMAN`
* * `FRENCH` * * `ENGLISH_UK`
* * `CROATIAN` * * `ENGLISH_US`
* * `ITALIAN` * * `SPANISH`
* * `LITHUANIAN` * * `FRENCH`
* * `HUNGARIAN` * * `CROATIAN`
* * `DUTCH` * * `ITALIAN`
* * `NORWEGIAN` * * `LITHUANIAN`
* * `POLISH` * * `HUNGARIAN`
* * `BRAZILIAN_PORTUGUESE` * * `DUTCH`
* * `ROMANIA_ROMANIAN` * * `NORWEGIAN`
* * `FINNISH` * * `POLISH`
* * `SWEDISH` * * `BRAZILIAN_PORTUGUESE`
* * `VIETNAMESE` * * `ROMANIA_ROMANIAN`
* * `TURKISH` * * `FINNISH`
* * `CZECH` * * `SWEDISH`
* * `GREEK` * * `VIETNAMESE`
* * `BULGARIAN` * * `TURKISH`
* * `RUSSIAN` * * `CZECH`
* * `UKRAINIAN` * * `GREEK`
* * `HINDI` * * `BULGARIAN`
* * `THAI` * * `RUSSIAN`
* * `CHINA_CHINESE` * * `UKRAINIAN`
* * `JAPANESE` * * `HINDI`
* * `TAIWAN_CHINESE` * * `THAI`
* * `KOREAN` * * `CHINA_CHINESE`
* @param {string} value * * `JAPANESE`
* @returns {locale} * * `TAIWAN_CHINESE`
*/ * * `KOREAN`
async setLocale(value) { * @param {string} value
if (this.client.bot) throw new Error('INVALID_BOT_METHOD'); * @returns {locale}
if (typeof value !== 'string') */
throw new TypeError('INVALID_TYPE', 'value', 'string', true); async setLocale(value) {
if (!localeObject[value]) throw new Error('INVALID_LOCALE'); if (this.client.bot) throw new Error('INVALID_BOT_METHOD');
if (localeObject[value] !== this.locale) { if (typeof value !== 'string')
await this.edit({ locale: localeObject[value] }); throw new TypeError('INVALID_TYPE', 'value', 'string', true);
} if (!localeObject[value]) throw new Error('INVALID_LOCALE');
return this.locale; if (localeObject[value] !== this.locale) {
} await this.edit({ locale: localeObject[value] });
// TODO: Guild positions & folders }
// Change Index in Array [Hidden] return this.locale;
/** }
* // TODO: Guild positions & folders
* @param {Array} array Array // Change Index in Array [Hidden]
* @param {Number} from Index1 /**
* @param {Number} to Index2 *
* @returns {Array} * @param {Array} array Array
* @private * @param {Number} from Index1
*/ * @param {Number} to Index2
_move(array, from, to) { * @returns {Array}
array.splice(to, 0, array.splice(from, 1)[0]); * @private
return array; */
} _move(array, from, to) {
// TODO: Move Guild array.splice(to, 0, array.splice(from, 1)[0]);
// folder to folder return array;
// folder to home }
// home to home // TODO: Move Guild
// home to folder // folder to folder
/** // folder to home
* Change Guild Position (from * to Folder or Home) // home to home
* @param {GuildIDResolve} guildId guild.id // home to folder
* @param {Number} newPosition Guild Position /**
* * **WARNING**: Type = `FOLDER`, newPosition is the guild's index in the Folder. * Change Guild Position (from * to Folder or Home)
* @param {number} type Move to folder or home * @param {GuildIDResolve} guildId guild.id
* * `FOLDER`: 1 * @param {Number} newPosition Guild Position
* * `HOME`: 2 * * **WARNING**: Type = `FOLDER`, newPosition is the guild's index in the Folder.
* @param {FolderID} folderId If you want to move to folder * @param {number} type Move to folder or home
* @private * * `FOLDER`: 1
*/ * * `HOME`: 2
async guildChangePosition(guildId, newPosition, type, folderId) { * @param {FolderID} folderId If you want to move to folder
// get Guild default position * @private
// Escape */
const oldGuildFolderPosition = this.rawSetting.guild_folders.findIndex( async guildChangePosition(guildId, newPosition, type, folderId) {
(value) => value.guild_ids.includes(guildId), // get Guild default position
); // Escape
const newGuildFolderPosition = this.rawSetting.guild_folders.findIndex( const oldGuildFolderPosition = this.rawSetting.guild_folders.findIndex(
(value) => (value) => value.guild_ids.includes(guildId),
value.guild_ids.includes(this.rawSetting.guild_positions[newPosition]), );
); const newGuildFolderPosition = this.rawSetting.guild_folders.findIndex(
if (type == 2 || `${type}`.toUpperCase() == 'HOME') { (value) =>
// Delete GuildID from Folder and create new Folder value.guild_ids.includes(this.rawSetting.guild_positions[newPosition]),
// Check it is folder );
const folder = this.rawSetting.guild_folders[oldGuildFolderPosition]; if (type == 2 || `${type}`.toUpperCase() == 'HOME') {
if (folder.id) { // Delete GuildID from Folder and create new Folder
this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids = // Check it is folder
this.rawSetting.guild_folders[ const folder = this.rawSetting.guild_folders[oldGuildFolderPosition];
oldGuildFolderPosition if (folder.id) {
].guild_ids.filter((v) => v !== guildId); this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids =
} this.rawSetting.guild_folders[
this.rawSetting.guild_folders = this._move( oldGuildFolderPosition
this.rawSetting.guild_folders, ].guild_ids.filter((v) => v !== guildId);
oldGuildFolderPosition, }
newGuildFolderPosition, this.rawSetting.guild_folders = this._move(
); this.rawSetting.guild_folders,
this.rawSetting.guild_folders[newGuildFolderPosition].id = null; oldGuildFolderPosition,
} else if (type == 1 || `${type}`.toUpperCase() == 'FOLDER') { newGuildFolderPosition,
// Delete GuildID from oldFolder );
this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids = this.rawSetting.guild_folders[newGuildFolderPosition].id = null;
this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids.filter( } else if (type == 1 || `${type}`.toUpperCase() == 'FOLDER') {
(v) => v !== guildId, // Delete GuildID from oldFolder
); this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids =
// Index new Folder this.rawSetting.guild_folders[oldGuildFolderPosition].guild_ids.filter(
const folderIndex = this.rawSetting.guild_folders.findIndex( (v) => v !== guildId,
(value) => value.id == folderId, );
); // Index new Folder
const folder = this.rawSetting.guild_folders[folderIndex]; const folderIndex = this.rawSetting.guild_folders.findIndex(
folder.guild_ids.push(guildId); (value) => value.id == folderId,
folder.guild_ids = [...new Set(folder.guild_ids)]; );
folder.guild_ids = this._move( const folder = this.rawSetting.guild_folders[folderIndex];
folder.guild_ids, folder.guild_ids.push(guildId);
folder.guild_ids.findIndex((v) => v == guildId), folder.guild_ids = [...new Set(folder.guild_ids)];
newPosition, folder.guild_ids = this._move(
); folder.guild_ids,
} folder.guild_ids.findIndex((v) => v == guildId),
this.edit({ guild_folders: this.rawSetting.guild_folders }); newPosition,
} );
} }
this.edit({ guild_folders: this.rawSetting.guild_folders });
module.exports = ClientUserSettingManager; }
}
module.exports = ClientUserSettingManager;

View File

@ -1,75 +1,75 @@
'use strict'; 'use strict';
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const GuildMember = require('../structures/GuildMember'); const GuildMember = require('../structures/GuildMember');
const Message = require('../structures/Message'); const Message = require('../structures/Message');
const ThreadMember = require('../structures/ThreadMember'); const ThreadMember = require('../structures/ThreadMember');
const User = require('../structures/User'); const User = require('../structures/User');
/** /**
* Manages API methods for users and stores their cache. * Manages API methods for users and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class FriendsManager extends CachedManager { class FriendsManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable) {
super(client, User, iterable); super(client, User, iterable);
} }
/** /**
* The cache of this manager * The cache of this manager
* @type {Collection<Snowflake, User>} * @type {Collection<Snowflake, User>}
* @name FriendsManager#cache * @name FriendsManager#cache
*/ */
/** /**
* Data that resolves to give a User object. This can be: * Data that resolves to give a User object. This can be:
* * A User object * * A User object
* * A Snowflake * * A Snowflake
* * A Message object (resolves to the message author) * * A Message object (resolves to the message author)
* * A GuildMember object * * A GuildMember object
* * A ThreadMember object * * A ThreadMember object
* @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable * @typedef {User|Snowflake|Message|GuildMember|ThreadMember} UserResolvable
*/ */
/** /**
* Resolves a {@link UserResolvable} to a {@link User} object. * Resolves a {@link UserResolvable} to a {@link User} object.
* @param {UserResolvable} user The UserResolvable to identify * @param {UserResolvable} user The UserResolvable to identify
* @returns {?User} * @returns {?User}
*/ */
resolve(user) { resolve(user) {
if (user instanceof GuildMember || user instanceof ThreadMember) return user.user; if (user instanceof GuildMember || user instanceof ThreadMember) return user.user;
if (user instanceof Message) return user.author; if (user instanceof Message) return user.author;
return super.resolve(user); return super.resolve(user);
} }
/** /**
* Resolves a {@link UserResolvable} to a {@link User} id. * Resolves a {@link UserResolvable} to a {@link User} id.
* @param {UserResolvable} user The UserResolvable to identify * @param {UserResolvable} user The UserResolvable to identify
* @returns {?Snowflake} * @returns {?Snowflake}
*/ */
resolveId(user) { resolveId(user) {
if (user instanceof ThreadMember) return user.id; if (user instanceof ThreadMember) return user.id;
if (user instanceof GuildMember) return user.user.id; if (user instanceof GuildMember) return user.user.id;
if (user instanceof Message) return user.author.id; if (user instanceof Message) return user.author.id;
return super.resolveId(user); return super.resolveId(user);
} }
/** /**
* Obtains a user from Discord, or the user cache if it's already available. * Obtains a user from Discord, or the user cache if it's already available.
* @param {UserResolvable} user The user to fetch * @param {UserResolvable} user The user to fetch
* @param {BaseFetchOptions} [options] Additional options for this fetch * @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<User>} * @returns {Promise<User>}
*/ */
async fetch(user, { cache = true, force = false } = {}) { async fetch(user, { cache = true, force = false } = {}) {
const id = this.resolveId(user); const id = this.resolveId(user);
if (!force) { if (!force) {
const existing = this.cache.get(id); const existing = this.cache.get(id);
if (existing && !existing.partial) return existing; if (existing && !existing.partial) return existing;
} }
const data = await this.client.api.users(id).get(); const data = await this.client.api.users(id).get();
return this._add(data, cache); return this._add(data, cache);
} }
} }
module.exports = FriendsManager; module.exports = FriendsManager;

View File

@ -1,247 +1,247 @@
'use strict'; 'use strict';
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { TypeError, Error } = require('../errors'); const { TypeError, Error } = require('../errors');
const { Message } = require('../structures/Message'); const { Message } = require('../structures/Message');
const MessagePayload = require('../structures/MessagePayload'); const MessagePayload = require('../structures/MessagePayload');
const Util = require('../util/Util'); const Util = require('../util/Util');
const BigNumber = require('bignumber.js'); const BigNumber = require('bignumber.js');
/** /**
* Manages API methods for Messages and holds their cache. * Manages API methods for Messages and holds their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class MessageManager extends CachedManager { class MessageManager extends CachedManager {
constructor(channel, iterable) { constructor(channel, iterable) {
super(channel.client, Message, iterable); super(channel.client, Message, iterable);
/** /**
* The channel that the messages belong to * The channel that the messages belong to
* @type {TextBasedChannels} * @type {TextBasedChannels}
*/ */
this.channel = channel; this.channel = channel;
} }
/** /**
* The cache of Messages * The cache of Messages
* @type {Collection<Snowflake, Message>} * @type {Collection<Snowflake, Message>}
* @name MessageManager#cache * @name MessageManager#cache
*/ */
_add(data, cache) { _add(data, cache) {
return super._add(data, cache); return super._add(data, cache);
} }
/** /**
* The parameters to pass in when requesting previous messages from a channel. `around`, `before` and * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and
* `after` are mutually exclusive. All the parameters are optional. * `after` are mutually exclusive. All the parameters are optional.
* @typedef {Object} ChannelLogsQueryOptions * @typedef {Object} ChannelLogsQueryOptions
* @property {number} [limit=50] Number of messages to acquire * @property {number} [limit=50] Number of messages to acquire
* @property {Snowflake} [before] The message's id to get the messages that were posted before it * @property {Snowflake} [before] The message's id to get the messages that were posted before it
* @property {Snowflake} [after] The message's id to get the messages that were posted after it * @property {Snowflake} [after] The message's id to get the messages that were posted after it
* @property {Snowflake} [around] The message's id to get the messages that were posted around it * @property {Snowflake} [around] The message's id to get the messages that were posted around it
*/ */
/** /**
* Gets a message, or messages, from this channel. * 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. * <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> * 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 {Snowflake|ChannelLogsQueryOptions} [message] The id of the message to fetch, or query parameters.
* @param {BaseFetchOptions} [options] Additional options for this fetch * @param {BaseFetchOptions} [options] Additional options for this fetch
* @returns {Promise<Message|Collection<Snowflake, Message>>} * @returns {Promise<Message|Collection<Snowflake, Message>>}
* @example * @example
* // Get message * // Get message
* channel.messages.fetch('99539446449315840') * channel.messages.fetch('99539446449315840')
* .then(message => console.log(message.content)) * .then(message => console.log(message.content))
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Get messages * // Get messages
* channel.messages.fetch({ limit: 10 }) * channel.messages.fetch({ limit: 10 })
* .then(messages => console.log(`Received ${messages.size} messages`)) * .then(messages => console.log(`Received ${messages.size} messages`))
* .catch(console.error); * .catch(console.error);
* @example * @example
* // Get messages and filter by user id * // Get messages and filter by user id
* channel.messages.fetch() * channel.messages.fetch()
* .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`)) * .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`))
* .catch(console.error); * .catch(console.error);
*/ */
fetch(message, { cache = true, force = false } = {}) { fetch(message, { cache = true, force = false } = {}) {
return typeof message === 'string' ? this._fetchId(message, cache, force) : this._fetchMany(message, cache); return typeof message === 'string' ? this._fetchId(message, cache, force) : this._fetchMany(message, cache);
} }
/** /**
* Fetches the pinned messages of this channel and returns a collection of them. * 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. * <info>The returned Collection does not contain any reaction data of the messages.
* Those need to be fetched separately.</info> * Those need to be fetched separately.</info>
* @param {boolean} [cache=true] Whether to cache the message(s) * @param {boolean} [cache=true] Whether to cache the message(s)
* @returns {Promise<Collection<Snowflake, Message>>} * @returns {Promise<Collection<Snowflake, Message>>}
* @example * @example
* // Get pinned messages * // Get pinned messages
* channel.messages.fetchPinned() * channel.messages.fetchPinned()
* .then(messages => console.log(`Received ${messages.size} messages`)) * .then(messages => console.log(`Received ${messages.size} messages`))
* .catch(console.error); * .catch(console.error);
*/ */
async fetchPinned(cache = true) { async fetchPinned(cache = true) {
const data = await this.client.api.channels[this.channel.id].pins.get(); const data = await this.client.api.channels[this.channel.id].pins.get();
const messages = new Collection(); const messages = new Collection();
for (const message of data) messages.set(message.id, this._add(message, cache)); for (const message of data) messages.set(message.id, this._add(message, cache));
return messages; return messages;
} }
/** /**
* Data that can be resolved to a Message object. This can be: * Data that can be resolved to a Message object. This can be:
* * A Message * * A Message
* * A Snowflake * * A Snowflake
* @typedef {Message|Snowflake} MessageResolvable * @typedef {Message|Snowflake} MessageResolvable
*/ */
/** /**
* Resolves a {@link MessageResolvable} to a {@link Message} object. * Resolves a {@link MessageResolvable} to a {@link Message} object.
* @method resolve * @method resolve
* @memberof MessageManager * @memberof MessageManager
* @instance * @instance
* @param {MessageResolvable} message The message resolvable to resolve * @param {MessageResolvable} message The message resolvable to resolve
* @returns {?Message} * @returns {?Message}
*/ */
/** /**
* Resolves a {@link MessageResolvable} to a {@link Message} id. * Resolves a {@link MessageResolvable} to a {@link Message} id.
* @method resolveId * @method resolveId
* @memberof MessageManager * @memberof MessageManager
* @instance * @instance
* @param {MessageResolvable} message The message resolvable to resolve * @param {MessageResolvable} message The message resolvable to resolve
* @returns {?Snowflake} * @returns {?Snowflake}
*/ */
/** /**
* Edits a message, even if it's not cached. * Edits a message, even if it's not cached.
* @param {MessageResolvable} message The message to edit * @param {MessageResolvable} message The message to edit
* @param {string|MessageEditOptions|MessagePayload} options The options to edit the message * @param {string|MessageEditOptions|MessagePayload} options The options to edit the message
* @returns {Promise<Message>} * @returns {Promise<Message>}
*/ */
async edit(message, options) { async edit(message, options) {
const messageId = this.resolveId(message); const messageId = this.resolveId(message);
if (!messageId) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); if (!messageId) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
let messagePayload; let messagePayload;
if (options instanceof MessagePayload) { if (options instanceof MessagePayload) {
messagePayload = await options.resolveData(); messagePayload = await options.resolveData();
} else { } else {
messagePayload = await MessagePayload.create( messagePayload = await MessagePayload.create(
message instanceof Message ? message : this, message instanceof Message ? message : this,
options, options,
).resolveData(); ).resolveData();
} }
const { data, files } = await messagePayload.resolveFiles(); const { data, files } = await messagePayload.resolveFiles();
const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data, files }); const d = await this.client.api.channels[this.channel.id].messages[messageId].patch({ data, files });
const existing = this.cache.get(messageId); const existing = this.cache.get(messageId);
if (existing) { if (existing) {
const clone = existing._clone(); const clone = existing._clone();
clone._patch(d); clone._patch(d);
return clone; return clone;
} }
return this._add(d); return this._add(d);
} }
/** /**
* Publishes a message in an announcement channel to all channels following it, even if it's not cached. * Publishes a message in an announcement channel to all channels following it, even if it's not cached.
* @param {MessageResolvable} message The message to publish * @param {MessageResolvable} message The message to publish
* @returns {Promise<Message>} * @returns {Promise<Message>}
*/ */
async crosspost(message) { async crosspost(message) {
message = this.resolveId(message); message = this.resolveId(message);
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
const data = await this.client.api.channels(this.channel.id).messages(message).crosspost.post(); const data = await this.client.api.channels(this.channel.id).messages(message).crosspost.post();
return this.cache.get(data.id) ?? this._add(data); return this.cache.get(data.id) ?? this._add(data);
} }
/** /**
* Pins a message to the channel's pinned messages, even if it's not cached. * Pins a message to the channel's pinned messages, even if it's not cached.
* @param {MessageResolvable} message The message to pin * @param {MessageResolvable} message The message to pin
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async pin(message) { async pin(message) {
message = this.resolveId(message); message = this.resolveId(message);
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
await this.client.api.channels(this.channel.id).pins(message).put(); await this.client.api.channels(this.channel.id).pins(message).put();
} }
/** /**
* Unpins a message from the channel's pinned messages, even if it's not cached. * Unpins a message from the channel's pinned messages, even if it's not cached.
* @param {MessageResolvable} message The message to unpin * @param {MessageResolvable} message The message to unpin
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async unpin(message) { async unpin(message) {
message = this.resolveId(message); message = this.resolveId(message);
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
await this.client.api.channels(this.channel.id).pins(message).delete(); await this.client.api.channels(this.channel.id).pins(message).delete();
} }
/** /**
* Adds a reaction to a message, even if it's not cached. * Adds a reaction to a message, even if it's not cached.
* @param {MessageResolvable} message The message to react to * @param {MessageResolvable} message The message to react to
* @param {EmojiIdentifierResolvable} emoji The emoji to react with * @param {EmojiIdentifierResolvable} emoji The emoji to react with
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async react(message, emoji) { async react(message, emoji) {
message = this.resolveId(message); message = this.resolveId(message);
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
emoji = Util.resolvePartialEmoji(emoji); emoji = Util.resolvePartialEmoji(emoji);
if (!emoji) throw new TypeError('EMOJI_TYPE', 'emoji', 'EmojiIdentifierResolvable'); if (!emoji) throw new TypeError('EMOJI_TYPE', 'emoji', 'EmojiIdentifierResolvable');
const emojiId = emoji.id const emojiId = emoji.id
? `${emoji.animated ? 'a:' : ''}${emoji.name}:${emoji.id}` ? `${emoji.animated ? 'a:' : ''}${emoji.name}:${emoji.id}`
: encodeURIComponent(emoji.name); : encodeURIComponent(emoji.name);
// eslint-disable-next-line newline-per-chained-call // eslint-disable-next-line newline-per-chained-call
await this.client.api.channels(this.channel.id).messages(message).reactions(emojiId, '@me').put(); await this.client.api.channels(this.channel.id).messages(message).reactions(emojiId, '@me').put();
} }
/** /**
* Deletes a message, even if it's not cached. * Deletes a message, even if it's not cached.
* @param {MessageResolvable} message The message to delete * @param {MessageResolvable} message The message to delete
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async delete(message) { async delete(message) {
message = this.resolveId(message); message = this.resolveId(message);
if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable'); if (!message) throw new TypeError('INVALID_TYPE', 'message', 'MessageResolvable');
await this.client.api.channels(this.channel.id).messages(message).delete(); await this.client.api.channels(this.channel.id).messages(message).delete();
} }
async _fetchId(messageId, cache, force) { async _fetchId(messageId, cache, force) {
if (!force) { if (!force) {
const existing = this.cache.get(messageId); const existing = this.cache.get(messageId);
if (existing && !existing.partial) return existing; if (existing && !existing.partial) return existing;
} }
// const data = await this.client.api.channels[this.channel.id].messages[messageId].get(); // Discord Block // const data = await this.client.api.channels[this.channel.id].messages[messageId].get(); // Discord Block
// https://canary.discord.com/api/v9/guilds/809133733591384155/messages/search?channel_id=840225732902518825&max_id=957254525360697375&min_id=957254525360697373 // https://canary.discord.com/api/v9/guilds/809133733591384155/messages/search?channel_id=840225732902518825&max_id=957254525360697375&min_id=957254525360697373
const data = ( const data = (
await this.client.api.guilds[this.channel.guild.id].messages.search.get({ await this.client.api.guilds[this.channel.guild.id].messages.search.get({
query: { query: {
channel_id: this.channel.id, channel_id: this.channel.id,
max_id: new BigNumber.BigNumber(messageId).plus(1).toString(), max_id: new BigNumber.BigNumber(messageId).plus(1).toString(),
min_id: new BigNumber.BigNumber(messageId).minus(1).toString(), min_id: new BigNumber.BigNumber(messageId).minus(1).toString(),
}, },
}) })
).messages[0] ).messages[0]
if (data) return this._add(data[0], cache); if (data) return this._add(data[0], cache);
else throw new Error('MESSAGE_ID_NOT_FOUND'); else throw new Error('MESSAGE_ID_NOT_FOUND');
} }
async _fetchMany(options = {}, cache) { async _fetchMany(options = {}, cache) {
const data = await this.client.api.channels[this.channel.id].messages.get({ query: options }); const data = await this.client.api.channels[this.channel.id].messages.get({ query: options });
const messages = new Collection(); const messages = new Collection();
for (const message of data) messages.set(message.id, this._add(message, cache)); for (const message of data) messages.set(message.id, this._add(message, cache));
return messages; return messages;
} }
} }
module.exports = MessageManager; module.exports = MessageManager;

View File

@ -3,14 +3,14 @@
const { Collection } = require('@discordjs/collection'); const { Collection } = require('@discordjs/collection');
const CachedManager = require('./CachedManager'); const CachedManager = require('./CachedManager');
const { Error } = require('../errors'); const { Error } = require('../errors');
const { User } = require('discord.js-selfbot-v13'); const Discord = require("discord.js-selfbot-v13")
/** /**
* Manages API methods for users who reacted to a reaction and stores their cache. * Manages API methods for users who reacted to a reaction and stores their cache.
* @extends {CachedManager} * @extends {CachedManager}
*/ */
class ReactionUserManager extends CachedManager { class ReactionUserManager extends CachedManager {
constructor(reaction, iterable) { constructor(reaction, iterable) {
super(reaction.client, User, iterable); super(reaction.client, Discord.User, iterable);
/** /**
* The reaction that this manager belongs to * The reaction that this manager belongs to
@ -21,7 +21,7 @@ class ReactionUserManager extends CachedManager {
/** /**
* The cache of this manager * The cache of this manager
* @type {Collection<Snowflake, User>} * @type {Collection<Snowflake, Discord.User>}
* @name ReactionUserManager#cache * @name ReactionUserManager#cache
*/ */
@ -35,7 +35,7 @@ class ReactionUserManager extends CachedManager {
/** /**
* Fetches all the users that gave this reaction. Resolves with a collection of users, mapped by their ids. * Fetches all the users that gave this reaction. Resolves with a collection of users, mapped by their ids.
* @param {FetchReactionUsersOptions} [options] Options for fetching the users * @param {FetchReactionUsersOptions} [options] Options for fetching the users
* @returns {Promise<Collection<Snowflake, User>>} * @returns {Promise<Collection<Snowflake, Discord.User>>}
*/ */
async fetch({ limit = 100, after } = {}) { async fetch({ limit = 100, after } = {}) {
const message = this.reaction.message; const message = this.reaction.message;