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

View File

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

View File

@ -10,7 +10,7 @@ class WebhooksUpdate extends Action {
/** /**
* Emitted whenever a channel has its webhooks changed. * Emitted whenever a channel has its webhooks changed.
* @event Client#webhookUpdate * @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); if (channel) client.emit(Events.WEBHOOKS_UPDATE, channel);
} }

View File

@ -20,7 +20,7 @@ const BeforeReadyWhitelist = [
WSEvents.GUILD_MEMBER_REMOVE, 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 = [ const UNRESUMABLE_CLOSE_CODES = [
RPCErrorCodes.UnknownError, RPCErrorCodes.UnknownError,
RPCErrorCodes.InvalidPermissions, RPCErrorCodes.InvalidPermissions,
@ -216,13 +216,8 @@ class WebSocketManager extends EventEmitter {
this.shardQueue.add(shard); this.shardQueue.add(shard);
if (shard.sessionId) { if (shard.sessionId) this.debug(`Session id is present, attempting an immediate reconnect...`, shard);
this.debug('Session id is present, attempting an immediate reconnect...', shard);
this.reconnect(); this.reconnect();
} else {
shard.destroy({ reset: true, emit: false, log: false });
this.reconnect();
}
}); });
shard.on(ShardEvents.INVALID_SESSION, () => { shard.on(ShardEvents.INVALID_SESSION, () => {
@ -352,6 +347,12 @@ class WebSocketManager extends EventEmitter {
} else if (packet) { } else if (packet) {
/* Debug mode */ /* Debug mode */
// console.log(`Unhandled packet: ${packet.t}`, packet); // 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); this.client.emit(Events.UNHANDLED_PACKET, packet, shard);
} }
return true; return true;

View File

