diff --git a/docs/general/updating.md b/docs/general/updating.md index d1087207e..cfcc27220 100644 --- a/docs/general/updating.md +++ b/docs/general/updating.md @@ -1,21 +1,34 @@ # Version 11 +Version 11 contains loads of new and improved features, optimisations, and bug fixes. +See [the changelog](https://github.com/hydrabolt/discord.js/releases/tag/11.0.0) for a full list of changes. -**Significant Additions (see the changelog for a full list):** +## Significant additions * Message Reactions and Embeds (rich text) * Support for uws and erlpack for better performance * OAuthApplication support +* Web distributions -### 1) Client.login() no longer supports logging in with email + password -Logging in with an email or password has been discouraged previously, mainly because of [this](https://github.com/hammerandchisel/discord-api-docs/issues/69#issuecomment-223886862), however we have made the decision to now remove all email and password logins in v11. Instead, you can use authentication tokens. You can find your token for a self-bot by entering `CTRL+SHIFT+I` in the Discord application, entering the console tab and executing `localStorage.token`. As always, you can get your token for real bot accounts [here.](https://discordapp.com/developers/applications/me) +## Breaking changes +### Client.login() no longer supports logging in with email + password +Logging in with an email and password has always been heavily discouraged since the advent of proper token support, but in v11 we have made the decision to completely remove the functionality, since Hammer & Chisel have [officially stated](https://github.com/hammerandchisel/discord-api-docs/issues/69#issuecomment-223886862) it simply shouldn't be done. -### 2) ClientUser.setEmail()/setPassword() now require the current password, as well as setUsername() on user accounts -In order to change email, password or username on user accounts (self-bots), you need to now pass a password parameter to these methods (changes highlighted in documentation for ClientUser). +User accounts can still log in with tokens just like bot accounts. To obtain the token for a user account, you can log in to Discord with that account, and use Ctrl + Shift + I to open the developer tools. In the console tab, evaluating `localStorage.token` will give you the token for that account. -### 3) Removed TextBasedChannel.sendTTSMessage() -This method was redundant and has been removed as the same results can be achieved using sendMessage() +### ClientUser.setEmail()/setPassword() now require the current password, as well as setUsername() on user accounts +Since you can no longer log in with email and password, you must provide the current account password to the `setEmail()`, `setPassword()`, and `setUsername()` methods for user accounts (self-bots). -### 4) Using Collection.find()/exists() with IDs will throw an error -To find something or check its existence using an ID, you should now use `.get()` and `.has()` which are part of a normal [Map.](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map) +### Removed TextBasedChannel.sendTTSMessage() +This method was deemed to be an entirely pointless shortcut that virtually nobody even used. +The same results can be achieved by passing options to `send()` or `sendMessage()`. + +Example: +```js +channel.send('Hi there', { tts: true }); +``` + +### Using Collection.find()/exists() with IDs will throw an error +This is simply to help prevent a common mistake that is made frequently. +To find something or check its existence using an ID, you should use `.get()` and `.has()` which are part of the [ES6 Map class](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map), which Collection is an extension of. # Version 10 Version 10's non-BC changes focus on cleaning up some inconsistencies that exist in previous versions. diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index 6a8532141..67e19f9c2 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -268,7 +268,7 @@ class ClientUser extends User { */ createGuild(name, region, icon = null) { if (!icon) return this.client.rest.methods.createGuild({ name, icon, region }); - if (icon.startsWith('data:')) { + if (typeof icon === 'string' && icon.startsWith('data:')) { return this.client.rest.methods.createGuild({ name, icon, region }); } else { return this.client.resolver.resolveBuffer(icon).then(data => diff --git a/src/structures/DMChannel.js b/src/structures/DMChannel.js index cb6ceddbc..6af543dd6 100644 --- a/src/structures/DMChannel.js +++ b/src/structures/DMChannel.js @@ -45,6 +45,7 @@ class DMChannel extends Channel { fetchMessage() { return; } fetchMessages() { return; } fetchPinnedMessages() { return; } + search() { return; } startTyping() { return; } stopTyping() { return; } get typing() { return; } @@ -53,7 +54,6 @@ class DMChannel extends Channel { awaitMessages() { return; } bulkDelete() { return; } _cacheMessage() { return; } - search() { return; } } TextBasedChannel.applyToClass(DMChannel, true); diff --git a/src/structures/GroupDMChannel.js b/src/structures/GroupDMChannel.js index 4cdee3ccb..d6b5338d4 100644 --- a/src/structures/GroupDMChannel.js +++ b/src/structures/GroupDMChannel.js @@ -129,6 +129,7 @@ class GroupDMChannel extends Channel { fetchMessage() { return; } fetchMessages() { return; } fetchPinnedMessages() { return; } + search() { return; } startTyping() { return; } stopTyping() { return; } get typing() { return; } @@ -137,7 +138,6 @@ class GroupDMChannel extends Channel { awaitMessages() { return; } bulkDelete() { return; } _cacheMessage() { return; } - search() { return; } } TextBasedChannel.applyToClass(GroupDMChannel, true); diff --git a/src/structures/Guild.js b/src/structures/Guild.js index 472166fbc..aa0f717ab 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -362,6 +362,25 @@ class Guild { }); } + /** + * Performs a search within the entire guild. + * @param {MessageSearchOptions} [options={}] Options to pass to the search + * @returns {Promise>} + * An array containing arrays of messages. Each inner array is a search context cluster. + * The message which has triggered the result will have the `hit` property set to `true`. + * @example + * guild.search({ + * content: 'discord.js', + * before: '2016-11-17' + * }).then(res => { + * const hit = res[0].find(m => m.hit).content; + * console.log(`I found: **${hit}**`); + * }).catch(console.error); + */ + search(options) { + return this.client.rest.methods.search(this, options); + } + /** * The data for editing a guild * @typedef {Object} GuildEditData @@ -600,9 +619,48 @@ class Guild { return create.then(role => role.edit(data)); } + /** + * Set the position of a role in this guild + * @param {string|Role} role the role to edit, can be a role object or a role ID. + * @param {number} position the new position of the role + * @returns {Promise} + */ + setRolePosition(role, position) { + if (typeof role === 'string') { + role = this.roles.get(role); + if (!role) return Promise.reject(new Error('Supplied role is not a role or string.')); + } + + position = Number(position); + if (isNaN(position)) return Promise.reject(new Error('Supplied position is not a number.')); + + const lowestAffected = Math.min(role.position, position); + const highestAffected = Math.max(role.position, position); + + const rolesToUpdate = this.roles.filter(r => r.position >= lowestAffected && r.position <= highestAffected); + + // stop role positions getting stupidly inflated + if (position > role.position) { + position = rolesToUpdate.first().position; + } else { + position = rolesToUpdate.last().position; + } + + const updatedRoles = []; + + for (const uRole of rolesToUpdate.values()) { + updatedRoles.push({ + id: uRole.id, + position: uRole.id === role.id ? position : uRole.position + (position < role.position ? 1 : -1), + }); + } + + return this.client.rest.methods.setRolePositions(this.id, updatedRoles); + } + /** * Creates a new custom emoji in the guild. - * @param {BufferResolvable} attachment The image for the emoji. + * @param {BufferResolvable|Base64Resolvable} attachment The image for the emoji. * @param {string} name The name for the emoji. * @returns {Promise} The created emoji. * @example @@ -618,7 +676,7 @@ class Guild { */ createEmoji(attachment, name) { return new Promise(resolve => { - if (attachment.startsWith('data:')) { + if (typeof attachment === 'string' && attachment.startsWith('data:')) { resolve(this.client.rest.methods.createEmoji(this, attachment, name)); } else { this.client.resolver.resolveBuffer(attachment).then(data => @@ -664,66 +722,6 @@ class Guild { return this.client.rest.methods.deleteGuild(this); } - /** - * Set the position of a role in this guild - * @param {string|Role} role the role to edit, can be a role object or a role ID. - * @param {number} position the new position of the role - * @returns {Promise} - */ - setRolePosition(role, position) { - if (typeof role === 'string') { - role = this.roles.get(role); - if (!role) return Promise.reject(new Error('Supplied role is not a role or string.')); - } - - position = Number(position); - if (isNaN(position)) return Promise.reject(new Error('Supplied position is not a number.')); - - const lowestAffected = Math.min(role.position, position); - const highestAffected = Math.max(role.position, position); - - const rolesToUpdate = this.roles.filter(r => r.position >= lowestAffected && r.position <= highestAffected); - - // stop role positions getting stupidly inflated - if (position > role.position) { - position = rolesToUpdate.first().position; - } else { - position = rolesToUpdate.last().position; - } - - const updatedRoles = []; - - for (const uRole of rolesToUpdate.values()) { - updatedRoles.push({ - id: uRole.id, - position: uRole.id === role.id ? position : uRole.position + (position < role.position ? 1 : -1), - }); - } - - return this.client.rest.methods.setRolePositions(this.id, updatedRoles); - } - - /** - * Performs a search - * @param {MessageSearchOptions} [options={}] Options to pass to the search - * @returns {Promise>} - * An array containing arrays of messages. Each inner array is a search context cluster. - * The message which has triggered the result will have the `hit` property set to `true`. - * @example - * guild.search({ - * content: 'discord.js', - * before: '2016-11-17' - * }) - * .then(res => { - * const hit = res[0].find(m => m.hit).content; - * console.log(`I found: **${hit}**`); - * }) - * .catch(console.error); - */ - search(options) { - return this.client.rest.methods.search(this, options); - } - /** * Whether this Guild equals another Guild. It compares all properties, so for most operations * it is advisable to just compare `guild.id === guild2.id` as it is much faster and is often diff --git a/src/structures/TextChannel.js b/src/structures/TextChannel.js index 395289ae9..6e98e9e89 100644 --- a/src/structures/TextChannel.js +++ b/src/structures/TextChannel.js @@ -53,7 +53,7 @@ class TextChannel extends GuildChannel { /** * Create a webhook for the channel. * @param {string} name The name of the webhook. - * @param {BufferResolvable} avatar The avatar for the webhook. + * @param {BufferResolvable|Base64Resolvable} avatar The avatar for the webhook. * @returns {Promise} webhook The created webhook. * @example * channel.createWebhook('Snek', 'http://snek.s3.amazonaws.com/topSnek.png') @@ -62,7 +62,7 @@ class TextChannel extends GuildChannel { */ createWebhook(name, avatar) { return new Promise(resolve => { - if (avatar.startsWith('data:')) { + if (typeof avatar === 'string' && avatar.startsWith('data:')) { resolve(this.client.rest.methods.createWebhook(this, name, avatar)); } else { this.client.resolver.resolveBuffer(avatar).then(data => @@ -81,6 +81,7 @@ class TextChannel extends GuildChannel { fetchMessage() { return; } fetchMessages() { return; } fetchPinnedMessages() { return; } + search() { return; } startTyping() { return; } stopTyping() { return; } get typing() { return; } @@ -89,7 +90,6 @@ class TextChannel extends GuildChannel { awaitMessages() { return; } bulkDelete() { return; } _cacheMessage() { return; } - search() { return; } } TextBasedChannel.applyToClass(TextChannel, true); diff --git a/src/structures/interface/TextBasedChannel.js b/src/structures/interface/TextBasedChannel.js index 1249b57dc..dd763d7f0 100644 --- a/src/structures/interface/TextBasedChannel.js +++ b/src/structures/interface/TextBasedChannel.js @@ -214,7 +214,7 @@ class TextBasedChannel { } /** - * Performs a search + * Performs a search within the channel. * @param {MessageSearchOptions} [options={}] Options to pass to the search * @returns {Promise>} * An array containing arrays of messages. Each inner array is a search context cluster. @@ -223,12 +223,10 @@ class TextBasedChannel { * channel.search({ * content: 'discord.js', * before: '2016-11-17' - * }) - * .then(res => { + * }).then(res => { * const hit = res[0].find(m => m.hit).content; * console.log(`I found: **${hit}**`); - * }) - * .catch(console.error); + * }).catch(console.error); */ search(options) { return this.client.rest.methods.search(this, options);