Discord.js v13.8.0
This commit is contained in:
March 7th 2022-06-06 11:21:36 +07:00
parent 832bc010c5
commit 305ccaac90
19 changed files with 382 additions and 240 deletions

View File

@ -14,7 +14,7 @@
## About
<strong>Welcome to `discord.js-selfbot-v13@v2`, based on `discord.js@13.7.0`</strong>
<strong>Welcome to `discord.js-selfbot-v13@v2.2`, based on `discord.js@13.8.0`</strong>
- discord.js-selfbot-v13 is a [Node.js](https://nodejs.org) module that allows user accounts to interact with the Discord API v9.

File diff suppressed because one or more lines are too long

174
package-lock.json generated
View File

@ -1,17 +1,17 @@
{
"name": "discord.js-selfbot-v13",
"version": "2.1.0",
"version": "2.2.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "discord.js-selfbot-v13",
"version": "2.1.0",
"version": "2.2.0",
"license": "GNU General Public License v3.0",
"dependencies": {
"@discordjs/builders": "^0.13.0",
"@discordjs/collection": "^0.6.0",
"@discordjs/voice": "^0.9.0",
"@discordjs/builders": "^0.14.0",
"@discordjs/collection": "^0.7.0",
"@discordjs/voice": "^0.10.0",
"@sapphire/async-queue": "^1.3.1",
"@sapphire/snowflake": "^3.2.2",
"@types/node-fetch": "^2.6.1",
@ -21,10 +21,10 @@
"bignumber.js": "^9.0.2",
"bufferutil": "^4.0.6",
"chalk": "^4.1.2",
"discord-api-types": "^0.33.2",
"discord-api-types": "^0.33.3",
"discord-bettermarkdown": "^1.2.0",
"discord-rpc-contructor": "^1.1.5",
"discord.js": "^13.7.0",
"discord.js": "^13.8.0",
"form-data": "^4.0.0",
"i": "^0.3.7",
"json-bigint": "^1.0.0",
@ -1015,30 +1015,25 @@
}
},
"node_modules/@discordjs/builders": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.13.0.tgz",
"integrity": "sha512-4L9y26KRNNU8Y7J78SRUN1Uhava9D8jfit/YqEaKi8gQRc7PdqKqk2poybo6RXaiyt/BgKYPfcjxT7WvzGfYCA==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.14.0.tgz",
"integrity": "sha512-+fqLIqa9wN3R+kvlld8sgG0nt04BAZxdCDP4t2qZ9TJsquLWA+xMtT8Waibb3d4li4AQS+IOfjiHAznv/dhHgQ==",
"dependencies": {
"@sapphire/shapeshift": "^2.0.0",
"@sapphire/shapeshift": "^3.1.0",
"@sindresorhus/is": "^4.6.0",
"discord-api-types": "^0.31.1",
"discord-api-types": "^0.33.3",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.1",
"tslib": "^2.3.1"
"tslib": "^2.4.0"
},
"engines": {
"node": ">=16.9.0"
}
},
"node_modules/@discordjs/builders/node_modules/discord-api-types": {
"version": "0.31.2",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.31.2.tgz",
"integrity": "sha512-gpzXTvFVg7AjKVVJFH0oJGC0q0tO34iJGSHZNz9u3aqLxlD6LfxEs9wWVVikJqn9gra940oUTaPFizCkRDcEiA=="
},
"node_modules/@discordjs/collection": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.6.0.tgz",
"integrity": "sha512-Ieaetb36l0nmAS5X9Upqk4W7euAO6FdXPxn3I8vBAKEcoIzEZI1mcVcPfCfagGJZSgBKpENnAnKkP4GAn+MV8w==",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz",
"integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA==",
"engines": {
"node": ">=16.9.0"
}
@ -1666,26 +1661,21 @@
}
},
"node_modules/@discordjs/voice": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.9.0.tgz",
"integrity": "sha512-fSAYtPCEfIyG56hC2cRJuyfvQGr+aawSssLCqYg70vZ51dKO4spEKOB8V6vNMP5HnEplbhmxkB3YbshFKtnCQQ==",
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.10.0.tgz",
"integrity": "sha512-tChDKRRbKwWHcyq1dSEzrnuCMJyLtKXcFhgQpqcS2YjZ1TLlXboc/mhrR8jA1Yb1rB81u8dgj/lPkkHsk0ItEg==",
"dependencies": {
"@types/ws": "^8.5.3",
"discord-api-types": "^0.29.0",
"discord-api-types": "^0.33.3",
"prism-media": "^1.3.2",
"tiny-typed-emitter": "^2.1.0",
"tslib": "^2.3.1",
"ws": "^8.5.0"
"tslib": "^2.4.0",
"ws": "^8.7.0"
},
"engines": {
"node": ">=16.9.0"
}
},
"node_modules/@discordjs/voice/node_modules/discord-api-types": {
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.29.0.tgz",
"integrity": "sha512-Ekq1ICNpOTVajXKZguNFrsDeTmam+ZeA38txsNLZnANdXUjU6QBPIZLUQTC6MzigFGb0Tt8vk4xLnXmzv0shNg=="
},
"node_modules/@eslint/eslintrc": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
@ -2337,9 +2327,9 @@
}
},
"node_modules/@sapphire/shapeshift": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-2.1.0.tgz",
"integrity": "sha512-Z1ISLP9pNI3jpKJxx82xtDpEsfomhJ3iPqcKdYLjAHmQP2X5yqQF6phA0Wd+MmdXp081wepOiT59XEN0xdTVvQ==",
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.1.0.tgz",
"integrity": "sha512-PkxFXd3QJ1qAPS05Dy2UkVGYPm/asF1Ugt2Xyzmv4DHzO3+G7l+873C4XFFcJ9M5Je+eCMC7SSifgPTSur5QuA==",
"engines": {
"node": ">=v15.0.0",
"npm": ">=7.0.0"
@ -4435,9 +4425,9 @@
}
},
"node_modules/discord-api-types": {
"version": "0.33.2",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.2.tgz",
"integrity": "sha512-ZiYvIUAOQnN98+TRaXH8HZdaCofNZDoUad4piVE1lfjlM1E3LRaRIyZOs4eWcWU/gI0P8oTTXyXCjfqKzV7YDw=="
"version": "0.33.3",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.3.tgz",
"integrity": "sha512-P3A1RJXKEDmGPHrFTN5+gYLsBPGUVGj+D3+fa3m0K/umc+LMfqGuEad+p7cNq7ry/icReVhS3bz9jvBvne/BRA=="
},
"node_modules/discord-bettermarkdown": {
"version": "1.2.0",
@ -4457,30 +4447,25 @@
}
},
"node_modules/discord.js": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.7.0.tgz",
"integrity": "sha512-iV/An3FEB/CiBGdjWHRtgskM4UuWPq5vjhjKsrQhdVU16dbKrBxA+eIV2HWA07B3tXUGM6eco1wkr42gxxV1BA==",
"version": "13.8.0",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.8.0.tgz",
"integrity": "sha512-EPAA/2VLycYN5wSzavqa4iJ6qj3UtQFtHw5TH/60Fj29ymfEsCQVn//o1mTpwDxzwb+rPIrWhkxKIGGnjfv0Iw==",
"dependencies": {
"@discordjs/builders": "^0.13.0",
"@discordjs/collection": "^0.6.0",
"@discordjs/builders": "^0.14.0",
"@discordjs/collection": "^0.7.0",
"@sapphire/async-queue": "^1.3.1",
"@types/node-fetch": "^2.6.1",
"@types/ws": "^8.5.3",
"discord-api-types": "^0.30.0",
"discord-api-types": "^0.33.3",
"form-data": "^4.0.0",
"node-fetch": "^2.6.1",
"ws": "^8.6.0"
"ws": "^8.7.0"
},
"engines": {
"node": ">=16.6.0",
"npm": ">=7.0.0"
}
},
"node_modules/discord.js/node_modules/discord-api-types": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.30.0.tgz",
"integrity": "sha512-wYst0jrT8EJs2tVlwUTQ2xT0oWMjUrRMpFTkNY3NMleWyQNHgWaKhqFfxdLPdC2im9IuR5EsxcEgjhf/npeftw=="
},
"node_modules/dmd": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/dmd/-/dmd-4.0.6.tgz",
@ -13934,9 +13919,9 @@
}
},
"node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/tslint": {
"version": "6.1.3",
@ -15446,29 +15431,22 @@
}
},
"@discordjs/builders": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.13.0.tgz",
"integrity": "sha512-4L9y26KRNNU8Y7J78SRUN1Uhava9D8jfit/YqEaKi8gQRc7PdqKqk2poybo6RXaiyt/BgKYPfcjxT7WvzGfYCA==",
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.14.0.tgz",
"integrity": "sha512-+fqLIqa9wN3R+kvlld8sgG0nt04BAZxdCDP4t2qZ9TJsquLWA+xMtT8Waibb3d4li4AQS+IOfjiHAznv/dhHgQ==",
"requires": {
"@sapphire/shapeshift": "^2.0.0",
"@sapphire/shapeshift": "^3.1.0",
"@sindresorhus/is": "^4.6.0",
"discord-api-types": "^0.31.1",
"discord-api-types": "^0.33.3",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.1",
"tslib": "^2.3.1"
},
"dependencies": {
"discord-api-types": {
"version": "0.31.2",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.31.2.tgz",
"integrity": "sha512-gpzXTvFVg7AjKVVJFH0oJGC0q0tO34iJGSHZNz9u3aqLxlD6LfxEs9wWVVikJqn9gra940oUTaPFizCkRDcEiA=="
}
"tslib": "^2.4.0"
}
},
"@discordjs/collection": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.6.0.tgz",
"integrity": "sha512-Ieaetb36l0nmAS5X9Upqk4W7euAO6FdXPxn3I8vBAKEcoIzEZI1mcVcPfCfagGJZSgBKpENnAnKkP4GAn+MV8w=="
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.7.0.tgz",
"integrity": "sha512-R5i8Wb8kIcBAFEPLLf7LVBQKBDYUL+ekb23sOgpkpyGT+V4P7V83wTxcsqmX+PbqHt4cEHn053uMWfRqh/Z/nA=="
},
"@discordjs/docgen": {
"version": "0.11.1",
@ -15953,23 +15931,16 @@
}
},
"@discordjs/voice": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.9.0.tgz",
"integrity": "sha512-fSAYtPCEfIyG56hC2cRJuyfvQGr+aawSssLCqYg70vZ51dKO4spEKOB8V6vNMP5HnEplbhmxkB3YbshFKtnCQQ==",
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.10.0.tgz",
"integrity": "sha512-tChDKRRbKwWHcyq1dSEzrnuCMJyLtKXcFhgQpqcS2YjZ1TLlXboc/mhrR8jA1Yb1rB81u8dgj/lPkkHsk0ItEg==",
"requires": {
"@types/ws": "^8.5.3",
"discord-api-types": "^0.29.0",
"discord-api-types": "^0.33.3",
"prism-media": "^1.3.2",
"tiny-typed-emitter": "^2.1.0",
"tslib": "^2.3.1",
"ws": "^8.5.0"
},
"dependencies": {
"discord-api-types": {
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.29.0.tgz",
"integrity": "sha512-Ekq1ICNpOTVajXKZguNFrsDeTmam+ZeA38txsNLZnANdXUjU6QBPIZLUQTC6MzigFGb0Tt8vk4xLnXmzv0shNg=="
}
"tslib": "^2.4.0",
"ws": "^8.7.0"
}
},
"@eslint/eslintrc": {
@ -16483,9 +16454,9 @@
}
},
"@sapphire/shapeshift": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-2.1.0.tgz",
"integrity": "sha512-Z1ISLP9pNI3jpKJxx82xtDpEsfomhJ3iPqcKdYLjAHmQP2X5yqQF6phA0Wd+MmdXp081wepOiT59XEN0xdTVvQ=="
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.1.0.tgz",
"integrity": "sha512-PkxFXd3QJ1qAPS05Dy2UkVGYPm/asF1Ugt2Xyzmv4DHzO3+G7l+873C4XFFcJ9M5Je+eCMC7SSifgPTSur5QuA=="
},
"@sapphire/snowflake": {
"version": "3.2.2",
@ -18148,9 +18119,9 @@
}
},
"discord-api-types": {
"version": "0.33.2",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.2.tgz",
"integrity": "sha512-ZiYvIUAOQnN98+TRaXH8HZdaCofNZDoUad4piVE1lfjlM1E3LRaRIyZOs4eWcWU/gI0P8oTTXyXCjfqKzV7YDw=="
"version": "0.33.3",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.33.3.tgz",
"integrity": "sha512-P3A1RJXKEDmGPHrFTN5+gYLsBPGUVGj+D3+fa3m0K/umc+LMfqGuEad+p7cNq7ry/icReVhS3bz9jvBvne/BRA=="
},
"discord-bettermarkdown": {
"version": "1.2.0",
@ -18170,26 +18141,19 @@
}
},
"discord.js": {
"version": "13.7.0",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.7.0.tgz",
"integrity": "sha512-iV/An3FEB/CiBGdjWHRtgskM4UuWPq5vjhjKsrQhdVU16dbKrBxA+eIV2HWA07B3tXUGM6eco1wkr42gxxV1BA==",
"version": "13.8.0",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.8.0.tgz",
"integrity": "sha512-EPAA/2VLycYN5wSzavqa4iJ6qj3UtQFtHw5TH/60Fj29ymfEsCQVn//o1mTpwDxzwb+rPIrWhkxKIGGnjfv0Iw==",
"requires": {
"@discordjs/builders": "^0.13.0",
"@discordjs/collection": "^0.6.0",
"@discordjs/builders": "^0.14.0",
"@discordjs/collection": "^0.7.0",
"@sapphire/async-queue": "^1.3.1",
"@types/node-fetch": "^2.6.1",
"@types/ws": "^8.5.3",
"discord-api-types": "^0.30.0",
"discord-api-types": "^0.33.3",
"form-data": "^4.0.0",
"node-fetch": "^2.6.1",
"ws": "^8.6.0"
},
"dependencies": {
"discord-api-types": {
"version": "0.30.0",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.30.0.tgz",
"integrity": "sha512-wYst0jrT8EJs2tVlwUTQ2xT0oWMjUrRMpFTkNY3NMleWyQNHgWaKhqFfxdLPdC2im9IuR5EsxcEgjhf/npeftw=="
}
"ws": "^8.7.0"
}
},
"dmd": {
@ -25169,9 +25133,9 @@
}
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"tslint": {
"version": "6.1.3",

View File

@ -1,12 +1,12 @@
{
"name": "discord.js-selfbot-v13",
"version": "2.1.0",
"version": "2.2.0",
"description": "A unofficial discord.js fork for creating selfbots [Based on discord.js v13]",
"main": "./src/index.js",
"types": "./typings/index.d.ts",
"scripts": {
"all": "npm run test && npm run test:typescript && npm run fix:all && npm run build",
"test": "npm run lint:all && npm run lint:typings && npm run docs:test",
"all": "npm run lint:fix && npm run lint:typings:fix && npm run fix:all && npm run build",
"test": "npm run lint:all && npm run docs:test && npm run test:typescript",
"fix:all": "npm run lint:fix && npm run lint:typings:fix && npm run format",
"test:typescript": "tsc --noEmit && tsd",
"lint": "eslint .",
@ -51,9 +51,9 @@
},
"homepage": "https://github.com/aiko-chan-ai/discord.js-selfbot-v13#readme",
"dependencies": {
"@discordjs/builders": "^0.13.0",
"@discordjs/collection": "^0.6.0",
"@discordjs/voice": "^0.9.0",
"@discordjs/builders": "^0.14.0",
"@discordjs/collection": "^0.7.0",
"@discordjs/voice": "^0.10.0",
"@sapphire/async-queue": "^1.3.1",
"@sapphire/snowflake": "^3.2.2",
"@types/node-fetch": "^2.6.1",
@ -63,10 +63,10 @@
"bignumber.js": "^9.0.2",
"bufferutil": "^4.0.6",
"chalk": "^4.1.2",
"discord-api-types": "^0.33.2",
"discord-api-types": "^0.33.3",
"discord-bettermarkdown": "^1.2.0",
"discord-rpc-contructor": "^1.1.5",
"discord.js": "^13.7.0",
"discord.js": "^13.8.0",
"form-data": "^4.0.0",
"i": "^0.3.7",
"json-bigint": "^1.0.0",

View File

@ -10,7 +10,7 @@ class WebhooksUpdate extends Action {
/**
* Emitted whenever a channel has its webhooks changed.
* @event Client#webhookUpdate
* @param {TextChannel|NewsChannel} channel The channel that had a webhook update
* @param {TextChannel|NewsChannel|VoiceChannel} channel The channel that had a webhook update
*/
if (channel) client.emit(Events.WEBHOOKS_UPDATE, channel);
}

View File

@ -20,7 +20,7 @@ const BeforeReadyWhitelist = [
WSEvents.GUILD_MEMBER_REMOVE,
];
const UNRECOVERABLE_CLOSE_CODES = Object.keys(WSCodes).slice(1).map(Number);
const UNRECOVERABLE_CLOSE_CODES = Object.keys(WSCodes).slice(2).map(Number);
const UNRESUMABLE_CLOSE_CODES = [
RPCErrorCodes.UnknownError,
RPCErrorCodes.InvalidPermissions,
@ -216,13 +216,8 @@ class WebSocketManager extends EventEmitter {
this.shardQueue.add(shard);
if (shard.sessionId) {
this.debug('Session id is present, attempting an immediate reconnect...', shard);
this.reconnect();
} else {
shard.destroy({ reset: true, emit: false, log: false });
this.reconnect();
}
if (shard.sessionId) this.debug(`Session id is present, attempting an immediate reconnect...`, shard);
this.reconnect();
});
shard.on(ShardEvents.INVALID_SESSION, () => {
@ -352,6 +347,12 @@ class WebSocketManager extends EventEmitter {
} else if (packet) {
/* Debug mode */
// console.log(`Unhandled packet: ${packet.t}`, packet);
/**
* Emitted whenever a packet isn't handled.
* @event Client#unhandledPacket
* @param {Object} packet The packet (t: Event name, d: Data)
* @param {Number} shard The shard that received the packet (Auto = 0)
*/
this.client.emit(Events.UNHANDLED_PACKET, packet, shard);
}
return true;

View File

@ -1,9 +1,9 @@
'use strict';
const EventEmitter = require('node:events');
const { setTimeout, setInterval } = require('node:timers');
const { setTimeout, setInterval, clearTimeout } = require('node:timers');
const WebSocket = require('../../WebSocket');
const { Status, Events, ShardEvents, Opcodes, WSEvents } = require('../../util/Constants');
const { Status, Events, ShardEvents, Opcodes, WSEvents, WSCodes } = require('../../util/Constants');
const Intents = require('../../util/Intents');
const STATUS_KEYS = Object.keys(Status);
@ -81,6 +81,13 @@ class WebSocketShard extends EventEmitter {
*/
this.lastHeartbeatAcked = true;
/**
* Used to prevent calling {@link WebSocketShard#event:close} twice while closing or terminating the WebSocket.
* @type {boolean}
* @private
*/
this.closeEmitted = false;
/**
* Contains the rate limit queue and metadata
* @name WebSocketShard#ratelimit
@ -126,6 +133,14 @@ class WebSocketShard extends EventEmitter {
*/
Object.defineProperty(this, 'helloTimeout', { value: null, writable: true });
/**
* The WebSocket timeout.
* @name WebSocketShard#wsCloseTimeout
* @type {?NodeJS.Timeout}
* @private
*/
Object.defineProperty(this, 'wsCloseTimeout', { value: null, writable: true });
/**
* If the manager attached its event handlers on the shard
* @name WebSocketShard#eventsAttached
@ -250,10 +265,11 @@ class WebSocketShard extends EventEmitter {
this.status = this.status === Status.DISCONNECTED ? Status.RECONNECTING : Status.CONNECTING;
this.setHelloTimeout();
this.setWsCloseTimeout(-1);
this.connectedAt = Date.now();
const ws = (this.connection = WebSocket.create(gateway, wsQuery));
// Adding a handshake timeout to just make sure no zombie connection appears.
const ws = (this.connection = WebSocket.create(gateway, wsQuery, { handshakeTimeout: 30_000 }));
ws.onopen = this.onOpen.bind(this);
ws.onmessage = this.onMessage.bind(this);
ws.onerror = this.onError.bind(this);
@ -340,21 +356,39 @@ class WebSocketShard extends EventEmitter {
* @private
*/
onClose(event) {
this.closeEmitted = true;
if (this.sequence !== -1) this.closeSequence = this.sequence;
this.sequence = -1;
this.setHeartbeatTimer(-1);
this.setHelloTimeout(-1);
// Clearing the WebSocket close timeout as close was emitted.
this.setWsCloseTimeout(-1);
// If we still have a connection object, clean up its listeners
if (this.connection) {
this._cleanupConnection();
// Having this after _cleanupConnection to just clean up the connection and not listen to ws.onclose
this.destroy({ reset: true, emit: false, log: false });
}
this.status = Status.DISCONNECTED;
this.emitClose(event);
}
/**
* This method is responsible to emit close event for this shard.
* This method helps the shard reconnect.
* @param {CloseEvent} [event] Close event that was received
*/
emitClose(
event = {
code: 1011,
reason: WSCodes[1011],
wasClean: false,
},
) {
this.debug(`[CLOSE]
Event Code: ${event.code}
Clean : ${event.wasClean}
Reason : ${event.reason ?? 'No reason received'}`);
this.setHeartbeatTimer(-1);
this.setHelloTimeout(-1);
// If we still have a connection object, clean up its listeners
if (this.connection) this._cleanupConnection();
this.status = Status.DISCONNECTED;
/**
* Emitted when a shard's WebSocket closes.
* @private
@ -523,6 +557,47 @@ class WebSocketShard extends EventEmitter {
}, 20_000).unref();
}
/**
* Sets the WebSocket Close timeout.
* This method is responsible for detecting any zombie connections if the WebSocket fails to close properly.
* @param {number} [time] If set to -1, it will clear the timeout
* @private
*/
setWsCloseTimeout(time) {
if (this.wsCloseTimeout) {
this.debug('[WebSocket] Clearing the close timeout.');
clearTimeout(this.wsCloseTimeout);
}
if (time === -1) {
this.wsCloseTimeout = null;
return;
}
this.wsCloseTimeout = setTimeout(() => {
this.setWsCloseTimeout(-1);
this.debug(`[WebSocket] Close Emitted: ${this.closeEmitted}`);
// Check if close event was emitted.
if (this.closeEmitted) {
this.debug(
`[WebSocket] was closed. | WS State: ${
CONNECTION_STATE[this.connection?.readyState ?? WebSocket.CLOSED]
} | Close Emitted: ${this.closeEmitted}`,
);
// Setting the variable false to check for zombie connections.
this.closeEmitted = false;
return;
}
this.debug(
// eslint-disable-next-line max-len
`[WebSocket] did not close properly, assuming a zombie connection.\nEmitting close and reconnecting again.`,
);
this.emitClose();
// Setting the variable false to check for zombie connections.
this.closeEmitted = false;
}, time).unref();
}
/**
* Sets the heartbeat timer for this shard.
* @param {number} time If -1, clears the interval, any other number sets an interval
@ -564,7 +639,7 @@ class WebSocketShard extends EventEmitter {
Connection State: ${this.connection ? CONNECTION_STATE[this.connection.readyState] : 'No Connection??'}`,
);
this.destroy({ closeCode: 4009, reset: true });
this.destroy({ reset: true, closeCode: 4009 });
return;
}
@ -723,22 +798,30 @@ class WebSocketShard extends EventEmitter {
// Step 0: Remove all timers
this.setHeartbeatTimer(-1);
this.setHelloTimeout(-1);
this.debug(
`[WebSocket] Destroy: Attempting to close the WebSocket. | WS State: ${
CONNECTION_STATE[this.connection?.readyState ?? WebSocket.CLOSED]
}`,
);
// Step 1: Close the WebSocket connection, if any, otherwise, emit DESTROYED
if (this.connection) {
// If the connection is currently opened, we will (hopefully) receive close
if (this.connection.readyState === WebSocket.OPEN) {
this.connection.close(closeCode);
this.debug(`[WebSocket] Close: Tried closing. | WS State: ${CONNECTION_STATE[this.connection.readyState]}`);
} else {
// Connection is not OPEN
this.debug(`WS State: ${CONNECTION_STATE[this.connection.readyState]}`);
// Remove listeners from the connection
this._cleanupConnection();
// Attempt to close the connection just in case
try {
this.connection.close(closeCode);
} catch {
// No-op
} catch (err) {
this.debug(
`[WebSocket] Close: Something went wrong while closing the WebSocket: ${
err.message || err
}. Forcefully terminating the connection | WS State: ${CONNECTION_STATE[this.connection.readyState]}`,
);
this.connection.terminate();
}
// Emit the destroyed event if needed
if (emit) this._emitDestroyed();
@ -748,6 +831,15 @@ class WebSocketShard extends EventEmitter {
this._emitDestroyed();
}
if (this.connection?.readyState === WebSocket.CLOSING || this.connection?.readyState === WebSocket.CLOSED) {
this.closeEmitted = false;
this.debug(
`[WebSocket] Adding a WebSocket close timeout to ensure a correct WS reconnect.
Timeout: ${this.manager.client.options.closeTimeout}ms`,
);
this.setWsCloseTimeout(this.manager.client.options.closeTimeout);
}
// Step 2: Null the connection object
this.connection = null;

View File

@ -308,7 +308,7 @@ class GuildChannelManager extends CachedManager {
channel,
position,
relative,
this.guild._sortedChannels(this),
this.guild._sortedChannels(channel),
this.client.api.guilds(this.guild.id).channels,
reason,
);

View File

@ -89,16 +89,6 @@ class BaseGuildTextChannel extends GuildChannel {
return this.edit({ defaultAutoArchiveDuration }, reason);
}
/**
* Sets whether this channel is flagged as NSFW.
* @param {boolean} [nsfw=true] Whether the channel should be considered NSFW
* @param {string} [reason] Reason for changing the channel's NSFW flag
* @returns {Promise<TextChannel>}
*/
setNSFW(nsfw = true, reason) {
return this.edit({ nsfw }, reason);
}
/**
* Sets the type of this channel (only conversion between text and news is supported)
* @param {string} type The new channel type
@ -109,44 +99,6 @@ class BaseGuildTextChannel extends GuildChannel {
return this.edit({ type }, reason);
}
/**
* Fetches all webhooks for the channel.
* @returns {Promise<Collection<Snowflake, Webhook>>}
* @example
* // Fetch webhooks
* channel.fetchWebhooks()
* .then(hooks => console.log(`This channel has ${hooks.size} hooks`))
* .catch(console.error);
*/
fetchWebhooks() {
return this.guild.channels.fetchWebhooks(this.id);
}
/**
* Options used to create a {@link Webhook} in a {@link TextChannel} or a {@link NewsChannel}.
* @typedef {Object} ChannelWebhookCreateOptions
* @property {?(BufferResolvable|Base64Resolvable)} [avatar] Avatar for the webhook
* @property {string} [reason] Reason for creating the webhook
*/
/**
* Creates a webhook for the channel.
* @param {string} name The name of the webhook
* @param {ChannelWebhookCreateOptions} [options] Options for creating the webhook
* @returns {Promise<Webhook>} Returns the created Webhook
* @example
* // Create a webhook for the current channel
* channel.createWebhook('Snek', {
* avatar: 'https://i.imgur.com/mI8XcpG.jpg',
* reason: 'Needed a cool new Webhook'
* })
* .then(console.log)
* .catch(console.error)
*/
createWebhook(name, options = {}) {
return this.guild.channels.createWebhook(this.id, name, options);
}
/**
* Sets a new topic for the guild channel.
* @param {?string} topic The new topic for the guild channel
@ -221,6 +173,10 @@ class BaseGuildTextChannel extends GuildChannel {
createMessageComponentCollector() {}
awaitMessageComponent() {}
bulkDelete() {}
fetchWebhooks() {}
createWebhook() {}
setRateLimitPerUser() {}
setNSFW() {}
}
TextBasedChannel.applyToClass(BaseGuildTextChannel, true);

View File

@ -96,6 +96,8 @@ class DMChannel extends Channel {
createMessageComponentCollector() {}
awaitMessageComponent() {}
// Doesn't work on DM channels; bulkDelete() {}
// Doesn't work on DM channels; setRateLimitPerUser() {}
// Doesn't work on DM channels; setNSFW() {}
// Testing feature: Call
// URL: https://discord.com/api/v9/channels/DMchannelId/call/ring
/**
@ -157,6 +159,12 @@ class DMChannel extends Channel {
}
}
TextBasedChannel.applyToClass(DMChannel, true, ['bulkDelete']);
TextBasedChannel.applyToClass(DMChannel, true, [
'bulkDelete',
'fetchWebhooks',
'createWebhook',
'setRateLimitPerUser',
'setNSFW',
]);
module.exports = DMChannel;

View File

@ -388,7 +388,7 @@ class Message extends Base {
/**
* The channel that the message was sent in
* @type {TextChannel|DMChannel|NewsChannel|ThreadChannel}
* @type {TextBasedChannel}
* @readonly
*/
get channel() {

View File

@ -309,7 +309,7 @@ module.exports = MessagePayload;
/**
* A target for a message.
* @typedef {TextChannel|DMChannel|User|GuildMember|Webhook|WebhookClient|Interaction|InteractionWebhook|
* @typedef {TextBasedChannel|DMChannel|User|GuildMember|Webhook|WebhookClient|Interaction|InteractionWebhook|
* Message|MessageManager} MessageTarget
*/

View File

@ -264,6 +264,12 @@ class PartialGroupDMChannel extends Channel {
}
}
TextBasedChannel.applyToClass(PartialGroupDMChannel, false);
TextBasedChannel.applyToClass(PartialGroupDMChannel, true, [
'bulkDelete',
'fetchWebhooks',
'createWebhook',
'setRateLimitPerUser',
'setNSFW',
]);
module.exports = PartialGroupDMChannel;

View File

@ -545,8 +545,10 @@ class ThreadChannel extends Channel {
createMessageComponentCollector() {}
awaitMessageComponent() {}
bulkDelete() {}
// Doesn't work on Thread channels; setRateLimitPerUser() {}
// Doesn't work on Thread channels; setNSFW() {}
}
TextBasedChannel.applyToClass(ThreadChannel, true);
TextBasedChannel.applyToClass(ThreadChannel, true, ['setRateLimitPerUser', 'setNSFW']);
module.exports = ThreadChannel;

View File

@ -2,6 +2,8 @@
const process = require('node:process');
const BaseGuildVoiceChannel = require('./BaseGuildVoiceChannel');
const TextBasedChannel = require('./interfaces/TextBasedChannel');
const MessageManager = require('../managers/MessageManager');
const { VideoQualityModes } = require('../util/Constants');
const Permissions = require('../util/Permissions');
@ -10,8 +12,21 @@ let deprecationEmittedForEditable = false;
/**
* Represents a guild voice channel on Discord.
* @extends {BaseGuildVoiceChannel}
* @implements {TextBasedChannel}
*/
class VoiceChannel extends BaseGuildVoiceChannel {
constructor(guild, data, client) {
super(guild, data, client, false);
/**
* A manager of the messages sent to this channel
* @type {MessageManager}
*/
this.messages = new MessageManager(this);
this._patch(data);
}
_patch(data) {
super._patch(data);
@ -20,10 +35,30 @@ class VoiceChannel extends BaseGuildVoiceChannel {
* The camera video quality mode of the channel.
* @type {?VideoQualityMode}
*/
this.videoQualityMode = VideoQualityModes[data.videoQualityMode];
this.videoQualityMode = VideoQualityModes[data.video_quality_mode];
} else {
this.videoQualityMode ??= null;
}
if ('last_message_id' in data) {
/**
* The last message id sent in the channel, if one was sent
* @type {?Snowflake}
*/
this.lastMessageId = data.last_message_id;
}
if ('messages' in data) {
for (const message of data.messages) this.messages._add(message);
}
if ('rate_limit_per_user' in data) {
/**
* The rate limit per user (slowmode) for this channel in seconds
* @type {number}
*/
this.rateLimitPerUser = data.rate_limit_per_user;
}
}
/**
@ -112,6 +147,21 @@ class VoiceChannel extends BaseGuildVoiceChannel {
return this.edit({ videoQualityMode }, reason);
}
// These are here only for documentation purposes - they are implemented by TextBasedChannel
/* eslint-disable no-empty-function */
get lastMessage() {}
send() {}
sendTyping() {}
createMessageCollector() {}
awaitMessages() {}
createMessageComponentCollector() {}
awaitMessageComponent() {}
bulkDelete() {}
fetchWebhooks() {}
createWebhook() {}
setRateLimitPerUser() {}
setNSFW() {}
/**
* Sets the RTC region of the channel.
* @name VoiceChannel#setRTCRegion
@ -127,4 +177,6 @@ class VoiceChannel extends BaseGuildVoiceChannel {
*/
}
TextBasedChannel.applyToClass(VoiceChannel, true, ['lastPinAt']);
module.exports = VoiceChannel;

View File

@ -331,6 +331,64 @@ class TextBasedChannel {
throw new TypeError('MESSAGE_BULK_DELETE_TYPE');
}
/**
* Fetches all webhooks for the channel.
* @returns {Promise<Collection<Snowflake, Webhook>>}
* @example
* // Fetch webhooks
* channel.fetchWebhooks()
* .then(hooks => console.log(`This channel has ${hooks.size} hooks`))
* .catch(console.error);
*/
fetchWebhooks() {
return this.guild.channels.fetchWebhooks(this.id);
}
/**
* Options used to create a {@link Webhook} in a guild text-based channel.
* @typedef {Object} ChannelWebhookCreateOptions
* @property {?(BufferResolvable|Base64Resolvable)} [avatar] Avatar for the webhook
* @property {string} [reason] Reason for creating the webhook
*/
/**
* Creates a webhook for the channel.
* @param {string} name The name of the webhook
* @param {ChannelWebhookCreateOptions} [options] Options for creating the webhook
* @returns {Promise<Webhook>} Returns the created Webhook
* @example
* // Create a webhook for the current channel
* channel.createWebhook('Snek', {
* avatar: 'https://i.imgur.com/mI8XcpG.jpg',
* reason: 'Needed a cool new Webhook'
* })
* .then(console.log)
* .catch(console.error)
*/
createWebhook(name, options = {}) {
return this.guild.channels.createWebhook(this.id, name, options);
}
/**
* Sets the rate limit per user (slowmode) for this channel.
* @param {number} rateLimitPerUser The new rate limit in seconds
* @param {string} [reason] Reason for changing the channel's rate limit
* @returns {Promise<this>}
*/
setRateLimitPerUser(rateLimitPerUser, reason) {
return this.edit({ rateLimitPerUser }, reason);
}
/**
* Sets whether this channel is flagged as NSFW.
* @param {boolean} [nsfw=true] Whether the channel should be considered NSFW
* @param {string} [reason] Reason for changing the channel's NSFW flag
* @returns {Promise<this>}
*/
setNSFW(nsfw = true, reason) {
return this.edit({ nsfw }, reason);
}
/**
* Send Slash to this channel
* @param {Snowflake} botId Bot Id
@ -385,6 +443,10 @@ class TextBasedChannel {
'awaitMessages',
'createMessageComponentCollector',
'awaitMessageComponent',
'fetchWebhooks',
'createWebhook',
'setRateLimitPerUser',
'setNSFW',
);
}
for (const prop of props) {

View File

@ -87,6 +87,7 @@ exports.randomUA = () => listUserAgent[Math.floor(Math.random() * listUserAgent.
exports.WSCodes = {
1000: 'WS_CLOSE_REQUESTED',
1011: 'INTERNAL_ERROR',
4004: 'TOKEN_INVALID',
4010: 'SHARDING_INVALID',
4011: 'SHARDING_REQUIRED',
@ -677,7 +678,8 @@ exports.ChannelTypes = createEnum([
* * TextChannel
* * NewsChannel
* * ThreadChannel
* @typedef {DMChannel|TextChannel|NewsChannel|ThreadChannel} TextBasedChannels
* * VoiceChannel
* @typedef {DMChannel|TextChannel|NewsChannel|ThreadChannel|VoiceChannel} TextBasedChannels
*/
/**
@ -695,6 +697,7 @@ exports.ChannelTypes = createEnum([
* * GUILD_NEWS_THREAD
* * GUILD_PUBLIC_THREAD
* * GUILD_PRIVATE_THREAD
* * GUILD_VOICE
* @typedef {string} TextBasedChannelTypes
*/
exports.TextBasedChannelTypes = [
@ -704,6 +707,7 @@ exports.TextBasedChannelTypes = [
'GUILD_NEWS_THREAD',
'GUILD_PUBLIC_THREAD',
'GUILD_PRIVATE_THREAD',
'GUILD_VOICE',
];
/**
@ -1156,6 +1160,7 @@ exports.ApplicationCommandTypes = createEnum([null, 'CHAT_INPUT', 'USER', 'MESSA
* * ROLE
* * MENTIONABLE
* * NUMBER
* * ATTACHMENT
* @typedef {string} ApplicationCommandOptionType
* @see {@link https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-type}
*/

View File

@ -34,6 +34,8 @@ const JSONBig = require('json-bigint');
* @property {number|number[]|string} [shards] The shard's id to run, or an array of shard ids. If not specified,
* the client will spawn {@link ClientOptions#shardCount} shards. If set to `auto`, it will fetch the
* recommended amount of shards from Discord and spawn that amount
* @property {number} [closeTimeout=5000] The amount of time in milliseconds to wait for the close frame to be received
* from the WebSocket. Don't have this too high/low. Its best to have it between 2_000-6_000 ms.
* @property {boolean} [checkUpdate=true] Check for module updates at startup
* @property {boolean} [readyStatus=true] Sync state with Discord Client
* @property {boolean} [autoCookie=true] Automatically add Cookies to Request on startup
@ -138,6 +140,7 @@ class Options extends null {
static createDefault() {
return {
jsonTransformer: object => JSONBig.stringify(object),
closeTimeout: 5_000,
checkUpdate: true,
readyStatus: true,
autoCookie: true,

75
typings/index.d.ts vendored
View File

@ -443,26 +443,23 @@ export class BaseGuildEmoji extends Emoji {
export class BaseGuildTextChannel extends TextBasedChannelMixin(GuildChannel) {
protected constructor(guild: Guild, data?: RawGuildChannelData, client?: Client, immediatePatch?: boolean);
public defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
public messages: MessageManager;
public rateLimitPerUser: number | null;
public nsfw: boolean;
public threads: ThreadManager<AllowedThreadTypeForTextChannel | AllowedThreadTypeForNewsChannel>;
public topic: string | null;
public createInvite(options?: CreateInviteOptions): Promise<Invite>;
public createWebhook(name: string, options?: ChannelWebhookCreateOptions): Promise<Webhook>;
public fetchInvites(cache?: boolean): Promise<Collection<string, Invite>>;
public setDefaultAutoArchiveDuration(
defaultAutoArchiveDuration: ThreadAutoArchiveDuration | 'MAX',
reason?: string,
): Promise<this>;
public setNSFW(nsfw?: boolean, reason?: string): Promise<this>;
public setTopic(topic: string | null, reason?: string): Promise<this>;
public setType(type: Pick<typeof ChannelTypes, 'GUILD_TEXT'>, reason?: string): Promise<TextChannel>;
public setType(type: Pick<typeof ChannelTypes, 'GUILD_NEWS'>, reason?: string): Promise<NewsChannel>;
public fetchWebhooks(): Promise<Collection<Snowflake, Webhook>>;
}
export class BaseGuildVoiceChannel extends GuildChannel {
protected constructor(guild: Guild, data?: RawGuildChannelData);
public constructor(guild: Guild, data?: RawGuildChannelData);
public readonly members: Collection<Snowflake, GuildMember>;
public readonly full: boolean;
public readonly joinable: boolean;
@ -936,9 +933,14 @@ export class DiscordAPIError extends Error {
public requestData: HTTPErrorData;
}
export class DMChannel extends TextBasedChannelMixin(Channel, ['bulkDelete']) {
export class DMChannel extends TextBasedChannelMixin(Channel, [
'bulkDelete',
'fetchWebhooks',
'createWebhook',
'setRateLimitPerUser',
'setNSFW',
]) {
private constructor(client: Client, data?: RawDMChannelData);
public messages: MessageManager;
public recipient: User;
public type: 'DM';
public fetch(force?: boolean): Promise<this>;
@ -2056,7 +2058,13 @@ export class OAuth2Guild extends BaseGuild {
public permissions: Readonly<Permissions>;
}
export class PartialGroupDMChannel extends TextBasedChannelMixin(Channel, ['bulkDelete']) {
export class PartialGroupDMChannel extends TextBasedChannelMixin(Channel, [
'bulkDelete',
'fetchWebhooks',
'createWebhook',
'setRateLimitPerUser',
'setNSFW',
]) {
private constructor(client: Client, data: RawPartialGroupDMChannelData);
public name: string | null;
public icon: string | null;
@ -2537,7 +2545,6 @@ export class TextChannel extends BaseGuildTextChannel {
public rateLimitPerUser: number;
public threads: ThreadManager<AllowedThreadTypeForTextChannel>;
public type: 'GUILD_TEXT';
public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise<TextChannel>;
public sendSlash(botID: Snowflake, commandName: string, args?: Options[]): Promise<undefined>;
}
@ -2563,7 +2570,7 @@ export class TextInputComponent extends BaseMessageComponent {
public static resolveStyle(style: TextInputStyleResolvable): TextInputStyle;
}
export class ThreadChannel extends TextBasedChannelMixin(Channel) {
export class ThreadChannel extends TextBasedChannelMixin(Channel, ['fetchWebhooks', 'createWebhook', 'setNSFW']) {
private constructor(guild: Guild, data?: RawThreadChannelData, client?: Client, fromInteraction?: boolean);
public archived: boolean | null;
public readonly archivedAt: Date | null;
@ -2585,7 +2592,6 @@ export class ThreadChannel extends TextBasedChannelMixin(Channel) {
public readonly sendable: boolean;
public memberCount: number | null;
public messageCount: number | null;
public messages: MessageManager;
public members: ThreadMemberManager;
public name: string;
public ownerId: Snowflake | null;
@ -2618,7 +2624,6 @@ export class ThreadChannel extends TextBasedChannelMixin(Channel) {
public setInvitable(invitable?: boolean, reason?: string): Promise<ThreadChannel>;
public setLocked(locked?: boolean, reason?: string): Promise<ThreadChannel>;
public setName(name: string, reason?: string): Promise<ThreadChannel>;
public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise<ThreadChannel>;
}
export class ThreadMember extends Base {
@ -2777,12 +2782,13 @@ export class Formatters extends null {
public static userMention: typeof userMention;
}
export class VoiceChannel extends BaseGuildVoiceChannel {
export class VoiceChannel extends TextBasedChannelMixin(BaseGuildVoiceChannel, ['lastPinTimestamp', 'lastPinAt']) {
public videoQualityMode: VideoQualityMode | null;
/** @deprecated Use manageable instead */
public readonly editable: boolean;
public readonly speakable: boolean;
public type: 'GUILD_VOICE';
public rateLimitPerUser: number | null;
public setBitrate(bitrate: number, reason?: string): Promise<VoiceChannel>;
public setUserLimit(userLimit: number, reason?: string): Promise<VoiceChannel>;
public setVideoQualityMode(videoQualityMode: VideoQualityMode | number, reason?: string): Promise<VoiceChannel>;
@ -2914,6 +2920,8 @@ export class WebSocketShard extends EventEmitter {
private eventsAttached: boolean;
private expectedGuilds: Set<Snowflake> | null;
private readyTimeout: NodeJS.Timeout | null;
private closeEmitted: boolean;
private wsCloseTimeout: NodeJS.Timeout | null;
public manager: WebSocketManager;
public id: number;
@ -2929,6 +2937,7 @@ export class WebSocketShard extends EventEmitter {
private onPacket(packet: unknown): void;
private checkReady(): void;
private setHelloTimeout(time?: number): void;
private setWsCloseTimeout(time?: number): void;
private setHeartbeatTimer(time: number): void;
private sendHeartbeat(): void;
private ackHeartbeat(): void;
@ -2938,6 +2947,7 @@ export class WebSocketShard extends EventEmitter {
private _send(data: unknown): void;
private processQueue(): void;
private destroy(destroyOptions?: { closeCode?: number; reset?: boolean; emit?: boolean; log?: boolean }): void;
private emitClose(event?: CloseEvent): void;
private _cleanupConnection(): void;
private _emitDestroyed(): void;
@ -3089,9 +3099,12 @@ export const Constants: {
};
WSCodes: {
1000: 'WS_CLOSE_REQUESTED';
1011: 'INTERNAL_ERROR';
4004: 'TOKEN_INVALID';
4010: 'SHARDING_INVALID';
4011: 'SHARDING_REQUIRED';
4013: 'INVALID_INTENTS';
4014: 'DISALLOWED_INTENTS';
};
Events: ConstantsEvents;
ShardEvents: ConstantsShardEvents;
@ -3679,6 +3692,7 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields {
readonly lastMessage: Message | null;
lastPinTimestamp: number | null;
readonly lastPinAt: Date | null;
messages: MessageManager;
awaitMessageComponent<T extends MessageComponentTypeResolvable = 'ACTION_ROW'>(
options?: AwaitMessageCollectorOptionsParams<T, true>,
): Promise<MappedInteractionTypes[T]>;
@ -3691,6 +3705,10 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields {
options?: MessageChannelCollectorOptionsParams<T, true>,
): InteractionCollector<MappedInteractionTypes[T]>;
createMessageCollector(options?: MessageCollectorOptions): MessageCollector;
createWebhook(name: string, options?: ChannelWebhookCreateOptions): Promise<Webhook>;
setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise<this>;
setNSFW(nsfw?: boolean, reason?: string): Promise<this>;
fetchWebhooks(): Promise<Collection<Snowflake, Webhook>>;
sendTyping(): Promise<void>;
}
@ -4017,7 +4035,7 @@ export interface ClientEvents extends BaseClientEvents {
userUpdate: [oldUser: User | PartialUser, newUser: User];
userSettingsUpdate: [setting: RawUserSettingsData];
voiceStateUpdate: [oldState: VoiceState, newState: VoiceState];
webhookUpdate: [channel: TextChannel | NewsChannel];
webhookUpdate: [channel: TextChannel | NewsChannel | VoiceChannel];
/** @deprecated Use interactionCreate instead */
interaction: [interaction: Interaction];
interactionCreate: [interaction: Interaction | { nonce: Snowflake; id: Snowflake }];
@ -4192,6 +4210,7 @@ export interface RawUserSettingsData {
export interface ClientOptions {
shards?: number | number[] | 'auto';
shardCount?: number;
closeTimeout?: number;
makeCache?: CacheFactory;
/** @deprecated Pass the value of this property as `lifetime` to `sweepers.messages` instead. */
messageCacheLifetime?: number;
@ -4596,34 +4615,6 @@ export interface ClientFetchInviteOptions {
guildScheduledEventId?: Snowflake;
}
export interface ClientOptions {
shards?: number | number[] | 'auto';
shardCount?: number;
makeCache?: CacheFactory;
/** @deprecated Pass the value of this property as `lifetime` to `sweepers.messages` instead. */
messageCacheLifetime?: number;
/** @deprecated Pass the value of this property as `interval` to `sweepers.messages` instead. */
messageSweepInterval?: number;
allowedMentions?: MessageMentionOptions;
invalidRequestWarningInterval?: number;
partials?: PartialTypes[];
restWsBridgeTimeout?: number;
restTimeOffset?: number;
restRequestTimeout?: number;
restGlobalRateLimit?: number;
restSweepInterval?: number;
retryLimit?: number;
failIfNotExists?: boolean;
userAgentSuffix?: string[];
presence?: PresenceData;
intents?: BitFieldResolvable<IntentsString, number>;
waitGuildTimeout?: number;
sweepers?: SweeperOptions;
ws?: WebSocketOptions;
http?: HTTPOptions;
rejectOnRateLimit?: string[] | ((data: RateLimitData) => boolean | Promise<boolean>);
}
export type ClientPresenceStatus = 'online' | 'idle' | 'dnd';
export interface ClientPresenceStatusData {