Skip to content

Commit

Permalink
chore: Miscellaneous fixes (#9271)
Browse files Browse the repository at this point in the history
* fix(Message#editable): update editable check in threads locked (#9216)

* fix(Message#editable): update editable check in threads locked

* fix(Message#editable): add check in archived threads

* fix: check manage threads permission only if thread is locked

* fix: adding a full stop at the end of a sentence

Co-authored-by: Jaworek <jaworekwiadomosci@gmail.com>

---------

Co-authored-by: Jaworek <jaworekwiadomosci@gmail.com>
Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

* fix(ThreadManager): Respect `cache` and `force` in fetching (#9239)

* fix(ThreadManager): Respect `cache` and `force` in fetching

* refactor: remove defaults

These are already defaulted down the line.

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* refactor(FetchThreadsOptions): Remove `active` (#9241)

* refactor(FetchThreadsOptions): remove `active`

* docs(FetchThreadsOptions): update description

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* docs: add more examples (#9252)

* docs: add more examples

* fix: fix grammar and syntax

Co-authored-by: Jaw0r3k <jaworekwiadomosci@gmail.com>

* chore: fine-tune examples

* chore: replace double quotes with singles

* fix: remove redundant example tag

* fix: fix timeout logging

* Update packages/discord.js/src/structures/GuildMember.js

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

---------

Co-authored-by: Jaw0r3k <jaworekwiadomosci@gmail.com>
Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* docs(Role): Fix example for `comparePositionTo()`

* docs(FetchArchivedThreadOptions): `before` respects `archive_timestamp`, not creation timestamp (#9240)

docs(FetchArchivedThreadOptions): correct `before` description

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* fix(snowflake): snowflakes length (#9144)

* fix(snowflake): fix snowflakes length

* fix(snowflake): fix length

* fix(Message): `bulkDeletable` permissions should be retrieved later for DMs (#9146)

* fix(Message): permissions check should be done later

the getter will error if used on a message originating from a DM

* refactor: remove unessercary chaining

* fix: invalid backport

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

* docs(Role): fix comparison example

* fix(ClientUser): no mutation on edit

* refactor: call bans.create directly

* fix(AutocompleteInteraction): Send `name_localizations` correctly (#9238)

fix(AutocompleteInteraction): send locale correctly

Co-authored-by: space <spaceeec@yahoo.com>

* fix: resolving string bitfield (#9262)

fix: resolving bitfield

Co-authored-by: space <spaceeec@yahoo.com>

* fix: Keep symbols in actions manager (#9293)

fix: keep symbols in actions manager

* fix: add support for new guild feature `GUILD_WEB_PAGE_VANITY_URL` (#9219)

Co-authored-by: space <spaceeec@yahoo.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* docs: differ `User#send` (#9251)

* docs: differate user#send

* chore: format

* chore: remove some examples

* docs: add GuildMember#send example

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

* docs: describe private properties (#8879)

* feat: describe private properties

* Update packages/discord.js/src/structures/GuildMember.js

Co-authored-by: MrMythicalYT <91077061+MrMythicalYT@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: MrMythicalYT <91077061+MrMythicalYT@users.noreply.github.com>

* Apply suggestions from code review

---------

Co-authored-by: MrMythicalYT <91077061+MrMythicalYT@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>

---------

Co-authored-by: Erwan <erwan977@gmail.com>
Co-authored-by: Jaworek <jaworekwiadomosci@gmail.com>
Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Tetie <tjvssr@gmail.com>
Co-authored-by: DraftMan <contact@draftman.fr>
Co-authored-by: Sugden <28943913+NotSugden@users.noreply.github.com>
Co-authored-by: Jaw0r3k <jaw0r3k.g@gmail.com>
Co-authored-by: space <spaceeec@yahoo.com>
Co-authored-by: pkdev08 <54294685+pkdev08@users.noreply.github.com>
Co-authored-by: MrMythicalYT <91077061+MrMythicalYT@users.noreply.github.com>
  • Loading branch information
12 people authored Apr 1, 2023
1 parent 51c3bf1 commit a51ddb2
Show file tree
Hide file tree
Showing 16 changed files with 117 additions and 58 deletions.
13 changes: 12 additions & 1 deletion scripts/generateRequires.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,18 @@ async function writeWebsocketHandlerImports() {
}

async function writeClientActionImports() {
const lines = ["'use strict';\n", 'class ActionsManager {', ' constructor(client) {', ' this.client = client;\n'];
const lines = [
"'use strict';\n",
'class ActionsManager {',
' constructor(client) {',
' this.client = client;\n',
' // These symbols represent fully built data that we inject at times when calling actions manually.',
' // Action#getUser for example, will return the injected data (which is assumed to be a built structure)',
' // instead of trying to make it from provided data',
" this.injectedUser = Symbol('djs.actions.injectedUser');",
" this.injectedChannel = Symbol('djs.actions.injectedChannel');",
" this.injectedMessage = Symbol('djs.actions.injectedMessage');\n",
];

const actionsDirectory = new URL('../src/client/actions', import.meta.url);
for (const file of (await readdir(actionsDirectory)).sort()) {
Expand Down
6 changes: 3 additions & 3 deletions src/client/actions/ActionsManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ class ActionsManager {
constructor(client) {
this.client = client;

// These symbols represent fully built data that we inject at times when calling actions manually. Action#getUser,
// for example, will return the injected data (which is assumed to be a built structure) instead of trying to make
// it from provided data
// These symbols represent fully built data that we inject at times when calling actions manually.
// Action#getUser for example, will return the injected data (which is assumed to be a built structure)
// instead of trying to make it from provided data
this.injectedUser = Symbol('djs.actions.injectedUser');
this.injectedChannel = Symbol('djs.actions.injectedChannel');
this.injectedMessage = Symbol('djs.actions.injectedMessage');
Expand Down
2 changes: 1 addition & 1 deletion src/client/websocket/handlers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,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')],
['GUILD_CREATE', require('./GUILD_CREATE')],
['GUILD_DELETE', require('./GUILD_DELETE')],
['GUILD_UPDATE', require('./GUILD_UPDATE')],
Expand Down Expand Up @@ -62,6 +61,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;
7 changes: 7 additions & 0 deletions src/managers/CachedManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]?.();
Expand Down
13 changes: 6 additions & 7 deletions src/managers/ThreadManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,9 @@ 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>
* @property {FetchArchivedThreadOptions} [archived] Options used to fetch archived threads
*/

/**
Expand All @@ -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);
}
Expand All @@ -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
*/
Expand All @@ -128,7 +127,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 {
Expand Down
4 changes: 1 addition & 3 deletions src/structures/AutocompleteInteraction.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,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,
});
Expand Down
12 changes: 7 additions & 5 deletions src/structures/ClientUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,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);
const newData = await this.client.api.users('@me').patch({ data });
this.client.token = newData.token;
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;
}

Expand Down
5 changes: 1 addition & 4 deletions src/structures/Guild.js
Original file line number Diff line number Diff line change
Expand Up @@ -716,9 +716,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;
Expand Down Expand Up @@ -1341,7 +1338,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() {
Expand Down
40 changes: 35 additions & 5 deletions src/structures/GuildMember.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ class GuildMember extends Base {
*/
this.communicationDisabledUntilTimestamp = null;

/**
* The role ids of the member
* @type {Snowflake[]}
* @private
*/
this._roles = [];
if (data) this._patch(data);
}
Expand Down Expand Up @@ -352,6 +357,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);
Expand Down Expand Up @@ -404,7 +419,7 @@ class GuildMember extends Base {
* .catch(console.error);
*/
ban(options) {
return this.guild.members.ban(this, options);
return this.guild.bans.create(this, options);
}

/**
Expand All @@ -418,6 +433,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);
Expand Down Expand Up @@ -494,12 +514,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;
Expand Down
13 changes: 9 additions & 4 deletions src/structures/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -599,11 +599,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;
}

Expand Down Expand Up @@ -645,12 +651,11 @@ class Message extends Base {
* channel.bulkDelete(messages.filter(message => message.bulkDeletable));
*/
get bulkDeletable() {
const permissions = this.channel?.permissionsFor(this.client.user);
return (
(this.inGuild() &&
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
);
}
Expand Down
12 changes: 12 additions & 0 deletions src/structures/Presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,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 });

/**
Expand Down Expand Up @@ -325,6 +331,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 });

/**
Expand Down
4 changes: 4 additions & 0 deletions src/structures/Role.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 14 additions & 4 deletions src/structures/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,22 @@ class User extends Base {
json.bannerURL = this.banner ? this.bannerURL() : this.banner;
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 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;
Expand Down
19 changes: 0 additions & 19 deletions src/structures/interfaces/TextBasedChannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,25 +136,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');
Expand Down
2 changes: 1 addition & 1 deletion src/util/BitField.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Loading

0 comments on commit a51ddb2

Please sign in to comment.