chore: Miscellaneous fixes
#9271 djs > Backports the following pull requests to version 13: > > * docs: describe private properties #8879 > * fix(snowflake): snowflakes length #9144 > * fix(Message): `bulkDeletable` permissions should be retrieved later for DMs #9146 > * fix(Message#editable): update editable check in threads locked #9216 > * fix: add support for new guild feature `GUILD_WEB_PAGE_VANITY_URL` #9219 > * fix(AutocompleteInteraction): Send `name_localizations` correctly #9238 > * fix(ThreadManager): Respect `cache` and `force` in fetching #9239 > * docs(FetchArchivedThreadOptions): `before` respects `archive_timestamp`, not creation timestamp #9240 > * refactor(FetchThreadsOptions): Remove `active` #9241 > * docs: differ `User#send` #9251 > * docs: add more examples #9252 > * fix(ClientUser): No mutation on edit #9259 > * fix: resolving string bitfield #9262 > * refactor: call `GuildBanManager#create()` directly #9263 > * docs(Role): Fix example for `comparePositionTo()` #9270 > * docs: fix compare position example #9272 > * fix: Keep symbols in actions manager #9293
This commit is contained in:
parent
89fe3a5f7e
commit
d1d842098a
@ -15,7 +15,6 @@ const handlers = Object.fromEntries([
|
||||
['AUTO_MODERATION_RULE_CREATE', require('./AUTO_MODERATION_RULE_CREATE')],
|
||||
['AUTO_MODERATION_RULE_DELETE', require('./AUTO_MODERATION_RULE_DELETE')],
|
||||
['AUTO_MODERATION_RULE_UPDATE', require('./AUTO_MODERATION_RULE_UPDATE')],
|
||||
['GUILD_AUDIT_LOG_ENTRY_CREATE', require('./GUILD_AUDIT_LOG_ENTRY_CREATE')],
|
||||
['CALL_CREATE', require('./CALL_CREATE')],
|
||||
['CALL_UPDATE', require('./CALL_UPDATE')],
|
||||
['CALL_DELETE', require('./CALL_DELETE')],
|
||||
@ -81,6 +80,7 @@ const handlers = Object.fromEntries([
|
||||
['GUILD_SCHEDULED_EVENT_DELETE', require('./GUILD_SCHEDULED_EVENT_DELETE')],
|
||||
['GUILD_SCHEDULED_EVENT_USER_ADD', require('./GUILD_SCHEDULED_EVENT_USER_ADD')],
|
||||
['GUILD_SCHEDULED_EVENT_USER_REMOVE', require('./GUILD_SCHEDULED_EVENT_USER_REMOVE')],
|
||||
['GUILD_AUDIT_LOG_ENTRY_CREATE', require('./GUILD_AUDIT_LOG_ENTRY_CREATE')],
|
||||
]);
|
||||
|
||||
module.exports = handlers;
|
||||
|
@ -12,6 +12,13 @@ class CachedManager extends DataManager {
|
||||
constructor(client, holds, iterable) {
|
||||
super(client, holds);
|
||||
|
||||
/**
|
||||
* The private cache of items for this manager.
|
||||
* @type {Collection}
|
||||
* @private
|
||||
* @readonly
|
||||
* @name CachedManager#_cache
|
||||
*/
|
||||
Object.defineProperty(this, '_cache', { value: this.client.options.makeCache(this.constructor, this.holds) });
|
||||
|
||||
let cleanup = this._cache[_cleanupSymbol]?.();
|
||||
|
@ -59,15 +59,14 @@ class ThreadManager extends CachedManager {
|
||||
*/
|
||||
|
||||
/**
|
||||
* The options for fetching multiple threads, the properties are mutually exclusive
|
||||
* Options for fetching multiple threads.
|
||||
* @typedef {Object} FetchThreadsOptions
|
||||
* @property {FetchArchivedThreadOptions} [archived] The options used to fetch archived threads
|
||||
* @property {boolean} [active] When true, fetches active threads. <warn>If `archived` is set, this is ignored!</warn>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Obtains a thread from Discord, or the channel cache if it's already available.
|
||||
* @param {ThreadChannelResolvable|FetchChannelThreadsOptions} [options] The options to fetch threads. If it is a
|
||||
* @param {ThreadChannelResolvable|FetchChannelThreadsOptions|FetchThreadsOptions} [options] The options to fetch threads. If it is a
|
||||
* ThreadChannelResolvable then the specified thread will be fetched. Fetches all active threads if `undefined`
|
||||
* @param {BaseFetchOptions} [cacheOptions] Additional options for this fetch. <warn>The `force` field gets ignored
|
||||
* if `options` is not a {@link ThreadChannelResolvable}</warn>
|
||||
@ -78,10 +77,10 @@ class ThreadManager extends CachedManager {
|
||||
* .then(channel => console.log(channel.name))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
fetch(options, { cache = true, force = false } = {}) {
|
||||
fetch(options, { cache, force } = {}) {
|
||||
if (!options) return this.fetchActive(cache);
|
||||
const channel = this.client.channels.resolveId(options);
|
||||
if (channel) return this.client.channels.fetch(channel, cache, force);
|
||||
if (channel) return this.client.channels.fetch(channel, { cache, force });
|
||||
if (options.archived) {
|
||||
return this.fetchArchived(options.archived, cache);
|
||||
}
|
||||
@ -102,7 +101,7 @@ class ThreadManager extends CachedManager {
|
||||
* @property {string} [type='public'] The type of threads to fetch, either `public` or `private`
|
||||
* @property {boolean} [fetchAll=false] Whether to fetch **all** archived threads when type is `private`.
|
||||
* Requires `MANAGE_THREADS` if true
|
||||
* @property {DateResolvable|ThreadChannelResolvable} [before] Only return threads that were created before this Date
|
||||
* @property {DateResolvable|ThreadChannelResolvable} [before] Only return threads that were archived before this Date
|
||||
* or Snowflake. <warn>Must be a {@link ThreadChannelResolvable} when type is `private` and fetchAll is `false`</warn>
|
||||
* @property {number} [limit] Maximum number of threads to return
|
||||
*/
|
||||
@ -130,7 +129,7 @@ class ThreadManager extends CachedManager {
|
||||
let timestamp;
|
||||
let id;
|
||||
if (typeof before !== 'undefined') {
|
||||
if (before instanceof ThreadChannel || /^\d{16,19}$/.test(String(before))) {
|
||||
if (before instanceof ThreadChannel || /^\d{17,19}$/.test(String(before))) {
|
||||
id = this.resolveId(before);
|
||||
timestamp = this.resolve(before)?.archivedAt?.toISOString();
|
||||
} else {
|
||||
|
@ -95,9 +95,7 @@ class AutocompleteInteraction extends Interaction {
|
||||
await this.client.api.interactions(this.id, this.token).callback.post({
|
||||
data: {
|
||||
type: InteractionResponseTypes.APPLICATION_COMMAND_AUTOCOMPLETE_RESULT,
|
||||
data: {
|
||||
choices: options,
|
||||
},
|
||||
data: { choices: { ...options, name_localizations: options.nameLocalizations } },
|
||||
},
|
||||
auth: false,
|
||||
});
|
||||
|
@ -142,17 +142,13 @@ class ClientUser extends User {
|
||||
* @param {ClientUserEditData} data The new data
|
||||
* @returns {Promise<ClientUser>}
|
||||
*/
|
||||
async edit(data) {
|
||||
if (typeof data.avatar !== 'undefined') {
|
||||
data.avatar = await DataResolver.resolveImage(data.avatar);
|
||||
}
|
||||
if (typeof data.banner !== 'undefined') {
|
||||
data.banner = await DataResolver.resolveImage(data.banner);
|
||||
}
|
||||
const newData = await this.client.api.users('@me').patch({ data });
|
||||
this.client.token = newData.token;
|
||||
this.client.password = data?.password ? data?.password : this.client.password;
|
||||
const { updated } = this.client.actions.UserUpdate.handle(newData);
|
||||
async edit({ username, avatar }) {
|
||||
const data = await this.client.api
|
||||
.users('@me')
|
||||
.patch({ username, avatar: avatar && (await DataResolver.resolveImage(avatar)) });
|
||||
|
||||
this.client.token = data.token;
|
||||
const { updated } = this.client.actions.UserUpdate.handle(data);
|
||||
return updated ?? this;
|
||||
}
|
||||
|
||||
|
@ -769,9 +769,6 @@ class Guild extends AnonymousGuild {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async fetchVanityData() {
|
||||
if (!this.features.includes('VANITY_URL')) {
|
||||
throw new Error('VANITY_URL');
|
||||
}
|
||||
const data = await this.client.api.guilds(this.id, 'vanity-url').get();
|
||||
this.vanityURLCode = data.code;
|
||||
this.vanityURLUses = data.uses;
|
||||
@ -1462,7 +1459,7 @@ class Guild extends AnonymousGuild {
|
||||
* @example
|
||||
* // Leave a guild
|
||||
* guild.leave()
|
||||
* .then(g => console.log(`Left the guild ${g}`))
|
||||
* .then(guild => console.log(`Left the guild ${guild.name}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async leave() {
|
||||
@ -1488,7 +1485,7 @@ class Guild extends AnonymousGuild {
|
||||
* @example
|
||||
* // Delete a guild
|
||||
* guild.delete()
|
||||
* .then(g => console.log(`Deleted the guild ${g}`))
|
||||
* .then(guild => console.log(`Deleted the guild ${guild.name}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async delete() {
|
||||
@ -1634,9 +1631,6 @@ class Guild extends AnonymousGuild {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async setVanityCode(code = '') {
|
||||
if (!this.features.includes('VANITY_URL')) {
|
||||
throw new Error('VANITY_URL');
|
||||
}
|
||||
if (typeof code !== 'string') throw new TypeError('INVALID_VANITY_URL_CODE');
|
||||
const data = await this.client.api.guilds(this.id, 'vanity-url').patch({
|
||||
data: { code },
|
||||
|
@ -63,6 +63,11 @@ class GuildMember extends Base {
|
||||
*/
|
||||
this.communicationDisabledUntilTimestamp = null;
|
||||
|
||||
/**
|
||||
* The role ids of the member
|
||||
* @type {Snowflake[]}
|
||||
* @private
|
||||
*/
|
||||
this._roles = [];
|
||||
if (data) this._patch(data);
|
||||
}
|
||||
@ -414,6 +419,16 @@ class GuildMember extends Base {
|
||||
* @param {?string} nick The nickname for the guild member, or `null` if you want to reset their nickname
|
||||
* @param {string} [reason] Reason for setting the nickname
|
||||
* @returns {Promise<GuildMember>}
|
||||
* @example
|
||||
* // Set a nickname for a guild member
|
||||
* guildMember.setNickname('cool nickname', 'Needed a new nickname')
|
||||
* .then(member => console.log(`Set nickname of ${member.user.username}`))
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Remove a nickname for a guild member
|
||||
* guildMember.setNickname(null, 'No nicknames allowed!')
|
||||
* .then(member => console.log(`Removed nickname for ${member.user.username}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
setNickname(nick, reason) {
|
||||
return this.edit({ nick }, reason);
|
||||
@ -539,7 +554,7 @@ class GuildMember extends Base {
|
||||
* .catch(console.error);
|
||||
*/
|
||||
ban(options) {
|
||||
return this.guild.members.ban(this, options);
|
||||
return this.guild.bans.create(this, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -553,6 +568,11 @@ class GuildMember extends Base {
|
||||
* guildMember.disableCommunicationUntil(Date.now() + (5 * 60 * 1000), 'They deserved it')
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Remove the timeout of a guild member
|
||||
* guildMember.disableCommunicationUntil(null)
|
||||
* .then(member => console.log(`Removed timeout for ${member.displayName}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
disableCommunicationUntil(communicationDisabledUntil, reason) {
|
||||
return this.edit({ communicationDisabledUntil }, reason);
|
||||
@ -639,12 +659,22 @@ class GuildMember extends Base {
|
||||
json.displayAvatarURL = this.displayAvatarURL();
|
||||
return json;
|
||||
}
|
||||
|
||||
// These are here only for documentation purposes - they are implemented by TextBasedChannel
|
||||
/* eslint-disable no-empty-function */
|
||||
send() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to this user.
|
||||
* @method send
|
||||
* @memberof GuildMember
|
||||
* @instance
|
||||
* @param {string|MessagePayload|MessageOptions} options The options to provide
|
||||
* @returns {Promise<Message>}
|
||||
* @example
|
||||
* // Send a direct message
|
||||
* guildMember.send('Hello!')
|
||||
* .then(message => console.log(`Sent message: ${message.content} to ${guildMember.displayName}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
|
||||
TextBasedChannel.applyToClass(GuildMember);
|
||||
|
||||
exports.GuildMember = GuildMember;
|
||||
|
@ -21,7 +21,6 @@ const MessageFlags = require('../util/MessageFlags');
|
||||
const Permissions = require('../util/Permissions');
|
||||
const SnowflakeUtil = require('../util/SnowflakeUtil');
|
||||
const Util = require('../util/Util');
|
||||
// Const { ApplicationCommand } = require('discord.js-selfbot-v13'); - Not being used in this file, not necessary.
|
||||
|
||||
/**
|
||||
* @type {WeakSet<Message>}
|
||||
@ -337,10 +336,7 @@ class Message extends Base {
|
||||
}
|
||||
|
||||
if (data.referenced_message) {
|
||||
this.channel?.messages._add({
|
||||
guild_id: data.message_reference?.guild_id,
|
||||
...data.referenced_message,
|
||||
});
|
||||
this.channel?.messages._add({ guild_id: data.message_reference?.guild_id, ...data.referenced_message });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -605,11 +601,17 @@ class Message extends Base {
|
||||
const precheck = Boolean(
|
||||
this.author.id === this.client.user.id && !deletedMessages.has(this) && (!this.guild || this.channel?.viewable),
|
||||
);
|
||||
|
||||
// Regardless of permissions thread messages cannot be edited if
|
||||
// the thread is locked.
|
||||
// the thread is archived or the thread is locked and the bot does not have permission to manage threads.
|
||||
if (this.channel?.isThread()) {
|
||||
return precheck && !this.channel.locked;
|
||||
if (this.channel.archived) return false;
|
||||
if (this.channel.locked) {
|
||||
const permissions = this.permissionsFor(this.client.user);
|
||||
if (!permissions?.has(Permissions.FLAGS.MANAGE_THREADS, true)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return precheck;
|
||||
}
|
||||
|
||||
@ -651,13 +653,12 @@ class Message extends Base {
|
||||
* channel.bulkDelete(messages.filter(message => message.bulkDeletable));
|
||||
*/
|
||||
get bulkDeletable() {
|
||||
if (!this.client.user.bot) return false;
|
||||
const permissions = this.channel?.permissionsFor(this.client.user);
|
||||
return (
|
||||
(this.inGuild() &&
|
||||
this.client.user.bot &&
|
||||
Date.now() - this.createdTimestamp < MaxBulkDeletableMessageAge &&
|
||||
this.deletable &&
|
||||
permissions?.has(Permissions.FLAGS.MANAGE_MESSAGES, false)) ??
|
||||
this.channel?.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_MESSAGES, false)) ??
|
||||
false
|
||||
);
|
||||
}
|
||||
@ -901,9 +902,7 @@ class Message extends Base {
|
||||
if (!['GUILD_TEXT', 'GUILD_NEWS'].includes(this.channel.type)) {
|
||||
return Promise.reject(new Error('MESSAGE_THREAD_PARENT'));
|
||||
}
|
||||
if (this.hasThread) {
|
||||
return Promise.reject(new Error('MESSAGE_EXISTING_THREAD'));
|
||||
}
|
||||
if (this.hasThread) return Promise.reject(new Error('MESSAGE_EXISTING_THREAD'));
|
||||
return this.channel.threads.create({ ...options, startMessage: this });
|
||||
}
|
||||
|
||||
@ -923,9 +922,7 @@ class Message extends Base {
|
||||
*/
|
||||
fetchWebhook() {
|
||||
if (!this.webhookId) return Promise.reject(new Error('WEBHOOK_MESSAGE'));
|
||||
if (this.webhookId === this.applicationId) {
|
||||
return Promise.reject(new Error('WEBHOOK_APPLICATION'));
|
||||
}
|
||||
if (this.webhookId === this.applicationId) return Promise.reject(new Error('WEBHOOK_APPLICATION'));
|
||||
return this.client.fetchWebhook(this.webhookId);
|
||||
}
|
||||
|
||||
@ -974,9 +971,7 @@ class Message extends Base {
|
||||
equals(message, rawData) {
|
||||
if (!message) return false;
|
||||
const embedUpdate = !message.author && !message.attachments;
|
||||
if (embedUpdate) {
|
||||
return this.id === message.id && this.embeds.length === message.embeds.length;
|
||||
}
|
||||
if (embedUpdate) return this.id === message.id && this.embeds.length === message.embeds.length;
|
||||
|
||||
let equal =
|
||||
this.id === message.id &&
|
||||
|
@ -176,6 +176,12 @@ class Presence extends Base {
|
||||
*/
|
||||
class Activity {
|
||||
constructor(presence, data) {
|
||||
/**
|
||||
* The presence of the Activity
|
||||
* @type {Presence}
|
||||
* @readonly
|
||||
* @name Activity#presence
|
||||
*/
|
||||
Object.defineProperty(this, 'presence', { value: presence });
|
||||
|
||||
/**
|
||||
@ -346,6 +352,12 @@ class Activity {
|
||||
*/
|
||||
class RichPresenceAssets {
|
||||
constructor(activity, assets) {
|
||||
/**
|
||||
* The activity of the RichPresenceAssets
|
||||
* @type {Activity}
|
||||
* @readonly
|
||||
* @name RichPresenceAssets#activity
|
||||
*/
|
||||
Object.defineProperty(this, 'activity', { value: activity });
|
||||
|
||||
/**
|
||||
|
@ -233,6 +233,10 @@ class Role extends Base {
|
||||
* @param {RoleResolvable} role Role to compare to this one
|
||||
* @returns {number} Negative number if this role's position is lower (other role's is higher),
|
||||
* positive number if this one is higher (other's is lower), 0 if equal
|
||||
* @example
|
||||
* // Compare the position of a role to another
|
||||
* const roleCompare = role.comparePositionTo(otherRole);
|
||||
* if (roleCompare >= 1) console.log(`${role.name} is higher than ${otherRole.name}`);
|
||||
*/
|
||||
comparePositionTo(role) {
|
||||
return this.guild.roles.comparePositions(this, role);
|
||||
|
@ -645,12 +645,22 @@ class User extends Base {
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
// These are here only for documentation purposes - they are implemented by TextBasedChannel
|
||||
/* eslint-disable no-empty-function */
|
||||
send() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to this user.
|
||||
* @method send
|
||||
* @memberof User
|
||||
* @instance
|
||||
* @param {string|MessagePayload|MessageOptions} options The options to provide
|
||||
* @returns {Promise<Message>}
|
||||
* @example
|
||||
* // Send a direct message
|
||||
* user.send('Hello!')
|
||||
* .then(message => console.log(`Sent message: ${message.content} to ${user.tag}`))
|
||||
* .catch(console.error);
|
||||
*/
|
||||
|
||||
TextBasedChannel.applyToClass(User);
|
||||
|
||||
module.exports = User;
|
||||
|
@ -155,25 +155,6 @@ class TextBasedChannel {
|
||||
* })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
* @example
|
||||
* // Send an embed with a local image inside
|
||||
* channel.send({
|
||||
* content: 'This is an embed',
|
||||
* embeds: [
|
||||
* {
|
||||
* thumbnail: {
|
||||
* url: 'attachment://file.jpg'
|
||||
* }
|
||||
* }
|
||||
* ],
|
||||
* files: [{
|
||||
* attachment: 'entire/path/to/file.jpg',
|
||||
* name: 'file.jpg'
|
||||
* description: 'A description of the file'
|
||||
* }]
|
||||
* })
|
||||
* .then(console.log)
|
||||
* .catch(console.error);
|
||||
*/
|
||||
async send(options) {
|
||||
const User = require('../User');
|
||||
|
@ -146,8 +146,8 @@ class BitField {
|
||||
if (bit instanceof BitField) return bit.bitfield;
|
||||
if (Array.isArray(bit)) return bit.map(p => this.resolve(p)).reduce((prev, p) => prev | p, defaultBit);
|
||||
if (typeof bit === 'string') {
|
||||
if (typeof this.FLAGS[bit] !== 'undefined') return this.FLAGS[bit];
|
||||
if (!isNaN(bit)) return typeof defaultBit === 'bigint' ? BigInt(bit) : Number(bit);
|
||||
if (this.FLAGS[bit] !== undefined) return this.FLAGS[bit];
|
||||
}
|
||||
throw new RangeError('BITFIELD_INVALID', bit);
|
||||
}
|
||||
|
5
typings/index.d.ts
vendored
5
typings/index.d.ts
vendored
@ -378,6 +378,7 @@ export class CustomStatus {
|
||||
|
||||
export class Activity {
|
||||
private constructor(presence: Presence, data?: RawActivityData);
|
||||
public readonly presence: Presence;
|
||||
public applicationId: Snowflake | null;
|
||||
public assets: RichPresenceAssets | null;
|
||||
public buttons: string[];
|
||||
@ -1595,6 +1596,7 @@ export class GuildEmoji extends BaseGuildEmoji {
|
||||
|
||||
export class GuildMember extends PartialTextBasedChannel(Base) {
|
||||
private constructor(client: Client, data: RawGuildMemberData, guild: Guild);
|
||||
private _roles: Snowflake[];
|
||||
public avatar: string | null;
|
||||
public readonly bannable: boolean;
|
||||
/** @deprecated This will be removed in the next major version, see https://github.com/discordjs/discord.js/issues/7091 */
|
||||
@ -2647,6 +2649,7 @@ export class ReactionEmoji extends Emoji {
|
||||
|
||||
export class RichPresenceAssets {
|
||||
private constructor(activity: Activity, assets: RawRichPresenceAssets);
|
||||
public readonly activity: Activity;
|
||||
public largeImage: Snowflake | null;
|
||||
public largeText: string | null;
|
||||
public smallImage: Snowflake | null;
|
||||
@ -3769,6 +3772,7 @@ export abstract class DataManager<K, Holds, R> extends BaseManager {
|
||||
|
||||
export abstract class CachedManager<K, Holds, R> extends DataManager<K, Holds, R> {
|
||||
protected constructor(client: Client, holds: Constructable<Holds>);
|
||||
private readonly _cache: Collection<K, Holds>;
|
||||
private _add(data: unknown, cache?: boolean, { id, extras }?: { id: K; extras: unknown[] }): Holds;
|
||||
}
|
||||
|
||||
@ -6007,7 +6011,6 @@ export type FetchThreadMembersOptions =
|
||||
|
||||
export interface FetchThreadsOptions {
|
||||
archived?: FetchArchivedThreadOptions;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export interface FileOptions {
|
||||
|
Loading…
Reference in New Issue
Block a user