Merge branch 'master' into reply-prefix

This commit is contained in:
Crawl 2020-03-01 19:19:37 +01:00 committed by GitHub
commit 1a699f2718
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 179 additions and 55 deletions

View file

@ -41,12 +41,12 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
## Installation
**Node.js 11.0.0 or newer is required.**
**Node.js 12.0.0 or newer is required.**
Ignore any warnings about unmet peer dependencies, as they're all optional.
Without voice support: `npm install discordjs/discord.js`
With voice support ([@discordjs/opus](https://www.npmjs.com/package/@discordjs/opus)): `npm install discordjs/discord.js @discordjs/opus`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discordjs/discord.js opusscript`
Without voice support: `npm install discord.js`
With voice support ([@discordjs/opus](https://www.npmjs.com/package/@discordjs/opus)): `npm install discord.js @discordjs/opus`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discord.js opusscript`
### Audio engines

View file

@ -1,3 +1,5 @@
'use strict';
/**
* Send a user a link to their avatar
*/

View file

@ -1,3 +1,5 @@
'use strict';
/**
* A bot that welcomes new guild members when they join
*/

View file

@ -1,3 +1,5 @@
'use strict';
/**
* A ping pong bot, whenever you send "ping", it replies "pong".
*/

View file

@ -1,3 +1,5 @@
'use strict';
/**
* Send a message using a webhook
*/

View file

@ -4,7 +4,7 @@ These questions are some of the most frequently asked.
## No matter what, I get `SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode`
Update to Node.js 11.0.0 or newer.
Update to Node.js 12.0.0 or newer.
## How do I get voice working?

View file

@ -1,3 +1,9 @@
# Version 12.0.0
v12.0.0 contains many new and improved features, optimisations, and bug fixes.
See [the changelog](https://github.com/discordjs/discord.js/releases/tag/12.0.0) for a full list of changes.
You can also visit [the guide](https://discordjs.guide/additional-info/changes-in-v12.html) for help with updating your v11 code to v12.
# Version 11.1.0
v11.1.0 features improved voice and gateway stability, as well as support for new features such as audit logs and searching for messages.

View file

@ -21,9 +21,6 @@
Welcome to the discord.js v12 documentation.
v12 is still very much a work-in-progress, as we're aiming to make it the best it can possibly be before releasing.
Only use it if you are fond of living life on the bleeding edge.
## About
discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to easily interact with the
@ -36,12 +33,12 @@ discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to
## Installation
**Node.js 11.0.0 or newer is required.**
**Node.js 12.0.0 or newer is required.**
Ignore any warnings about unmet peer dependencies, as they're all optional.
Without voice support: `npm install discordjs/discord.js`
With voice support ([@discordjs/opus](https://www.npmjs.com/package/@discordjs/opus)): `npm install discordjs/discord.js @discordjs/opus`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discordjs/discord.js opusscript`
Without voice support: `npm install discord.js`
With voice support ([@discordjs/opus](https://www.npmjs.com/package/@discordjs/opus)): `npm install discord.js @discordjs/opus`
With voice support ([opusscript](https://www.npmjs.com/package/opusscript)): `npm install discord.js opusscript`
### Audio engines

View file

@ -1,6 +1,6 @@
{
"name": "discord.js",
"version": "12.0.0-dev",
"version": "12.0.0",
"description": "A powerful library for interacting with the Discord API",
"main": "./src/index",
"types": "./typings/index.d.ts",
@ -141,7 +141,9 @@
"*.ts": "prettier --write --single-quote --print-width 120 --trailing-comma all --end-of-line lf"
},
"commitlint": {
"extends": ["@commitlint/config-angular"],
"extends": [
"@commitlint/config-angular"
],
"rules": {
"type-enum": [
2,

View file

@ -17,6 +17,7 @@ const Webhook = require('../structures/Webhook');
const Collection = require('../util/Collection');
const { Events, browser, DefaultOptions } = require('../util/Constants');
const DataResolver = require('../util/DataResolver');
const Intents = require('../util/Intents');
const Permissions = require('../util/Permissions');
const Structures = require('../util/Structures');
@ -381,6 +382,9 @@ class Client extends BaseClient {
* @private
*/
_validateOptions(options = this.options) {
if (typeof options.ws.intents !== 'undefined') {
options.ws.intents = Intents.resolve(options.ws.intents);
}
if (typeof options.shardCount !== 'number' || isNaN(options.shardCount) || options.shardCount < 1) {
throw new TypeError('CLIENT_INVALID_OPTION', 'shardCount', 'a number greater than or equal to 1');
}
@ -415,9 +419,6 @@ class Client extends BaseClient {
if (typeof options.restSweepInterval !== 'number' || isNaN(options.restSweepInterval)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'restSweepInterval', 'a number');
}
if (!Array.isArray(options.disabledEvents)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'disabledEvents', 'an Array');
}
if (typeof options.retryLimit !== 'number' || isNaN(options.retryLimit)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'retryLimit', 'a number');
}

View file

@ -60,7 +60,8 @@ class PlayInterface {
* @returns {StreamDispatcher}
*/
play(resource, options = {}) {
if (resource instanceof Broadcast) {
const VoiceBroadcast = require('../VoiceBroadcast');
if (resource instanceof VoiceBroadcast) {
if (!this.player.playBroadcast) throw new Error('VOICE_PLAY_INTERFACE_NO_BROADCAST');
return this.player.playBroadcast(resource, options);
}
@ -91,6 +92,3 @@ class PlayInterface {
}
module.exports = PlayInterface;
// eslint-disable-next-line import/order
const Broadcast = require('../VoiceBroadcast');

View file

@ -382,7 +382,7 @@ class WebSocketManager extends EventEmitter {
});
}
if (packet && !this.client.options.disabledEvents.includes(packet.t) && PacketHandlers[packet.t]) {
if (packet && PacketHandlers[packet.t]) {
PacketHandlers[packet.t](this.client, packet, shard);
}

View file

@ -17,6 +17,8 @@ const Messages = {
SHARDING_INVALID: 'Invalid shard settings were provided.',
SHARDING_REQUIRED: 'This session would have handled too many guilds - Sharding is required.',
INVALID_INTENTS: 'Invalid intent provided for WebSocket intents.',
DISALLOWED_INTENTS: 'Privileged intent provided is not enabled or whitelisted.',
SHARDING_NO_SHARDS: 'No shards have been spawned.',
SHARDING_IN_PROCESS: 'Shards are still being spawned.',
SHARDING_ALREADY_SPAWNED: count => `Already spawned ${count} shards.`,

View file

@ -17,11 +17,11 @@ module.exports = {
Collection: require('./util/Collection'),
Constants: require('./util/Constants'),
DataResolver: require('./util/DataResolver'),
LimitedCollection: require('./util/LimitedCollection'),
BaseManager: require('./managers/BaseManager'),
DiscordAPIError: require('./rest/DiscordAPIError'),
HTTPError: require('./rest/HTTPError'),
MessageFlags: require('./util/MessageFlags'),
Intents: require('./util/Intents'),
Permissions: require('./util/Permissions'),
Speaking: require('./util/Speaking'),
Snowflake: require('./util/Snowflake'),

View file

@ -46,7 +46,7 @@ class GuildManager extends BaseManager {
/**
* Partial data for a Role.
* @typedef {Object} PartialRoleData
* @property {number} id The ID for this role, used to set channel overrides,
* @property {number} [id] The ID for this role, used to set channel overrides,
* this is a placeholder and will be replaced by the API after consumption
* @property {string} [name] The name of the role
* @property {ColorResolvable} [color] The color of the role, either a hex string or a base 10 number

View file

@ -21,7 +21,7 @@ class MessageManager extends BaseManager {
/**
* The cache of Messages
* @type {LimitedCollection<Snowflake, Message>}
* @type {Collection<Snowflake, Message>}
* @name MessageManager#cache
*/

View file

@ -220,7 +220,7 @@ class Shard extends EventEmitter {
* @param {string} prop Name of the client property to get, using periods for nesting
* @returns {Promise<*>}
* @example
* shard.fetchClientValue('guilds.size')
* shard.fetchClientValue('guilds.cache.size')
* .then(count => console.log(`${count} guilds in shard ${shard.id}`))
* .catch(console.error);
*/

View file

@ -212,7 +212,8 @@ class GuildMember extends Base {
}
/**
* Whether this member is manageable in terms of role hierarchy by the client user
* Whether the client user is above this user in the hierarchy, according to role position and guild ownership.
* This is a prerequisite for many moderative actions.
* @type {boolean}
* @readonly
*/

View file

@ -59,7 +59,7 @@ class TextBasedChannel {
* @property {MessageEmbed|Object} [embed] An embed for the message
* (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
* @property {'none' | 'all' | 'everyone'} [disableMentions=this.client.options.disableMentions] Whether or not
* all mentionsor everyone/here mentions should be sanitized to prevent unexpected mentions
* all mentions or everyone/here mentions should be sanitized to prevent unexpected mentions
* @property {FileOptions[]|BufferResolvable[]} [files] Files to send with the message
* @property {string|boolean} [code] Language for optional codeblock formatting to apply
* @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if

View file

@ -37,10 +37,6 @@ const browser = (exports.browser = typeof window !== 'undefined');
* (or 0 for never)
* @property {number} [retryLimit=1] How many times to retry on 5XX errors (Infinity for indefinite amount of retries)
* @property {PresenceData} [presence] Presence data to use upon login
* @property {WSEventType[]} [disabledEvents] An array of disabled websocket events. Events in this array will not be
* processed, potentially resulting in performance improvements for larger bots. Only disable events you are
* 100% certain you don't need, as many are important, but not obviously so. The safest one to disable with the
* most impact is typically `TYPING_START`.
* @property {WebsocketOptions} [ws] Options for the WebSocket
* @property {HTTPOptions} [http] HTTP options
*/
@ -60,7 +56,6 @@ exports.DefaultOptions = {
*/
replyPrefixer: null,
restWsBridgeTimeout: 5000,
disabledEvents: [],
restRequestTimeout: 15000,
retryLimit: 1,
restTimeOffset: 500,
@ -71,6 +66,7 @@ exports.DefaultOptions = {
* WebSocket options (these are left as snake_case to match the API)
* @typedef {Object} WebsocketOptions
* @property {number} [large_threshold=250] Number of members in a guild to be considered large
* @property {IntentsResolvable} [intents] Intents to enable for this connection
*/
ws: {
large_threshold: 250,
@ -108,6 +104,8 @@ exports.WSCodes = {
4004: 'TOKEN_INVALID',
4010: 'SHARDING_INVALID',
4011: 'SHARDING_REQUIRED',
4013: 'INVALID_INTENTS',
4014: 'DISALLOWED_INTENTS',
};
const AllowedImageFormats = ['webp', 'png', 'jpg', 'gif'];

76
src/util/Intents.js Normal file
View file

@ -0,0 +1,76 @@
'use strict';
const BitField = require('./BitField');
/**
* Data structure that makes it easy to calculate intents.
* @extends {BitField}
*/
class Intents extends BitField {}
/**
* Data that can be resolved to give a permission number. This can be:
* * A string (see {@link Intents.FLAGS})
* * An intents flag
* * An instance of Intents
* * An array of IntentsResolvable
* @typedef {string|number|Intents|IntentsResolvable[]} IntentsResolvable
*/
/**
* Numeric websocket intents. All available properties:
* * `GUILDS`
* * `GUILD_MEMBERS`
* * `GUILD_BANS`
* * `GUILD_EMOJIS`
* * `GUILD_INTEGRATIONS`
* * `GUILD_WEBHOOKS`
* * `GUILD_INVITES`
* * `GUILD_VOICE_STATES`
* * `GUILD_PRESENCES`
* * `GUILD_MESSAGES`
* * `GUILD_MESSAGE_REACTIONS`
* * `GUILD_MESSAGE_TYPING`
* * `DIRECT_MESSAGES`
* * `DIRECT_MESSAGE_REACTIONS`
* * `DIRECT_MESSAGE_TYPING`
* @type {Object}
* @see {@link https://discordapp.com/developers/docs/topics/gateway#list-of-intents}
*/
Intents.FLAGS = {
GUILDS: 1 << 0,
GUILD_MEMBERS: 1 << 1,
GUILD_BANS: 1 << 2,
GUILD_EMOJIS: 1 << 3,
GUILD_INTEGRATIONS: 1 << 4,
GUILD_WEBHOOKS: 1 << 5,
GUILD_INVITES: 1 << 6,
GUILD_VOICE_STATES: 1 << 7,
GUILD_PRESENCES: 1 << 8,
GUILD_MESSAGES: 1 << 9,
GUILD_MESSAGE_REACTIONS: 1 << 10,
GUILD_MESSAGE_TYPING: 1 << 11,
DIRECT_MESSAGES: 1 << 12,
DIRECT_MESSAGE_REACTIONS: 1 << 13,
DIRECT_MESSAGE_TYPING: 1 << 14,
};
/**
* Bitfield representing all privileged intents
* @type {number}
* @see {@link https://discordapp.com/developers/docs/topics/gateway#privileged-intents}
*/
Intents.PRIVILEGED = Intents.FLAGS.GUILD_MEMBERS | Intents.FLAGS.GUILD_PRESENCES;
/**
* Bitfield representing all intents combined
* @type {number}
*/
Intents.ALL = Object.values(Intents.FLAGS).reduce((acc, p) => acc | p, 0);
/**
* Bitfield representing all non-privileged intents
* @type {number}
*/
Intents.NON_PRIVILEGED = Intents.ALL & ~Intents.PRIVILEGED;
module.exports = Intents;

View file

@ -8,6 +8,7 @@ const Collection = require('./Collection.js');
* @extends {Collection}
* @param {number} [maxSize=0] The maximum size of the Collection
* @param {Iterable} [iterable=null] Optional entries passed to the Map constructor.
* @private
*/
class LimitedCollection extends Collection {
constructor(maxSize = 0, iterable = null) {
@ -24,6 +25,10 @@ class LimitedCollection extends Collection {
if (this.size >= this.maxSize && !this.has(key)) this.delete(this.firstKey());
return super.set(key, value);
}
static get [Symbol.species]() {
return Collection;
}
}
module.exports = LimitedCollection;

View file

@ -5,10 +5,10 @@ const Discord = require('../src');
const { Util } = Discord;
const client = new Discord.Client({
// To see a difference, comment out disableMentions and run the same tests using disableEveryone
// You will notice that all messages will mention @everyone
//disableEveryone: true
disableMentions: 'everyone'
// To see a difference, comment out disableMentions and run the same tests using disableEveryone
// You will notice that all messages will mention @everyone
// disableEveryone: true
disableMentions: 'everyone',
});
const tests = [

View file

@ -1,5 +1,7 @@
const Discord = require('../');
'use strict';
const { token } = require('./auth');
const Discord = require('../');
const sharder = new Discord.ShardingManager(`${process.cwd()}/test/shard.js`, { token, respawn: false });

62
typings/index.d.ts vendored
View file

@ -1,12 +1,12 @@
declare enum ChannelType {
text,
dm,
voice,
group,
category,
news,
store,
unknown,
text = 0,
dm = 1,
voice = 2,
group = 3,
category = 4,
news = 5,
store = 6,
unknown = 7,
}
declare module 'discord.js' {
@ -133,6 +133,7 @@ declare module 'discord.js' {
export class CategoryChannel extends GuildChannel {
public readonly children: Collection<Snowflake, GuildChannel>;
public type: 'category';
}
export class Channel extends Base {
@ -552,7 +553,6 @@ declare module 'discord.js' {
DARK_BUT_NOT_BLACK: 0x2c2f33;
NOT_QUITE_BLACK: 0x23272a;
};
VerificationLevels: ['None', 'Low', 'Medium', '(╯°□°)╯︵ ┻━┻', '┻━┻ ミヽ(ಠ益ಠ)ノ彡┻━┻'];
Status: {
READY: 0;
CONNECTING: 1;
@ -689,6 +689,7 @@ declare module 'discord.js' {
public messages: MessageManager;
public recipient: User;
public readonly partial: false;
public type: 'dm';
public fetch(): Promise<DMChannel>;
}
@ -861,6 +862,7 @@ declare module 'discord.js' {
public readonly permissionsLocked: boolean | null;
public readonly position: number;
public rawPosition: number;
public type: Exclude<keyof typeof ChannelType, 'dm' | 'group' | 'unknown'>;
public readonly viewable: boolean;
public clone(options?: GuildChannelCloneOptions): Promise<this>;
public createInvite(options?: InviteOptions): Promise<Invite>;
@ -977,6 +979,14 @@ declare module 'discord.js' {
public sync(): Promise<Integration>;
}
export class Intents extends BitField<IntentsString> {
public static FLAGS: Record<IntentsString, number>;
public static PRIVILEGED: number;
public static ALL: number;
public static NON_PRIVILEGED: number;
public static resolve(bit?: BitFieldResolvable<IntentsString>): number;
}
export class Invite extends Base {
constructor(client: Client, data: object);
public channel: GuildChannel | PartialGroupDMChannel;
@ -1211,6 +1221,7 @@ declare module 'discord.js' {
public messages: MessageManager;
public nsfw: boolean;
public topic: string | null;
public type: 'news';
public createWebhook(
name: string,
options?: { avatar?: BufferResolvable | Base64Resolvable; reason?: string },
@ -1546,6 +1557,7 @@ declare module 'discord.js' {
constructor(guild: Guild, data?: object);
public messages: MessageManager;
public nsfw: boolean;
public type: 'text';
public rateLimitPerUser: number;
public topic: string | null;
public createWebhook(
@ -1654,6 +1666,7 @@ declare module 'discord.js' {
public readonly full: boolean;
public readonly joinable: boolean;
public readonly speakable: boolean;
public type: 'voice';
public userLimit: number;
public join(): Promise<VoiceConnection>;
public leave(): void;
@ -1901,11 +1914,6 @@ declare module 'discord.js' {
public toJSON(): object;
}
export class LimitedCollection<K, V> extends Collection<K, V> {
public constructor(maxSize: number, iterable: Iterable<any>);
public maxSize: number;
}
//#endregion
//#region Managers
@ -2003,7 +2011,7 @@ declare module 'discord.js' {
export class MessageManager extends BaseManager<Snowflake, Message, MessageResolvable> {
constructor(channel: TextChannel | DMChannel, iterable?: Iterable<any>);
public channel: TextBasedChannelFields;
public cache: LimitedCollection<Snowflake, Message>;
public cache: Collection<Snowflake, Message>;
public fetch(message: Snowflake, cache?: boolean): Promise<Message>;
public fetch(options?: ChannelLogsQueryOptions, cache?: boolean): Promise<Collection<Snowflake, Message>>;
public fetchPinned(cache?: boolean): Promise<Collection<Snowflake, Message>>;
@ -2293,7 +2301,6 @@ declare module 'discord.js' {
restSweepInterval?: number;
retryLimit?: number;
presence?: PresenceData;
disabledEvents?: WSEventType[];
ws?: WebSocketOptions;
http?: HTTPOptions;
}
@ -2513,7 +2520,10 @@ declare module 'discord.js' {
interface GuildCreateChannelOptions {
permissionOverwrites?: OverwriteResolvable[] | Collection<Snowflake, OverwriteResolvable>;
topic?: string;
type?: Exclude<keyof typeof ChannelType, 'dm' | 'group' | 'unknown'>;
type?: Exclude<
keyof typeof ChannelType | ChannelType,
'dm' | 'group' | 'unknown' | ChannelType.dm | ChannelType.group | ChannelType.unknown
>;
nsfw?: boolean;
parent?: ChannelResolvable;
bitrate?: number;
@ -2623,6 +2633,23 @@ declare module 'discord.js' {
name: string;
}
type IntentsString =
| 'GUILDS'
| 'GUILD_MEMBERS'
| 'GUILD_BANS'
| 'GUILD_EMOJIS'
| 'GUILD_INTEGRATIONS'
| 'GUILD_WEBHOOKS'
| 'GUILD_INVITES'
| 'GUILD_VOICE_STATES'
| 'GUILD_PRESENCES'
| 'GUILD_MESSAGES'
| 'GUILD_MESSAGE_REACTIONS'
| 'GUILD_MESSAGE_TYPING'
| 'DIRECT_MESSAGES'
| 'DIRECT_MESSAGE_REACTIONS'
| 'DIRECT_MESSAGE_TYPING';
interface InviteOptions {
temporary?: boolean;
maxAge?: number;
@ -2981,6 +3008,7 @@ declare module 'discord.js' {
interface WebSocketOptions {
large_threshold?: number;
compress?: boolean;
intents?: BitFieldResolvable<IntentsString> | number;
}
type WSEventType =