@ -1,9 +1,9 @@
'use strict'; 'use strict';
const EventEmitter = require('node:events'); const EventEmitter = require('node:events');
const { setTimeout, setInterval } = require('node:timers'); const { setTimeout, setInterval, clearTimeout } = require('node:timers');
const WebSocket = require('../../WebSocket'); 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 Intents = require('../../util/Intents');
const STATUS_KEYS = Object.keys(Status); const STATUS_KEYS = Object.keys(Status);
@ -81,6 +81,13 @@ class WebSocketShard extends EventEmitter {
*/ */
this.lastHeartbeatAcked = true; 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 * Contains the rate limit queue and metadata
* @name WebSocketShard#ratelimit * @name WebSocketShard#ratelimit
@ -126,6 +133,14 @@ class WebSocketShard extends EventEmitter {
*/ */
Object.defineProperty(this, 'helloTimeout', { value: null, writable: true }); 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 * If the manager attached its event handlers on the shard
* @name WebSocketShard#eventsAttached * @name WebSocketShard#eventsAttached
@ -250,10 +265,11 @@ class WebSocketShard extends EventEmitter {
this.status = this.status === Status.DISCONNECTED ? Status.RECONNECTING : Status.CONNECTING; this.status = this.status === Status.DISCONNECTED ? Status.RECONNECTING : Status.CONNECTING;
this.setHelloTimeout(); this.setHelloTimeout();
this.setWsCloseTimeout(-1);
this.connectedAt = Date.now(); 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.onopen = this.onOpen.bind(this);
ws.onmessage = this.onMessage.bind(this); ws.onmessage = this.onMessage.bind(this);
ws.onerror = this.onError.bind(this); ws.onerror = this.onError.bind(this);
@ -340,21 +356,39 @@ class WebSocketShard extends EventEmitter {
* @private * @private
*/ */
onClose(event) { onClose(event) {
this.closeEmitted = true;
if (this.sequence !== -1) this.closeSequence = this.sequence; if (this.sequence !== -1) this.closeSequence = this.sequence;
this.sequence = -1; 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] this.debug(`[CLOSE]
Event Code: ${event.code} Event Code: ${event.code}
Clean : ${event.wasClean} Clean : ${event.wasClean}
Reason : ${event.reason ?? 'No reason received'}`); 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. * Emitted when a shard's WebSocket closes.
* @private * @private
@ -523,6 +557,47 @@ class WebSocketShard extends EventEmitter {
}, 20_000).unref(); }, 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. * Sets the heartbeat timer for this shard.
* @param {number} time If -1, clears the interval, any other number sets an interval * @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??'}`, Connection State: ${this.connection ? CONNECTION_STATE[this.connection.readyState] : 'No Connection??'}`,
); );
this.destroy({ closeCode: 4009, reset: true }); this.destroy({ reset: true, closeCode: 4009 });
return; return;
} }
@ -723,22 +798,30 @@ class WebSocketShard extends EventEmitter {
// Step 0: Remove all timers // Step 0: Remove all timers
this.setHeartbeatTimer(-1); this.setHeartbeatTimer(-1);
this.setHelloTimeout(-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 // Step 1: Close the WebSocket connection, if any, otherwise, emit DESTROYED
if (this.connection) { if (this.connection) {
// If the connection is currently opened, we will (hopefully) receive close // If the connection is currently opened, we will (hopefully) receive close
if (this.connection.readyState === WebSocket.OPEN) { if (this.connection.readyState === WebSocket.OPEN) {
this.connection.close(closeCode); this.connection.close(closeCode);
this.debug(`[WebSocket] Close: Tried closing. | WS State: ${CONNECTION_STATE[this.connection.readyState]}`);
} else { } else {
// Connection is not OPEN // Connection is not OPEN
this.debug(`WS State: ${CONNECTION_STATE[this.connection.readyState]}`); this.debug(`WS State: ${CONNECTION_STATE[this.connection.readyState]}`);
// Remove listeners from the connection
this._cleanupConnection();
// Attempt to close the connection just in case // Attempt to close the connection just in case
try { try {
this.connection.close(closeCode); this.connection.close(closeCode);
} catch { } catch (err) {
// No-op 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 // Emit the destroyed event if needed
if (emit) this._emitDestroyed(); if (emit) this._emitDestroyed();
@ -748,6 +831,15 @@ class WebSocketShard extends EventEmitter {
this._emitDestroyed(); 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 // Step 2: Null the connection object
this.connection = null; this.connection = null;

View File

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

View File

@ -89,16 +89,6 @@ class BaseGuildTextChannel extends GuildChannel {
return this.edit({ defaultAutoArchiveDuration }, reason); 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) * Sets the type of this channel (only conversion between text and news is supported)
* @param {string} type The new channel type * @param {string} type The new channel type
@ -109,44 +99,6 @@ class BaseGuildTextChannel extends GuildChannel {
return this.edit({ type }, reason); 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. * Sets a new topic for the guild channel.
* @param {?string} topic The 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() {} createMessageComponentCollector() {}
awaitMessageComponent() {} awaitMessageComponent() {}
bulkDelete() {} bulkDelete() {}
fetchWebhooks() {}
createWebhook() {}
setRateLimitPerUser() {}
setNSFW() {}
} }
TextBasedChannel.applyToClass(BaseGuildTextChannel, true); TextBasedChannel.applyToClass(BaseGuildTextChannel, true);

View File

@ -96,6 +96,8 @@ class DMChannel extends Channel {
createMessageComponentCollector() {} createMessageComponentCollector() {}
awaitMessageComponent() {} awaitMessageComponent() {}
// Doesn't work on DM channels; bulkDelete() {} // Doesn't work on DM channels; bulkDelete() {}
// Doesn't work on DM channels; setRateLimitPerUser() {}
// Doesn't work on DM channels; setNSFW() {}
// Testing feature: Call // Testing feature: Call
// URL: https://discord.com/api/v9/channels/DMchannelId/call/ring // 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; module.exports = DMChannel;

View File

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

View File

@ -309,7 +309,7 @@ module.exports = MessagePayload;
/** /**
* A target for a message. * 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 * 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; module.exports = PartialGroupDMChannel;

View File

@ -545,8 +545,10 @@ class ThreadChannel extends Channel {
createMessageComponentCollector() {} createMessageComponentCollector() {}
awaitMessageComponent() {} awaitMessageComponent() {}
bulkDelete() {} 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; module.exports = ThreadChannel;

View File

@ -2,6 +2,8 @@
const process = require('node:process'); const process = require('node:process');
const BaseGuildVoiceChannel = require('./BaseGuildVoiceChannel'); const BaseGuildVoiceChannel = require('./BaseGuildVoiceChannel');
const TextBasedChannel = require('./interfaces/TextBasedChannel');
const MessageManager = require('../managers/MessageManager');
const { VideoQualityModes } = require('../util/Constants'); const { VideoQualityModes } = require('../util/Constants');
const Permissions = require('../util/Permissions'); const Permissions = require('../util/Permissions');
@ -10,8 +12,21 @@ let deprecationEmittedForEditable = false;
/** /**
* Represents a guild voice channel on Discord. * Represents a guild voice channel on Discord.
* @extends {BaseGuildVoiceChannel} * @extends {BaseGuildVoiceChannel}
* @implements {TextBasedChannel}
*/ */
class VoiceChannel extends BaseGuildVoiceChannel { 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) { _patch(data) {
super._patch(data); super._patch(data);
@ -20,10 +35,30 @@ class VoiceChannel extends BaseGuildVoiceChannel {
* The camera video quality mode of the channel. * The camera video quality mode of the channel.
* @type {?VideoQualityMode} * @type {?VideoQualityMode}
*/ */
this.videoQualityMode = VideoQualityModes[data.videoQualityMode]; this.videoQualityMode = VideoQualityModes[data.video_quality_mode];
} else { } else {
this.videoQualityMode ??= null; 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); 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. * Sets the RTC region of the channel.
* @name VoiceChannel#setRTCRegion * @name VoiceChannel#setRTCRegion
@ -127,4 +177,6 @@ class VoiceChannel extends BaseGuildVoiceChannel {
*/ */
} }
TextBasedChannel.applyToClass(VoiceChannel, true, ['lastPinAt']);
module.exports = VoiceChannel; module.exports = VoiceChannel;

View File

@ -331,6 +331,64 @@ class TextBasedChannel {
throw new TypeError('MESSAGE_BULK_DELETE_TYPE'); 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 * Send Slash to this channel
* @param {Snowflake} botId Bot Id * @param {Snowflake} botId Bot Id
@ -385,6 +443,10 @@ class TextBasedChannel {
'awaitMessages', 'awaitMessages',
'createMessageComponentCollector', 'createMessageComponentCollector',
'awaitMessageComponent', 'awaitMessageComponent',
'fetchWebhooks',
'createWebhook',
'setRateLimitPerUser',
'setNSFW',
); );
} }
for (const prop of props) { for (const prop of props) {

View File

@ -87,6 +87,7 @@ exports.randomUA = () => listUserAgent[Math.floor(Math.random() * listUserAgent.
exports.WSCodes = { exports.WSCodes = {
1000: 'WS_CLOSE_REQUESTED', 1000: 'WS_CLOSE_REQUESTED',
1011: 'INTERNAL_ERROR',
4004: 'TOKEN_INVALID', 4004: 'TOKEN_INVALID',
4010: 'SHARDING_INVALID', 4010: 'SHARDING_INVALID',
4011: 'SHARDING_REQUIRED', 4011: 'SHARDING_REQUIRED',
@ -677,7 +678,8 @@ exports.ChannelTypes = createEnum([
* * TextChannel * * TextChannel
* * NewsChannel * * NewsChannel
* * ThreadChannel * * 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_NEWS_THREAD
* * GUILD_PUBLIC_THREAD * * GUILD_PUBLIC_THREAD
* * GUILD_PRIVATE_THREAD * * GUILD_PRIVATE_THREAD
* * GUILD_VOICE
* @typedef {string} TextBasedChannelTypes * @typedef {string} TextBasedChannelTypes
*/ */
exports.TextBasedChannelTypes = [ exports.TextBasedChannelTypes = [
@ -704,6 +707,7 @@ exports.TextBasedChannelTypes = [
'GUILD_NEWS_THREAD', 'GUILD_NEWS_THREAD',
'GUILD_PUBLIC_THREAD', 'GUILD_PUBLIC_THREAD',
'GUILD_PRIVATE_THREAD', 'GUILD_PRIVATE_THREAD',
'GUILD_VOICE',
]; ];
/** /**
@ -1156,6 +1160,7 @@ exports.ApplicationCommandTypes = createEnum([null, 'CHAT_INPUT', 'USER', 'MESSA
* * ROLE * * ROLE
* * MENTIONABLE * * MENTIONABLE
* * NUMBER * * NUMBER
* * ATTACHMENT
* @typedef {string} ApplicationCommandOptionType * @typedef {string} ApplicationCommandOptionType
* @see {@link https://discord.com/developers/docs/interactions/application-commands#application-command-object-application-command-option-type} * @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, * @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 * 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 * 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} [checkUpdate=true] Check for module updates at startup
* @property {boolean} [readyStatus=true] Sync state with Discord Client * @property {boolean} [readyStatus=true] Sync state with Discord Client
* @property {boolean} [autoCookie=true] Automatically add Cookies to Request on startup * @property {boolean} [autoCookie=true] Automatically add Cookies to Request on startup
@ -138,6 +140,7 @@ class Options extends null {
static createDefault() { static createDefault() {
return { return {
jsonTransformer: object => JSONBig.stringify(object), jsonTransformer: object => JSONBig.stringify(object),
closeTimeout: 5_000,
checkUpdate: true, checkUpdate: true,
readyStatus: true, readyStatus: true,
autoCookie: 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) { export class BaseGuildTextChannel extends TextBasedChannelMixin(GuildChannel) {
protected constructor(guild: Guild, data?: RawGuildChannelData, client?: Client, immediatePatch?: boolean); protected constructor(guild: Guild, data?: RawGuildChannelData, client?: Client, immediatePatch?: boolean);
public defaultAutoArchiveDuration?: ThreadAutoArchiveDuration; public defaultAutoArchiveDuration?: ThreadAutoArchiveDuration;
public messages: MessageManager; public rateLimitPerUser: number | null;
public nsfw: boolean; public nsfw: boolean;
public threads: ThreadManager<AllowedThreadTypeForTextChannel | AllowedThreadTypeForNewsChannel>; public threads: ThreadManager<AllowedThreadTypeForTextChannel | AllowedThreadTypeForNewsChannel>;
public topic: string | null; public topic: string | null;
public createInvite(options?: CreateInviteOptions): Promise<Invite>; public createInvite(options?: CreateInviteOptions): Promise<Invite>;
public createWebhook(name: string, options?: ChannelWebhookCreateOptions): Promise<Webhook>;
public fetchInvites(cache?: boolean): Promise<Collection<string, Invite>>; public fetchInvites(cache?: boolean): Promise<Collection<string, Invite>>;
public setDefaultAutoArchiveDuration( public setDefaultAutoArchiveDuration(
defaultAutoArchiveDuration: ThreadAutoArchiveDuration | 'MAX', defaultAutoArchiveDuration: ThreadAutoArchiveDuration | 'MAX',
reason?: string, reason?: string,
): Promise<this>; ): Promise<this>;
public setNSFW(nsfw?: boolean, reason?: string): Promise<this>;
public setTopic(topic: string | null, 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_TEXT'>, reason?: string): Promise<TextChannel>;
public setType(type: Pick<typeof ChannelTypes, 'GUILD_NEWS'>, reason?: string): Promise<NewsChannel>; public setType(type: Pick<typeof ChannelTypes, 'GUILD_NEWS'>, reason?: string): Promise<NewsChannel>;
public fetchWebhooks(): Promise<Collection<Snowflake, Webhook>>;
} }
export class BaseGuildVoiceChannel extends GuildChannel { 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 members: Collection<Snowflake, GuildMember>;
public readonly full: boolean; public readonly full: boolean;
public readonly joinable: boolean; public readonly joinable: boolean;
@ -936,9 +933,14 @@ export class DiscordAPIError extends Error {
public requestData: HTTPErrorData; 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); private constructor(client: Client, data?: RawDMChannelData);
public messages: MessageManager;
public recipient: User; public recipient: User;
public type: 'DM'; public type: 'DM';
public fetch(force?: boolean): Promise<this>; public fetch(force?: boolean): Promise<this>;
@ -2056,7 +2058,13 @@ export class OAuth2Guild extends BaseGuild {
public permissions: Readonly<Permissions>; 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); private constructor(client: Client, data: RawPartialGroupDMChannelData);
public name: string | null; public name: string | null;
public icon: string | null; public icon: string | null;
@ -2537,7 +2545,6 @@ export class TextChannel extends BaseGuildTextChannel {
public rateLimitPerUser: number; public rateLimitPerUser: number;
public threads: ThreadManager<AllowedThreadTypeForTextChannel>; public threads: ThreadManager<AllowedThreadTypeForTextChannel>;
public type: 'GUILD_TEXT'; public type: 'GUILD_TEXT';
public setRateLimitPerUser(rateLimitPerUser: number, reason?: string): Promise<TextChannel>;
public sendSlash(botID: Snowflake, commandName: string, args?: Options[]): Promise<undefined>; 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; 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); private constructor(guild: Guild, data?: RawThreadChannelData, client?: Client, fromInteraction?: boolean);
public archived: boolean | null; public archived: boolean | null;
public readonly archivedAt: Date | null; public readonly archivedAt: Date | null;
@ -2585,7 +2592,6 @@ export class ThreadChannel extends TextBasedChannelMixin(Channel) {
public readonly sendable: boolean; public readonly sendable: boolean;
public memberCount: number | null; public memberCount: number | null;
public messageCount: number | null; public messageCount: number | null;
public messages: MessageManager;
public members: ThreadMemberManager; public members: ThreadMemberManager;
public name: string; public name: string;
public ownerId: Snowflake | null; public ownerId: Snowflake | null;
@ -2618,7 +2624,6 @@ export class ThreadChannel extends TextBasedChannelMixin(Channel) {
public setInvitable(invitable?: boolean, reason?: string): Promise<ThreadChannel>; public setInvitable(invitable?: boolean, reason?: string): Promise<ThreadChannel>;
public setLocked(locked?: boolean, reason?: string): Promise<ThreadChannel>; public setLocked(locked?: boolean, reason?: string): Promise<ThreadChannel>;
public setName(name: string, 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 { export class ThreadMember extends Base {
@ -2777,12 +2782,13 @@ export class Formatters extends null {
public static userMention: typeof userMention; public static userMention: typeof userMention;
} }
export class VoiceChannel extends BaseGuildVoiceChannel { export class VoiceChannel extends TextBasedChannelMixin(BaseGuildVoiceChannel, ['lastPinTimestamp', 'lastPinAt']) {
public videoQualityMode: VideoQualityMode | null; public videoQualityMode: VideoQualityMode | null;
/** @deprecated Use manageable instead */ /** @deprecated Use manageable instead */
public readonly editable: boolean; public readonly editable: boolean;
public readonly speakable: boolean; public readonly speakable: boolean;
public type: 'GUILD_VOICE'; public type: 'GUILD_VOICE';
public rateLimitPerUser: number | null;
public setBitrate(bitrate: number, reason?: string): Promise<VoiceChannel>; public setBitrate(bitrate: number, reason?: string): Promise<VoiceChannel>;
public setUserLimit(userLimit: number, reason?: string): Promise<VoiceChannel>; public setUserLimit(userLimit: number, reason?: string): Promise<VoiceChannel>;
public setVideoQualityMode(videoQualityMode: VideoQualityMode | 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 eventsAttached: boolean;
private expectedGuilds: Set<Snowflake> | null; private expectedGuilds: Set<Snowflake> | null;
private readyTimeout: NodeJS.Timeout | null; private readyTimeout: NodeJS.Timeout | null;
private closeEmitted: boolean;
private wsCloseTimeout: NodeJS.Timeout | null;
public manager: WebSocketManager; public manager: WebSocketManager;
public id: number; public id: number;
@ -2929,6 +2937,7 @@ export class WebSocketShard extends EventEmitter {
private onPacket(packet: unknown): void; private onPacket(packet: unknown): void;
private checkReady(): void; private checkReady(): void;
private setHelloTimeout(time?: number): void; private setHelloTimeout(time?: number): void;
private setWsCloseTimeout(time?: number): void;
private setHeartbeatTimer(time: number): void; private setHeartbeatTimer(time: number): void;
private sendHeartbeat(): void; private sendHeartbeat(): void;
private ackHeartbeat(): void; private ackHeartbeat(): void;
@ -2938,6 +2947,7 @@ export class WebSocketShard extends EventEmitter {
private _send(data: unknown): void; private _send(data: unknown): void;
private processQueue(): void; private processQueue(): void;
private destroy(destroyOptions?: { closeCode?: number; reset?: boolean; emit?: boolean; log?: boolean }): void; private destroy(destroyOptions?: { closeCode?: number; reset?: boolean; emit?: boolean; log?: boolean }): void;
private emitClose(event?: CloseEvent): void;
private _cleanupConnection(): void; private _cleanupConnection(): void;
private _emitDestroyed(): void; private _emitDestroyed(): void;
@ -3089,9 +3099,12 @@ export const Constants: {
}; };
WSCodes: { WSCodes: {
1000: 'WS_CLOSE_REQUESTED'; 1000: 'WS_CLOSE_REQUESTED';
1011: 'INTERNAL_ERROR';
4004: 'TOKEN_INVALID'; 4004: 'TOKEN_INVALID';
4010: 'SHARDING_INVALID'; 4010: 'SHARDING_INVALID';
4011: 'SHARDING_REQUIRED'; 4011: 'SHARDING_REQUIRED';
4013: 'INVALID_INTENTS';
4014: 'DISALLOWED_INTENTS';
}; };
Events: ConstantsEvents; Events: ConstantsEvents;
ShardEvents: ConstantsShardEvents; ShardEvents: ConstantsShardEvents;
@ -3679,6 +3692,7 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields {
readonly lastMessage: Message | null; readonly lastMessage: Message | null;
lastPinTimestamp: number | null; lastPinTimestamp: number | null;
readonly lastPinAt: Date | null; readonly lastPinAt: Date | null;
messages: MessageManager;
awaitMessageComponent<T extends MessageComponentTypeResolvable = 'ACTION_ROW'>( awaitMessageComponent<T extends MessageComponentTypeResolvable = 'ACTION_ROW'>(
options?: AwaitMessageCollectorOptionsParams<T, true>, options?: AwaitMessageCollectorOptionsParams<T, true>,
): Promise<MappedInteractionTypes[T]>; ): Promise<MappedInteractionTypes[T]>;
@ -3691,6 +3705,10 @@ export interface TextBasedChannelFields extends PartialTextBasedChannelFields {
options?: MessageChannelCollectorOptionsParams<T, true>, options?: MessageChannelCollectorOptionsParams<T, true>,
): InteractionCollector<MappedInteractionTypes[T]>; ): InteractionCollector<MappedInteractionTypes[T]>;
createMessageCollector(options?: MessageCollectorOptions): MessageCollector; 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>; sendTyping(): Promise<void>;
} }
@ -4017,7 +4035,7 @@ export interface ClientEvents extends BaseClientEvents {
userUpdate: [oldUser: User | PartialUser, newUser: User]; userUpdate: [oldUser: User | PartialUser, newUser: User];
userSettingsUpdate: [setting: RawUserSettingsData]; userSettingsUpdate: [setting: RawUserSettingsData];
voiceStateUpdate: [oldState: VoiceState, newState: VoiceState]; voiceStateUpdate: [oldState: VoiceState, newState: VoiceState];
webhookUpdate: [channel: TextChannel | NewsChannel]; webhookUpdate: [channel: TextChannel | NewsChannel | VoiceChannel];
/** @deprecated Use interactionCreate instead */ /** @deprecated Use interactionCreate instead */
interaction: [interaction: Interaction]; interaction: [interaction: Interaction];
interactionCreate: [interaction: Interaction | { nonce: Snowflake; id: Snowflake }]; interactionCreate: [interaction: Interaction | { nonce: Snowflake; id: Snowflake }];
@ -4192,6 +4210,7 @@ export interface RawUserSettingsData {
export interface ClientOptions { export interface ClientOptions {
shards?: number | number[] | 'auto'; shards?: number | number[] | 'auto';
shardCount?: number; shardCount?: number;
closeTimeout?: number;
makeCache?: CacheFactory; makeCache?: CacheFactory;
/** @deprecated Pass the value of this property as `lifetime` to `sweepers.messages` instead. */ /** @deprecated Pass the value of this property as `lifetime` to `sweepers.messages` instead. */
messageCacheLifetime?: number; messageCacheLifetime?: number;
@ -4596,34 +4615,6 @@ export interface ClientFetchInviteOptions {
guildScheduledEventId?: Snowflake; 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 type ClientPresenceStatus = 'online' | 'idle' | 'dnd';
export interface ClientPresenceStatusData { export interface ClientPresenceStatusData {